resampler: Generate normalized rows in calc_map_table()

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.533 0.000 0.267 0.200
  O01 | 0.000 0.750 0.375 0.375      O01 | 0.000 0.533 0.267 0.200

Building the matrix is done in several steps. However, only insufficient
measures are taken in order to preserve a row-sum of 1.0 (or leaves it
at 0.0) after each step. The current patch adds a post-processing step
in order check for each row whether the sum exceeds 1.0 and, if
necessary, normalizes this row. This allows for further simplifactions:
 - The insufficient normalizations after some steps are removed. Gains
   are adapted to (partially) resemble the old matrices.
 - Handling unconnected input channls becomes a lot simpler.
This commit is contained in:
Stefan Huber 2013-02-07 14:03:17 +01:00 committed by Tanu Kaskinen
parent 1a40af9c3b
commit 930654a3af

View file

@ -727,16 +727,17 @@ static void calc_map_table(pa_resampler *r) {
* *
* 6) Make sure S:Left/S:Right is used: S:Left/S:Right: If not * 6) Make sure S:Left/S:Right is used: S:Left/S:Right: If not
* connected, mix into all D:left and all D:right channels. Gain is * connected, mix into all D:left and all D:right channels. Gain is
* 0.1, the current left and right should be multiplied by 0.9. * 1/9.
* *
* 7) Make sure S:Center, S:LFE is used: * 7) Make sure S:Center, S:LFE is used:
* *
* S:Center, S:LFE: If not connected, mix into all D:left, all * 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 * D:right, all D:center channels. Gain is 0.5 for center and 0.375
* result of 1..6) factors should be multiplied by 0.75. (Alt. * for LFE. C-front is only mixed into L-front/R-front if available,
* suggestion: 0.25 vs. 0.5) If C-front is only mixed into * otherwise into all L/R channels. Similarly for C-rear.
* L-front/R-front if available, otherwise into all L/R channels. *
* Similarly for C-rear. * 8) Normalize each row in the matrix such that the sum for each row is
* not larger than 1.0 in order to avoid clipping.
* *
* S: and D: shall relate to the source resp. destination channels. * S: and D: shall relate to the source resp. destination channels.
* *
@ -759,6 +760,7 @@ static void calc_map_table(pa_resampler *r) {
ic_unconnected_right = 0, ic_unconnected_right = 0,
ic_unconnected_center = 0, ic_unconnected_center = 0,
ic_unconnected_lfe = 0; ic_unconnected_lfe = 0;
bool ic_unconnected_center_mixed_in = 0;
pa_assert(remix); pa_assert(remix);
@ -885,81 +887,31 @@ static void calc_map_table(pa_resampler *r) {
ic_unconnected_lfe++; ic_unconnected_lfe++;
} }
if (ic_unconnected_left > 0) { for (ic = 0; ic < n_ic; ic++) {
pa_channel_position_t a = r->i_cm.map[ic];
/* OK, so there are unconnected input channels on the left. Let's if (ic_connected[ic])
* multiply all already connected channels on the left side by .9 continue;
* and add in our averaged unconnected channels multiplied by .1 */
for (oc = 0; oc < n_oc; oc++) { for (oc = 0; oc < n_oc; oc++) {
pa_channel_position_t b = r->o_cm.map[oc];
if (!on_left(r->o_cm.map[oc])) if (on_left(a) && on_left(b))
continue; m->map_table_f[oc][ic] = (1.f/9.f) / (float) ic_unconnected_left;
for (ic = 0; ic < n_ic; ic++) { else if (on_right(a) && on_right(b))
m->map_table_f[oc][ic] = (1.f/9.f) / (float) ic_unconnected_right;
if (ic_connected[ic]) { else if (on_center(a) && on_center(b)) {
m->map_table_f[oc][ic] *= .9f; m->map_table_f[oc][ic] = (1.f/9.f) / (float) ic_unconnected_center;
continue; ic_unconnected_center_mixed_in = true;
}
if (on_left(r->i_cm.map[ic])) } else if (on_lfe(a) && !(r->flags & PA_RESAMPLER_NO_LFE))
m->map_table_f[oc][ic] = .1f / (float) ic_unconnected_left; m->map_table_f[oc][ic] = .375f / (float) ic_unconnected_lfe;
}
} }
} }
if (ic_unconnected_right > 0) { if (ic_unconnected_center > 0 && !ic_unconnected_center_mixed_in) {
/* OK, so there are unconnected input channels on the right. Let's
* multiply all already connected channels on the right side by .9
* and add in our averaged unconnected channels multiplied by .1 */
for (oc = 0; oc < n_oc; oc++) {
if (!on_right(r->o_cm.map[oc]))
continue;
for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic]) {
m->map_table_f[oc][ic] *= .9f;
continue;
}
if (on_right(r->i_cm.map[ic]))
m->map_table_f[oc][ic] = .1f / (float) ic_unconnected_right;
}
}
}
if (ic_unconnected_center > 0) {
bool mixed_in = false;
/* OK, so there are unconnected input channels on the center. Let's
* multiply all already connected channels on the center side by .9
* and add in our averaged unconnected channels multiplied by .1 */
for (oc = 0; oc < n_oc; oc++) {
if (!on_center(r->o_cm.map[oc]))
continue;
for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic]) {
m->map_table_f[oc][ic] *= .9f;
continue;
}
if (on_center(r->i_cm.map[ic])) {
m->map_table_f[oc][ic] = .1f / (float) ic_unconnected_center;
mixed_in = true;
}
}
}
if (!mixed_in) {
unsigned ncenter[PA_CHANNELS_MAX]; unsigned ncenter[PA_CHANNELS_MAX];
bool found_frs[PA_CHANNELS_MAX]; bool found_frs[PA_CHANNELS_MAX];
@ -968,7 +920,7 @@ static void calc_map_table(pa_resampler *r) {
/* Hmm, as it appears there was no center channel we /* Hmm, as it appears there was no center channel we
could mix our center channel in. In this case, mix it into 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 .5 as the factor. */
for (ic = 0; ic < n_ic; ic++) { for (ic = 0; ic < n_ic; ic++) {
@ -1009,35 +961,24 @@ static void calc_map_table(pa_resampler *r) {
for (ic = 0; ic < n_ic; ic++) { for (ic = 0; ic < n_ic; ic++) {
if (ic_connected[ic]) {
m->map_table_f[oc][ic] *= .75f;
continue;
}
if (!on_center(r->i_cm.map[ic])) if (!on_center(r->i_cm.map[ic]))
continue; continue;
if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc])) 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] = .5f / (float) ncenter[oc];
} }
} }
} }
} }
if (ic_unconnected_lfe > 0 && !(r->flags & PA_RESAMPLER_NO_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];
/* OK, so there is an unconnected LFE channel. Let's mix it into if (sum > 1.0f)
* all channels, with factor 0.375 */ for (ic = 0; ic < n_ic; ic++)
m->map_table_f[oc][ic] /= sum;
for (ic = 0; ic < n_ic; ic++) {
if (!on_lfe(r->i_cm.map[ic]))
continue;
for (oc = 0; oc < n_oc; oc++)
m->map_table_f[oc][ic] = 0.375f / (float) ic_unconnected_lfe;
}
}
} }
/* make an 16:16 int version of the matrix */ /* make an 16:16 int version of the matrix */