From 45ef6b5e13fc65283793e906a8fc144976b3e793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 29 May 2008 11:34:09 +0000 Subject: [PATCH] gst-libs/gst/audio/multichannel.c: Allow rear center together with rear left/right and other previously conflicting c... Original commit message from CVS: * gst-libs/gst/audio/multichannel.c: (gst_audio_check_channel_positions), (gst_audio_set_structure_channel_positions_list), (gst_audio_fixate_channel_positions): Allow rear center together with rear left/right and other previously conflicting channel positions. The reason why they weren't allowed was the channel mixing implementation in audioconvert. Also take this into account when fixing channel layouts. Allow setting channel positions for 1/2 channels when using gst_audio_set_structure_channel_position(). * gst/audioconvert/gstchannelmix.c: (gst_channel_mix_fill_compatible), (gst_channel_mix_detect_pos), (gst_channel_mix_fill_one_other), (gst_channel_mix_fill_others), (gst_channel_mix_fill_special), (gst_channel_mix_fill_matrix): Major rewrite of the channel mixing. We now allow previously conflicting channel positions to appear together (rear center and rear left/right for example). Fixes bug #533817. Rework the way channels are mixed together to take more possible channel positions into account, properly mix from/to side channels and don't assume that either center, left&right or nothing of a specific position is available anymore. * tests/check/elements/audioconvert.c: (GST_START_TEST): Adjust unit tests with non-standard 1/2 channel layouts to the more correct new behaviour. Add a unit test for 5.1->Stereo downmixing. --- ChangeLog | 35 ++ gst-libs/gst/audio/multichannel.c | 52 +-- gst/audioconvert/gstchannelmix.c | 596 ++++++++++++++-------------- tests/check/elements/audioconvert.c | 17 +- 4 files changed, 364 insertions(+), 336 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6e6aa23cae..118f43160a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,38 @@ +2008-05-29 Sebastian Dröge + + * gst-libs/gst/audio/multichannel.c: + (gst_audio_check_channel_positions), + (gst_audio_set_structure_channel_positions_list), + (gst_audio_fixate_channel_positions): + Allow rear center together with rear left/right and other previously + conflicting channel positions. The reason why they weren't allowed + was the channel mixing implementation in audioconvert. + Also take this into account when fixing channel layouts. + + Allow setting channel positions for 1/2 channels when using + gst_audio_set_structure_channel_position(). + + * gst/audioconvert/gstchannelmix.c: + (gst_channel_mix_fill_compatible), (gst_channel_mix_detect_pos), + (gst_channel_mix_fill_one_other), (gst_channel_mix_fill_others), + (gst_channel_mix_fill_special), (gst_channel_mix_fill_matrix): + Major rewrite of the channel mixing. + + We now allow previously conflicting channel positions to appear + together (rear center and rear left/right for example). + Fixes bug #533817. + + Rework the way channels are mixed together to take more possible + channel positions into account, properly mix from/to side channels + and don't assume that either center, left&right or nothing of a + specific position is available anymore. + + * tests/check/elements/audioconvert.c: (GST_START_TEST): + Adjust unit tests with non-standard 1/2 channel layouts to the more + correct new behaviour. + + Add a unit test for 5.1->Stereo downmixing. + 2008-05-29 Sebastian Dröge * ext/vorbis/vorbisdec.c: (vorbis_handle_identification_packet): diff --git a/gst-libs/gst/audio/multichannel.c b/gst-libs/gst/audio/multichannel.c index 435b1c1d96..2a28394295 100644 --- a/gst-libs/gst/audio/multichannel.c +++ b/gst-libs/gst/audio/multichannel.c @@ -28,13 +28,10 @@ /* * This function checks if basic assumptions apply: * - does each position occur at most once? + * - invalid positions? * - do conflicting positions occur? * + front_mono vs. front_left/right - * + front_center vs. front_left/right_of_center - * + rear_center vs. rear_left/right - * It also adds some hacks that 0.8.x needs for compatibility: - * - if channels == 1, are we really mono? - * - if channels == 2, are we really stereo? + * + none vs not-none */ static gboolean @@ -51,17 +48,7 @@ gst_audio_check_channel_positions (const GstAudioChannelPosition * pos, { { GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, { - GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}}, - /* front center: 2 <-> 1 */ - { { - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, { - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}}, - /* rear: 2 <-> 1 */ - { { - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, { - GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}}, { { + GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}}, { { GST_AUDIO_CHANNEL_POSITION_INVALID}} }; @@ -133,17 +120,6 @@ gst_audio_check_channel_positions (const GstAudioChannelPosition * pos, } } - /* Throw warning if we encounter an unusual 2-channel configuration, - * at least until someone finds a reason why we should not */ -#if 0 - if (channels == 2 && (pos[0] != GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT || - pos[1] != GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT)) { - g_warning ("channels=2 implies stereo, but channel positions are " - "< %d, %d>", pos[0], pos[1]); - return FALSE; - } -#endif - return TRUE; } @@ -373,11 +349,6 @@ gst_audio_set_structure_channel_positions_list (GstStructure * str, g_return_if_fail (res); g_return_if_fail (channels > 0); - /* 0.8.x: channels=1 or channels=2 is mono/stereo, no positions needed - * there (we discard them anyway) */ - if (channels == 1 || channels == 2) - return; - /* create the array of lists */ g_value_init (&pos_val_arr, GST_TYPE_ARRAY); g_value_init (&pos_val_entry, GST_TYPE_AUDIO_CHANNEL_POSITION); @@ -525,19 +496,20 @@ gst_audio_fixate_channel_positions (GstStructure * str) const GstAudioChannelPosition pos2[1]; } conf[] = { /* front: mono <-> stereo */ - { { - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + { + { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, { - GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}}, - /* front center: 2 <-> 1 */ - { { + GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}}, { { GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, { - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}}, - /* rear: 2 <-> 1 */ - { { + GST_AUDIO_CHANNEL_POSITION_INVALID}}, { { + GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID}, { + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}}, { { GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, { + GST_AUDIO_CHANNEL_POSITION_INVALID}}, { { + GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID}, { GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}}, { { GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID}, { GST_AUDIO_CHANNEL_POSITION_LFE}}, { { diff --git a/gst/audioconvert/gstchannelmix.c b/gst/audioconvert/gstchannelmix.c index 8be1dd75c7..9f681c307b 100644 --- a/gst/audioconvert/gstchannelmix.c +++ b/gst/audioconvert/gstchannelmix.c @@ -1,5 +1,6 @@ /* GStreamer * Copyright (C) 2004 Ronald Bultje + * Copyright (C) 2008 Sebastian Dröge * * gstchannelmix.c: setup of channel conversion matrices * @@ -110,53 +111,64 @@ gst_channel_mix_fill_compatible (AudioConvertCtx * this) }; gint c; - /* conversions from compatible (but not the same) channel schemes. This - * goes two ways: if the sink has both pos1[0,1] and src has pos2[0] or - * if the src has both pos1[0,1] and sink has pos2[0], then we do the - * conversion. We hereby assume that the existance of pos1[0,1] and - * pos2[0] are mututally exclusive. There are no checks for that, - * unfortunately. This shouldn't lead to issues (like crashes or so), - * though. */ + /* conversions from compatible (but not the same) channel schemes */ for (c = 0; conv[c].pos1[0] != GST_AUDIO_CHANNEL_POSITION_INVALID; c++) { - gint pos1_0 = -1, pos1_1 = -1, pos2_0 = -1, n; + gint pos1_0 = -1, pos1_1 = -1, pos1_2 = -1; + gint pos2_0 = -1, pos2_1 = -1, pos2_2 = -1; + gint n; - /* Try to go from the given 2 channels to the given 1 channel */ for (n = 0; n < this->in.channels; n++) { if (this->in.pos[n] == conv[c].pos1[0]) pos1_0 = n; else if (this->in.pos[n] == conv[c].pos1[1]) pos1_1 = n; + else if (this->in.pos[n] == conv[c].pos2[0]) + pos1_2 = n; } - for (n = 0; n < this->out.channels; n++) { - if (this->out.pos[n] == conv[c].pos2[0]) - pos2_0 = n; - } - - if (pos1_0 != -1 && pos1_1 != -1 && pos2_0 != -1) { - this->matrix[pos1_0][pos2_0] = 1.0; - this->matrix[pos1_1][pos2_0] = 1.0; - } - - /* Try to go from the given 1 channel to the given 2 channels */ - pos1_0 = -1; - pos1_1 = -1; - pos2_0 = -1; - for (n = 0; n < this->out.channels; n++) { if (this->out.pos[n] == conv[c].pos1[0]) - pos1_0 = n; - else if (this->out.pos[n] == conv[c].pos1[1]) - pos1_1 = n; - } - for (n = 0; n < this->in.channels; n++) { - if (this->in.pos[n] == conv[c].pos2[0]) pos2_0 = n; + else if (this->out.pos[n] == conv[c].pos1[1]) + pos2_1 = n; + else if (this->out.pos[n] == conv[c].pos2[0]) + pos2_2 = n; } - if (pos1_0 != -1 && pos1_1 != -1 && pos2_0 != -1) { - this->matrix[pos2_0][pos1_0] = 1.0; - this->matrix[pos2_0][pos1_1] = 1.0; - } + /* The general idea here is to fill in channels from the same position + * as good as possible. This means mixing left<->center and right<->center. + */ + + /* left -> center */ + if (pos1_0 != -1 && pos1_2 == -1 && pos2_0 == -1 && pos2_2 != -1) + this->matrix[pos1_0][pos2_2] = 1.0; + else if (pos1_0 != -1 && pos1_2 != -1 && pos2_0 == -1 && pos2_2 != -1) + this->matrix[pos1_0][pos2_2] = 0.5; + else if (pos1_0 != -1 && pos1_2 == -1 && pos2_0 != -1 && pos2_2 != -1) + this->matrix[pos1_0][pos2_2] = 1.0; + + /* right -> center */ + if (pos1_1 != -1 && pos1_2 == -1 && pos2_1 == -1 && pos2_2 != -1) + this->matrix[pos1_1][pos2_2] = 1.0; + else if (pos1_1 != -1 && pos1_2 != -1 && pos2_1 == -1 && pos2_2 != -1) + this->matrix[pos1_1][pos2_2] = 0.5; + else if (pos1_1 != -1 && pos1_2 == -1 && pos2_1 != -1 && pos2_2 != -1) + this->matrix[pos1_1][pos2_2] = 1.0; + + /* center -> left */ + if (pos1_2 != -1 && pos1_0 == -1 && pos2_2 == -1 && pos2_0 != -1) + this->matrix[pos1_2][pos2_0] = 1.0; + else if (pos1_2 != -1 && pos1_0 != -1 && pos2_2 == -1 && pos2_0 != -1) + this->matrix[pos1_2][pos2_0] = 0.5; + else if (pos1_2 != -1 && pos1_0 == -1 && pos2_2 != -1 && pos2_0 != -1) + this->matrix[pos1_2][pos2_0] = 1.0; + + /* center -> right */ + if (pos1_2 != -1 && pos1_1 == -1 && pos2_2 == -1 && pos2_1 != -1) + this->matrix[pos1_2][pos2_1] = 1.0; + else if (pos1_2 != -1 && pos1_1 != -1 && pos2_2 == -1 && pos2_1 != -1) + this->matrix[pos1_2][pos2_1] = 0.5; + else if (pos1_2 != -1 && pos1_1 == -1 && pos2_2 != -1 && pos2_1 != -1) + this->matrix[pos1_2][pos2_1] = 1.0; } } @@ -180,43 +192,52 @@ gst_channel_mix_detect_pos (AudioConvertFmt * caps, for (n = 0; n < caps->channels; n++) { switch (caps->pos[n]) { case GST_AUDIO_CHANNEL_POSITION_FRONT_MONO: - case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT: - case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT: + f[1] = n; + *has_f = TRUE; + break; + case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT: + f[0] = n; + *has_f = TRUE; + break; + case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT: + f[2] = n; *has_f = TRUE; - if (f[0] == -1) - f[0] = n; - else - f[1] = n; break; case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER: - case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: - case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: + c[1] = n; + *has_c = TRUE; + break; + case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: + c[0] = n; + *has_c = TRUE; + break; + case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: + c[2] = n; *has_c = TRUE; - if (c[0] == -1) - c[0] = n; - else - c[1] = n; break; case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER: - case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT: - case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT: + r[1] = n; + *has_r = TRUE; + break; + case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT: + r[0] = n; + *has_r = TRUE; + break; + case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT: + r[2] = n; *has_r = TRUE; - if (r[0] == -1) - r[0] = n; - else - r[1] = n; break; case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT: - case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT: + s[0] = n; + *has_s = TRUE; + break; + case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT: + s[2] = n; *has_s = TRUE; - if (s[0] == -1) - s[0] = n; - else - s[1] = n; break; case GST_AUDIO_CHANNEL_POSITION_LFE: *has_b = TRUE; - b[0] = n; + b[1] = n; break; default: break; @@ -227,59 +248,59 @@ gst_channel_mix_detect_pos (AudioConvertFmt * caps, static void gst_channel_mix_fill_one_other (gfloat ** matrix, AudioConvertFmt * from_caps, gint * from_idx, - GstAudioChannelPosition from_pos_l, - GstAudioChannelPosition from_pos_r, - GstAudioChannelPosition from_pos_c, - AudioConvertFmt * to_caps, gint * to_idx, - GstAudioChannelPosition to_pos_l, - GstAudioChannelPosition to_pos_r, - GstAudioChannelPosition to_pos_c, gfloat ratio) + AudioConvertFmt * to_caps, gint * to_idx, gfloat ratio) { - gfloat in_r, out_r[2] = { 0.f, 0.f }; - /* - * The idea is that we add up from the input (which means that if we - * have stereo input, we divide their sum by two) and put that in - * the matrix for their output ratio (given in $ratio). - * For left channels, we need to invert the signal sign (* -1). - */ - - if (from_caps->pos[from_idx[0]] == from_pos_c) - in_r = 1.0; - else - in_r = 0.5; - - if (to_caps->pos[to_idx[0]] == to_pos_l) - out_r[0] = in_r * -ratio; - else - out_r[0] = in_r * ratio; - - if (to_idx[1] != -1) { - if (to_caps->pos[to_idx[1]] == to_pos_l) - out_r[1] = in_r * -ratio; - else - out_r[1] = in_r * ratio; + /* src & dst have center => passthrough */ + if (from_idx[1] != -1 && to_idx[1] != -1) { + matrix[from_idx[1]][to_idx[1]] = ratio; } - matrix[from_idx[0]][to_idx[0]] = out_r[0]; - if (to_idx[1] != -1) - matrix[from_idx[0]][to_idx[1]] = out_r[1]; - if (from_idx[1] != -1) { - matrix[from_idx[1]][to_idx[0]] = out_r[0]; - if (to_idx[1] != -1) - matrix[from_idx[1]][to_idx[1]] = out_r[1]; + /* src & dst have left => passthrough */ + if (from_idx[0] != -1 && to_idx[0] != -1) { + matrix[from_idx[0]][to_idx[0]] = ratio; + } + + /* src & dst have right => passthrough */ + if (from_idx[2] != -1 && to_idx[2] != -1) { + matrix[from_idx[2]][to_idx[2]] = ratio; + } + + /* src has left & dst has center => put into center */ + if (from_idx[0] != -1 && to_idx[1] != -1) { + matrix[from_idx[0]][to_idx[1]] = ratio; + } + /* src has right & dst has center => put into center */ + if (from_idx[2] != -1 && to_idx[1] != -1) { + matrix[from_idx[2]][to_idx[1]] = ratio; + } + + /* src has center & dst has left => passthrough */ + if (from_idx[1] != -1 && to_idx[0] != -1) { + matrix[from_idx[1]][to_idx[0]] = ratio; + } + /* src has center & dst has right => passthrough */ + if (from_idx[1] != -1 && to_idx[2] != -1) { + matrix[from_idx[1]][to_idx[2]] = ratio; } } -#define RATIO_FRONT_CENTER (1.0 / sqrt (2.0)) -#define RATIO_FRONT_REAR (1.0 / sqrt (2.0)) -#define RATIO_FRONT_BASS (1.0) -#define RATIO_REAR_BASS (1.0 / sqrt (2.0)) -#define RATIO_CENTER_BASS (1.0 / sqrt (2.0)) -#define RATIO_SIDE_BASS (1.0 / sqrt (2.0)) -#define RATIO_FRONT_SIDE (1.0 / sqrt (2.0)) -#define RATIO_REAR_SIDE (1.0 / sqrt (2.0)) +#define RATIO_CENTER_FRONT (1.0 / sqrt (2.0)) #define RATIO_CENTER_SIDE (1.0 / 2.0) +#define RATIO_CENTER_REAR (1.0 / sqrt (8.0)) + +#define RATIO_FRONT_CENTER (1.0 / sqrt (2.0)) +#define RATIO_FRONT_SIDE (1.0 / sqrt (2.0)) +#define RATIO_FRONT_REAR (1.0 / 2.0) + +#define RATIO_SIDE_CENTER (1.0 / 2.0) +#define RATIO_SIDE_FRONT (1.0 / sqrt (2.0)) +#define RATIO_SIDE_REAR (1.0 / sqrt (2.0)) + +#define RATIO_CENTER_BASS (1.0 / sqrt (2.0)) +#define RATIO_FRONT_BASS (1.0) +#define RATIO_SIDE_BASS (1.0 / sqrt (2.0)) +#define RATIO_REAR_BASS (1.0 / sqrt (2.0)) static void gst_channel_mix_fill_others (AudioConvertCtx * this) @@ -289,16 +310,21 @@ gst_channel_mix_fill_others (AudioConvertCtx * this) in_has_rear = FALSE, out_has_rear = FALSE, in_has_side = FALSE, out_has_side = FALSE, in_has_bass = FALSE, out_has_bass = FALSE; - gint in_f[2] = { -1, -1 }, out_f[2] = { - -1, -1}, in_c[2] = { - -1, -1}, out_c[2] = { - -1, -1}, in_r[2] = { - -1, -1}, out_r[2] = { - -1, -1}, in_s[2] = { - -1, -1}, out_s[2] = { - -1, -1}, in_b[2] = { - -1, -1}, out_b[2] = { - -1, -1}; + /* LEFT, RIGHT, MONO */ + gint in_f[3] = { -1, -1, -1 }; + gint out_f[3] = { -1, -1, -1 }; + /* LOC, ROC, CENTER */ + gint in_c[3] = { -1, -1, -1 }; + gint out_c[3] = { -1, -1, -1 }; + /* RLEFT, RRIGHT, RCENTER */ + gint in_r[3] = { -1, -1, -1 }; + gint out_r[3] = { -1, -1, -1 }; + /* SLEFT, INVALID, SRIGHT */ + gint in_s[3] = { -1, -1, -1 }; + gint out_s[3] = { -1, -1, -1 }; + /* INVALID, LFE, INVALID */ + gint in_b[3] = { -1, -1, -1 }; + gint out_b[3] = { -1, -1, -1 }; /* First see where (if at all) the various channels from/to * which we want to convert are located in our matrix/array. */ @@ -311,213 +337,163 @@ gst_channel_mix_fill_others (AudioConvertCtx * this) out_c, &out_has_center, out_r, &out_has_rear, out_s, &out_has_side, out_b, &out_has_bass); - /* center/front */ + /* The general idea here is: + * - if the source has a channel that the destination doesn't have mix + * it into the nearest available destination channel + * - if the destination has a channel that the source doesn't have mix + * the nearest source channel into the destination channel + * + * The ratio for the mixing becomes lower as the distance between the + * channels gets larger + */ + + /* center <-> front/side/rear */ if (!in_has_center && in_has_front && out_has_center) { gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_f, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, - &this->out, out_c, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, RATIO_FRONT_CENTER); + &this->in, in_f, &this->out, out_c, RATIO_CENTER_FRONT); + } else if (!in_has_center && !in_has_front && in_has_side && out_has_center) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_s, &this->out, out_c, RATIO_CENTER_SIDE); + } else if (!in_has_center && !in_has_front && !in_has_side && in_has_rear + && out_has_center) { + gst_channel_mix_fill_one_other (this->matrix, &this->in, in_r, &this->out, + out_c, RATIO_CENTER_REAR); } else if (in_has_center && !out_has_center && out_has_front) { gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_c, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - &this->out, out_f, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, RATIO_FRONT_CENTER); + &this->in, in_c, &this->out, out_f, RATIO_CENTER_FRONT); + } else if (in_has_center && !out_has_center && !out_has_front && out_has_side) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_c, &this->out, out_s, RATIO_CENTER_SIDE); + } else if (in_has_center && !out_has_center && !out_has_front && !out_has_side + && out_has_rear) { + gst_channel_mix_fill_one_other (this->matrix, &this->in, in_c, &this->out, + out_r, RATIO_CENTER_REAR); } - /* rear/front */ - if (!in_has_rear && in_has_front && out_has_rear) { + /* front <-> center/side/rear */ + if (!in_has_front && in_has_center && !in_has_side && out_has_front) { gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_f, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, - &this->out, out_r, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, RATIO_FRONT_REAR); - } else if (in_has_rear && !out_has_rear && out_has_front) { + &this->in, in_c, &this->out, out_f, RATIO_CENTER_FRONT); + } else if (!in_has_front && !in_has_center && in_has_side && out_has_front) { gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_r, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, - &this->out, out_f, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, RATIO_FRONT_REAR); + &this->in, in_s, &this->out, out_f, RATIO_FRONT_SIDE); + } else if (!in_has_front && in_has_center && in_has_side && out_has_front) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_c, &this->out, out_f, 0.5 * RATIO_CENTER_FRONT); + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_s, &this->out, out_f, 0.5 * RATIO_FRONT_SIDE); + } else if (!in_has_front && !in_has_center && !in_has_side && in_has_rear + && out_has_front) { + gst_channel_mix_fill_one_other (this->matrix, &this->in, in_r, &this->out, + out_f, RATIO_FRONT_REAR); + } else if (in_has_front && out_has_center && !out_has_side && !out_has_front) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_f, &this->out, out_c, RATIO_CENTER_FRONT); + } else if (in_has_front && !out_has_center && out_has_side && !out_has_front) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_f, &this->out, out_s, RATIO_FRONT_SIDE); + } else if (in_has_front && out_has_center && out_has_side && !out_has_front) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_f, &this->out, out_c, 0.5 * RATIO_CENTER_FRONT); + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_f, &this->out, out_s, 0.5 * RATIO_FRONT_SIDE); + } else if (in_has_front && !out_has_center && !out_has_side && !out_has_front + && out_has_rear) { + gst_channel_mix_fill_one_other (this->matrix, &this->in, in_f, &this->out, + out_r, RATIO_FRONT_REAR); } - /* bass/any */ + /* side <-> center/front/rear */ + if (!in_has_side && in_has_front && !in_has_rear && out_has_side) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_f, &this->out, out_s, RATIO_FRONT_SIDE); + } else if (!in_has_side && !in_has_front && in_has_rear && out_has_side) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_r, &this->out, out_s, RATIO_SIDE_REAR); + } else if (!in_has_side && in_has_front && in_has_rear && out_has_side) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_f, &this->out, out_s, 0.5 * RATIO_FRONT_SIDE); + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_r, &this->out, out_s, 0.5 * RATIO_SIDE_REAR); + } else if (!in_has_side && !in_has_front && !in_has_rear && in_has_center + && out_has_side) { + gst_channel_mix_fill_one_other (this->matrix, &this->in, in_c, &this->out, + out_s, RATIO_CENTER_SIDE); + } else if (in_has_side && out_has_front && !out_has_rear && !out_has_side) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_s, &this->out, out_f, RATIO_FRONT_SIDE); + } else if (in_has_side && !out_has_front && out_has_rear && !out_has_side) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_s, &this->out, out_r, RATIO_SIDE_REAR); + } else if (in_has_side && out_has_front && out_has_rear && !out_has_side) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_s, &this->out, out_f, 0.5 * RATIO_FRONT_SIDE); + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_s, &this->out, out_r, 0.5 * RATIO_SIDE_REAR); + } else if (in_has_side && !out_has_front && !out_has_rear && out_has_center + && !out_has_side) { + gst_channel_mix_fill_one_other (this->matrix, &this->in, in_s, &this->out, + out_c, RATIO_CENTER_SIDE); + } + + /* rear <-> center/front/side */ + if (!in_has_rear && in_has_side && out_has_rear) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_s, &this->out, out_r, RATIO_SIDE_REAR); + } else if (!in_has_rear && !in_has_side && in_has_front && out_has_rear) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_f, &this->out, out_r, RATIO_FRONT_REAR); + } else if (!in_has_rear && !in_has_side && !in_has_front && in_has_center + && out_has_rear) { + gst_channel_mix_fill_one_other (this->matrix, &this->in, in_c, &this->out, + out_r, RATIO_CENTER_REAR); + } else if (in_has_rear && !out_has_rear && out_has_side) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_r, &this->out, out_s, RATIO_SIDE_REAR); + } else if (in_has_rear && !out_has_rear && !out_has_side && out_has_front) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_r, &this->out, out_f, RATIO_FRONT_REAR); + } else if (in_has_rear && !out_has_rear && !out_has_side && !out_has_front + && out_has_center) { + gst_channel_mix_fill_one_other (this->matrix, &this->in, in_r, &this->out, + out_c, RATIO_CENTER_REAR); + } + + /* bass <-> any */ if (in_has_bass && !out_has_bass) { - if (out_has_front) { - gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_b, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_LFE, - &this->out, out_f, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, RATIO_FRONT_BASS); - } if (out_has_center) { gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_b, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_LFE, - &this->out, out_c, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, RATIO_CENTER_BASS); + &this->in, in_b, &this->out, out_c, RATIO_CENTER_BASS); } - if (out_has_rear) { + if (out_has_front) { gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_b, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_LFE, - &this->out, out_r, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, RATIO_REAR_BASS); + &this->in, in_b, &this->out, out_f, RATIO_FRONT_BASS); } if (out_has_side) { gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_b, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_LFE, - &this->out, out_s, - GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, - GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, - GST_AUDIO_CHANNEL_POSITION_INVALID, RATIO_SIDE_BASS); + &this->in, in_b, &this->out, out_s, RATIO_SIDE_BASS); + } + if (out_has_rear) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_b, &this->out, out_r, RATIO_REAR_BASS); } } else if (!in_has_bass && out_has_bass) { - if (in_has_front) { - gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_f, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, - &this->out, out_b, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_LFE, RATIO_FRONT_BASS); - } if (in_has_center) { gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_c, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - &this->out, out_b, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_LFE, RATIO_CENTER_BASS); + &this->in, in_c, &this->out, out_b, RATIO_CENTER_BASS); } - if (in_has_rear) { + if (in_has_front) { gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_r, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, - &this->out, out_b, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_LFE, RATIO_REAR_BASS); + &this->in, in_f, &this->out, out_b, RATIO_FRONT_BASS); } if (in_has_side) { gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_s, - GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, - GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, - GST_AUDIO_CHANNEL_POSITION_INVALID, - &this->out, out_b, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_INVALID, - GST_AUDIO_CHANNEL_POSITION_LFE, RATIO_REAR_BASS); + &this->in, in_s, &this->out, out_b, RATIO_REAR_BASS); + } + if (in_has_rear) { + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_r, &this->out, out_b, RATIO_REAR_BASS); } - } - - /* front/side */ - if (!in_has_side && in_has_front && out_has_side) { - gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_f, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, - &this->out, out_s, - GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, - GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, - GST_AUDIO_CHANNEL_POSITION_INVALID, RATIO_FRONT_SIDE); - } else if (in_has_side && !out_has_side && out_has_front) { - gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_s, - GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, - GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, - GST_AUDIO_CHANNEL_POSITION_INVALID, - &this->out, out_f, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, RATIO_FRONT_SIDE); - } - - /* rear/side */ - if (!in_has_side && in_has_rear && out_has_side) { - gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_r, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, - &this->out, out_s, - GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, - GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, - GST_AUDIO_CHANNEL_POSITION_INVALID, RATIO_REAR_SIDE); - } else if (in_has_side && !out_has_side && out_has_rear) { - gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_s, - GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, - GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, - GST_AUDIO_CHANNEL_POSITION_INVALID, - &this->out, out_r, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, RATIO_REAR_SIDE); - } - - /* center/side */ - /* FIXME: should center<->side have influence on eachother? */ - if (!in_has_side && in_has_center && out_has_side) { - gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_c, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - &this->out, out_s, - GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, - GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, - GST_AUDIO_CHANNEL_POSITION_INVALID, RATIO_CENTER_SIDE); - } else if (in_has_side && !out_has_side && out_has_center) { - gst_channel_mix_fill_one_other (this->matrix, - &this->in, in_s, - GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, - GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, - GST_AUDIO_CHANNEL_POSITION_INVALID, - &this->out, out_c, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, RATIO_CENTER_SIDE); } } @@ -553,6 +529,39 @@ gst_channel_mix_fill_normalize (AudioConvertCtx * this) } } +static gboolean +gst_channel_mix_fill_special (AudioConvertCtx * this) +{ + AudioConvertFmt *in = &this->in, *out = &this->out; + + /* Special, standard conversions here */ + + /* Mono<->Stereo, just a fast-path */ + if (in->channels == 2 && out->channels == 1 && + ((in->pos[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT && + in->pos[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) || + (in->pos[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT && + in->pos[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) && + out->pos[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_MONO) { + this->matrix[0][0] = 0.5; + this->matrix[1][0] = 0.5; + return TRUE; + } else if (in->channels == 1 && out->channels == 2 && + ((out->pos[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT && + out->pos[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) || + (out->pos[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT && + out->pos[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) && + in->pos[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_MONO) { + this->matrix[0][0] = 1.0; + this->matrix[0][1] = 1.0; + return TRUE; + } + + /* TODO: 5.1 <-> Stereo and other standard conversions */ + + return FALSE; +} + /* * Automagically generate conversion matrix. */ @@ -560,6 +569,9 @@ gst_channel_mix_fill_normalize (AudioConvertCtx * this) static void gst_channel_mix_fill_matrix (AudioConvertCtx * this) { + if (gst_channel_mix_fill_special (this)) + return; + gst_channel_mix_fill_identical (this); if (!this->in.unpositioned_layout) { diff --git a/tests/check/elements/audioconvert.c b/tests/check/elements/audioconvert.c index becb7a6ec0..8331b4aa82 100644 --- a/tests/check/elements/audioconvert.c +++ b/tests/check/elements/audioconvert.c @@ -918,7 +918,7 @@ GST_START_TEST (test_multichannel_conversion) { gint16 in[] = { 1, 2, 3, 4 }; - gint16 out[] = { 2, 4 }; + gint16 out[] = { 1, 3 }; GstCaps *in_caps = get_int_mc_caps (2, "BYTE_ORDER", 16, 16, TRUE, FALSE); GstCaps *out_caps = get_int_mc_caps (1, "BYTE_ORDER", 16, 16, TRUE, FALSE); GstAudioChannelPosition in_layout[2] = @@ -953,6 +953,15 @@ GST_START_TEST (test_multichannel_conversion) RUN_CONVERSION ("2 channels to 1 with non-standard layout", in, in_caps, out, out_caps); } + { + gint16 in[] = { 4, 5, 4, 2, 2, 1 }; + gint16 out[] = { 3, 3 }; + GstCaps *in_caps = get_int_mc_caps (6, "BYTE_ORDER", 16, 16, TRUE, FALSE); + GstCaps *out_caps = get_int_caps (2, "BYTE_ORDER", 16, 16, TRUE); + + RUN_CONVERSION ("5.1 to 2 channels", in, in_caps, out, out_caps); + } + } GST_END_TEST; @@ -1053,7 +1062,7 @@ GST_START_TEST (test_channel_remapping) /* gint16 to gint16 with 2 channels and non-standard layout */ { gint16 in[] = { 1, 2, 3, 4 }; - gint16 out[] = { -1, 2, -2, 4 }; + gint16 out[] = { 1, 2, 2, 4 }; GstCaps *in_caps = get_int_mc_caps (2, "BYTE_ORDER", 16, 16, TRUE, FALSE); GstCaps *out_caps = get_int_mc_caps (2, "BYTE_ORDER", 16, 16, TRUE, FALSE); GstAudioChannelPosition in_layout[2] = @@ -1097,7 +1106,7 @@ GST_START_TEST (test_channel_remapping) /* gint16 to gint16 with 2 channels and non-standard layout */ { gint16 in[] = { 1, 2, 3, 4 }; - gint16 out[] = { -1, 2, -3, 4 }; + gint16 out[] = { 1, 1, 3, 3 }; GstCaps *in_caps = get_int_mc_caps (2, "BYTE_ORDER", 16, 16, TRUE, FALSE); GstCaps *out_caps = get_int_mc_caps (2, "BYTE_ORDER", 16, 16, TRUE, FALSE); GstAudioChannelPosition in_layout[2] = @@ -1155,7 +1164,7 @@ GST_START_TEST (test_channel_remapping) /* gint16 to gint16 with 1 channel and non-standard layout */ { gint16 in[] = { 1, 2, 3, 4 }; - gint16 out[] = { -1, -2, -3, -4 }; + gint16 out[] = { 1, 2, 3, 4 }; GstCaps *in_caps = get_int_mc_caps (1, "BYTE_ORDER", 16, 16, TRUE, FALSE); GstCaps *out_caps = get_int_mc_caps (1, "BYTE_ORDER", 16, 16, TRUE, FALSE); GstAudioChannelPosition in_layout[1] =