[pulseaudio-discuss] [PATCHv3 3/3] resampler: Generate normalized rows in calc_map_table()

Stefan Huber s.huber at bct-electronic.com
Thu Feb 7 05:03:18 PST 2013


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



More information about the pulseaudio-discuss mailing list