From 6b0abd20578fac9d06f5f6551eb426ff93425773 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 15 Jan 2021 17:51:07 +0100 Subject: [PATCH] channelmix: Improve unknown channel layout handling Clamp position to valid range. so that AUX becomes UNKNOWN. If we have one mono source channel and unknown destination, copy it to all destination channels. If we have one mono destination channel and unknown source layout, average all channels. Otherwise, pair source and dest channels. See #538 --- spa/plugins/audioconvert/channelmix-ops.c | 15 ++- spa/plugins/audioconvert/channelmix.c | 14 +- spa/plugins/audioconvert/test-channelmix.c | 147 ++++++++++++++++----- 3 files changed, 136 insertions(+), 40 deletions(-) diff --git a/spa/plugins/audioconvert/channelmix-ops.c b/spa/plugins/audioconvert/channelmix-ops.c index 51f843c4e..70260e07d 100644 --- a/spa/plugins/audioconvert/channelmix-ops.c +++ b/spa/plugins/audioconvert/channelmix-ops.c @@ -177,9 +177,20 @@ static int make_matrix(struct channelmix *mix) dst_mask = _MASK(FC); if (src_mask == 0 || dst_mask == 0) { + if (src_mask == _MASK(FC) && mix->src_chan == 1) { + /* one mono src goes everywhere */ + for (i = 0; i < NUM_CHAN; i++) + matrix[i][0]= 1.0f; + } else if (dst_mask == _MASK(FC) && mix->dst_chan == 1) { + /* one mono dst get average of everything */ + for (i = 0; i < NUM_CHAN; i++) + matrix[0][i]= 1.0f / mix->src_chan; + } else { + /* just pair channels */ + for (i = 0; i < NUM_CHAN; i++) + matrix[i][i]= 1.0f; + } src_mask = dst_mask = ~0LU; - for (i = 0; i < NUM_CHAN; i++) - matrix[i][i]= 1.0f; goto done; } else { for (i = 0; i < NUM_CHAN; i++) { diff --git a/spa/plugins/audioconvert/channelmix.c b/spa/plugins/audioconvert/channelmix.c index 30c94ef67..c837c350f 100644 --- a/spa/plugins/audioconvert/channelmix.c +++ b/spa/plugins/audioconvert/channelmix.c @@ -236,7 +236,7 @@ static int setup_convert(struct impl *this, const struct spa_audio_info *info) { const struct spa_audio_info *src_info, *dst_info; - uint32_t i, src_chan, dst_chan; + uint32_t i, src_chan, dst_chan, p; uint64_t src_mask, dst_mask; int res; @@ -251,10 +251,14 @@ static int setup_convert(struct impl *this, src_chan = src_info->info.raw.channels; dst_chan = dst_info->info.raw.channels; - for (i = 0, src_mask = 0; i < src_chan; i++) - src_mask |= 1UL << src_info->info.raw.position[i]; - for (i = 0, dst_mask = 0; i < dst_chan; i++) - dst_mask |= 1UL << dst_info->info.raw.position[i]; + for (i = 0, src_mask = 0; i < src_chan; i++) { + p = src_info->info.raw.position[i]; + src_mask |= 1ULL << (p < 64 ? p : 0); + } + for (i = 0, dst_mask = 0; i < dst_chan; i++) { + p = dst_info->info.raw.position[i]; + dst_mask |= 1ULL << (p < 64 ? p : 0); + } if (src_mask & 1 || src_chan == 1) src_mask = default_mask(src_chan); diff --git a/spa/plugins/audioconvert/test-channelmix.c b/spa/plugins/audioconvert/test-channelmix.c index 87b2934a3..fa279cd1d 100644 --- a/spa/plugins/audioconvert/test-channelmix.c +++ b/spa/plugins/audioconvert/test-channelmix.c @@ -34,15 +34,19 @@ SPA_LOG_IMPL(logger); +#define MATRIX(...) (float[]) { __VA_ARGS__ } + #include "channelmix-ops.c" -static void dump_matrix(struct channelmix *mix) +static void dump_matrix(struct channelmix *mix, float *coeff) { uint32_t i, j; for (i = 0; i < mix->dst_chan; i++) { for (j = 0; j < mix->src_chan; j++) { float v = mix->matrix_orig[i][j]; - spa_log_debug(mix->log, "%d %d: %f", i, j, v); + spa_log_debug(mix->log, "%d %d: %f <-> %f", i, j, v, *coeff); + spa_assert(fabs(v - *coeff) < 0.000001); + coeff++; } } } @@ -61,61 +65,138 @@ static void test_mix(uint32_t src_chan, uint32_t src_mask, uint32_t dst_chan, ui mix.log = &logger.log; channelmix_init(&mix); - dump_matrix(&mix); + dump_matrix(&mix, coeff); } static void test_1_N(void) { - test_mix(1, _M(MONO), 2, _M(FL)|_M(FR), (float[]) { 1.0, 1.0 }); - test_mix(1, _M(MONO), 3, _M(FL)|_M(FR)|_M(LFE), (float[]) { 1.0, 1.0, 0.0 }); - test_mix(1, _M(MONO), 4, _M(FL)|_M(FR)|_M(RL)|_M(RR), (float[]) { 1.0, 1.0, 1.0, 1.0 }); + test_mix(1, _M(MONO), 2, _M(FL)|_M(FR), + MATRIX(0.707107, 0.707107)); + test_mix(1, _M(MONO), 3, _M(FL)|_M(FR)|_M(LFE), + MATRIX(0.707107, 0.707107, 0.0)); + test_mix(1, _M(MONO), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), + MATRIX(0.0, 0.0, 1.0, 0.0)); + test_mix(1, _M(MONO), 4, _M(FL)|_M(FR)|_M(RL)|_M(RR), + MATRIX(0.707107, 0.707107, 0.0, 0.0)); + test_mix(1, _M(MONO), 12, 0, + MATRIX(1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)); } static void test_N_1(void) { - test_mix(1, _M(MONO), 1, _M(MONO), (float[]) { 1.0 }); - test_mix(1, _M(MONO), 1, _M(FC), (float[]) { 1.0 }); - test_mix(1, _M(FC), 1, _M(MONO), (float[]) { 1.0 }); - test_mix(1, _M(FC), 1, _M(FC), (float[]) { 1.0 }); - test_mix(2, _M(FL)|_M(FR), 1, _M(MONO), (float[]) { 0.5, 0.5 }); + test_mix(1, _M(MONO), 1, _M(MONO), + MATRIX(1.0)); + test_mix(1, _M(MONO), 1, _M(FC), + MATRIX(1.0)); + test_mix(1, _M(FC), 1, _M(MONO), + MATRIX(1.0)); + test_mix(1, _M(FC), 1, _M(FC), + MATRIX(1.0)); + test_mix(2, _M(FL)|_M(FR), 1, _M(MONO), + MATRIX(0.707107, 0.707107)); + test_mix(12, 0, 1, _M(MONO), + MATRIX(0.083333, 0.083333, 0.083333, 0.083333, 0.083333, 0.083333, + 0.083333, 0.083333, 0.083333, 0.083333, 0.083333, 0.0833333)); } static void test_3p1_N(void) { - test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 1, _M(MONO), (float[]) { 0.5, 0.5 }); - test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 2, _M(FL)|_M(FR), (float[]) { 0.5, 0.5 }); - test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 3, _M(FL)|_M(FR)|_M(LFE), (float[]) { 0.5, 0.5 }); - test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), (float[]) { 0.5, 0.5 }); - test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 4, _M(FL)|_M(FR)|_M(RL)|_M(RR), (float[]) { 0.5, 0.5 }); + test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 1, _M(MONO), + MATRIX(0.707107, 0.707107, 1.0, 0.0)); + test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 2, _M(FL)|_M(FR), + MATRIX(1.0, 0.0, 0.707107, 0.0, + 0.0, 1.0, 0.707107, 0.0 )); + test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 3, _M(FL)|_M(FR)|_M(LFE), + MATRIX(1.0, 0.0, 0.707107, 0.0, + 0.0, 1.0, 0.707107, 0.0, + 0.0, 0.0, 0.0, 1.0 )); + test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), + MATRIX(1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0,)); + test_mix(4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), 4, _M(FL)|_M(FR)|_M(RL)|_M(RR), + MATRIX(1.0, 0.0, 0.707107, 0.0, + 0.0, 1.0, 0.707107, 0.0, + 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0,)); } static void test_4_N(void) { - test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 1, _M(MONO), (float[]) { 0.5, 0.5 }); - test_mix(4, _M(FL)|_M(FR)|_M(SL)|_M(SR), 1, _M(MONO), (float[]) { 0.5, 0.5 }); - test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 2, _M(FL)|_M(FR), (float[]) { 0.5, 0.5 }); - test_mix(4, _M(FL)|_M(FR)|_M(SL)|_M(SR), 2, _M(FL)|_M(FR), (float[]) { 0.5, 0.5 }); - test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 3, _M(FL)|_M(FR)|_M(LFE), (float[]) { 0.5, 0.5 }); - test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 4, _M(FL)|_M(FR)|_M(RL)|_M(RR), (float[]) { 0.5, 0.5 }); - test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), (float[]) { 0.5, 0.5 }); + test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 1, _M(MONO), + MATRIX(0.707107, 0.707107, 0.5, 0.5)); + test_mix(4, _M(FL)|_M(FR)|_M(SL)|_M(SR), 1, _M(MONO), + MATRIX(0.707107, 0.707107, 0.5, 0.5)); + test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 2, _M(FL)|_M(FR), + MATRIX(1.0, 0.0, 0.707107, 0.0, + 0.0, 1.0, 0.0, 0.707107)); + test_mix(4, _M(FL)|_M(FR)|_M(SL)|_M(SR), 2, _M(FL)|_M(FR), + MATRIX(1.0, 0.0, 0.707107, 0.0, + 0.0, 1.0, 0.0, 0.707107)); + test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 3, _M(FL)|_M(FR)|_M(LFE), + MATRIX(1.0, 0.0, 0.707107, 0.0, + 0.0, 1.0, 0.0, 0.707107, + 0.0, 0.0, 0.0, 0.0)); + test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 4, _M(FL)|_M(FR)|_M(RL)|_M(RR), + MATRIX(1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0)); + test_mix(4, _M(FL)|_M(FR)|_M(RL)|_M(RR), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), + MATRIX(1.0, 0.0, 0.707107, 0.0, + 0.0, 1.0, 0.0, 0.707107, + 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0)); } static void test_5p1_N(void) { - test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 1, _M(MONO), (float[]) { 0.5, 0.5 }); - test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 2, _M(FL)|_M(FR), (float[]) { 0.5, 0.5 }); - test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(RL)|_M(RR), 2, _M(FL)|_M(FR), (float[]) { 0.5, 0.5 }); - test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 3, _M(FL)|_M(FR)|_M(LFE), (float[]) { 0.5, 0.5 }); - test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), (float[]) { 0.5, 0.5 }); - test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 4, _M(FL)|_M(FR)|_M(RL)|_M(RR), (float[]) { 0.5, 0.5 }); - test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 5, _M(FL)|_M(FR)|_M(FC)|_M(SL)|_M(SR), (float[]) { 0.5, 0.5 }); - test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), (float[]) { 0.5, 0.5 }); + test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 1, _M(MONO), + MATRIX(0.707107, 0.707107, 1.0, 0.0, 0.5, 0.5)); + test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 2, _M(FL)|_M(FR), + MATRIX(1.0, 0.0, 0.707107, 0.0, 0.707107, 0.0, + 0.0, 1.0, 0.707107, 0.0, 0.0, 0.707107)); + test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(RL)|_M(RR), 2, _M(FL)|_M(FR), + MATRIX(1.0, 0.0, 0.707107, 0.0, 0.707107, 0.0, + 0.0, 1.0, 0.707107, 0.0, 0.0, 0.707107)); + test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 3, _M(FL)|_M(FR)|_M(LFE), + MATRIX(1.0, 0.0, 0.707107, 0.0, 0.707107, 0.0, + 0.0, 1.0, 0.707107, 0.0, 0.0, 0.707107, + 0.0, 0.0, 0.0, 1.0, 0.0, 0.0)); + test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 4, _M(FL)|_M(FR)|_M(LFE)|_M(FC), + MATRIX(1.0, 0.0, 0.0, 0.0, 0.707107, 0.0, + 0.0, 1.0, 0.0, 0.0, 0.0, 0.707107, + 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 1.0, 0.0, 0.0)); + test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 4, _M(FL)|_M(FR)|_M(RL)|_M(RR), + MATRIX(1.0, 0.0, 0.707107, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.707107, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 1.0)); + test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 5, _M(FL)|_M(FR)|_M(FC)|_M(SL)|_M(SR), + MATRIX(1.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 1.0)); + test_mix(6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), 6, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR), + MATRIX(1.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 1.0)); } static void test_7p1_N(void) { - test_mix(8, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR)|_M(RL)|_M(RR), 1, _M(MONO), (float[]) { 0.5, 0.5 }); - test_mix(8, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR)|_M(RL)|_M(RR), 2, _M(FL)|_M(FR), (float[]) { 0.5, 0.5 }); + test_mix(8, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR)|_M(RL)|_M(RR), 1, _M(MONO), + MATRIX(0.707107, 0.707107, 1.0, 0.0, 0.5, 0.5, 0.5, 0.5)); + test_mix(8, _M(FL)|_M(FR)|_M(LFE)|_M(FC)|_M(SL)|_M(SR)|_M(RL)|_M(RR), 2, _M(FL)|_M(FR), + MATRIX(1.0, 0.0, 0.707107, 0.0, 0.707107, 0.0, 0.707107, 0.0, + 0.0, 1.0, 0.707107, 0.0, 0.0, 0.707107, 0.0, 0.707107)); } int main(int argc, char *argv[])