Remixing one channel map to another is (except for special cases) done via a linear mapping between channels, whose corresponding matrix is computed by calc_map_table(). The k-th row in this matrix corresponds to the coefficients of the linear combination of the input channels that result in the k-th output channel. In order to avoid clipping of samples we require that the sum of these coefficients is (at most) 1. This commit ensures this. Prior to this commit tests/remix-test.c gives 52 of 132 matrices that violate this property. For example: 'front-left,front-right,front-center,lfe' -> 'front-left,front-right' prior this commit after this commit I00 I01 I02 I03 I00 I01 I02 I03 +------------------------ +------------------------ O00 | 0.750 0.000 0.375 0.375 O00 | 0.560 0.000 0.240 0.200 O01 | 0.000 0.750 0.375 0.375 O01 | 0.000 0.560 0.240 0.200 Building the matrix is done in several steps. In order to guarantee normalized rows, this patch assures that each step preserves a row-sum of 1.0 (or leaves it at 0.0). Most notably, if a center channel is mixed onto left and right channels then the weights 0.7 and 0.3 (which sum up to 1.0) are used rather than 0.75 and 0.375. Similarly, if the LFE channel is mixed onto left and right channels then the weights 0.8 and 0.2 are used instead of 0.75 and 0.375. --- src/pulsecore/resampler.c | 68 +++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index 88e74e0..bf6eb91 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -732,11 +732,11 @@ static void calc_map_table(pa_resampler *r) { * 7) Make sure S:Center, S:LFE is used: * * S:Center, S:LFE: If not connected, mix into all D:left, all - * D:right, all D:center channels, gain is 0.375. The current (as - * result of 1..6) factors should be multiplied by 0.75. (Alt. - * suggestion: 0.25 vs. 0.5) If C-front is only mixed into - * L-front/R-front if available, otherwise into all L/R channels. - * Similarly for C-rear. + * D:right, all D:center channels, gain is 0.3. The current (as + * result of 1..6) factors should be multiplied by 0.7 (in the case + * of S:Center) and 0.8 (in the case of S:LFE). C-front is only + * mixed into L-front/R-front if available, otherwise into all L/R + * channels. Similarly for C-rear. * * S: and D: shall relate to the source resp. destination channels. * @@ -898,12 +898,9 @@ static void calc_map_table(pa_resampler *r) { for (ic = 0; ic < n_ic; ic++) { - if (ic_connected[ic]) { - m->map_table_f[oc][ic] *= .9f; - continue; - } + m->map_table_f[oc][ic] *= .9f; - if (on_left(r->i_cm.map[ic])) + if (!ic_connected[ic] && on_left(r->i_cm.map[ic])) m->map_table_f[oc][ic] = .1f / (float) ic_unconnected_left; } } @@ -922,12 +919,9 @@ static void calc_map_table(pa_resampler *r) { for (ic = 0; ic < n_ic; ic++) { - if (ic_connected[ic]) { - m->map_table_f[oc][ic] *= .9f; - continue; - } + m->map_table_f[oc][ic] *= .9f; - if (on_right(r->i_cm.map[ic])) + if (!ic_connected[ic] && on_right(r->i_cm.map[ic])) m->map_table_f[oc][ic] = .1f / (float) ic_unconnected_right; } } @@ -947,12 +941,9 @@ static void calc_map_table(pa_resampler *r) { for (ic = 0; ic < n_ic; ic++) { - if (ic_connected[ic]) { - m->map_table_f[oc][ic] *= .9f; - continue; - } + m->map_table_f[oc][ic] *= .9f; - if (on_center(r->i_cm.map[ic])) { + if (!ic_connected[ic] && on_center(r->i_cm.map[ic])) { m->map_table_f[oc][ic] = .1f / (float) ic_unconnected_center; mixed_in = true; } @@ -968,7 +959,7 @@ static void calc_map_table(pa_resampler *r) { /* Hmm, as it appears there was no center channel we could mix our center channel in. In this case, mix it into - left and right. Using .375 and 0.75 as factors. */ + left and right. Using .3 and .7 as factors. */ for (ic = 0; ic < n_ic; ic++) { @@ -1009,16 +1000,13 @@ static void calc_map_table(pa_resampler *r) { for (ic = 0; ic < n_ic; ic++) { - if (ic_connected[ic]) { - m->map_table_f[oc][ic] *= .75f; - continue; - } + m->map_table_f[oc][ic] *= .7f; - if (!on_center(r->i_cm.map[ic])) + if (ic_connected[ic] || !on_center(r->i_cm.map[ic])) continue; if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc])) - m->map_table_f[oc][ic] = .375f / (float) ncenter[oc]; + m->map_table_f[oc][ic] = .3f / (float) ncenter[oc]; } } } @@ -1027,19 +1015,33 @@ static void calc_map_table(pa_resampler *r) { if (ic_unconnected_lfe > 0 && !(r->flags & PA_RESAMPLER_NO_LFE)) { /* OK, so there is an unconnected LFE channel. Let's mix it into - * all channels, with factor 0.375 */ + * all channels. Using .2 and .8 as factors. */ - for (ic = 0; ic < n_ic; ic++) { + for (oc = 0; oc < n_oc; oc++) { + for (ic = 0; ic < n_ic; ic++) { - if (!on_lfe(r->i_cm.map[ic])) - continue; + m->map_table_f[oc][ic] *= .8f; - for (oc = 0; oc < n_oc; oc++) - m->map_table_f[oc][ic] = 0.375f / (float) ic_unconnected_lfe; + if (!ic_connected[ic] && on_lfe(r->i_cm.map[ic])) + m->map_table_f[oc][ic] = .2f / (float) ic_unconnected_lfe; + } } } } + for (oc = 0; oc < n_oc; oc++) { + float sum = 0.0f; + + for (ic = 0; ic < n_ic; ic++) { + sum += m->map_table_f[oc][ic]; + if (m->map_table_f[oc][ic] < 0.0f || m->map_table_f[oc][ic] > 1.0f ) + pa_log_error("Coefficient of input channel %d for output channel %d is out of bounds.\n", ic, oc); + } + + if (sum > 1.001f) + pa_log_error("Matrix row for output channel %d is not normalized, which may cause clipping.\n", oc); + } + /* make an 16:16 int version of the matrix */ for (oc = 0; oc < n_oc; oc++) for (ic = 0; ic < n_ic; ic++) -- 1.7.9.5