2
// banshee-player-vis.c
5
// Chris Howie <cdhowie@gmail.com>
7
// Copyright (C) 2008 Chris Howie
9
// Permission is hereby granted, free of charge, to any person obtaining
10
// a copy of this software and associated documentation files (the
11
// "Software"), to deal in the Software without restriction, including
12
// without limitation the rights to use, copy, modify, merge, publish,
13
// distribute, sublicense, and/or sell copies of the Software, and to
14
// permit persons to whom the Software is furnished to do so, subject to
15
// the following conditions:
17
// The above copyright notice and this permission notice shall be
18
// included in all copies or substantial portions of the Software.
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31
#include "banshee-player-vis.h"
33
#define SLICE_SIZE 735
35
static GstStaticCaps vis_data_sink_caps = GST_STATIC_CAPS (
37
"rate = (int) 44100, "
38
"channels = (int) 2, "
39
"endianness = (int) BYTE_ORDER, "
43
// ---------------------------------------------------------------------------
45
// ---------------------------------------------------------------------------
48
bp_vis_pcm_handoff (GstElement *sink, GstBuffer *buffer, GstPad *pad, gpointer userdata)
50
BansheePlayer *player = (BansheePlayer*)userdata;
51
GstStructure *structure;
52
gint channels, wanted_size;
54
BansheePlayerVisDataCallback vis_data_cb;
56
g_return_if_fail (IS_BANSHEE_PLAYER (player));
58
vis_data_cb = player->vis_data_cb;
60
if (vis_data_cb == NULL) {
64
if (player->vis_thawing) {
65
// Flush our buffers out.
66
gst_adapter_clear (player->vis_buffer);
67
memset (player->vis_fft_sample_buffer, 0, sizeof(gfloat) * SLICE_SIZE);
69
player->vis_thawing = FALSE;
72
structure = gst_caps_get_structure (gst_buffer_get_caps (buffer), 0);
73
gst_structure_get_int (structure, "channels", &channels);
75
wanted_size = channels * SLICE_SIZE * sizeof (gfloat);
77
gst_adapter_push (player->vis_buffer, gst_buffer_copy (buffer));
79
while ((data = (gfloat *)gst_adapter_peek (player->vis_buffer, wanted_size)) != NULL) {
80
gfloat *deinterlaced = g_malloc (wanted_size);
81
gfloat *specbuf = g_new (gfloat, SLICE_SIZE * 2);
85
memcpy (specbuf, player->vis_fft_sample_buffer, SLICE_SIZE * sizeof(gfloat));
87
for (i = 0; i < SLICE_SIZE; i++) {
90
for (j = 0; j < channels; j++) {
91
gfloat sample = data[i * channels + j];
93
deinterlaced[j * SLICE_SIZE + i] = sample;
98
specbuf[i + SLICE_SIZE] = avg;
101
memcpy (player->vis_fft_sample_buffer, &specbuf[SLICE_SIZE], SLICE_SIZE * sizeof(gfloat));
103
gst_fft_f32_window (player->vis_fft, specbuf, GST_FFT_WINDOW_HAMMING);
104
gst_fft_f32_fft (player->vis_fft, specbuf, player->vis_fft_buffer);
106
for (i = 0; i < SLICE_SIZE; i++) {
109
GstFFTF32Complex cplx = player->vis_fft_buffer[i];
111
val = cplx.r * cplx.r + cplx.i * cplx.i;
112
val /= SLICE_SIZE * SLICE_SIZE;
113
val = 10.0f * log10f(val);
115
val = (val + 60.0f) / 60.0f;
122
vis_data_cb (player, channels, SLICE_SIZE, deinterlaced, SLICE_SIZE, specbuf);
124
g_free (deinterlaced);
127
gst_adapter_flush (player->vis_buffer, wanted_size);
131
// ---------------------------------------------------------------------------
132
// Internal Functions
133
// ---------------------------------------------------------------------------
136
_bp_vis_pipeline_block_callback (GstPad *pad, gboolean blocked, gpointer data)
138
BansheePlayer *player = (BansheePlayer *) data;
141
// Set thawing mode (discards buffers that are too old from the queue).
142
player->vis_thawing = TRUE;
147
_bp_vis_pipeline_set_blocked (BansheePlayer *player, gboolean blocked)
151
if (player->vis_resampler == NULL)
154
queue_sink = gst_element_get_static_pad (player->vis_resampler, "src");
156
gst_pad_set_blocked_async (queue_sink, blocked, _bp_vis_pipeline_block_callback, (gpointer) player);
158
gst_object_unref (GST_OBJECT (queue_sink));
162
_bp_vis_pipeline_event_probe (GstPad *pad, GstEvent *event, gpointer data)
164
BansheePlayer *player = (BansheePlayer *) data;
166
switch (GST_EVENT_TYPE (event)) {
167
case GST_EVENT_FLUSH_START:
168
case GST_EVENT_FLUSH_STOP:
170
case GST_EVENT_NEWSEGMENT:
171
case GST_EVENT_CUSTOM_DOWNSTREAM:
172
player->vis_thawing = TRUE;
177
if (player->vis_enabled)
180
switch (GST_EVENT_TYPE (event)) {
182
case GST_EVENT_CUSTOM_DOWNSTREAM_OOB:
183
_bp_vis_pipeline_set_blocked (player, FALSE);
185
case GST_EVENT_CUSTOM_DOWNSTREAM:
186
case GST_EVENT_NEWSEGMENT:
187
_bp_vis_pipeline_set_blocked (player, TRUE);
197
_bp_vis_pipeline_setup (BansheePlayer *player)
199
// The basic pipeline we're constructing is:
200
// .audiotee ! queue ! audioresample ! audioconvert ! fakesink
202
GstElement *fakesink, *converter, *resampler, *audiosinkqueue;
208
player->vis_buffer = NULL;
209
player->vis_fft = gst_fft_f32_new (SLICE_SIZE * 2, FALSE);
210
player->vis_fft_buffer = g_new (GstFFTF32Complex, SLICE_SIZE + 1);
211
player->vis_fft_sample_buffer = g_new0 (gfloat, SLICE_SIZE);
213
// Core elements, if something fails here, it's the end of the world
214
audiosinkqueue = gst_element_factory_make ("queue", "vis-queue");
216
pad = gst_element_get_static_pad (audiosinkqueue, "sink");
217
gst_pad_add_event_probe (pad, G_CALLBACK (_bp_vis_pipeline_event_probe), player);
218
gst_object_unref (GST_OBJECT (pad));
220
resampler = gst_element_factory_make ("audioresample", "vis-resample");
221
converter = gst_element_factory_make ("audioconvert", "vis-convert");
222
fakesink = gst_element_factory_make ("fakesink", "vis-sink");
224
// channels * slice size * float size = size of chunks we want
225
wanted_size = 2 * SLICE_SIZE * sizeof(gfloat);
227
if (audiosinkqueue == NULL || resampler == NULL || converter == NULL || fakesink == NULL) {
228
bp_debug ("Could not construct visualization pipeline, a fundamental element could not be created");
232
// Keep around the 5 most recent seconds of audio so that when resuming
233
// visualization we have something to show right away.
234
g_object_set (G_OBJECT (audiosinkqueue),
236
"max-size-buffers", 0,
238
"max-size-time", GST_SECOND * 5,
241
g_signal_connect (G_OBJECT (fakesink), "handoff", G_CALLBACK (bp_vis_pcm_handoff), player);
243
g_object_set (G_OBJECT (fakesink),
244
// This enables the handoff signal.
245
"signal-handoffs", TRUE,
246
// Synchronize so we see vis at the same time as we hear it.
248
// Drop buffers if they come in too late. This is mainly used when
249
// thawing the vis pipeline.
250
"max-lateness", GST_SECOND / 120,
251
// Deliver buffers one frame early. This allows for rendering
252
// time. (TODO: It would be great to calculate this on-the-fly so
253
// we match the rendering time.
254
"ts-offset", -GST_SECOND / 60,
255
// Don't go to PAUSED when we freeze the pipeline.
256
"async", FALSE, NULL);
258
gst_bin_add_many (GST_BIN (player->audiobin), audiosinkqueue, resampler,
259
converter, fakesink, NULL);
261
pad = gst_element_get_static_pad (audiosinkqueue, "sink");
262
teepad = gst_element_get_request_pad (player->audiotee, "src%d");
263
gst_pad_link (teepad, pad);
264
gst_object_unref (GST_OBJECT (teepad));
265
gst_object_unref (GST_OBJECT (pad));
267
gst_element_link_many (audiosinkqueue, resampler, converter, NULL);
269
caps = gst_static_caps_get (&vis_data_sink_caps);
270
gst_element_link_filtered (converter, fakesink, caps);
271
gst_caps_unref (caps);
273
player->vis_buffer = gst_adapter_new ();
274
player->vis_resampler = resampler;
275
player->vis_thawing = FALSE;
276
player->vis_enabled = FALSE;
278
// Disable the pipeline till we hear otherwise from managed land.
279
_bp_vis_pipeline_set_blocked (player, TRUE);
283
_bp_vis_pipeline_destroy (BansheePlayer *player)
285
if (player->vis_buffer != NULL) {
286
gst_object_unref (player->vis_buffer);
287
player->vis_buffer = NULL;
290
if (player->vis_fft != NULL) {
291
gst_fft_f32_free (player->vis_fft);
292
player->vis_fft = NULL;
295
if (player->vis_fft_buffer != NULL) {
296
g_free (player->vis_fft_buffer);
297
player->vis_fft_buffer = NULL;
300
if (player->vis_fft_sample_buffer != NULL) {
301
g_free (player->vis_fft_sample_buffer);
302
player->vis_fft_sample_buffer = NULL;
305
player->vis_resampler = NULL;
306
player->vis_enabled = FALSE;
307
player->vis_thawing = FALSE;
310
// ---------------------------------------------------------------------------
312
// ---------------------------------------------------------------------------
315
bp_set_vis_data_callback (BansheePlayer *player, BansheePlayerVisDataCallback cb)
320
player->vis_data_cb = cb;
322
_bp_vis_pipeline_set_blocked (player, cb == NULL);
323
player->vis_enabled = cb != NULL;