From 2895961b48b004d37af06b1ef03b9e0ea3b122cd Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 14 Feb 2023 11:26:53 +0100 Subject: [PATCH] audioconvert: improve channelmix with unknown layouts Use a 0 mask to handle unknown layouts. When the source or destination is an unknown layout, pair, distribute or average. When pairing, keep track how we paired and use that to construct the matrix later. This fixes [ UNK UNK ] -> [ FL FR ] mapping by pairing. --- spa/plugins/audioconvert/channelmix-ops.c | 119 +++++++++++++++------- 1 file changed, 83 insertions(+), 36 deletions(-) diff --git a/spa/plugins/audioconvert/channelmix-ops.c b/spa/plugins/audioconvert/channelmix-ops.c index f0d937bef..a47a545f1 100644 --- a/spa/plugins/audioconvert/channelmix-ops.c +++ b/spa/plugins/audioconvert/channelmix-ops.c @@ -150,11 +150,59 @@ static const struct channelmix_info *find_channelmix_info(uint32_t src_chan, uin #define REAR (_MASK(RL)|_MASK(RR)) #define SIDE (_MASK(SL)|_MASK(SR)) +static uint32_t mask_to_ch(struct channelmix *mix, uint64_t mask) +{ + uint32_t ch = 0; + while (mask > 1) { + ch++; + mask >>= 1; + } + return ch; +} + +static void distribute_mix(struct channelmix *mix, + float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS], + uint64_t mask) +{ + uint32_t i, ch = mask_to_ch(mix, mask); + for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + matrix[i][ch]= 1.0f; +} +static void average_mix(struct channelmix *mix, + float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS], + uint64_t mask) +{ + uint32_t i, ch = mask_to_ch(mix, mask); + for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + matrix[ch][i]= 1.0f; +} +static void pair_mix(float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS]) +{ + uint32_t i; + for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + matrix[i][i]= 1.0f; +} +static bool match_mix(struct channelmix *mix, + float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS], + uint64_t src_mask, uint64_t dst_mask) +{ + bool matched = false; + uint32_t i; + for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { + if ((src_mask & dst_mask & (1ULL << i))) { + spa_log_info(mix->log, "matched channel %u (%f)", i, 1.0f); + matrix[i][i] = 1.0f; + matched = true; + } + } + return matched; +} + static int make_matrix(struct channelmix *mix) { float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS] = {{ 0.0f }}; - uint64_t src_mask = mix->src_mask; - uint64_t dst_mask = mix->dst_mask; + uint64_t src_mask = mix->src_mask, src_paired; + uint64_t dst_mask = mix->dst_mask, dst_paired; uint32_t src_chan = mix->src_chan; uint32_t dst_chan = mix->dst_chan; uint64_t unassigned, keep; @@ -175,39 +223,38 @@ static int make_matrix(struct channelmix *mix) src_mask >>= _SH; dst_mask >>= _SH; - if (src_mask == 0) - src_mask = _MASK(MONO); - if (dst_mask == 0) - dst_mask = _MASK(MONO); + if (src_chan > 1 && (src_mask & _MASK(MONO))) + src_mask = 0; + if (dst_chan > 1 && (dst_mask & _MASK(MONO))) + dst_mask = 0; - /* unknown channels or just mono channels */ - if (src_mask & _MASK(MONO) && dst_mask & _MASK(MONO)) { + src_paired = src_mask; + dst_paired = dst_mask; + + /* unknown channels */ + if (src_mask == 0 || dst_mask == 0) { if (src_chan == 1) { - /* one MONO src goes everywhere */ - spa_log_info(mix->log, "distribute MONO (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) - matrix[i][0]= 1.0f; + /* one src channel goes everywhere */ + spa_log_info(mix->log, "distribute UNK (%f) %"PRIu64, 1.0f, src_mask); + distribute_mix(mix, matrix, src_mask); } else if (dst_chan == 1) { - /* one MONO dst get average of everything */ - spa_log_info(mix->log, "average MONO (%f)", 1.0f / src_chan); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) - matrix[0][i]= 1.0f / src_chan; + /* one dst channel get average of everything */ + spa_log_info(mix->log, "average UNK (%f) %"PRIu64, 1.0f / src_chan, dst_mask); + average_mix(mix, matrix, dst_mask); + normalize = true; } else { /* just pair channels */ - spa_log_info(mix->log, "pairing channels (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) - matrix[i][i]= 1.0f; + spa_log_info(mix->log, "pairing UNK channels (%f)", 1.0f); + if (src_mask == 0) + src_paired = dst_mask; + else if (dst_mask == 0) + dst_paired = src_mask; + pair_mix(matrix); } goto done; } else { spa_log_debug(mix->log, "matching channels"); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { - if ((src_mask & dst_mask & (1ULL << i))) { - spa_log_info(mix->log, "matched channel %u (%f)", i, 1.0f); - matrix[i][i]= 1.0f; - matched = true; - } - } + matched = match_mix(mix, matrix, src_mask, dst_mask); } unassigned = src_mask & ~dst_mask; @@ -284,7 +331,7 @@ static int make_matrix(struct channelmix *mix) } keep &= ~FRONT; } else if ((dst_mask & _MASK(MONO))){ - spa_log_info(mix->log, "assign STEREO to MONO (%f)", 0.5f); + spa_log_info(mix->log, "assign STEREO to MONO (%f)", 1.0f); for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { matrix[i][_CH(FL)]= 1.0f; matrix[i][_CH(FR)]= 1.0f; @@ -463,7 +510,7 @@ static int make_matrix(struct channelmix *mix) _MATRIX(FL,LFE) += llev * SQRT1_2; _MATRIX(FR,LFE) += llev * SQRT1_2; } else if ((dst_mask & _MASK(MONO))){ - spa_log_info(mix->log, "assign LFE to MONO (%f)", 0.5f); + spa_log_info(mix->log, "assign LFE to MONO (%f)", 1.0f); for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) matrix[i][_CH(LFE)]= 1.0f; normalize = true; @@ -595,26 +642,26 @@ static int make_matrix(struct channelmix *mix) } done: - if (dst_mask & _MASK(MONO)) - dst_mask = ~0LU; - if (src_mask & _MASK(MONO)) - src_mask = ~0LU; + if (dst_paired == 0) + dst_paired = ~0LU; + if (src_paired == 0) + src_paired = ~0LU; for (jc = 0, ic = 0, i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { float sum = 0.0f; char str[1024], str2[1024]; int idx = 0, idx2 = 0; - if ((dst_mask & (1UL << i)) == 0) + if ((dst_paired & (1UL << i)) == 0) continue; for (jc = 0, j = 0; j < SPA_AUDIO_MAX_CHANNELS; j++) { - if ((src_mask & (1UL << j)) == 0) + if ((src_paired & (1UL << j)) == 0) continue; if (ic >= dst_chan || jc >= src_chan) continue; if (ic == 0) idx2 += snprintf(str2 + idx2, sizeof(str2) - idx2, "%-4.4s ", - src_mask == ~0LU ? "MONO" : + src_mask == 0 ? "UNK" : spa_debug_type_find_short_name(spa_type_audio_channel, j + _SH)); mix->matrix_orig[ic][jc++] = matrix[i][j]; @@ -629,7 +676,7 @@ done: spa_log_info(mix->log, " %s", str2); if (idx > 0) { spa_log_info(mix->log, "%-4.4s %s %f", - dst_mask == ~0LU ? "MONO" : + dst_mask == 0 ? "UNK" : spa_debug_type_find_short_name(spa_type_audio_channel, i + _SH), str, sum); }