gstreamer/tests/check/elements/rganalysis.c
Jan Schmidt fa15557bf5 check: Remove assertion that breaks check again git master
Remove the assertion that the sender of the tags message is the
element until we decide whether that's going to be true or not.
2009-05-16 01:21:34 +01:00

1881 lines
62 KiB
C

/* GStreamer ReplayGain analysis
*
* Copyright (C) 2006 Rene Stadler <mail@renestadler.de>
*
* rganalysis.c: Unit test for the rganalysis element
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
/* Some things to note about the RMS window length of the analysis algorithm and
* thus the implementation used in the element: Processing divides input data
* into 50ms windows at some point. Some details about this that normally do
* not matter:
*
* 1. At the end of a stream, the remainder of data that did not fill up the
* last 50ms window is simply discarded.
*
* 2. If the sample rate changes during a stream, the currently running window
* is discarded and the equal loudness filter gets reset as if a new stream
* started.
*
* 3. For the album gain, it is not entirely correct to think of obtaining it
* like "as if all the tracks are analyzed as one track". There isn't a
* separate window being tracked for album processing, so at stream (track)
* end, the remaining unfilled window does not contribute to the album gain
* either.
*
* 4. If a waveform with a result gain G is concatenated to itself and the
* result processed as a track, the gain can be different from G if and only
* if the duration of the original waveform is not an integer multiple of
* 50ms. If the original waveform gets processed as a single track and then
* the same data again as a subsequent track, the album result gain will
* always match G (this is implied by 3.).
*
* 5. A stream shorter than 50ms cannot be analyzed. At 8000 and 48000 Hz,
* this corresponds to 400 resp. 2400 frames. If a stream is shorter than
* 50ms, the element will not generate tags at EOS (only if an album
* finished, but only album tags are generated then). This is not an
* erroneous condition, the element should behave normally.
*
* The limitations outlined in 1.-4. do not apply to the peak values. Every
* single sample is accounted for when looking for the peak. Thus the album
* peak is guaranteed to be the maximum value of all track peaks.
*
* In normal day-to-day use, these little facts are unlikely to be relevant, but
* they have to be kept in mind for writing the tests here.
*/
#include <gst/check/gstcheck.h>
GList *buffers = NULL;
/* For ease of programming we use globals to keep refs for our floating src and
* sink pads we create; otherwise we always have to do get_pad, get_peer, and
* then remove references in every test function */
static GstPad *mysrcpad, *mysinkpad;
/* Mapping from supported sample rates to the correct result gain for the
* following test waveform: 20 * 512 samples with a quarter-full amplitude of
* toggling sign, changing every 48 samples and starting with the positive
* value.
*
* Even if we would generate a wave describing a signal with the same frequency
* at each sampling rate, the results would vary (slightly). Hence the simple
* generation method, since we cannot use a constant value as expected result
* anyways. For all sample rates, changing the sign every 48 frames gives a
* sane frequency. Buffers containing data that forms such a waveform is
* created using the test_buffer_square_{float,int16}_{mono,stereo} functions
* below.
*
* The results have been checked against what the metaflac and wavegain programs
* generate for such a stream. If you want to verify these, be sure that the
* metaflac program does not produce incorrect results in your environment: I
* found a strange bug in the (defacto) reference code for the analysis that
* sometimes leads to incorrect RMS window lengths. */
struct rate_test
{
guint sample_rate;
gdouble gain;
};
static const struct rate_test supported_rates[] = {
{8000, -0.91},
{11025, -2.80},
{12000, -3.13},
{16000, -4.26},
{22050, -5.64},
{24000, -5.87},
{32000, -6.03},
{44100, -6.20},
{48000, -6.14}
};
/* Lookup the correct gain adjustment result in above array. */
static gdouble
get_expected_gain (guint sample_rate)
{
gint i;
for (i = G_N_ELEMENTS (supported_rates); i--;)
if (supported_rates[i].sample_rate == sample_rate)
return supported_rates[i].gain;
g_return_val_if_reached (0.0);
}
#define SILENCE_GAIN 64.82
#define REPLAY_GAIN_CAPS \
"channels = (int) { 1, 2 }, " \
"rate = (int) { 8000, 11025, 12000, 16000, 22050, " \
"24000, 32000, 44100, 48000 }"
#define RG_ANALYSIS_CAPS_TEMPLATE_STRING \
"audio/x-raw-float, " \
"width = (int) 32, " \
"endianness = (int) BYTE_ORDER, " \
REPLAY_GAIN_CAPS \
"; " \
"audio/x-raw-int, " \
"width = (int) 16, " \
"depth = (int) [ 1, 16 ], " \
"signed = (boolean) true, " \
"endianness = (int) BYTE_ORDER, " \
REPLAY_GAIN_CAPS
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (RG_ANALYSIS_CAPS_TEMPLATE_STRING)
);
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (RG_ANALYSIS_CAPS_TEMPLATE_STRING)
);
GstElement *
setup_rganalysis ()
{
GstElement *analysis;
GstBus *bus;
GST_DEBUG ("setup_rganalysis");
analysis = gst_check_setup_element ("rganalysis");
mysrcpad = gst_check_setup_src_pad (analysis, &srctemplate, NULL);
mysinkpad = gst_check_setup_sink_pad (analysis, &sinktemplate, NULL);
gst_pad_set_active (mysrcpad, TRUE);
gst_pad_set_active (mysinkpad, TRUE);
bus = gst_bus_new ();
gst_element_set_bus (analysis, bus);
/* gst_element_set_bus does not steal a reference. */
gst_object_unref (bus);
return analysis;
}
void
cleanup_rganalysis (GstElement * element)
{
GST_DEBUG ("cleanup_rganalysis");
g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
g_list_free (buffers);
buffers = NULL;
/* The bus owns references to the element: */
gst_element_set_bus (element, NULL);
gst_pad_set_active (mysrcpad, FALSE);
gst_pad_set_active (mysinkpad, FALSE);
gst_check_teardown_src_pad (element);
gst_check_teardown_sink_pad (element);
gst_check_teardown_element (element);
}
static void
set_playing_state (GstElement * element)
{
fail_unless (gst_element_set_state (element,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"Could not set state to PLAYING");
}
static void
send_eos_event (GstElement * element)
{
GstBus *bus = gst_element_get_bus (element);
GstPad *pad = gst_element_get_static_pad (element, "sink");
GstEvent *event = gst_event_new_eos ();
fail_unless (gst_pad_send_event (pad, event),
"Cannot send EOS event: Not handled.");
/* There is no sink element, so _we_ post the EOS message on the bus here. Of
* course we generate any EOS ourselves, but this allows us to poll for the
* EOS message in poll_eos if we expect the element to _not_ generate a TAG
* message. That's better than waiting for a timeout to lapse. */
fail_unless (gst_bus_post (bus, gst_message_new_eos (NULL)));
gst_object_unref (bus);
gst_object_unref (pad);
}
static void
send_tag_event (GstElement * element, GstTagList * tag_list)
{
GstPad *pad = gst_element_get_static_pad (element, "sink");
GstEvent *event = gst_event_new_tag (tag_list);
fail_unless (gst_pad_send_event (pad, event),
"Cannot send TAG event: Not handled.");
gst_object_unref (pad);
}
static void
poll_eos (GstElement * element)
{
GstBus *bus = gst_element_get_bus (element);
GstMessage *message;
message = gst_bus_poll (bus, GST_MESSAGE_EOS | GST_MESSAGE_TAG, GST_SECOND);
fail_unless (message != NULL, "Could not poll for EOS message: Timed out");
fail_unless (message->type == GST_MESSAGE_EOS,
"Could not poll for eos message: got message of type %s instead",
gst_message_type_get_name (message->type));
gst_message_unref (message);
gst_object_unref (bus);
}
/* This also polls for EOS since the TAG message comes right before the end of
* streams. */
static GstTagList *
poll_tags (GstElement * element)
{
GstBus *bus = gst_element_get_bus (element);
GstTagList *tag_list;
GstMessage *message;
message = gst_bus_poll (bus, GST_MESSAGE_TAG, GST_SECOND);
fail_unless (message != NULL, "Could not poll for TAG message: Timed out");
gst_message_parse_tag (message, &tag_list);
gst_message_unref (message);
gst_object_unref (bus);
poll_eos (element);
return tag_list;
}
#define MATCH_PEAK(p1, p2) ((p1 < p2 + 1e-6) && (p2 < p1 + 1e-6))
#define MATCH_GAIN(g1, g2) ((g1 < g2 + 1e-13) && (g2 < g1 + 1e-13))
static void
fail_unless_track_gain (const GstTagList * tag_list, gdouble gain)
{
gdouble result;
fail_unless (gst_tag_list_get_double (tag_list, GST_TAG_TRACK_GAIN, &result),
"Tag list contains no track gain value");
fail_unless (MATCH_GAIN (gain, result),
"Track gain %+.2f does not match, expected %+.2f", result, gain);
}
static void
fail_unless_track_peak (const GstTagList * tag_list, gdouble peak)
{
gdouble result;
fail_unless (gst_tag_list_get_double (tag_list, GST_TAG_TRACK_PEAK, &result),
"Tag list contains no track peak value");
fail_unless (MATCH_PEAK (peak, result),
"Track peak %f does not match, expected %f", result, peak);
}
static void
fail_unless_album_gain (const GstTagList * tag_list, gdouble gain)
{
gdouble result;
fail_unless (gst_tag_list_get_double (tag_list, GST_TAG_ALBUM_GAIN, &result),
"Tag list contains no album gain value");
fail_unless (MATCH_GAIN (result, gain),
"Album gain %+.2f does not match, expected %+.2f", result, gain);
}
static void
fail_unless_album_peak (const GstTagList * tag_list, gdouble peak)
{
gdouble result;
fail_unless (gst_tag_list_get_double (tag_list, GST_TAG_ALBUM_PEAK, &result),
"Tag list contains no album peak value");
fail_unless (MATCH_PEAK (peak, result),
"Album peak %f does not match, expected %f", result, peak);
}
static void
fail_if_track_tags (const GstTagList * tag_list)
{
gdouble result;
fail_if (gst_tag_list_get_double (tag_list, GST_TAG_TRACK_GAIN, &result),
"Tag list contains track gain value (but should not)");
fail_if (gst_tag_list_get_double (tag_list, GST_TAG_TRACK_PEAK, &result),
"Tag list contains track peak value (but should not)");
}
static void
fail_if_album_tags (const GstTagList * tag_list)
{
gdouble result;
fail_if (gst_tag_list_get_double (tag_list, GST_TAG_ALBUM_GAIN, &result),
"Tag list contains album gain value (but should not)");
fail_if (gst_tag_list_get_double (tag_list, GST_TAG_ALBUM_PEAK, &result),
"Tag list contains album peak value (but should not)");
}
static void
fail_unless_num_tracks (GstElement * element, guint num_tracks)
{
guint current;
g_object_get (element, "num-tracks", &current, NULL);
fail_unless (current == num_tracks,
"num-tracks property has incorrect value %u, expected %u",
current, num_tracks);
}
/* Functions that create buffers with constant sample values, for peak
* tests. */
static GstBuffer *
test_buffer_const_float_mono (gint sample_rate, gsize n_frames, gfloat value)
{
GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gfloat));
gfloat *data = (gfloat *) GST_BUFFER_DATA (buf);
GstCaps *caps;
gint i;
for (i = n_frames; i--;)
*data++ = value;
caps = gst_caps_new_simple ("audio/x-raw-float",
"rate", G_TYPE_INT, sample_rate, "channels", G_TYPE_INT, 1,
"endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32, NULL);
gst_buffer_set_caps (buf, caps);
gst_caps_unref (caps);
ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
return buf;
}
static GstBuffer *
test_buffer_const_float_stereo (gint sample_rate, gsize n_frames,
gfloat value_l, gfloat value_r)
{
GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gfloat) * 2);
gfloat *data = (gfloat *) GST_BUFFER_DATA (buf);
GstCaps *caps;
gint i;
for (i = n_frames; i--;) {
*data++ = value_l;
*data++ = value_r;
}
caps = gst_caps_new_simple ("audio/x-raw-float",
"rate", G_TYPE_INT, sample_rate, "channels", G_TYPE_INT, 2,
"endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32, NULL);
gst_buffer_set_caps (buf, caps);
gst_caps_unref (caps);
ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
return buf;
}
static GstBuffer *
test_buffer_const_int16_mono (gint sample_rate, gint depth, gsize n_frames,
gint16 value)
{
GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gint16));
gint16 *data = (gint16 *) GST_BUFFER_DATA (buf);
GstCaps *caps;
gint i;
for (i = n_frames; i--;)
*data++ = value;
caps = gst_caps_new_simple ("audio/x-raw-int",
"rate", G_TYPE_INT, sample_rate, "channels", G_TYPE_INT, 1,
"endianness", G_TYPE_INT, G_BYTE_ORDER, "signed", G_TYPE_BOOLEAN, TRUE,
"width", G_TYPE_INT, 16, "depth", G_TYPE_INT, depth, NULL);
gst_buffer_set_caps (buf, caps);
gst_caps_unref (caps);
ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
return buf;
}
static GstBuffer *
test_buffer_const_int16_stereo (gint sample_rate, gint depth, gsize n_frames,
gint16 value_l, gint16 value_r)
{
GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gint16) * 2);
gint16 *data = (gint16 *) GST_BUFFER_DATA (buf);
GstCaps *caps;
gint i;
for (i = n_frames; i--;) {
*data++ = value_l;
*data++ = value_r;
}
caps = gst_caps_new_simple ("audio/x-raw-int",
"rate", G_TYPE_INT, sample_rate, "channels", G_TYPE_INT, 2,
"endianness", G_TYPE_INT, G_BYTE_ORDER, "signed", G_TYPE_BOOLEAN, TRUE,
"width", G_TYPE_INT, 16, "depth", G_TYPE_INT, depth, NULL);
gst_buffer_set_caps (buf, caps);
gst_caps_unref (caps);
ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
return buf;
}
/* Functions that create data buffers containing square signal
* waveforms. */
static GstBuffer *
test_buffer_square_float_mono (gint * accumulator, gint sample_rate,
gsize n_frames, gfloat value)
{
GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gfloat));
gfloat *data = (gfloat *) GST_BUFFER_DATA (buf);
GstCaps *caps;
gint i;
for (i = n_frames; i--;) {
*accumulator += 1;
*accumulator %= 96;
if (*accumulator < 48)
*data++ = value;
else
*data++ = -value;
}
caps = gst_caps_new_simple ("audio/x-raw-float",
"rate", G_TYPE_INT, sample_rate, "channels", G_TYPE_INT, 1,
"endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32, NULL);
gst_buffer_set_caps (buf, caps);
gst_caps_unref (caps);
ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
return buf;
}
static GstBuffer *
test_buffer_square_float_stereo (gint * accumulator, gint sample_rate,
gsize n_frames, gfloat value_l, gfloat value_r)
{
GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gfloat) * 2);
gfloat *data = (gfloat *) GST_BUFFER_DATA (buf);
GstCaps *caps;
gint i;
for (i = n_frames; i--;) {
*accumulator += 1;
*accumulator %= 96;
if (*accumulator < 48) {
*data++ = value_l;
*data++ = value_r;
} else {
*data++ = -value_l;
*data++ = -value_r;
}
}
caps = gst_caps_new_simple ("audio/x-raw-float",
"rate", G_TYPE_INT, sample_rate, "channels", G_TYPE_INT, 2,
"endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32, NULL);
gst_buffer_set_caps (buf, caps);
gst_caps_unref (caps);
ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
return buf;
}
static GstBuffer *
test_buffer_square_int16_mono (gint * accumulator, gint sample_rate,
gint depth, gsize n_frames, gint16 value)
{
GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gint16));
gint16 *data = (gint16 *) GST_BUFFER_DATA (buf);
GstCaps *caps;
gint i;
for (i = n_frames; i--;) {
*accumulator += 1;
*accumulator %= 96;
if (*accumulator < 48)
*data++ = value;
else
*data++ = -MAX (value, -32767);
}
caps = gst_caps_new_simple ("audio/x-raw-int",
"rate", G_TYPE_INT, sample_rate, "channels", G_TYPE_INT, 1,
"endianness", G_TYPE_INT, G_BYTE_ORDER, "signed", G_TYPE_BOOLEAN, TRUE,
"width", G_TYPE_INT, 16, "depth", G_TYPE_INT, depth, NULL);
gst_buffer_set_caps (buf, caps);
gst_caps_unref (caps);
ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
return buf;
}
static GstBuffer *
test_buffer_square_int16_stereo (gint * accumulator, gint sample_rate,
gint depth, gsize n_frames, gint16 value_l, gint16 value_r)
{
GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gint16) * 2);
gint16 *data = (gint16 *) GST_BUFFER_DATA (buf);
GstCaps *caps;
gint i;
for (i = n_frames; i--;) {
*accumulator += 1;
*accumulator %= 96;
if (*accumulator < 48) {
*data++ = value_l;
*data++ = value_r;
} else {
*data++ = -MAX (value_l, -32767);
*data++ = -MAX (value_r, -32767);
}
}
caps = gst_caps_new_simple ("audio/x-raw-int",
"rate", G_TYPE_INT, sample_rate, "channels", G_TYPE_INT, 2,
"endianness", G_TYPE_INT, G_BYTE_ORDER, "signed", G_TYPE_BOOLEAN, TRUE,
"width", G_TYPE_INT, 16, "depth", G_TYPE_INT, depth, NULL);
gst_buffer_set_caps (buf, caps);
gst_caps_unref (caps);
ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
return buf;
}
static void
push_buffer (GstBuffer * buf)
{
/* gst_pad_push steals a reference. */
fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK);
ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
}
/*** Start of the tests. ***/
/* This test looks redundant, but early versions of the element
* crashed when doing, well, nothing: */
GST_START_TEST (test_no_buffer)
{
GstElement *element = setup_rganalysis ();
set_playing_state (element);
send_eos_event (element);
poll_eos (element);
cleanup_rganalysis (element);
}
GST_END_TEST;
GST_START_TEST (test_no_buffer_album_1)
{
GstElement *element = setup_rganalysis ();
set_playing_state (element);
/* Single track: */
send_eos_event (element);
poll_eos (element);
/* First album: */
g_object_set (element, "num-tracks", 3, NULL);
send_eos_event (element);
poll_eos (element);
fail_unless_num_tracks (element, 2);
send_eos_event (element);
poll_eos (element);
fail_unless_num_tracks (element, 1);
send_eos_event (element);
poll_eos (element);
fail_unless_num_tracks (element, 0);
/* Second album: */
g_object_set (element, "num-tracks", 2, NULL);
send_eos_event (element);
poll_eos (element);
fail_unless_num_tracks (element, 1);
send_eos_event (element);
poll_eos (element);
fail_unless_num_tracks (element, 0);
/* Single track: */
send_eos_event (element);
poll_eos (element);
fail_unless_num_tracks (element, 0);
cleanup_rganalysis (element);
}
GST_END_TEST;
GST_START_TEST (test_no_buffer_album_2)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
gint accumulator = 0;
gint i;
g_object_set (element, "num-tracks", 3, NULL);
set_playing_state (element);
/* No buffer for the first track. */
send_eos_event (element);
/* No tags should be posted, there was nothing to analyze: */
poll_eos (element);
fail_unless_num_tracks (element, 2);
/* A test waveform with known gain result as second track: */
for (i = 20; i--;)
push_buffer (test_buffer_square_float_mono (&accumulator, 44100, 512,
0.25));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.25);
fail_unless_track_gain (tag_list, -6.20);
/* Album is not finished yet: */
fail_if_album_tags (tag_list);
gst_tag_list_free (tag_list);
fail_unless_num_tracks (element, 1);
/* No buffer for the last track. */
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_album_peak (tag_list, 0.25);
fail_unless_album_gain (tag_list, -6.20);
/* No track tags should be posted, as there was no data for it: */
fail_if_track_tags (tag_list);
gst_tag_list_free (tag_list);
fail_unless_num_tracks (element, 0);
cleanup_rganalysis (element);
}
GST_END_TEST;
GST_START_TEST (test_empty_buffers)
{
GstElement *element = setup_rganalysis ();
set_playing_state (element);
/* Single track: */
push_buffer (test_buffer_const_float_stereo (44100, 0, 0.0, 0.0));
send_eos_event (element);
poll_eos (element);
/* First album: */
g_object_set (element, "num-tracks", 2, NULL);
push_buffer (test_buffer_const_float_stereo (44100, 0, 0.0, 0.0));
send_eos_event (element);
poll_eos (element);
fail_unless_num_tracks (element, 1);
push_buffer (test_buffer_const_float_stereo (44100, 0, 0.0, 0.0));
send_eos_event (element);
poll_eos (element);
fail_unless_num_tracks (element, 0);
/* Second album, with a single track: */
g_object_set (element, "num-tracks", 1, NULL);
push_buffer (test_buffer_const_float_stereo (44100, 0, 0.0, 0.0));
send_eos_event (element);
poll_eos (element);
fail_unless_num_tracks (element, 0);
/* Single track: */
push_buffer (test_buffer_const_float_stereo (44100, 0, 0.0, 0.0));
send_eos_event (element);
poll_eos (element);
cleanup_rganalysis (element);
}
GST_END_TEST;
/* Tests for correctness of the peak values. */
/* Float peak test. For stereo, one channel has the constant value of -1.369,
* the other one 0.0. This tests many things: The result peak value should
* occur on any channel. The peak is of course the absolute amplitude, so 1.369
* should be the result. This will also detect if the code uses the absolute
* value during the comparison. If it is buggy it will return 0.0 since 0.0 >
* -1.369. Furthermore, this makes sure that there is no problem with headroom
* (exceeding 0dBFS). In the wild you get float samples > 1.0 from stuff like
* vorbis. */
GST_START_TEST (test_peak_float)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
set_playing_state (element);
push_buffer (test_buffer_const_float_stereo (8000, 512, -1.369, 0.0));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 1.369);
gst_tag_list_free (tag_list);
/* Swapped channels. */
push_buffer (test_buffer_const_float_stereo (8000, 512, 0.0, -1.369));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 1.369);
gst_tag_list_free (tag_list);
/* Mono. */
push_buffer (test_buffer_const_float_mono (8000, 512, -1.369));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 1.369);
gst_tag_list_free (tag_list);
cleanup_rganalysis (element);
}
GST_END_TEST;
GST_START_TEST (test_peak_int16_16)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
set_playing_state (element);
/* Half amplitude. */
push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, 1 << 14, 0));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
gst_tag_list_free (tag_list);
/* Swapped channels. */
push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, 0, 1 << 14));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
gst_tag_list_free (tag_list);
/* Mono. */
push_buffer (test_buffer_const_int16_mono (8000, 16, 512, 1 << 14));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
gst_tag_list_free (tag_list);
/* Half amplitude, negative variant. */
push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, -1 << 14, 0));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
gst_tag_list_free (tag_list);
/* Swapped channels. */
push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, 0, -1 << 14));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
gst_tag_list_free (tag_list);
/* Mono. */
push_buffer (test_buffer_const_int16_mono (8000, 16, 512, -1 << 14));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
gst_tag_list_free (tag_list);
/* Now check for correct normalization of the peak value: Sample
* values of this format range from -32768 to 32767. So for the
* highest positive amplitude we do not reach 1.0, only for
* -32768! */
push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, 32767, 0));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 32767. / 32768.);
gst_tag_list_free (tag_list);
/* Swapped channels. */
push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, 0, 32767));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 32767. / 32768.);
gst_tag_list_free (tag_list);
/* Mono. */
push_buffer (test_buffer_const_int16_mono (8000, 16, 512, 32767));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 32767. / 32768.);
gst_tag_list_free (tag_list);
/* Negative variant, reaching 1.0. */
push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, -32768, 0));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 1.0);
gst_tag_list_free (tag_list);
/* Swapped channels. */
push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, 0, -32768));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 1.0);
gst_tag_list_free (tag_list);
/* Mono. */
push_buffer (test_buffer_const_int16_mono (8000, 16, 512, -32768));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 1.0);
gst_tag_list_free (tag_list);
cleanup_rganalysis (element);
}
GST_END_TEST;
/* Same as the test before, but with 8 bits (packed into 16 bits). */
GST_START_TEST (test_peak_int16_8)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
set_playing_state (element);
/* Half amplitude. */
push_buffer (test_buffer_const_int16_stereo (8000, 8, 512, 1 << 6, 0));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
gst_tag_list_free (tag_list);
/* Swapped channels. */
push_buffer (test_buffer_const_int16_stereo (8000, 8, 512, 0, 1 << 6));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
gst_tag_list_free (tag_list);
/* Mono. */
push_buffer (test_buffer_const_int16_mono (8000, 8, 512, 1 << 6));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
gst_tag_list_free (tag_list);
/* Half amplitude, negative variant. */
push_buffer (test_buffer_const_int16_stereo (8000, 8, 512, -1 << 6, 0));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
gst_tag_list_free (tag_list);
/* Swapped channels. */
push_buffer (test_buffer_const_int16_stereo (8000, 8, 512, 0, -1 << 6));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
gst_tag_list_free (tag_list);
/* Mono. */
push_buffer (test_buffer_const_int16_mono (8000, 8, 512, -1 << 6));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
gst_tag_list_free (tag_list);
/* Almost full amplitude (maximum positive value). */
push_buffer (test_buffer_const_int16_stereo (8000, 8, 512, (1 << 7) - 1, 0));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.9921875);
gst_tag_list_free (tag_list);
/* Swapped channels. */
push_buffer (test_buffer_const_int16_stereo (8000, 8, 512, 0, (1 << 7) - 1));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.9921875);
gst_tag_list_free (tag_list);
/* Mono. */
push_buffer (test_buffer_const_int16_mono (8000, 8, 512, (1 << 7) - 1));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.9921875);
gst_tag_list_free (tag_list);
/* Full amplitude (maximum negative value). */
push_buffer (test_buffer_const_int16_stereo (8000, 8, 512, -1 << 7, 0));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 1.0);
gst_tag_list_free (tag_list);
/* Swapped channels. */
push_buffer (test_buffer_const_int16_stereo (8000, 8, 512, 0, -1 << 7));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 1.0);
gst_tag_list_free (tag_list);
/* Mono. */
push_buffer (test_buffer_const_int16_mono (8000, 8, 512, -1 << 7));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 1.0);
gst_tag_list_free (tag_list);
cleanup_rganalysis (element);
}
GST_END_TEST;
GST_START_TEST (test_peak_album)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
g_object_set (element, "num-tracks", 2, NULL);
set_playing_state (element);
push_buffer (test_buffer_const_float_stereo (8000, 1024, 1.0, 0.0));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 1.0);
fail_if_album_tags (tag_list);
gst_tag_list_free (tag_list);
fail_unless_num_tracks (element, 1);
push_buffer (test_buffer_const_float_stereo (8000, 1024, 0.0, 0.5));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
fail_unless_album_peak (tag_list, 1.0);
gst_tag_list_free (tag_list);
fail_unless_num_tracks (element, 0);
/* Try a second album: */
g_object_set (element, "num-tracks", 3, NULL);
push_buffer (test_buffer_const_float_stereo (8000, 1024, 0.4, 0.4));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.4);
fail_if_album_tags (tag_list);
gst_tag_list_free (tag_list);
fail_unless_num_tracks (element, 2);
push_buffer (test_buffer_const_float_stereo (8000, 1024, 0.45, 0.45));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.45);
fail_if_album_tags (tag_list);
gst_tag_list_free (tag_list);
fail_unless_num_tracks (element, 1);
push_buffer (test_buffer_const_float_stereo (8000, 1024, 0.2, 0.2));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.2);
fail_unless_album_peak (tag_list, 0.45);
gst_tag_list_free (tag_list);
fail_unless_num_tracks (element, 0);
/* And now a single track, not in album mode (num-tracks is 0
* now): */
push_buffer (test_buffer_const_float_stereo (8000, 1024, 0.1, 0.1));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.1);
fail_if_album_tags (tag_list);
gst_tag_list_free (tag_list);
cleanup_rganalysis (element);
}
GST_END_TEST;
/* Switching from track to album mode. */
GST_START_TEST (test_peak_track_album)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
set_playing_state (element);
push_buffer (test_buffer_const_float_mono (8000, 1024, 1.0));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 1.0);
fail_if_album_tags (tag_list);
gst_tag_list_free (tag_list);
g_object_set (element, "num-tracks", 1, NULL);
push_buffer (test_buffer_const_float_mono (8000, 1024, 0.5));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
fail_unless_album_peak (tag_list, 0.5);
gst_tag_list_free (tag_list);
fail_unless_num_tracks (element, 0);
cleanup_rganalysis (element);
}
GST_END_TEST;
/* Disabling album processing before the end of the album. Probably a rare edge
* case and applications should not rely on this to work. They need to send the
* element to the READY state to clear up after an aborted album anyway since
* they might need to process another album afterwards. */
GST_START_TEST (test_peak_album_abort_to_track)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
g_object_set (element, "num-tracks", 2, NULL);
set_playing_state (element);
push_buffer (test_buffer_const_float_stereo (8000, 1024, 1.0, 0.0));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 1.0);
fail_if_album_tags (tag_list);
gst_tag_list_free (tag_list);
fail_unless_num_tracks (element, 1);
g_object_set (element, "num-tracks", 0, NULL);
push_buffer (test_buffer_const_float_stereo (8000, 1024, 0.0, 0.5));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
fail_if_album_tags (tag_list);
gst_tag_list_free (tag_list);
cleanup_rganalysis (element);
}
GST_END_TEST;
GST_START_TEST (test_gain_album)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
gint accumulator;
gint i;
g_object_set (element, "num-tracks", 3, NULL);
set_playing_state (element);
/* The three tracks are constructed such that if any of these is in fact
* ignored for the album gain, the album gain will differ. */
accumulator = 0;
for (i = 8; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
0.75, 0.75));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.75);
fail_unless_track_gain (tag_list, -15.70);
fail_if_album_tags (tag_list);
gst_tag_list_free (tag_list);
accumulator = 0;
for (i = 12; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
0.5, 0.5));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.5);
fail_unless_track_gain (tag_list, -12.22);
fail_if_album_tags (tag_list);
gst_tag_list_free (tag_list);
accumulator = 0;
for (i = 180; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
0.25, 0.25));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.25);
fail_unless_track_gain (tag_list, -6.20);
fail_unless_album_peak (tag_list, 0.75);
/* Strangely, wavegain reports -12.17 for the album, but the fixed
* metaflac agrees to us. Could be a 32767 vs. 32768 issue. */
fail_unless_album_gain (tag_list, -12.18);
gst_tag_list_free (tag_list);
cleanup_rganalysis (element);
}
GST_END_TEST;
/* Checks ensuring that the "forced" property works as advertised. */
GST_START_TEST (test_forced)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
gint accumulator = 0;
gint i;
g_object_set (element, "forced", FALSE, NULL);
set_playing_state (element);
tag_list = gst_tag_list_new ();
/* Provided values are totally arbitrary. */
gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
GST_TAG_TRACK_PEAK, 1.0, GST_TAG_TRACK_GAIN, 2.21, NULL);
send_tag_event (element, tag_list);
for (i = 20; i--;)
push_buffer (test_buffer_const_float_stereo (44100, 512, 0.5, 0.5));
send_eos_event (element);
/* This fails if a tag message is generated: */
poll_eos (element);
/* Now back to a track without tags. */
for (i = 20; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
0.25, 0.25));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.25);
fail_unless_track_gain (tag_list, get_expected_gain (44100));
gst_tag_list_free (tag_list);
cleanup_rganalysis (element);
}
GST_END_TEST;
/* Sending track gain and peak in separate tag lists. */
GST_START_TEST (test_forced_separate)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
gint accumulator = 0;
gint i;
g_object_set (element, "forced", FALSE, NULL);
set_playing_state (element);
tag_list = gst_tag_list_new ();
gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND, GST_TAG_TRACK_GAIN, 2.21,
NULL);
send_tag_event (element, tag_list);
tag_list = gst_tag_list_new ();
gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND, GST_TAG_TRACK_PEAK, 1.0,
NULL);
send_tag_event (element, tag_list);
for (i = 20; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
0.5, 0.5));
send_eos_event (element);
/* This fails if a tag message is generated: */
poll_eos (element);
/* Now a track without tags. */
accumulator = 0;
for (i = 20; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
0.25, 0.25));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.25);
fail_unless_track_gain (tag_list, get_expected_gain (44100));
fail_if_album_tags (tag_list);
gst_tag_list_free (tag_list);
cleanup_rganalysis (element);
}
GST_END_TEST;
/* A TAG event is sent _after_ data has already been processed. In real
* pipelines, this could happen if there is more than one rganalysis element (by
* accident). While it would have analyzed all the data prior to receiving the
* event, I expect it to not post its results if not forced. This test is
* almost equivalent to test_forced. */
GST_START_TEST (test_forced_after_data)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
gint accumulator = 0;
gint i;
g_object_set (element, "forced", FALSE, NULL);
set_playing_state (element);
for (i = 20; i--;)
push_buffer (test_buffer_const_float_stereo (8000, 512, 0.5, 0.5));
tag_list = gst_tag_list_new ();
gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
GST_TAG_TRACK_PEAK, 1.0, GST_TAG_TRACK_GAIN, 2.21, NULL);
send_tag_event (element, tag_list);
send_eos_event (element);
poll_eos (element);
/* Now back to a normal track, this one has no tags: */
for (i = 20; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 8000, 512, 0.25,
0.25));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.25);
fail_unless_track_gain (tag_list, get_expected_gain (8000));
gst_tag_list_free (tag_list);
cleanup_rganalysis (element);
}
GST_END_TEST;
/* Like test_forced, but *analyze* an album afterwards. The two tests following
* this one check the *skipping* of albums. */
GST_START_TEST (test_forced_album)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
gint accumulator;
gint i;
g_object_set (element, "forced", FALSE, NULL);
set_playing_state (element);
tag_list = gst_tag_list_new ();
/* Provided values are totally arbitrary. */
gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
GST_TAG_TRACK_PEAK, 1.0, GST_TAG_TRACK_GAIN, 2.21, NULL);
send_tag_event (element, tag_list);
accumulator = 0;
for (i = 20; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
0.5, 0.5));
send_eos_event (element);
/* This fails if a tag message is generated: */
poll_eos (element);
/* Now an album without tags. */
g_object_set (element, "num-tracks", 2, NULL);
accumulator = 0;
for (i = 20; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
0.25, 0.25));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.25);
fail_unless_track_gain (tag_list, get_expected_gain (44100));
fail_if_album_tags (tag_list);
gst_tag_list_free (tag_list);
fail_unless_num_tracks (element, 1);
accumulator = 0;
for (i = 20; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
0.25, 0.25));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.25);
fail_unless_track_gain (tag_list, get_expected_gain (44100));
fail_unless_album_peak (tag_list, 0.25);
fail_unless_album_gain (tag_list, get_expected_gain (44100));
gst_tag_list_free (tag_list);
fail_unless_num_tracks (element, 0);
cleanup_rganalysis (element);
}
GST_END_TEST;
GST_START_TEST (test_forced_album_skip)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
gint accumulator = 0;
gint i;
g_object_set (element, "forced", FALSE, "num-tracks", 2, NULL);
set_playing_state (element);
tag_list = gst_tag_list_new ();
/* Provided values are totally arbitrary. */
gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
GST_TAG_TRACK_PEAK, 0.75, GST_TAG_TRACK_GAIN, 2.21,
GST_TAG_ALBUM_PEAK, 0.80, GST_TAG_ALBUM_GAIN, -0.11, NULL);
send_tag_event (element, tag_list);
for (i = 20; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 8000, 512, 0.25,
0.25));
send_eos_event (element);
poll_eos (element);
fail_unless_num_tracks (element, 1);
/* This track has no tags, but needs to be skipped anyways since we
* are in album processing mode. */
for (i = 20; i--;)
push_buffer (test_buffer_const_float_stereo (8000, 512, 0.0, 0.0));
send_eos_event (element);
poll_eos (element);
fail_unless_num_tracks (element, 0);
/* Normal track after the album. Of course not to be skipped. */
accumulator = 0;
for (i = 20; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 8000, 512, 0.25,
0.25));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.25);
fail_unless_track_gain (tag_list, get_expected_gain (8000));
fail_if_album_tags (tag_list);
gst_tag_list_free (tag_list);
cleanup_rganalysis (element);
}
GST_END_TEST;
GST_START_TEST (test_forced_album_no_skip)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
gint accumulator = 0;
gint i;
g_object_set (element, "forced", FALSE, "num-tracks", 2, NULL);
set_playing_state (element);
for (i = 20; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 8000, 512, 0.25,
0.25));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.25);
fail_unless_track_gain (tag_list, get_expected_gain (8000));
fail_if_album_tags (tag_list);
gst_tag_list_free (tag_list);
fail_unless_num_tracks (element, 1);
/* The second track has indeed full tags, but although being not forced, this
* one has to be processed because album processing is on. */
tag_list = gst_tag_list_new ();
/* Provided values are totally arbitrary. */
gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
GST_TAG_TRACK_PEAK, 0.75, GST_TAG_TRACK_GAIN, 2.21,
GST_TAG_ALBUM_PEAK, 0.80, GST_TAG_ALBUM_GAIN, -0.11, NULL);
send_tag_event (element, tag_list);
for (i = 20; i--;)
push_buffer (test_buffer_const_float_stereo (8000, 512, 0.0, 0.0));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.0);
fail_unless_track_gain (tag_list, SILENCE_GAIN);
/* Second track was just silence so the album peak equals the first
* track's peak. */
fail_unless_album_peak (tag_list, 0.25);
/* Statistical processing leads to the second track being
* ignored for the gain (because it is so short): */
fail_unless_album_gain (tag_list, get_expected_gain (8000));
gst_tag_list_free (tag_list);
fail_unless_num_tracks (element, 0);
cleanup_rganalysis (element);
}
GST_END_TEST;
GST_START_TEST (test_forced_abort_album_no_skip)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
gint accumulator = 0;
gint i;
g_object_set (element, "forced", FALSE, "num-tracks", 2, NULL);
set_playing_state (element);
for (i = 20; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 8000, 512, 0.25,
0.25));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.25);
fail_unless_track_gain (tag_list, get_expected_gain (8000));
fail_if_album_tags (tag_list);
gst_tag_list_free (tag_list);
fail_unless_num_tracks (element, 1);
/* Disabling album processing before end of album: */
g_object_set (element, "num-tracks", 0, NULL);
/* Processing a track that has to be skipped. */
tag_list = gst_tag_list_new ();
/* Provided values are totally arbitrary. */
gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
GST_TAG_TRACK_PEAK, 0.75, GST_TAG_TRACK_GAIN, 2.21,
GST_TAG_ALBUM_PEAK, 0.80, GST_TAG_ALBUM_GAIN, -0.11, NULL);
send_tag_event (element, tag_list);
for (i = 20; i--;)
push_buffer (test_buffer_const_float_stereo (8000, 512, 0.0, 0.0));
send_eos_event (element);
poll_eos (element);
cleanup_rganalysis (element);
}
GST_END_TEST;
GST_START_TEST (test_reference_level)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
gdouble ref_level;
gint accumulator = 0;
gint i;
set_playing_state (element);
for (i = 20; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
0.25, 0.25));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.25);
fail_unless_track_gain (tag_list, get_expected_gain (44100));
fail_if_album_tags (tag_list);
fail_unless (gst_tag_list_get_double (tag_list, GST_TAG_REFERENCE_LEVEL,
&ref_level) && MATCH_GAIN (ref_level, 89.),
"Incorrect reference level tag");
gst_tag_list_free (tag_list);
g_object_set (element, "reference-level", 83., "num-tracks", 2, NULL);
for (i = 20; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
0.25, 0.25));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.25);
fail_unless_track_gain (tag_list, get_expected_gain (44100) - 6.);
fail_if_album_tags (tag_list);
fail_unless (gst_tag_list_get_double (tag_list, GST_TAG_REFERENCE_LEVEL,
&ref_level) && MATCH_GAIN (ref_level, 83.),
"Incorrect reference level tag");
gst_tag_list_free (tag_list);
accumulator = 0;
for (i = 20; i--;)
push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
0.25, 0.25));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.25);
fail_unless_track_gain (tag_list, get_expected_gain (44100) - 6.);
fail_unless_album_peak (tag_list, 0.25);
/* We provided the same waveform twice, with a reset separating
* them. Therefore, the album gain matches the track gain. */
fail_unless_album_gain (tag_list, get_expected_gain (44100) - 6.);
fail_unless (gst_tag_list_get_double (tag_list, GST_TAG_REFERENCE_LEVEL,
&ref_level) && MATCH_GAIN (ref_level, 83.),
"Incorrect reference level tag");
gst_tag_list_free (tag_list);
cleanup_rganalysis (element);
}
GST_END_TEST;
GST_START_TEST (test_all_formats)
{
GstElement *element = setup_rganalysis ();
GstTagList *tag_list;
gint accumulator = 0;
gint i, j;
set_playing_state (element);
for (i = G_N_ELEMENTS (supported_rates); i--;) {
accumulator = 0;
for (j = 0; j < 4; j++)
push_buffer (test_buffer_square_float_stereo (&accumulator,
supported_rates[i].sample_rate, 512, 0.25, 0.25));
for (j = 0; j < 3; j++)
push_buffer (test_buffer_square_float_mono (&accumulator,
supported_rates[i].sample_rate, 512, 0.25));
for (j = 0; j < 4; j++)
push_buffer (test_buffer_square_int16_stereo (&accumulator,
supported_rates[i].sample_rate, 16, 512, 1 << 13, 1 << 13));
for (j = 0; j < 3; j++)
push_buffer (test_buffer_square_int16_mono (&accumulator,
supported_rates[i].sample_rate, 16, 512, 1 << 13));
for (j = 0; j < 3; j++)
push_buffer (test_buffer_square_int16_stereo (&accumulator,
supported_rates[i].sample_rate, 8, 512, 1 << 5, 1 << 5));
for (j = 0; j < 3; j++)
push_buffer (test_buffer_square_int16_mono (&accumulator,
supported_rates[i].sample_rate, 8, 512, 1 << 5));
send_eos_event (element);
tag_list = poll_tags (element);
fail_unless_track_peak (tag_list, 0.25);
fail_unless_track_gain (tag_list, supported_rates[i].gain);
gst_tag_list_free (tag_list);
}
cleanup_rganalysis (element);
}
GST_END_TEST;
/* Checks ensuring all advertised supported sample rates are really
* accepted, for integer and float, mono and stereo. This also
* verifies that the correct gain is computed for all formats (except
* odd bit depths). */
#define MAKE_GAIN_TEST_FLOAT_MONO(sample_rate) \
GST_START_TEST (test_gain_float_mono_##sample_rate) \
{ \
GstElement *element = setup_rganalysis (); \
GstTagList *tag_list; \
gint accumulator = 0; \
gint i; \
\
set_playing_state (element); \
\
for (i = 0; i < 20; i++) \
push_buffer (test_buffer_square_float_mono (&accumulator, \
sample_rate, 512, 0.25)); \
send_eos_event (element); \
tag_list = poll_tags (element); \
fail_unless_track_peak (tag_list, 0.25); \
fail_unless_track_gain (tag_list, \
get_expected_gain (sample_rate)); \
gst_tag_list_free (tag_list); \
\
cleanup_rganalysis (element); \
} \
\
GST_END_TEST;
#define MAKE_GAIN_TEST_FLOAT_STEREO(sample_rate) \
GST_START_TEST (test_gain_float_stereo_##sample_rate) \
{ \
GstElement *element = setup_rganalysis (); \
GstTagList *tag_list; \
gint accumulator = 0; \
gint i; \
\
set_playing_state (element); \
\
for (i = 0; i < 20; i++) \
push_buffer (test_buffer_square_float_stereo (&accumulator, \
sample_rate, 512, 0.25, 0.25)); \
send_eos_event (element); \
tag_list = poll_tags (element); \
fail_unless_track_peak (tag_list, 0.25); \
fail_unless_track_gain (tag_list, \
get_expected_gain (sample_rate)); \
gst_tag_list_free (tag_list); \
\
cleanup_rganalysis (element); \
} \
\
GST_END_TEST;
#define MAKE_GAIN_TEST_INT16_MONO(sample_rate, depth) \
GST_START_TEST (test_gain_int16_##depth##_mono_##sample_rate) \
{ \
GstElement *element = setup_rganalysis (); \
GstTagList *tag_list; \
gint accumulator = 0; \
gint i; \
\
set_playing_state (element); \
\
for (i = 0; i < 20; i++) \
push_buffer (test_buffer_square_int16_mono (&accumulator, \
sample_rate, depth, 512, 1 << (13 + depth - 16))); \
\
send_eos_event (element); \
tag_list = poll_tags (element); \
fail_unless_track_peak (tag_list, 0.25); \
fail_unless_track_gain (tag_list, \
get_expected_gain (sample_rate)); \
gst_tag_list_free (tag_list); \
\
cleanup_rganalysis (element); \
} \
\
GST_END_TEST;
#define MAKE_GAIN_TEST_INT16_STEREO(sample_rate, depth) \
GST_START_TEST (test_gain_int16_##depth##_stereo_##sample_rate) \
{ \
GstElement *element = setup_rganalysis (); \
GstTagList *tag_list; \
gint accumulator = 0; \
gint i; \
\
set_playing_state (element); \
\
for (i = 0; i < 20; i++) \
push_buffer (test_buffer_square_int16_stereo (&accumulator, \
sample_rate, depth, 512, 1 << (13 + depth - 16), \
1 << (13 + depth - 16))); \
send_eos_event (element); \
tag_list = poll_tags (element); \
fail_unless_track_peak (tag_list, 0.25); \
fail_unless_track_gain (tag_list, \
get_expected_gain (sample_rate)); \
gst_tag_list_free (tag_list); \
\
cleanup_rganalysis (element); \
} \
\
GST_END_TEST;
MAKE_GAIN_TEST_FLOAT_MONO (8000);
MAKE_GAIN_TEST_FLOAT_MONO (11025);
MAKE_GAIN_TEST_FLOAT_MONO (12000);
MAKE_GAIN_TEST_FLOAT_MONO (16000);
MAKE_GAIN_TEST_FLOAT_MONO (22050);
MAKE_GAIN_TEST_FLOAT_MONO (24000);
MAKE_GAIN_TEST_FLOAT_MONO (32000);
MAKE_GAIN_TEST_FLOAT_MONO (44100);
MAKE_GAIN_TEST_FLOAT_MONO (48000);
MAKE_GAIN_TEST_FLOAT_STEREO (8000);
MAKE_GAIN_TEST_FLOAT_STEREO (11025);
MAKE_GAIN_TEST_FLOAT_STEREO (12000);
MAKE_GAIN_TEST_FLOAT_STEREO (16000);
MAKE_GAIN_TEST_FLOAT_STEREO (22050);
MAKE_GAIN_TEST_FLOAT_STEREO (24000);
MAKE_GAIN_TEST_FLOAT_STEREO (32000);
MAKE_GAIN_TEST_FLOAT_STEREO (44100);
MAKE_GAIN_TEST_FLOAT_STEREO (48000);
MAKE_GAIN_TEST_INT16_MONO (8000, 16);
MAKE_GAIN_TEST_INT16_MONO (11025, 16);
MAKE_GAIN_TEST_INT16_MONO (12000, 16);
MAKE_GAIN_TEST_INT16_MONO (16000, 16);
MAKE_GAIN_TEST_INT16_MONO (22050, 16);
MAKE_GAIN_TEST_INT16_MONO (24000, 16);
MAKE_GAIN_TEST_INT16_MONO (32000, 16);
MAKE_GAIN_TEST_INT16_MONO (44100, 16);
MAKE_GAIN_TEST_INT16_MONO (48000, 16);
MAKE_GAIN_TEST_INT16_STEREO (8000, 16);
MAKE_GAIN_TEST_INT16_STEREO (11025, 16);
MAKE_GAIN_TEST_INT16_STEREO (12000, 16);
MAKE_GAIN_TEST_INT16_STEREO (16000, 16);
MAKE_GAIN_TEST_INT16_STEREO (22050, 16);
MAKE_GAIN_TEST_INT16_STEREO (24000, 16);
MAKE_GAIN_TEST_INT16_STEREO (32000, 16);
MAKE_GAIN_TEST_INT16_STEREO (44100, 16);
MAKE_GAIN_TEST_INT16_STEREO (48000, 16);
MAKE_GAIN_TEST_INT16_MONO (8000, 8);
MAKE_GAIN_TEST_INT16_MONO (11025, 8);
MAKE_GAIN_TEST_INT16_MONO (12000, 8);
MAKE_GAIN_TEST_INT16_MONO (16000, 8);
MAKE_GAIN_TEST_INT16_MONO (22050, 8);
MAKE_GAIN_TEST_INT16_MONO (24000, 8);
MAKE_GAIN_TEST_INT16_MONO (32000, 8);
MAKE_GAIN_TEST_INT16_MONO (44100, 8);
MAKE_GAIN_TEST_INT16_MONO (48000, 8);
MAKE_GAIN_TEST_INT16_STEREO (8000, 8);
MAKE_GAIN_TEST_INT16_STEREO (11025, 8);
MAKE_GAIN_TEST_INT16_STEREO (12000, 8);
MAKE_GAIN_TEST_INT16_STEREO (16000, 8);
MAKE_GAIN_TEST_INT16_STEREO (22050, 8);
MAKE_GAIN_TEST_INT16_STEREO (24000, 8);
MAKE_GAIN_TEST_INT16_STEREO (32000, 8);
MAKE_GAIN_TEST_INT16_STEREO (44100, 8);
MAKE_GAIN_TEST_INT16_STEREO (48000, 8);
Suite *
rganalysis_suite (void)
{
Suite *s = suite_create ("rganalysis");
TCase *tc_chain = tcase_create ("general");
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_no_buffer);
tcase_add_test (tc_chain, test_no_buffer_album_1);
tcase_add_test (tc_chain, test_no_buffer_album_2);
tcase_add_test (tc_chain, test_empty_buffers);
tcase_add_test (tc_chain, test_peak_float);
tcase_add_test (tc_chain, test_peak_int16_16);
tcase_add_test (tc_chain, test_peak_int16_8);
tcase_add_test (tc_chain, test_peak_album);
tcase_add_test (tc_chain, test_peak_track_album);
tcase_add_test (tc_chain, test_peak_album_abort_to_track);
tcase_add_test (tc_chain, test_gain_album);
tcase_add_test (tc_chain, test_forced);
tcase_add_test (tc_chain, test_forced_separate);
tcase_add_test (tc_chain, test_forced_after_data);
tcase_add_test (tc_chain, test_forced_album);
tcase_add_test (tc_chain, test_forced_album_skip);
tcase_add_test (tc_chain, test_forced_album_no_skip);
tcase_add_test (tc_chain, test_forced_abort_album_no_skip);
tcase_add_test (tc_chain, test_reference_level);
tcase_add_test (tc_chain, test_all_formats);
tcase_add_test (tc_chain, test_gain_float_mono_8000);
tcase_add_test (tc_chain, test_gain_float_mono_11025);
tcase_add_test (tc_chain, test_gain_float_mono_12000);
tcase_add_test (tc_chain, test_gain_float_mono_16000);
tcase_add_test (tc_chain, test_gain_float_mono_22050);
tcase_add_test (tc_chain, test_gain_float_mono_24000);
tcase_add_test (tc_chain, test_gain_float_mono_32000);
tcase_add_test (tc_chain, test_gain_float_mono_44100);
tcase_add_test (tc_chain, test_gain_float_mono_48000);
tcase_add_test (tc_chain, test_gain_float_stereo_8000);
tcase_add_test (tc_chain, test_gain_float_stereo_11025);
tcase_add_test (tc_chain, test_gain_float_stereo_12000);
tcase_add_test (tc_chain, test_gain_float_stereo_16000);
tcase_add_test (tc_chain, test_gain_float_stereo_22050);
tcase_add_test (tc_chain, test_gain_float_stereo_24000);
tcase_add_test (tc_chain, test_gain_float_stereo_32000);
tcase_add_test (tc_chain, test_gain_float_stereo_44100);
tcase_add_test (tc_chain, test_gain_float_stereo_48000);
tcase_add_test (tc_chain, test_gain_int16_16_mono_8000);
tcase_add_test (tc_chain, test_gain_int16_16_mono_11025);
tcase_add_test (tc_chain, test_gain_int16_16_mono_12000);
tcase_add_test (tc_chain, test_gain_int16_16_mono_16000);
tcase_add_test (tc_chain, test_gain_int16_16_mono_22050);
tcase_add_test (tc_chain, test_gain_int16_16_mono_24000);
tcase_add_test (tc_chain, test_gain_int16_16_mono_32000);
tcase_add_test (tc_chain, test_gain_int16_16_mono_44100);
tcase_add_test (tc_chain, test_gain_int16_16_mono_48000);
tcase_add_test (tc_chain, test_gain_int16_16_stereo_8000);
tcase_add_test (tc_chain, test_gain_int16_16_stereo_11025);
tcase_add_test (tc_chain, test_gain_int16_16_stereo_12000);
tcase_add_test (tc_chain, test_gain_int16_16_stereo_16000);
tcase_add_test (tc_chain, test_gain_int16_16_stereo_22050);
tcase_add_test (tc_chain, test_gain_int16_16_stereo_24000);
tcase_add_test (tc_chain, test_gain_int16_16_stereo_32000);
tcase_add_test (tc_chain, test_gain_int16_16_stereo_44100);
tcase_add_test (tc_chain, test_gain_int16_16_stereo_48000);
tcase_add_test (tc_chain, test_gain_int16_8_mono_8000);
tcase_add_test (tc_chain, test_gain_int16_8_mono_11025);
tcase_add_test (tc_chain, test_gain_int16_8_mono_12000);
tcase_add_test (tc_chain, test_gain_int16_8_mono_16000);
tcase_add_test (tc_chain, test_gain_int16_8_mono_22050);
tcase_add_test (tc_chain, test_gain_int16_8_mono_24000);
tcase_add_test (tc_chain, test_gain_int16_8_mono_32000);
tcase_add_test (tc_chain, test_gain_int16_8_mono_44100);
tcase_add_test (tc_chain, test_gain_int16_8_mono_48000);
tcase_add_test (tc_chain, test_gain_int16_8_stereo_8000);
tcase_add_test (tc_chain, test_gain_int16_8_stereo_11025);
tcase_add_test (tc_chain, test_gain_int16_8_stereo_12000);
tcase_add_test (tc_chain, test_gain_int16_8_stereo_16000);
tcase_add_test (tc_chain, test_gain_int16_8_stereo_22050);
tcase_add_test (tc_chain, test_gain_int16_8_stereo_24000);
tcase_add_test (tc_chain, test_gain_int16_8_stereo_32000);
tcase_add_test (tc_chain, test_gain_int16_8_stereo_44100);
tcase_add_test (tc_chain, test_gain_int16_8_stereo_48000);
return s;
}
int
main (int argc, char **argv)
{
gint nf;
Suite *s = rganalysis_suite ();
SRunner *sr = srunner_create (s);
gst_check_init (&argc, &argv);
srunner_run_all (sr, CK_ENV);
nf = srunner_ntests_failed (sr);
srunner_free (sr);
return nf;
}