1 /* GStreamer ReplayGain volume adjustment
2  *
3  * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
4  *
5  * rgvolume.c: Unit test for the rgvolume element
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  */
22 
23 #include <gst/check/gstcheck.h>
24 #include <gst/audio/audio.h>
25 
26 #include <math.h>
27 
28 static GList *events = NULL;
29 
30 /* For ease of programming we use globals to keep refs for our floating src and
31  * sink pads we create; otherwise we always have to do get_pad, get_peer, and
32  * then remove references in every test function */
33 static GstPad *mysrcpad, *mysinkpad;
34 
35 #define RG_VOLUME_CAPS_TEMPLATE_STRING        \
36   "audio/x-raw, "                             \
37   "format = (string) "GST_AUDIO_NE (F32) ", " \
38   "layout = (string) interleaved, "           \
39   "channels = (int) [ 1, MAX ], "             \
40   "rate = (int) [ 1, MAX ]"
41 
42 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
43     GST_PAD_SINK,
44     GST_PAD_ALWAYS,
45     GST_STATIC_CAPS (RG_VOLUME_CAPS_TEMPLATE_STRING)
46     );
47 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
48     GST_PAD_SRC,
49     GST_PAD_ALWAYS,
50     GST_STATIC_CAPS (RG_VOLUME_CAPS_TEMPLATE_STRING)
51     );
52 
53 static GstBuffer *test_buffer_new (gfloat value);
54 
55 /* gstcheck sets up a chain function that appends buffers to a global list.
56  * This is our equivalent of that for event handling. */
57 static gboolean
event_func(GstPad * pad,GstObject * parent,GstEvent * event)58 event_func (GstPad * pad, GstObject * parent, GstEvent * event)
59 {
60   GST_DEBUG ("received event %p (%s)", event, GST_EVENT_TYPE_NAME (event));
61   events = g_list_append (events, event);
62 
63   return TRUE;
64 }
65 
66 static GstElement *
setup_rgvolume(void)67 setup_rgvolume (void)
68 {
69   GstElement *element;
70 
71   GST_DEBUG ("setup_rgvolume");
72   element = gst_check_setup_element ("rgvolume");
73   mysrcpad = gst_check_setup_src_pad (element, &srctemplate);
74   mysinkpad = gst_check_setup_sink_pad (element, &sinktemplate);
75 
76   /* Capture events, to test tag filtering behavior: */
77   gst_pad_set_event_function (mysinkpad, event_func);
78 
79   gst_pad_set_active (mysrcpad, TRUE);
80   gst_pad_set_active (mysinkpad, TRUE);
81 
82   return element;
83 }
84 
85 static void
send_empty_buffer(void)86 send_empty_buffer (void)
87 {
88   GstBuffer *buf;
89 
90   buf = test_buffer_new (0.0);
91   gst_buffer_resize (buf, 0, 0);
92   GST_BUFFER_DURATION (buf) = 0;
93   GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET (buf);
94   fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK);
95 
96   fail_unless (g_list_length (buffers) == 1);
97   fail_unless (buffers->data == buf);
98   gst_mini_object_unref ((GstMiniObject *) buffers->data);
99   buffers = g_list_remove (buffers, buf);
100 }
101 
102 static void
cleanup_rgvolume(GstElement * element)103 cleanup_rgvolume (GstElement * element)
104 {
105   GST_DEBUG ("cleanup_rgvolume");
106 
107   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
108   g_list_free (buffers);
109   buffers = NULL;
110 
111   g_list_foreach (events, (GFunc) gst_mini_object_unref, NULL);
112   g_list_free (events);
113   events = NULL;
114 
115   gst_pad_set_active (mysrcpad, FALSE);
116   gst_pad_set_active (mysinkpad, FALSE);
117   gst_check_teardown_src_pad (element);
118   gst_check_teardown_sink_pad (element);
119   gst_check_teardown_element (element);
120 }
121 
122 static void
set_playing_state(GstElement * element)123 set_playing_state (GstElement * element)
124 {
125   fail_unless (gst_element_set_state (element,
126           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
127       "Could not set state to PLAYING");
128 }
129 
130 static void
set_null_state(GstElement * element)131 set_null_state (GstElement * element)
132 {
133   fail_unless (gst_element_set_state (element,
134           GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS,
135       "Could not set state to NULL");
136 }
137 
138 static void
send_flush_events(GstElement * element)139 send_flush_events (GstElement * element)
140 {
141   gboolean res;
142 
143   res = gst_pad_push_event (mysrcpad, gst_event_new_flush_start ());
144   fail_unless (res, "flush-start even not handled");
145 
146   res = gst_pad_push_event (mysrcpad, gst_event_new_flush_stop (TRUE));
147   fail_unless (res, "flush-stop event not handled");
148 }
149 
150 static void
send_stream_start_event(GstElement * element)151 send_stream_start_event (GstElement * element)
152 {
153   gboolean res;
154 
155   res = gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("test"));
156   fail_unless (res, "STREAM_START event not handled");
157 }
158 
159 static void
send_caps_event(GstElement * element)160 send_caps_event (GstElement * element)
161 {
162   GstCaps *caps;
163   gboolean res;
164 
165   caps = gst_caps_from_string ("audio/x-raw, format = " GST_AUDIO_NE (F32) ", "
166       "layout = interleaved, rate = 8000, channels = 1");
167   res = gst_pad_push_event (mysrcpad, gst_event_new_caps (caps));
168   fail_unless (res, "CAPS event not handled");
169   gst_caps_unref (caps);
170 }
171 
172 static void
send_segment_event(GstElement * element)173 send_segment_event (GstElement * element)
174 {
175   GstSegment segment;
176   gboolean res;
177 
178   gst_segment_init (&segment, GST_FORMAT_TIME);
179   res = gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment));
180   fail_unless (res, "SEGMENT event not handled");
181 }
182 
183 static void
send_eos_event(GstElement * element)184 send_eos_event (GstElement * element)
185 {
186   GstEvent *event = gst_event_new_eos ();
187 
188   fail_unless (gst_pad_push_event (mysrcpad, event),
189       "Pushing EOS event failed");
190 }
191 
192 static GstEvent *
send_tag_event(GstElement * element,GstEvent * event)193 send_tag_event (GstElement * element, GstEvent * event)
194 {
195   GList *l;
196   GstTagList *tag_list;
197   gdouble dummy;
198 
199   g_return_val_if_fail (event->type == GST_EVENT_TAG, NULL);
200 
201   fail_unless (gst_pad_push_event (mysrcpad, event),
202       "Pushing tag event failed");
203 
204   event = NULL;
205 
206   for (l = g_list_last (events); l; l = l->prev) {
207     if (GST_EVENT_TYPE (l->data) == GST_EVENT_TAG) {
208       event = l->data;
209       events = g_list_delete_link (events, l);
210       break;
211     }
212   }
213 
214   /* Event got filtered out */
215   if (event == NULL)
216     return NULL;
217 
218   fail_unless (event->type == GST_EVENT_TAG);
219   gst_event_parse_tag (event, &tag_list);
220 
221   /* The element is supposed to filter out ReplayGain related tags. */
222   fail_if (gst_tag_list_get_double (tag_list, GST_TAG_TRACK_GAIN, &dummy),
223       "tag event still contains track gain tag");
224   fail_if (gst_tag_list_get_double (tag_list, GST_TAG_TRACK_PEAK, &dummy),
225       "tag event still contains track peak tag");
226   fail_if (gst_tag_list_get_double (tag_list, GST_TAG_ALBUM_GAIN, &dummy),
227       "tag event still contains album gain tag");
228   fail_if (gst_tag_list_get_double (tag_list, GST_TAG_ALBUM_PEAK, &dummy),
229       "tag event still contains album peak tag");
230 
231   return event;
232 }
233 
234 static GstBuffer *
test_buffer_new(gfloat value)235 test_buffer_new (gfloat value)
236 {
237   GstBuffer *buf;
238   GstMapInfo map;
239   gfloat *data;
240   gint i;
241 
242   buf = gst_buffer_new_and_alloc (8 * sizeof (gfloat));
243   gst_buffer_map (buf, &map, GST_MAP_WRITE);
244   data = (gfloat *) map.data;
245   for (i = 0; i < 8; i++)
246     data[i] = value;
247   gst_buffer_unmap (buf, &map);
248 
249 
250   ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
251 
252   return buf;
253 }
254 
255 #define MATCH_GAIN(g1, g2) ((g1 < g2 + 1e-6) && (g2 < g1 + 1e-6))
256 
257 static void
fail_unless_target_gain(GstElement * element,gdouble expected_gain)258 fail_unless_target_gain (GstElement * element, gdouble expected_gain)
259 {
260   gdouble prop_gain;
261 
262   g_object_get (element, "target-gain", &prop_gain, NULL);
263 
264   fail_unless (MATCH_GAIN (prop_gain, expected_gain),
265       "Target gain is %.2f dB, expected %.2f dB", prop_gain, expected_gain);
266 }
267 
268 static void
fail_unless_result_gain(GstElement * element,gdouble expected_gain)269 fail_unless_result_gain (GstElement * element, gdouble expected_gain)
270 {
271   GstBuffer *input_buf, *output_buf;
272   gfloat *data;
273   gfloat input_sample, output_sample;
274   gdouble gain, prop_gain;
275   gboolean is_passthrough, expect_passthrough;
276   gint i;
277   GstMapInfo map;
278 
279   fail_unless (g_list_length (buffers) == 0);
280 
281   input_sample = 1.0;
282   input_buf = test_buffer_new (input_sample);
283 
284   /* We keep an extra reference to detect passthrough mode. */
285   gst_buffer_ref (input_buf);
286   /* Pushing steals a reference. */
287   fail_unless (gst_pad_push (mysrcpad, input_buf) == GST_FLOW_OK);
288   gst_buffer_unref (input_buf);
289 
290   /* The output buffer ends up on the global buffer list. */
291   fail_unless (g_list_length (buffers) == 1);
292   output_buf = buffers->data;
293   fail_if (output_buf == NULL);
294 
295   buffers = g_list_remove (buffers, output_buf);
296   ASSERT_BUFFER_REFCOUNT (output_buf, "output_buf", 1);
297 
298   fail_unless_equals_int (gst_buffer_get_size (output_buf),
299       8 * sizeof (gfloat));
300 
301   gst_buffer_map (output_buf, &map, GST_MAP_READ);
302   data = (gfloat *) map.data;
303 
304   output_sample = *data;
305   fail_if (output_sample == 0.0, "First output sample is zero");
306   for (i = 1; i < 8; i++) {
307     fail_unless (output_sample == data[i], "Output samples not uniform");
308   };
309   gst_buffer_unmap (output_buf, &map);
310 
311   gain = 20. * log10 (output_sample / input_sample);
312   fail_unless (MATCH_GAIN (gain, expected_gain),
313       "Applied gain is %.2f dB, expected %.2f dB", gain, expected_gain);
314   g_object_get (element, "result-gain", &prop_gain, NULL);
315   fail_unless (MATCH_GAIN (prop_gain, expected_gain),
316       "Result gain is %.2f dB, expected %.2f dB", prop_gain, expected_gain);
317 
318   is_passthrough = (output_buf == input_buf);
319   expect_passthrough = MATCH_GAIN (expected_gain, +0.00);
320   fail_unless (is_passthrough == expect_passthrough,
321       expect_passthrough
322       ? "Expected operation in passthrough mode"
323       : "Incorrect passthrough behaviour");
324 
325   gst_buffer_unref (output_buf);
326 }
327 
328 static void
fail_unless_gain(GstElement * element,gdouble expected_gain)329 fail_unless_gain (GstElement * element, gdouble expected_gain)
330 {
331   fail_unless_target_gain (element, expected_gain);
332   fail_unless_result_gain (element, expected_gain);
333 }
334 
335 /* Start of tests. */
336 
GST_START_TEST(test_no_buffer)337 GST_START_TEST (test_no_buffer)
338 {
339   GstElement *element = setup_rgvolume ();
340 
341   set_playing_state (element);
342   set_null_state (element);
343   set_playing_state (element);
344   send_eos_event (element);
345 
346   cleanup_rgvolume (element);
347 }
348 
349 GST_END_TEST;
350 
GST_START_TEST(test_events)351 GST_START_TEST (test_events)
352 {
353   GstElement *element = setup_rgvolume ();
354   GstEvent *event;
355   GstEvent *new_event;
356   GstTagList *tag_list;
357   gchar *artist;
358 
359   set_playing_state (element);
360   send_stream_start_event (element);
361   send_caps_event (element);
362   send_segment_event (element);
363 
364   send_empty_buffer ();
365 
366   tag_list = gst_tag_list_new_empty ();
367   gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
368       GST_TAG_TRACK_GAIN, +4.95, GST_TAG_TRACK_PEAK, 0.59463,
369       GST_TAG_ALBUM_GAIN, -1.54, GST_TAG_ALBUM_PEAK, 0.693415,
370       GST_TAG_ARTIST, "Foobar", NULL);
371   event = gst_event_new_tag (tag_list);
372   new_event = send_tag_event (element, event);
373   gst_event_parse_tag (new_event, &tag_list);
374   fail_unless (gst_tag_list_get_string (tag_list, GST_TAG_ARTIST, &artist));
375   fail_unless (g_str_equal (artist, "Foobar"));
376   g_free (artist);
377   gst_event_unref (new_event);
378 
379   /* Same as above, but with a non-writable event. */
380 
381   tag_list = gst_tag_list_new_empty ();
382   gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
383       GST_TAG_TRACK_GAIN, +4.95, GST_TAG_TRACK_PEAK, 0.59463,
384       GST_TAG_ALBUM_GAIN, -1.54, GST_TAG_ALBUM_PEAK, 0.693415,
385       GST_TAG_ARTIST, "Foobar", NULL);
386   gst_tag_list_ref (tag_list);
387   event = gst_event_new_tag (tag_list);
388   new_event = send_tag_event (element, event);
389 
390   /* Make sure our tags weren't modified in place while we still got a ref */
391   fail_unless_equals_int (5, gst_tag_list_n_tags (tag_list));
392   gst_tag_list_unref (tag_list);
393 
394   gst_event_parse_tag (new_event, &tag_list);
395   fail_unless (gst_tag_list_get_string (tag_list, GST_TAG_ARTIST, &artist));
396   fail_unless (g_str_equal (artist, "Foobar"));
397   g_free (artist);
398   gst_event_unref (new_event);
399 
400   cleanup_rgvolume (element);
401 }
402 
403 GST_END_TEST;
404 
GST_START_TEST(test_simple)405 GST_START_TEST (test_simple)
406 {
407   GstElement *element = setup_rgvolume ();
408   GstTagList *tag_list;
409 
410   g_object_set (element, "album-mode", FALSE, "headroom", +0.00,
411       "pre-amp", -6.00, "fallback-gain", +1.23, NULL);
412   set_playing_state (element);
413   send_stream_start_event (element);
414   send_caps_event (element);
415   send_segment_event (element);
416 
417   send_empty_buffer ();
418 
419   tag_list = gst_tag_list_new_empty ();
420   gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
421       GST_TAG_TRACK_GAIN, -3.45, GST_TAG_TRACK_PEAK, 1.0,
422       GST_TAG_ALBUM_GAIN, +2.09, GST_TAG_ALBUM_PEAK, 1.0, NULL);
423   fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
424   fail_unless_gain (element, -9.45);    /* pre-amp + track gain */
425   send_eos_event (element);
426 
427   g_object_set (element, "album-mode", TRUE, NULL);
428 
429   send_flush_events (element);
430   send_segment_event (element);
431 
432   tag_list = gst_tag_list_new_empty ();
433   gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
434       GST_TAG_TRACK_GAIN, -3.45, GST_TAG_TRACK_PEAK, 1.0,
435       GST_TAG_ALBUM_GAIN, +2.09, GST_TAG_ALBUM_PEAK, 1.0, NULL);
436   fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
437   fail_unless_gain (element, -3.91);    /* pre-amp + album gain */
438 
439   /* Switching back to track mode in the middle of a stream: */
440   g_object_set (element, "album-mode", FALSE, NULL);
441   fail_unless_gain (element, -9.45);    /* pre-amp + track gain */
442   send_eos_event (element);
443 
444   cleanup_rgvolume (element);
445 }
446 
447 GST_END_TEST;
448 
449 /* If there are no gain tags at all, the fallback gain is used. */
450 
GST_START_TEST(test_fallback_gain)451 GST_START_TEST (test_fallback_gain)
452 {
453   GstElement *element = setup_rgvolume ();
454   GstTagList *tag_list;
455 
456   /* First some track where fallback does _not_ apply. */
457 
458   g_object_set (element, "album-mode", FALSE, "headroom", 10.00,
459       "pre-amp", -6.00, "fallback-gain", -3.00, NULL);
460   set_playing_state (element);
461   send_stream_start_event (element);
462   send_caps_event (element);
463   send_segment_event (element);
464 
465   send_empty_buffer ();
466 
467   tag_list = gst_tag_list_new_empty ();
468   gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
469       GST_TAG_TRACK_GAIN, +3.5, GST_TAG_TRACK_PEAK, 1.0,
470       GST_TAG_ALBUM_GAIN, -0.5, GST_TAG_ALBUM_PEAK, 1.0, NULL);
471   fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
472   fail_unless_gain (element, -2.50);    /* pre-amp + track gain */
473   send_eos_event (element);
474 
475   /* Now a track completely missing tags. */
476   send_flush_events (element);
477   send_segment_event (element);
478 
479   fail_unless_gain (element, -9.00);    /* pre-amp + fallback-gain */
480 
481   /* Changing the fallback gain in the middle of a stream, going to pass-through
482    * mode: */
483   g_object_set (element, "fallback-gain", +6.00, NULL);
484   fail_unless_gain (element, +0.00);    /* pre-amp + fallback-gain */
485   send_eos_event (element);
486 
487   /* Verify that result gain is set to +0.00 with pre-amp + fallback-gain >
488    * +0.00 and no headroom. */
489   send_flush_events (element);
490   send_segment_event (element);
491 
492   g_object_set (element, "fallback-gain", +12.00, "headroom", +0.00, NULL);
493   fail_unless_target_gain (element, +6.00);     /* pre-amp + fallback-gain */
494   fail_unless_result_gain (element, +0.00);
495   send_eos_event (element);
496 
497   cleanup_rgvolume (element);
498 }
499 
500 GST_END_TEST;
501 
502 /* If album gain is to be preferred but not available, the track gain is to be
503  * taken instead. */
504 
GST_START_TEST(test_fallback_track)505 GST_START_TEST (test_fallback_track)
506 {
507   GstElement *element = setup_rgvolume ();
508   GstTagList *tag_list;
509 
510   g_object_set (element, "album-mode", TRUE, "headroom", +0.00,
511       "pre-amp", -6.00, "fallback-gain", +1.23, NULL);
512   set_playing_state (element);
513   send_stream_start_event (element);
514   send_caps_event (element);
515   send_segment_event (element);
516 
517   send_empty_buffer ();
518 
519   tag_list = gst_tag_list_new_empty ();
520   gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
521       GST_TAG_TRACK_GAIN, +2.11, GST_TAG_TRACK_PEAK, 1.0, NULL);
522   fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
523   fail_unless_gain (element, -3.89);    /* pre-amp + track gain */
524 
525   send_eos_event (element);
526 
527   cleanup_rgvolume (element);
528 }
529 
530 GST_END_TEST;
531 
532 /* If track gain is to be preferred but not available, the album gain is to be
533  * taken instead. */
534 
GST_START_TEST(test_fallback_album)535 GST_START_TEST (test_fallback_album)
536 {
537   GstElement *element = setup_rgvolume ();
538   GstTagList *tag_list;
539 
540   g_object_set (element, "album-mode", FALSE, "headroom", +0.00,
541       "pre-amp", -6.00, "fallback-gain", +1.23, NULL);
542   set_playing_state (element);
543   send_stream_start_event (element);
544   send_caps_event (element);
545   send_segment_event (element);
546 
547   send_empty_buffer ();
548 
549   tag_list = gst_tag_list_new_empty ();
550   gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
551       GST_TAG_ALBUM_GAIN, +3.73, GST_TAG_ALBUM_PEAK, 1.0, NULL);
552   fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
553   fail_unless_gain (element, -2.27);    /* pre-amp + album gain */
554 
555   send_eos_event (element);
556 
557   cleanup_rgvolume (element);
558 }
559 
560 GST_END_TEST;
561 
GST_START_TEST(test_headroom)562 GST_START_TEST (test_headroom)
563 {
564   GstElement *element = setup_rgvolume ();
565   GstTagList *tag_list;
566 
567   g_object_set (element, "album-mode", FALSE, "headroom", +0.00,
568       "pre-amp", +0.00, "fallback-gain", +1.23, NULL);
569   set_playing_state (element);
570   send_stream_start_event (element);
571   send_caps_event (element);
572   send_segment_event (element);
573 
574   send_empty_buffer ();
575 
576   tag_list = gst_tag_list_new_empty ();
577   gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
578       GST_TAG_TRACK_GAIN, +3.50, GST_TAG_TRACK_PEAK, 1.0, NULL);
579   fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
580   fail_unless_target_gain (element, +3.50);     /* pre-amp + track gain */
581   fail_unless_result_gain (element, +0.00);
582   send_eos_event (element);
583 
584   send_flush_events (element);
585   send_segment_event (element);
586 
587   g_object_set (element, "headroom", +2.00, NULL);
588   tag_list = gst_tag_list_new_empty ();
589   gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
590       GST_TAG_TRACK_GAIN, +9.18, GST_TAG_TRACK_PEAK, 0.687149, NULL);
591   fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
592   fail_unless_target_gain (element, +9.18);     /* pre-amp + track gain */
593   /* Result is 20. * log10 (1. / peak) + headroom. */
594   fail_unless_result_gain (element, 5.2589816238303335);
595   send_eos_event (element);
596 
597   send_flush_events (element);
598   send_segment_event (element);
599 
600   g_object_set (element, "album-mode", TRUE, NULL);
601   tag_list = gst_tag_list_new_empty ();
602   gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
603       GST_TAG_ALBUM_GAIN, +5.50, GST_TAG_ALBUM_PEAK, 1.0, NULL);
604   fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
605   fail_unless_target_gain (element, +5.50);     /* pre-amp + album gain */
606   fail_unless_result_gain (element, +2.00);     /* headroom */
607   send_eos_event (element);
608 
609   cleanup_rgvolume (element);
610 }
611 
612 GST_END_TEST;
613 
GST_START_TEST(test_reference_level)614 GST_START_TEST (test_reference_level)
615 {
616   GstElement *element = setup_rgvolume ();
617   GstTagList *tag_list;
618 
619   g_object_set (element,
620       "album-mode", FALSE,
621       "headroom", +0.00, "pre-amp", +0.00, "fallback-gain", +1.23, NULL);
622   set_playing_state (element);
623 
624   send_stream_start_event (element);
625   send_caps_event (element);
626   send_segment_event (element);
627 
628   send_empty_buffer ();
629 
630   tag_list = gst_tag_list_new_empty ();
631   gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
632       GST_TAG_TRACK_GAIN, 0.00, GST_TAG_TRACK_PEAK, 0.2,
633       GST_TAG_REFERENCE_LEVEL, 83., NULL);
634   fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
635   /* Because our authorative reference is 89 dB, we bump it up by +6 dB. */
636   fail_unless_gain (element, +6.00);    /* pre-amp + track gain */
637   send_eos_event (element);
638 
639   g_object_set (element, "album-mode", TRUE, NULL);
640 
641   /* Same as above, but with album gain. */
642   send_flush_events (element);
643   send_segment_event (element);
644 
645   tag_list = gst_tag_list_new_empty ();
646   gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
647       GST_TAG_TRACK_GAIN, 1.23, GST_TAG_TRACK_PEAK, 0.1,
648       GST_TAG_ALBUM_GAIN, 0.00, GST_TAG_ALBUM_PEAK, 0.2,
649       GST_TAG_REFERENCE_LEVEL, 83., NULL);
650   fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
651   fail_unless_gain (element, +6.00);    /* pre-amp + album gain */
652 
653   cleanup_rgvolume (element);
654 }
655 
656 GST_END_TEST;
657 
658 static Suite *
rgvolume_suite(void)659 rgvolume_suite (void)
660 {
661   Suite *s = suite_create ("rgvolume");
662   TCase *tc_chain = tcase_create ("general");
663 
664   suite_add_tcase (s, tc_chain);
665 
666   tcase_add_test (tc_chain, test_no_buffer);
667   tcase_add_test (tc_chain, test_events);
668   tcase_add_test (tc_chain, test_simple);
669   tcase_add_test (tc_chain, test_fallback_gain);
670   tcase_add_test (tc_chain, test_fallback_track);
671   tcase_add_test (tc_chain, test_fallback_album);
672   tcase_add_test (tc_chain, test_headroom);
673   tcase_add_test (tc_chain, test_reference_level);
674 
675   return s;
676 }
677 
678 GST_CHECK_MAIN (rgvolume);
679