50 #include <gst/audio/audio.h> |
46 #include <gst/audio/audio.h> |
51 #include <gst/interfaces/mixer.h> |
47 #include <gst/interfaces/mixer.h> |
52 #include <gst/controller/gstcontroller.h> |
48 #include <gst/controller/gstcontroller.h> |
53 #include <gst/audio/audio.h> |
49 #include <gst/audio/audio.h> |
54 #include <gst/audio/gstaudiofilter.h> |
50 #include <gst/audio/gstaudiofilter.h> |
55 |
51 #include <liboil/liboil.h> |
56 #include <gst/liboil.h> |
52 #ifdef __SYMBIAN32__ |
57 |
53 #include <liboil/globals.h> |
|
54 #endif |
58 #include "gstvolume.h" |
55 #include "gstvolume.h" |
59 |
56 |
60 /* some defines for audio processing */ |
57 /* some defines for audio processing */ |
61 /* the volume factor is a range from 0.0 to (arbitrary) VOLUME_MAX_DOUBLE = 10.0 |
58 /* the volume factor is a range from 0.0 to (arbitrary) VOLUME_MAX_DOUBLE = 10.0 |
62 * we map 1.0 to VOLUME_UNITY_INT* |
59 * we map 1.0 to VOLUME_UNITY_INT* |
166 |
160 |
167 static void volume_set_property (GObject * object, guint prop_id, |
161 static void volume_set_property (GObject * object, guint prop_id, |
168 const GValue * value, GParamSpec * pspec); |
162 const GValue * value, GParamSpec * pspec); |
169 static void volume_get_property (GObject * object, guint prop_id, |
163 static void volume_get_property (GObject * object, guint prop_id, |
170 GValue * value, GParamSpec * pspec); |
164 GValue * value, GParamSpec * pspec); |
171 static void volume_update_volume (const GValue * value, gpointer data); |
165 |
172 static void volume_update_mute (const GValue * value, gpointer data); |
166 static void volume_before_transform (GstBaseTransform * base, |
173 |
167 GstBuffer * buffer); |
174 static GstFlowReturn volume_transform_ip (GstBaseTransform * base, |
168 static GstFlowReturn volume_transform_ip (GstBaseTransform * base, |
175 GstBuffer * outbuf); |
169 GstBuffer * outbuf); |
176 static gboolean volume_setup (GstAudioFilter * filter, |
170 static gboolean volume_setup (GstAudioFilter * filter, |
177 GstRingBufferSpec * format); |
171 GstRingBufferSpec * format); |
178 |
172 |
211 switch (GST_AUDIO_FILTER (this)->format.type) { |
205 switch (GST_AUDIO_FILTER (this)->format.type) { |
212 case GST_BUFTYPE_LINEAR: |
206 case GST_BUFTYPE_LINEAR: |
213 switch (GST_AUDIO_FILTER (this)->format.width) { |
207 switch (GST_AUDIO_FILTER (this)->format.width) { |
214 case 32: |
208 case 32: |
215 /* only clamp if the gain is greater than 1.0 |
209 /* only clamp if the gain is greater than 1.0 |
216 * FIXME: real_vol_i can change while processing the buffer! |
210 * FIXME: current_vol_i can change while processing the buffer! |
217 */ |
211 */ |
218 if (this->real_vol_i32 > VOLUME_UNITY_INT32) |
212 if (this->current_vol_i32 > VOLUME_UNITY_INT32) |
219 this->process = volume_process_int32_clamp; |
213 this->process = volume_process_int32_clamp; |
220 else |
214 else |
221 this->process = volume_process_int32; |
215 this->process = volume_process_int32; |
222 break; |
216 break; |
223 case 24: |
217 case 24: |
224 /* only clamp if the gain is greater than 1.0 |
218 /* only clamp if the gain is greater than 1.0 |
225 * FIXME: real_vol_i can change while processing the buffer! |
219 * FIXME: current_vol_i can change while processing the buffer! |
226 */ |
220 */ |
227 if (this->real_vol_i24 > VOLUME_UNITY_INT24) |
221 if (this->current_vol_i24 > VOLUME_UNITY_INT24) |
228 this->process = volume_process_int24_clamp; |
222 this->process = volume_process_int24_clamp; |
229 else |
223 else |
230 this->process = volume_process_int24; |
224 this->process = volume_process_int24; |
231 break; |
225 break; |
232 case 16: |
226 case 16: |
233 /* only clamp if the gain is greater than 1.0 |
227 /* only clamp if the gain is greater than 1.0 |
234 * FIXME: real_vol_i can change while processing the buffer! |
228 * FIXME: current_vol_i can change while processing the buffer! |
235 */ |
229 */ |
236 if (this->real_vol_i16 > VOLUME_UNITY_INT16) |
230 if (this->current_vol_i16 > VOLUME_UNITY_INT16) |
237 this->process = volume_process_int16_clamp; |
231 this->process = volume_process_int16_clamp; |
238 else |
232 else |
239 this->process = volume_process_int16; |
233 this->process = volume_process_int16; |
240 break; |
234 break; |
241 case 8: |
235 case 8: |
242 /* only clamp if the gain is greater than 1.0 |
236 /* only clamp if the gain is greater than 1.0 |
243 * FIXME: real_vol_i can change while processing the buffer! |
237 * FIXME: current_vol_i can change while processing the buffer! |
244 */ |
238 */ |
245 if (this->real_vol_i16 > VOLUME_UNITY_INT8) |
239 if (this->current_vol_i16 > VOLUME_UNITY_INT8) |
246 this->process = volume_process_int8_clamp; |
240 this->process = volume_process_int8_clamp; |
247 else |
241 else |
248 this->process = volume_process_int8; |
242 this->process = volume_process_int8; |
249 break; |
243 break; |
250 } |
244 } |
264 } |
258 } |
265 |
259 |
266 return (this->process != NULL); |
260 return (this->process != NULL); |
267 } |
261 } |
268 |
262 |
269 static void |
263 static gboolean |
270 volume_update_real_volume (GstVolume * this) |
264 volume_update_volume (GstVolume * this, gfloat volume, gboolean mute) |
271 { |
265 { |
272 gboolean passthrough = FALSE; |
266 gboolean passthrough; |
273 |
267 gboolean res; |
274 if (this->mute) { |
268 |
275 this->real_vol_f = 0.0; |
269 GST_DEBUG_OBJECT (this, "configure mute %d, volume %f", mute, volume); |
276 this->real_vol_i8 = this->real_vol_i16 = this->real_vol_i24 = |
270 |
277 this->real_vol_i32 = 0; |
271 if (mute) { |
|
272 this->current_mute = TRUE; |
|
273 this->current_volume = 0.0; |
|
274 |
|
275 this->current_vol_i8 = 0; |
|
276 this->current_vol_i16 = 0; |
|
277 this->current_vol_i24 = 0; |
|
278 this->current_vol_i32 = 0; |
|
279 |
|
280 passthrough = FALSE; |
278 } else { |
281 } else { |
279 this->real_vol_f = this->volume_f; |
282 this->current_mute = FALSE; |
280 this->real_vol_i8 = this->volume_i8; |
283 this->current_volume = volume; |
281 this->real_vol_i16 = this->volume_i16; |
284 |
282 this->real_vol_i24 = this->volume_i24; |
285 this->current_vol_i8 = volume * VOLUME_UNITY_INT8; |
283 this->real_vol_i32 = this->volume_i32; |
286 this->current_vol_i16 = volume * VOLUME_UNITY_INT16; |
284 passthrough = (this->volume_i16 == VOLUME_UNITY_INT16); |
287 this->current_vol_i24 = volume * VOLUME_UNITY_INT24; |
285 } |
288 this->current_vol_i32 = volume * VOLUME_UNITY_INT32; |
286 if (this->real_vol_f != 0.0) |
289 |
287 this->silent_buffer = FALSE; |
290 passthrough = (this->current_vol_i16 == VOLUME_UNITY_INT16); |
288 volume_choose_func (this); |
291 } |
|
292 |
|
293 GST_DEBUG_OBJECT (this, "set passthrough %d", passthrough); |
|
294 |
289 gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (this), passthrough); |
295 gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (this), passthrough); |
|
296 |
|
297 res = this->negotiated = volume_choose_func (this); |
|
298 |
|
299 return res; |
290 } |
300 } |
291 |
301 |
292 /* Mixer interface */ |
302 /* Mixer interface */ |
293 |
303 |
294 static gboolean |
304 static gboolean |
295 gst_volume_interface_supported (GstImplementsInterface * iface, GType type) |
305 gst_volume_interface_supported (GstImplementsInterface * iface, GType type) |
296 { |
306 { |
297 g_assert (type == GST_TYPE_MIXER); |
307 g_return_val_if_fail (type == GST_TYPE_MIXER, FALSE); |
298 return TRUE; |
308 return TRUE; |
299 } |
309 } |
300 |
310 |
301 static void |
311 static void |
302 gst_volume_interface_init (GstImplementsInterfaceClass * klass) |
312 gst_volume_interface_init (GstImplementsInterfaceClass * klass) |
321 GstVolume *this = GST_VOLUME (mixer); |
331 GstVolume *this = GST_VOLUME (mixer); |
322 |
332 |
323 g_return_if_fail (this != NULL); |
333 g_return_if_fail (this != NULL); |
324 g_return_if_fail (GST_IS_VOLUME (this)); |
334 g_return_if_fail (GST_IS_VOLUME (this)); |
325 |
335 |
326 this->volume_f = (gfloat) volumes[0] / VOLUME_STEPS; |
336 GST_OBJECT_LOCK (this); |
327 this->volume_i32 = this->volume_f * VOLUME_UNITY_INT32; |
337 this->volume = (gfloat) volumes[0] / VOLUME_STEPS; |
328 this->volume_i24 = this->volume_f * VOLUME_UNITY_INT24; |
338 GST_OBJECT_UNLOCK (this); |
329 this->volume_i16 = this->volume_f * VOLUME_UNITY_INT16; |
|
330 this->volume_i8 = this->volume_f * VOLUME_UNITY_INT8; |
|
331 |
|
332 volume_update_real_volume (this); |
|
333 } |
339 } |
334 |
340 |
335 static void |
341 static void |
336 gst_volume_get_volume (GstMixer * mixer, GstMixerTrack * track, gint * volumes) |
342 gst_volume_get_volume (GstMixer * mixer, GstMixerTrack * track, gint * volumes) |
337 { |
343 { |
338 GstVolume *this = GST_VOLUME (mixer); |
344 GstVolume *this = GST_VOLUME (mixer); |
339 |
345 |
340 g_return_if_fail (this != NULL); |
346 g_return_if_fail (this != NULL); |
341 g_return_if_fail (GST_IS_VOLUME (this)); |
347 g_return_if_fail (GST_IS_VOLUME (this)); |
342 |
348 |
343 volumes[0] = (gint) this->volume_f * VOLUME_STEPS; |
349 GST_OBJECT_LOCK (this); |
|
350 volumes[0] = (gint) this->volume * VOLUME_STEPS; |
|
351 GST_OBJECT_UNLOCK (this); |
344 } |
352 } |
345 |
353 |
346 static void |
354 static void |
347 gst_volume_set_mute (GstMixer * mixer, GstMixerTrack * track, gboolean mute) |
355 gst_volume_set_mute (GstMixer * mixer, GstMixerTrack * track, gboolean mute) |
348 { |
356 { |
349 GstVolume *this = GST_VOLUME (mixer); |
357 GstVolume *this = GST_VOLUME (mixer); |
350 |
358 |
351 g_return_if_fail (this != NULL); |
359 g_return_if_fail (this != NULL); |
352 g_return_if_fail (GST_IS_VOLUME (this)); |
360 g_return_if_fail (GST_IS_VOLUME (this)); |
353 |
361 |
|
362 GST_OBJECT_LOCK (this); |
354 this->mute = mute; |
363 this->mute = mute; |
355 |
364 GST_OBJECT_UNLOCK (this); |
356 volume_update_real_volume (this); |
|
357 } |
365 } |
358 |
366 |
359 static void |
367 static void |
360 gst_volume_mixer_init (GstMixerClass * klass) |
368 gst_volume_mixer_init (GstMixerClass * klass) |
361 { |
369 { |
390 { |
398 { |
391 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); |
399 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); |
392 GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS (g_class); |
400 GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS (g_class); |
393 GstCaps *caps; |
401 GstCaps *caps; |
394 |
402 |
395 gst_element_class_set_details (element_class, &volume_details); |
403 gst_element_class_set_details_simple (element_class, "Volume", |
|
404 "Filter/Effect/Audio", |
|
405 "Set volume on audio/raw streams", "Andy Wingo <wingo@pobox.com>"); |
396 |
406 |
397 caps = gst_caps_from_string (ALLOWED_CAPS); |
407 caps = gst_caps_from_string (ALLOWED_CAPS); |
398 gst_audio_filter_class_add_pad_templates (filter_class, caps); |
408 gst_audio_filter_class_add_pad_templates (filter_class, caps); |
399 gst_caps_unref (caps); |
409 gst_caps_unref (caps); |
400 } |
410 } |
414 gobject_class->get_property = volume_get_property; |
424 gobject_class->get_property = volume_get_property; |
415 gobject_class->dispose = gst_volume_dispose; |
425 gobject_class->dispose = gst_volume_dispose; |
416 |
426 |
417 g_object_class_install_property (gobject_class, PROP_MUTE, |
427 g_object_class_install_property (gobject_class, PROP_MUTE, |
418 g_param_spec_boolean ("mute", "Mute", "mute channel", |
428 g_param_spec_boolean ("mute", "Mute", "mute channel", |
419 FALSE, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); |
429 DEFAULT_PROP_MUTE, |
|
430 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); |
420 |
431 |
421 g_object_class_install_property (gobject_class, PROP_VOLUME, |
432 g_object_class_install_property (gobject_class, PROP_VOLUME, |
422 g_param_spec_double ("volume", "Volume", "volume factor", |
433 g_param_spec_double ("volume", "Volume", "volume factor, 1.0=100%", |
423 0.0, VOLUME_MAX_DOUBLE, 1.0, |
434 0.0, VOLUME_MAX_DOUBLE, DEFAULT_PROP_VOLUME, |
424 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); |
435 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); |
425 |
436 |
426 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "volume", 0, "Volume gain"); |
437 trans_class->before_transform = GST_DEBUG_FUNCPTR (volume_before_transform); |
427 |
|
428 trans_class->transform_ip = GST_DEBUG_FUNCPTR (volume_transform_ip); |
438 trans_class->transform_ip = GST_DEBUG_FUNCPTR (volume_transform_ip); |
429 filter_class->setup = GST_DEBUG_FUNCPTR (volume_setup); |
439 filter_class->setup = GST_DEBUG_FUNCPTR (volume_setup); |
430 } |
440 } |
431 |
441 |
432 static void |
442 static void |
433 gst_volume_init (GstVolume * this, GstVolumeClass * g_class) |
443 gst_volume_init (GstVolume * this, GstVolumeClass * g_class) |
434 { |
444 { |
435 GstMixerTrack *track = NULL; |
445 GstMixerTrack *track = NULL; |
436 |
446 |
437 this->mute = FALSE; |
447 this->mute = DEFAULT_PROP_MUTE;; |
438 this->volume_i8 = this->real_vol_i8 = VOLUME_UNITY_INT8; |
448 this->volume = DEFAULT_PROP_VOLUME; |
439 this->volume_i16 = this->real_vol_i16 = VOLUME_UNITY_INT16; |
449 |
440 this->volume_i24 = this->real_vol_i24 = VOLUME_UNITY_INT24; |
|
441 this->volume_i32 = this->real_vol_i32 = VOLUME_UNITY_INT32; |
|
442 this->volume_f = this->real_vol_f = 1.0; |
|
443 this->tracklist = NULL; |
450 this->tracklist = NULL; |
|
451 this->negotiated = FALSE; |
444 |
452 |
445 track = g_object_new (GST_TYPE_MIXER_TRACK, NULL); |
453 track = g_object_new (GST_TYPE_MIXER_TRACK, NULL); |
446 |
454 |
447 if (GST_IS_MIXER_TRACK (track)) { |
455 if (GST_IS_MIXER_TRACK (track)) { |
448 track->label = g_strdup ("volume"); |
456 track->label = g_strdup ("volume"); |
454 } |
462 } |
455 |
463 |
456 gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (this), TRUE); |
464 gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (this), TRUE); |
457 } |
465 } |
458 |
466 |
459 /* NOTE: although it might be tempting to have volume_process_mute() which uses |
|
460 * memset(bytes, 0, nbytes) for the vol=0 case, this has the downside that |
|
461 * unmuting would only take place after processing a buffer. |
|
462 */ |
|
463 |
|
464 static void |
467 static void |
465 volume_process_double (GstVolume * this, gpointer bytes, guint n_bytes) |
468 volume_process_double (GstVolume * this, gpointer bytes, guint n_bytes) |
466 { |
469 { |
467 gdouble *data = (gdouble *) bytes; |
470 gdouble *data = (gdouble *) bytes; |
468 guint i, num_samples = n_bytes / sizeof (gdouble); |
471 guint num_samples = n_bytes / sizeof (gdouble); |
|
472 |
|
473 gdouble vol = this->current_volume; |
|
474 |
|
475 oil_scalarmultiply_f64_ns (data, data, &vol, num_samples); |
|
476 } |
|
477 |
|
478 static void |
|
479 volume_process_float (GstVolume * this, gpointer bytes, guint n_bytes) |
|
480 { |
|
481 gfloat *data = (gfloat *) bytes; |
|
482 guint num_samples = n_bytes / sizeof (gfloat); |
|
483 |
|
484 #if 0 |
|
485 guint i; |
469 |
486 |
470 for (i = 0; i < num_samples; i++) { |
487 for (i = 0; i < num_samples; i++) { |
471 *data++ *= this->real_vol_f; |
488 *data++ *= this->real_vol_f; |
472 } |
489 } |
473 } |
490 /* time "gst-launch 2>/dev/null audiotestsrc wave=7 num-buffers=10000 ! audio/x-raw-float ! |
474 |
491 * volume volume=1.5 ! fakesink" goes from 0m0.850s -> 0m0.717s with liboil |
475 static void |
|
476 volume_process_float (GstVolume * this, gpointer bytes, guint n_bytes) |
|
477 { |
|
478 gfloat *data = (gfloat *) bytes; |
|
479 guint num_samples = n_bytes / sizeof (gfloat); |
|
480 |
|
481 /* |
|
482 guint i; |
|
483 for (i = 0; i < num_samples; i++) { |
|
484 *data++ *= this->real_vol_f; |
|
485 } |
|
486 */ |
492 */ |
487 /* time gst-launch 2>/dev/null audiotestsrc wave=7 num-buffers=10000 ! audio/x-raw-float ! volume volume=1.5 ! fakesink |
493 #endif |
488 * goes from 0m0.850s -> 0m0.717s with liboil |
494 oil_scalarmultiply_f32_ns (data, data, &this->current_volume, num_samples); |
489 */ |
|
490 oil_scalarmultiply_f32_ns (data, data, &this->real_vol_f, num_samples); |
|
491 } |
495 } |
492 |
496 |
493 static void |
497 static void |
494 volume_process_int32 (GstVolume * this, gpointer bytes, guint n_bytes) |
498 volume_process_int32 (GstVolume * this, gpointer bytes, guint n_bytes) |
495 { |
499 { |
516 num_samples = n_bytes / sizeof (gint); |
522 num_samples = n_bytes / sizeof (gint); |
517 |
523 |
518 for (i = 0; i < num_samples; i++) { |
524 for (i = 0; i < num_samples; i++) { |
519 /* we use bitshifting instead of dividing by UNITY_INT for speed */ |
525 /* we use bitshifting instead of dividing by UNITY_INT for speed */ |
520 val = (gint64) * data; |
526 val = (gint64) * data; |
521 val = (((gint64) this->real_vol_i32 * val) >> VOLUME_UNITY_INT32_BIT_SHIFT); |
527 val = |
|
528 (((gint64) this->current_vol_i32 * |
|
529 val) >> VOLUME_UNITY_INT32_BIT_SHIFT); |
522 *data++ = (gint32) CLAMP (val, VOLUME_MIN_INT32, VOLUME_MAX_INT32); |
530 *data++ = (gint32) CLAMP (val, VOLUME_MIN_INT32, VOLUME_MAX_INT32); |
523 } |
531 } |
524 } |
532 } |
525 |
533 |
526 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN) |
534 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN) |
527 #define get_unaligned_i24(_x) ( (((guint8*)_x)[0]) | ((((guint8*)_x)[1]) << 8) | ((((gint8*)_x)[2]) << 16) ) |
535 #define get_unaligned_i24(_x) ( (((guint8*)_x)[0]) | ((((guint8*)_x)[1]) << 8) | ((((gint8*)_x)[2]) << 16) ) |
528 #define write_unaligned_u24(_x,samp) do { *(_x)++ = samp & 0xFF; *(_x)++ = (samp >> 8) & 0xFF; *(_x)++ = (samp >> 16) & 0xFF; } while (0) |
536 |
|
537 #define write_unaligned_u24(_x,samp) \ |
|
538 G_STMT_START { \ |
|
539 *(_x)++ = samp & 0xFF; \ |
|
540 *(_x)++ = (samp >> 8) & 0xFF; \ |
|
541 *(_x)++ = (samp >> 16) & 0xFF; \ |
|
542 } G_STMT_END |
|
543 |
529 #else /* BIG ENDIAN */ |
544 #else /* BIG ENDIAN */ |
530 #define get_unaligned_i24(_x) ( (((guint8*)_x)[2]) | ((((guint8*)_x)[1]) << 8) | ((((gint8*)_x)[0]) << 16) ) |
545 #define get_unaligned_i24(_x) ( (((guint8*)_x)[2]) | ((((guint8*)_x)[1]) << 8) | ((((gint8*)_x)[0]) << 16) ) |
531 #define write_unaligned_u24(_x,samp) do { *(_x)++ = (samp >> 16) & 0xFF; *(_x)++ = (samp >> 8) & 0xFF; *(_x)++ = samp & 0xFF; } while (0) |
546 #define write_unaligned_u24(_x,samp) \ |
|
547 G_STMT_START { \ |
|
548 *(_x)++ = (samp >> 16) & 0xFF; \ |
|
549 *(_x)++ = (samp >> 8) & 0xFF; \ |
|
550 *(_x)++ = samp & 0xFF; \ |
|
551 } G_STMT_END |
532 #endif |
552 #endif |
533 |
553 |
534 static void |
554 static void |
535 volume_process_int24 (GstVolume * this, gpointer bytes, guint n_bytes) |
555 volume_process_int24 (GstVolume * this, gpointer bytes, guint n_bytes) |
536 { |
556 { |
563 num_samples = n_bytes / (sizeof (gint8) * 3); |
585 num_samples = n_bytes / (sizeof (gint8) * 3); |
564 for (i = 0; i < num_samples; i++) { |
586 for (i = 0; i < num_samples; i++) { |
565 samp = get_unaligned_i24 (data); |
587 samp = get_unaligned_i24 (data); |
566 |
588 |
567 val = (gint32) samp; |
589 val = (gint32) samp; |
568 val = (((gint64) this->real_vol_i24 * val) >> VOLUME_UNITY_INT24_BIT_SHIFT); |
590 val = |
|
591 (((gint64) this->current_vol_i24 * |
|
592 val) >> VOLUME_UNITY_INT24_BIT_SHIFT); |
569 samp = (guint32) CLAMP (val, VOLUME_MIN_INT24, VOLUME_MAX_INT24); |
593 samp = (guint32) CLAMP (val, VOLUME_MIN_INT24, VOLUME_MAX_INT24); |
570 |
594 |
571 /* write the value back into the stream */ |
595 /* write the value back into the stream */ |
572 write_unaligned_u24 (data, samp); |
596 write_unaligned_u24 (data, samp); |
573 } |
597 } |
585 |
609 |
586 for (i = 0; i < num_samples; i++) { |
610 for (i = 0; i < num_samples; i++) { |
587 /* we use bitshifting instead of dividing by UNITY_INT for speed */ |
611 /* we use bitshifting instead of dividing by UNITY_INT for speed */ |
588 val = (gint) * data; |
612 val = (gint) * data; |
589 *data++ = |
613 *data++ = |
590 (gint16) ((this->real_vol_i16 * val) >> VOLUME_UNITY_INT16_BIT_SHIFT); |
614 (gint16) ((this->current_vol_i16 * |
|
615 val) >> VOLUME_UNITY_INT16_BIT_SHIFT); |
591 } |
616 } |
592 #else |
617 #else |
593 /* FIXME: need oil_scalarmultiply_s16_ns ? |
618 /* FIXME: need oil_scalarmultiply_s16_ns ? |
594 * https://bugs.freedesktop.org/show_bug.cgi?id=7060 |
619 * https://bugs.freedesktop.org/show_bug.cgi?id=7060 |
595 * code below |
620 * code below |
596 * - crashes :/ |
621 * - crashes :/ |
597 * - real_vol_i is scaled by VOLUME_UNITY_INT16 and needs the bitshift |
622 * - real_vol_i is scaled by VOLUME_UNITY_INT16 and needs the bitshift |
598 * time gst-launch 2>/dev/null audiotestsrc wave=7 num-buffers=100 ! volume volume=1.5 ! fakesink |
623 * time gst-launch 2>/dev/null audiotestsrc wave=7 num-buffers=100 ! volume volume=1.5 ! fakesink |
599 */ |
624 */ |
600 oil_scalarmult_s16 (data, 0, data, 0, |
625 oil_scalarmult_s16 (data, 0, data, 0, |
601 ((gint16 *) (void *) (&this->real_vol_i)), num_samples); |
626 ((gint16 *) (void *) (&this->current_vol_i)), num_samples); |
602 #endif |
627 #endif |
603 } |
628 } |
604 |
629 |
605 static void |
630 static void |
606 volume_process_int16_clamp (GstVolume * this, gpointer bytes, guint n_bytes) |
631 volume_process_int16_clamp (GstVolume * this, gpointer bytes, guint n_bytes) |
663 |
688 |
664 /* get notified of caps and plug in the correct process function */ |
689 /* get notified of caps and plug in the correct process function */ |
665 static gboolean |
690 static gboolean |
666 volume_setup (GstAudioFilter * filter, GstRingBufferSpec * format) |
691 volume_setup (GstAudioFilter * filter, GstRingBufferSpec * format) |
667 { |
692 { |
|
693 gboolean res; |
668 GstVolume *this = GST_VOLUME (filter); |
694 GstVolume *this = GST_VOLUME (filter); |
669 |
695 gfloat volume; |
670 if (volume_choose_func (this)) { |
696 gboolean mute; |
671 return TRUE; |
697 |
672 } else { |
698 GST_OBJECT_LOCK (this); |
|
699 volume = this->volume; |
|
700 mute = this->mute; |
|
701 GST_OBJECT_UNLOCK (this); |
|
702 |
|
703 res = volume_update_volume (this, volume, mute); |
|
704 if (!res) { |
673 GST_ELEMENT_ERROR (this, CORE, NEGOTIATION, |
705 GST_ELEMENT_ERROR (this, CORE, NEGOTIATION, |
674 ("Invalid incoming format"), (NULL)); |
706 ("Invalid incoming format"), (NULL)); |
675 return FALSE; |
707 } |
|
708 this->negotiated = res; |
|
709 |
|
710 return res; |
|
711 } |
|
712 |
|
713 static void |
|
714 volume_before_transform (GstBaseTransform * base, GstBuffer * buffer) |
|
715 { |
|
716 GstClockTime timestamp; |
|
717 GstVolume *this = GST_VOLUME (base); |
|
718 gfloat volume; |
|
719 gboolean mute; |
|
720 |
|
721 /* FIXME: if controllers are bound, subdivide GST_BUFFER_SIZE into small |
|
722 * chunks for smooth fades, what is small? 1/10th sec. |
|
723 */ |
|
724 timestamp = GST_BUFFER_TIMESTAMP (buffer); |
|
725 timestamp = |
|
726 gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp); |
|
727 |
|
728 GST_DEBUG_OBJECT (base, "sync to %" GST_TIME_FORMAT, |
|
729 GST_TIME_ARGS (timestamp)); |
|
730 |
|
731 if (GST_CLOCK_TIME_IS_VALID (timestamp)) |
|
732 gst_object_sync_values (G_OBJECT (this), timestamp); |
|
733 |
|
734 /* get latest values */ |
|
735 GST_OBJECT_LOCK (this); |
|
736 volume = this->volume; |
|
737 mute = this->mute; |
|
738 GST_OBJECT_UNLOCK (this); |
|
739 |
|
740 if ((volume != this->current_volume) || (mute != this->current_mute)) { |
|
741 /* the volume or mute was updated, update our internal state before |
|
742 * we continue processing. */ |
|
743 volume_update_volume (this, volume, mute); |
676 } |
744 } |
677 } |
745 } |
678 |
746 |
679 /* call the plugged-in process function for this instance |
747 /* call the plugged-in process function for this instance |
680 * needs to be done with this indirection since volume_transform is |
748 * needs to be done with this indirection since volume_transform is |
682 */ |
750 */ |
683 static GstFlowReturn |
751 static GstFlowReturn |
684 volume_transform_ip (GstBaseTransform * base, GstBuffer * outbuf) |
752 volume_transform_ip (GstBaseTransform * base, GstBuffer * outbuf) |
685 { |
753 { |
686 GstVolume *this = GST_VOLUME (base); |
754 GstVolume *this = GST_VOLUME (base); |
687 GstClockTime timestamp; |
755 guint8 *data; |
688 |
756 guint size; |
689 /* FIXME: if controllers are bound, subdivide GST_BUFFER_SIZE into small |
757 |
690 * chunks for smooth fades, what is small? 1/10th sec. |
758 if (G_UNLIKELY (!this->negotiated)) |
691 */ |
759 goto not_negotiated; |
692 timestamp = GST_BUFFER_TIMESTAMP (outbuf); |
|
693 timestamp = |
|
694 gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp); |
|
695 |
|
696 GST_DEBUG_OBJECT (base, "sync to %" GST_TIME_FORMAT, |
|
697 GST_TIME_ARGS (timestamp)); |
|
698 |
|
699 if (GST_CLOCK_TIME_IS_VALID (timestamp)) |
|
700 gst_object_sync_values (G_OBJECT (this), timestamp); |
|
701 |
760 |
702 /* don't process data in passthrough-mode */ |
761 /* don't process data in passthrough-mode */ |
703 if (gst_base_transform_is_passthrough (base) || |
762 if (gst_base_transform_is_passthrough (base) || |
704 GST_BUFFER_FLAG_IS_SET (outbuf, GST_BUFFER_FLAG_GAP)) |
763 GST_BUFFER_FLAG_IS_SET (outbuf, GST_BUFFER_FLAG_GAP)) |
705 return GST_FLOW_OK; |
764 return GST_FLOW_OK; |
706 |
765 |
707 if (this->real_vol_f == 0.0) |
766 data = GST_BUFFER_DATA (outbuf); |
708 this->silent_buffer = TRUE; |
767 size = GST_BUFFER_SIZE (outbuf); |
709 |
768 |
710 this->process (this, GST_BUFFER_DATA (outbuf), GST_BUFFER_SIZE (outbuf)); |
769 if (this->current_volume == 0.0) { |
711 |
770 memset (data, 0, size); |
712 if (this->silent_buffer) |
|
713 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP); |
771 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP); |
714 this->silent_buffer = FALSE; |
772 } else if (this->current_volume != 1.0) { |
|
773 this->process (this, data, size); |
|
774 } |
715 |
775 |
716 return GST_FLOW_OK; |
776 return GST_FLOW_OK; |
717 } |
777 |
718 |
778 /* ERRORS */ |
719 static void |
779 not_negotiated: |
720 volume_update_mute (const GValue * value, gpointer data) |
780 { |
721 { |
781 GST_ELEMENT_ERROR (this, CORE, NEGOTIATION, |
722 GstVolume *this = (GstVolume *) data; |
782 ("No format was negotiated"), (NULL)); |
723 |
783 return GST_FLOW_NOT_NEGOTIATED; |
724 g_return_if_fail (GST_IS_VOLUME (this)); |
784 } |
725 |
|
726 if (G_VALUE_HOLDS_BOOLEAN (value)) { |
|
727 this->mute = g_value_get_boolean (value); |
|
728 } else if (G_VALUE_HOLDS_INT (value)) { |
|
729 this->mute = (g_value_get_int (value) == 1); |
|
730 } |
|
731 |
|
732 volume_update_real_volume (this); |
|
733 } |
|
734 |
|
735 static void |
|
736 volume_update_volume (const GValue * value, gpointer data) |
|
737 { |
|
738 GstVolume *this = (GstVolume *) data; |
|
739 |
|
740 g_return_if_fail (GST_IS_VOLUME (this)); |
|
741 |
|
742 this->volume_f = g_value_get_double (value); |
|
743 this->volume_i8 = this->volume_f * VOLUME_UNITY_INT8; |
|
744 this->volume_i16 = this->volume_f * VOLUME_UNITY_INT16; |
|
745 this->volume_i24 = this->volume_f * VOLUME_UNITY_INT24; |
|
746 this->volume_i32 = this->volume_f * VOLUME_UNITY_INT32; |
|
747 |
|
748 volume_update_real_volume (this); |
|
749 } |
785 } |
750 |
786 |
751 static void |
787 static void |
752 volume_set_property (GObject * object, guint prop_id, const GValue * value, |
788 volume_set_property (GObject * object, guint prop_id, const GValue * value, |
753 GParamSpec * pspec) |
789 GParamSpec * pspec) |
754 { |
790 { |
755 GstVolume *this = GST_VOLUME (object); |
791 GstVolume *this = GST_VOLUME (object); |
756 |
792 |
757 switch (prop_id) { |
793 switch (prop_id) { |
758 case PROP_MUTE: |
794 case PROP_MUTE: |
759 volume_update_mute (value, this); |
795 GST_OBJECT_LOCK (this); |
|
796 this->mute = g_value_get_boolean (value); |
|
797 GST_OBJECT_UNLOCK (this); |
760 break; |
798 break; |
761 case PROP_VOLUME: |
799 case PROP_VOLUME: |
762 volume_update_volume (value, this); |
800 GST_OBJECT_LOCK (this); |
|
801 this->volume = g_value_get_double (value); |
|
802 GST_OBJECT_UNLOCK (this); |
763 break; |
803 break; |
764 default: |
804 default: |
765 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
805 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
766 break; |
806 break; |
767 } |
807 } |
773 { |
813 { |
774 GstVolume *this = GST_VOLUME (object); |
814 GstVolume *this = GST_VOLUME (object); |
775 |
815 |
776 switch (prop_id) { |
816 switch (prop_id) { |
777 case PROP_MUTE: |
817 case PROP_MUTE: |
|
818 GST_OBJECT_LOCK (this); |
778 g_value_set_boolean (value, this->mute); |
819 g_value_set_boolean (value, this->mute); |
|
820 GST_OBJECT_UNLOCK (this); |
779 break; |
821 break; |
780 case PROP_VOLUME: |
822 case PROP_VOLUME: |
781 g_value_set_double (value, this->volume_f); |
823 GST_OBJECT_LOCK (this); |
|
824 g_value_set_double (value, this->volume); |
|
825 GST_OBJECT_UNLOCK (this); |
782 break; |
826 break; |
783 default: |
827 default: |
784 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
828 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
785 break; |
829 break; |
786 } |
830 } |
787 } |
831 } |
788 |
832 |
789 static gboolean |
833 static gboolean |
790 plugin_init (GstPlugin * plugin) |
834 plugin_init (GstPlugin * plugin) |
791 { |
835 { |
792 #ifndef __SYMBIAN32__ |
|
793 oil_init (); |
836 oil_init (); |
794 #endif |
|
795 |
837 |
796 /* initialize gst controller library */ |
838 /* initialize gst controller library */ |
797 gst_controller_init (NULL, NULL); |
839 gst_controller_init (NULL, NULL); |
|
840 |
|
841 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "volume", 0, "Volume gain"); |
|
842 |
|
843 /* ref class from a thread-safe context to work around missing bit of |
|
844 * thread-safety in GObject */ |
|
845 g_type_class_ref (GST_TYPE_MIXER_TRACK); |
798 |
846 |
799 return gst_element_register (plugin, "volume", GST_RANK_NONE, |
847 return gst_element_register (plugin, "volume", GST_RANK_NONE, |
800 GST_TYPE_VOLUME); |
848 GST_TYPE_VOLUME); |
801 } |
849 } |
802 |
850 |