channelmix: support more than 19 channels

The channelmatrix was limited to the 19 known channels, leaving
only the first 19 channels usable.

Use the full 64x64 matrix instead and make sure to fill all 64
channels with the identity matrix in the case we need to copy or
distribute the matrix to unknown positions.

See #57
This commit is contained in:
Wim Taymans 2021-04-16 10:56:24 +02:00
parent 4bfa4a3964
commit fa3bd6b0e1

View file

@ -124,27 +124,6 @@ static const struct channelmix_info *find_channelmix_info(uint32_t src_chan, uin
return NULL; return NULL;
} }
#define M 0
#define FL 1
#define FR 2
#define FC 3
#define LFE 4
#define SL 5
#define SR 6
#define FLC 7
#define FRC 8
#define RC 9
#define RL 10
#define RR 11
#define TC 12
#define TFL 13
#define TFC 14
#define TFR 15
#define TRL 16
#define TRC 17
#define TRR 18
#define NUM_CHAN 19
#define SQRT3_2 1.224744871f /* sqrt(3/2) */ #define SQRT3_2 1.224744871f /* sqrt(3/2) */
#define SQRT1_2 0.707106781f #define SQRT1_2 0.707106781f
#define SQRT2 1.414213562f #define SQRT2 1.414213562f
@ -153,14 +132,15 @@ static const struct channelmix_info *find_channelmix_info(uint32_t src_chan, uin
#define MATRIX_DOLBY 1 #define MATRIX_DOLBY 1
#define MATRIX_DPLII 2 #define MATRIX_DPLII 2
#define _MASK(ch) (1ULL << SPA_AUDIO_CHANNEL_ ## ch) #define _CH(ch) ((SPA_AUDIO_CHANNEL_ ## ch)-3)
#define _MASK(ch) (1ULL << _CH(ch))
#define STEREO (_MASK(FL)|_MASK(FR)) #define STEREO (_MASK(FL)|_MASK(FR))
#define REAR (_MASK(RL)|_MASK(RR)) #define REAR (_MASK(RL)|_MASK(RR))
#define SIDE (_MASK(SL)|_MASK(SR)) #define SIDE (_MASK(SL)|_MASK(SR))
static int make_matrix(struct channelmix *mix) static int make_matrix(struct channelmix *mix)
{ {
float matrix[NUM_CHAN][NUM_CHAN] = {{ 0.0f }}; float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS] = {{ 0.0f }};
uint64_t src_mask = mix->src_mask; uint64_t src_mask = mix->src_mask;
uint64_t dst_mask = mix->dst_mask; uint64_t dst_mask = mix->dst_mask;
uint64_t unassigned; uint64_t unassigned;
@ -169,37 +149,50 @@ static int make_matrix(struct channelmix *mix)
float slev = SQRT1_2; float slev = SQRT1_2;
float llev = 0.5f; float llev = 0.5f;
float maxsum = 0.0f; float maxsum = 0.0f;
#define _MATRIX(s,d) matrix[_CH(s)][_CH(d)]
spa_log_debug(mix->log, "src-mask:%08"PRIx64" dst-mask:%08"PRIx64, spa_log_debug(mix->log, "src-mask:%08"PRIx64" dst-mask:%08"PRIx64,
src_mask, dst_mask); src_mask, dst_mask);
if ((src_mask & _MASK(MONO)) == _MASK(MONO)) /* move the MONO mask to FC so that the lower bits can be shifed
src_mask = _MASK(FC); * away. */
if ((dst_mask & _MASK(MONO)) == _MASK(MONO)) if ((src_mask & (1Ull << SPA_AUDIO_CHANNEL_MONO)) != 0)
dst_mask = _MASK(FC); src_mask |= (1ULL << SPA_AUDIO_CHANNEL_FC);
if ((dst_mask & (1Ull << SPA_AUDIO_CHANNEL_MONO)) != 0)
dst_mask |= (1ULL << SPA_AUDIO_CHANNEL_FC);
/* shift so that bit 0 is FL */
src_mask >>= 3;
dst_mask >>= 3;
if (src_mask == 0 || dst_mask == 0) { if (src_mask == 0 || dst_mask == 0) {
if (src_mask == _MASK(FC) && mix->src_chan == 1) { if (src_mask == _MASK(FC) && mix->src_chan == 1) {
/* one mono src goes everywhere */ /* one FC/MONO src goes everywhere */
for (i = 0; i < NUM_CHAN; i++) spa_log_debug(mix->log, "distribute FC/MONO");
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
matrix[i][0]= 1.0f; matrix[i][0]= 1.0f;
} else if (dst_mask == _MASK(FC) && mix->dst_chan == 1) { } else if (dst_mask == _MASK(FC) && mix->dst_chan == 1) {
/* one mono dst get average of everything */ /* one FC/MONO dst get average of everything */
for (i = 0; i < NUM_CHAN; i++) spa_log_debug(mix->log, "average FC/MONO");
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
matrix[0][i]= 1.0f / mix->src_chan; matrix[0][i]= 1.0f / mix->src_chan;
} else { } else {
/* just pair channels */ /* just pair channels */
for (i = 0; i < NUM_CHAN; i++) spa_log_debug(mix->log, "paring channels");
for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++)
matrix[i][i]= 1.0f; matrix[i][i]= 1.0f;
} }
src_mask = dst_mask = ~0LU; src_mask = dst_mask = ~0LU;
goto done; goto done;
} else { } else {
for (i = 0; i < NUM_CHAN; i++) { spa_log_debug(mix->log, "matching channels");
if ((src_mask & dst_mask & (1ULL << (i + 2)))) for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
if ((src_mask & dst_mask & (1ULL << i))) {
spa_log_debug(mix->log, "matched %u", i);
matrix[i][i]= 1.0f; matrix[i][i]= 1.0f;
} }
} }
}
unassigned = src_mask & ~dst_mask; unassigned = src_mask & ~dst_mask;
@ -209,11 +202,11 @@ static int make_matrix(struct channelmix *mix)
if ((dst_mask & STEREO) == STEREO){ if ((dst_mask & STEREO) == STEREO){
spa_log_debug(mix->log, "assign FC to STEREO"); spa_log_debug(mix->log, "assign FC to STEREO");
if(src_mask & STEREO) { if(src_mask & STEREO) {
matrix[FL][FC] += clev; _MATRIX(FL,FC) += clev;
matrix[FR][FC] += clev; _MATRIX(FR,FC) += clev;
} else { } else {
matrix[FL][FC] += SQRT1_2; _MATRIX(FL,FC) += SQRT1_2;
matrix[FR][FC] += SQRT1_2; _MATRIX(FR,FC) += SQRT1_2;
} }
} else { } else {
spa_log_warn(mix->log, "can't assign FC"); spa_log_warn(mix->log, "can't assign FC");
@ -223,10 +216,10 @@ static int make_matrix(struct channelmix *mix)
if (unassigned & STEREO){ if (unassigned & STEREO){
if (dst_mask & _MASK(FC)) { if (dst_mask & _MASK(FC)) {
spa_log_debug(mix->log, "assign STEREO to FC"); spa_log_debug(mix->log, "assign STEREO to FC");
matrix[FC][FL] += SQRT1_2; _MATRIX(FC,FL) += SQRT1_2;
matrix[FC][FR] += SQRT1_2; _MATRIX(FC,FR) += SQRT1_2;
if (src_mask & _MASK(FC)) if (src_mask & _MASK(FC))
matrix[FC][FC] = clev * SQRT2; _MATRIX(FC,FC) = clev * SQRT2;
} else { } else {
spa_log_warn(mix->log, "can't assign STEREO"); spa_log_warn(mix->log, "can't assign STEREO");
} }
@ -235,30 +228,30 @@ static int make_matrix(struct channelmix *mix)
if (unassigned & _MASK(RC)) { if (unassigned & _MASK(RC)) {
if (dst_mask & REAR){ if (dst_mask & REAR){
spa_log_debug(mix->log, "assign RC to RL+RR"); spa_log_debug(mix->log, "assign RC to RL+RR");
matrix[RL][RC] += SQRT1_2; _MATRIX(RL,RC) += SQRT1_2;
matrix[RR][RC] += SQRT1_2; _MATRIX(RR,RC) += SQRT1_2;
} else if (dst_mask & SIDE) { } else if (dst_mask & SIDE) {
spa_log_debug(mix->log, "assign RC to SL+SR"); spa_log_debug(mix->log, "assign RC to SL+SR");
matrix[SL][RC] += SQRT1_2; _MATRIX(SL,RC) += SQRT1_2;
matrix[SR][RC] += SQRT1_2; _MATRIX(SR,RC) += SQRT1_2;
} else if(dst_mask & STEREO) { } else if(dst_mask & STEREO) {
spa_log_debug(mix->log, "assign RC to FL+FR"); spa_log_debug(mix->log, "assign RC to FL+FR");
if (matrix_encoding == MATRIX_DOLBY || if (matrix_encoding == MATRIX_DOLBY ||
matrix_encoding == MATRIX_DPLII) { matrix_encoding == MATRIX_DPLII) {
if (unassigned & (_MASK(RL)|_MASK(RR))) { if (unassigned & (_MASK(RL)|_MASK(RR))) {
matrix[FL][RC] -= slev * SQRT1_2; _MATRIX(FL,RC) -= slev * SQRT1_2;
matrix[FR][RC] += slev * SQRT1_2; _MATRIX(FR,RC) += slev * SQRT1_2;
} else { } else {
matrix[FL][RC] -= slev; _MATRIX(FL,RC) -= slev;
matrix[FR][RC] += slev; _MATRIX(FR,RC) += slev;
} }
} else { } else {
matrix[FL][RC] += slev * SQRT1_2; _MATRIX(FL,RC) += slev * SQRT1_2;
matrix[FR][RC] += slev * SQRT1_2; _MATRIX(FR,RC) += slev * SQRT1_2;
} }
} else if (dst_mask & _MASK(FC)) { } else if (dst_mask & _MASK(FC)) {
spa_log_debug(mix->log, "assign RC to FC"); spa_log_debug(mix->log, "assign RC to FC");
matrix[FC][RC] += slev * SQRT1_2; _MATRIX(FC,RC) += slev * SQRT1_2;
} else { } else {
spa_log_warn(mix->log, "can't assign RC"); spa_log_warn(mix->log, "can't assign RC");
} }
@ -267,37 +260,37 @@ static int make_matrix(struct channelmix *mix)
if (unassigned & REAR) { if (unassigned & REAR) {
if (dst_mask & _MASK(RC)) { if (dst_mask & _MASK(RC)) {
spa_log_debug(mix->log, "assign RL+RR to RC"); spa_log_debug(mix->log, "assign RL+RR to RC");
matrix[RC][RL] += SQRT1_2; _MATRIX(RC,RL) += SQRT1_2;
matrix[RC][RR] += SQRT1_2; _MATRIX(RC,RR) += SQRT1_2;
} else if (dst_mask & SIDE) { } else if (dst_mask & SIDE) {
spa_log_debug(mix->log, "assign RL+RR to SL+SR"); spa_log_debug(mix->log, "assign RL+RR to SL+SR");
if (src_mask & SIDE) { if (src_mask & SIDE) {
matrix[SL][RL] += SQRT1_2; _MATRIX(SL,RL) += SQRT1_2;
matrix[SR][RR] += SQRT1_2; _MATRIX(SR,RR) += SQRT1_2;
} else { } else {
matrix[SL][RL] += 1.0f; _MATRIX(SL,RL) += 1.0f;
matrix[SR][RR] += 1.0f; _MATRIX(SR,RR) += 1.0f;
} }
} else if (dst_mask & STEREO) { } else if (dst_mask & STEREO) {
spa_log_debug(mix->log, "assign RL+RR to FL+FR %f", slev); spa_log_debug(mix->log, "assign RL+RR to FL+FR %f", slev);
if (matrix_encoding == MATRIX_DOLBY) { if (matrix_encoding == MATRIX_DOLBY) {
matrix[FL][RL] -= slev * SQRT1_2; _MATRIX(FL,RL) -= slev * SQRT1_2;
matrix[FL][RR] -= slev * SQRT1_2; _MATRIX(FL,RR) -= slev * SQRT1_2;
matrix[FR][RL] += slev * SQRT1_2; _MATRIX(FR,RL) += slev * SQRT1_2;
matrix[FR][RR] += slev * SQRT1_2; _MATRIX(FR,RR) += slev * SQRT1_2;
} else if (matrix_encoding == MATRIX_DPLII) { } else if (matrix_encoding == MATRIX_DPLII) {
matrix[FL][RL] -= slev * SQRT3_2; _MATRIX(FL,RL) -= slev * SQRT3_2;
matrix[FL][RR] -= slev * SQRT1_2; _MATRIX(FL,RR) -= slev * SQRT1_2;
matrix[FR][RL] += slev * SQRT1_2; _MATRIX(FR,RL) += slev * SQRT1_2;
matrix[FR][RR] += slev * SQRT3_2; _MATRIX(FR,RR) += slev * SQRT3_2;
} else { } else {
matrix[FL][RL] += slev; _MATRIX(FL,RL) += slev;
matrix[FR][RR] += slev; _MATRIX(FR,RR) += slev;
} }
} else if (dst_mask & _MASK(FC)) { } else if (dst_mask & _MASK(FC)) {
spa_log_debug(mix->log, "assign RL+RR to FC"); spa_log_debug(mix->log, "assign RL+RR to FC");
matrix[FC][RL]+= slev * SQRT1_2; _MATRIX(FC,RL)+= slev * SQRT1_2;
matrix[FC][RR]+= slev * SQRT1_2; _MATRIX(FC,RR)+= slev * SQRT1_2;
} else { } else {
spa_log_warn(mix->log, "can't assign RL"); spa_log_warn(mix->log, "can't assign RL");
} }
@ -307,36 +300,36 @@ static int make_matrix(struct channelmix *mix)
if (dst_mask & REAR) { if (dst_mask & REAR) {
spa_log_debug(mix->log, "assign SL+SR to RL+RR"); spa_log_debug(mix->log, "assign SL+SR to RL+RR");
if (src_mask & _MASK(RL)) { if (src_mask & _MASK(RL)) {
matrix[RL][SL] += SQRT1_2; _MATRIX(RL,SL) += SQRT1_2;
matrix[RR][SR] += SQRT1_2; _MATRIX(RR,SR) += SQRT1_2;
} else { } else {
matrix[RL][SL] += 1.0f; _MATRIX(RL,SL) += 1.0f;
matrix[RR][SR] += 1.0f; _MATRIX(RR,SR) += 1.0f;
} }
} else if (dst_mask & _MASK(RC)) { } else if (dst_mask & _MASK(RC)) {
spa_log_debug(mix->log, "assign SL+SR to RC"); spa_log_debug(mix->log, "assign SL+SR to RC");
matrix[RC][SL]+= SQRT1_2; _MATRIX(RC,SL)+= SQRT1_2;
matrix[RC][SR]+= SQRT1_2; _MATRIX(RC,SR)+= SQRT1_2;
} else if (dst_mask & STEREO) { } else if (dst_mask & STEREO) {
spa_log_debug(mix->log, "assign SL+SR to FL+FR"); spa_log_debug(mix->log, "assign SL+SR to FL+FR");
if (matrix_encoding == MATRIX_DOLBY) { if (matrix_encoding == MATRIX_DOLBY) {
matrix[FL][SL] -= slev * SQRT1_2; _MATRIX(FL,SL) -= slev * SQRT1_2;
matrix[FL][SR] -= slev * SQRT1_2; _MATRIX(FL,SR) -= slev * SQRT1_2;
matrix[FR][SL] += slev * SQRT1_2; _MATRIX(FR,SL) += slev * SQRT1_2;
matrix[FR][SR] += slev * SQRT1_2; _MATRIX(FR,SR) += slev * SQRT1_2;
} else if (matrix_encoding == MATRIX_DPLII) { } else if (matrix_encoding == MATRIX_DPLII) {
matrix[FL][SL] -= slev * SQRT3_2; _MATRIX(FL,SL) -= slev * SQRT3_2;
matrix[FL][SR] -= slev * SQRT1_2; _MATRIX(FL,SR) -= slev * SQRT1_2;
matrix[FR][SL] += slev * SQRT1_2; _MATRIX(FR,SL) += slev * SQRT1_2;
matrix[FR][SR] += slev * SQRT3_2; _MATRIX(FR,SR) += slev * SQRT3_2;
} else { } else {
matrix[FL][SL] += slev; _MATRIX(FL,SL) += slev;
matrix[FR][SR] += slev; _MATRIX(FR,SR) += slev;
} }
} else if (dst_mask & _MASK(FC)) { } else if (dst_mask & _MASK(FC)) {
spa_log_debug(mix->log, "assign SL+SR to FC"); spa_log_debug(mix->log, "assign SL+SR to FC");
matrix[FC][SL] += slev * SQRT1_2; _MATRIX(FC,SL) += slev * SQRT1_2;
matrix[FC][SR] += slev * SQRT1_2; _MATRIX(FC,SR) += slev * SQRT1_2;
} else { } else {
spa_log_warn(mix->log, "can't assign SL"); spa_log_warn(mix->log, "can't assign SL");
} }
@ -345,12 +338,12 @@ static int make_matrix(struct channelmix *mix)
if (unassigned & _MASK(FLC)) { if (unassigned & _MASK(FLC)) {
if (dst_mask & STEREO) { if (dst_mask & STEREO) {
spa_log_debug(mix->log, "assign FLC+FRC to FL+FR"); spa_log_debug(mix->log, "assign FLC+FRC to FL+FR");
matrix[FL][FLC]+= 1.0f; _MATRIX(FL,FLC)+= 1.0f;
matrix[FR][FRC]+= 1.0f; _MATRIX(FR,FRC)+= 1.0f;
} else if(dst_mask & _MASK(FC)) { } else if(dst_mask & _MASK(FC)) {
spa_log_debug(mix->log, "assign FLC+FRC to FC"); spa_log_debug(mix->log, "assign FLC+FRC to FC");
matrix[FC][FLC]+= SQRT1_2; _MATRIX(FC,FLC)+= SQRT1_2;
matrix[FC][FRC]+= SQRT1_2; _MATRIX(FC,FRC)+= SQRT1_2;
} else { } else {
spa_log_warn(mix->log, "can't assign FLC"); spa_log_warn(mix->log, "can't assign FLC");
} }
@ -359,11 +352,11 @@ static int make_matrix(struct channelmix *mix)
SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_MIX_LFE)) { SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_MIX_LFE)) {
if (dst_mask & _MASK(FC)) { if (dst_mask & _MASK(FC)) {
spa_log_debug(mix->log, "assign LFE to FC"); spa_log_debug(mix->log, "assign LFE to FC");
matrix[FC][LFE] += llev; _MATRIX(FC,LFE) += llev;
} else if (dst_mask & STEREO) { } else if (dst_mask & STEREO) {
spa_log_debug(mix->log, "assign LFE to FL+FR"); spa_log_debug(mix->log, "assign LFE to FL+FR");
matrix[FL][LFE] += llev * SQRT1_2; _MATRIX(FL,LFE) += llev * SQRT1_2;
matrix[FR][LFE] += llev * SQRT1_2; _MATRIX(FR,LFE) += llev * SQRT1_2;
} else { } else {
spa_log_warn(mix->log, "can't assign LFE"); spa_log_warn(mix->log, "can't assign LFE");
} }
@ -379,8 +372,8 @@ static int make_matrix(struct channelmix *mix)
if (unassigned & _MASK(FC)) { if (unassigned & _MASK(FC)) {
if ((src_mask & STEREO) == STEREO) { if ((src_mask & STEREO) == STEREO) {
spa_log_debug(mix->log, "produce FC from STEREO"); spa_log_debug(mix->log, "produce FC from STEREO");
matrix[FC][FL] += clev; _MATRIX(FC,FL) += clev;
matrix[FC][FR] += clev; _MATRIX(FC,FR) += clev;
} else { } else {
spa_log_warn(mix->log, "can't produce FC"); spa_log_warn(mix->log, "can't produce FC");
} }
@ -388,8 +381,8 @@ static int make_matrix(struct channelmix *mix)
if (unassigned & _MASK(LFE) && mix->lfe_cutoff > 0.0f) { if (unassigned & _MASK(LFE) && mix->lfe_cutoff > 0.0f) {
if ((src_mask & STEREO) == STEREO) { if ((src_mask & STEREO) == STEREO) {
spa_log_debug(mix->log, "produce LFE from STEREO"); spa_log_debug(mix->log, "produce LFE from STEREO");
matrix[LFE][FL] += llev; _MATRIX(LFE,FL) += llev;
matrix[LFE][FR] += llev; _MATRIX(LFE,FR) += llev;
} else { } else {
spa_log_warn(mix->log, "can't produce LFE"); spa_log_warn(mix->log, "can't produce LFE");
} }
@ -397,39 +390,39 @@ static int make_matrix(struct channelmix *mix)
if (unassigned & SIDE) { if (unassigned & SIDE) {
if ((src_mask & REAR) == REAR) { if ((src_mask & REAR) == REAR) {
spa_log_debug(mix->log, "produce SIDE from REAR"); spa_log_debug(mix->log, "produce SIDE from REAR");
matrix[SL][RL] += 1.0f; _MATRIX(SL,RL) += 1.0f;
matrix[SR][RR] += 1.0f; _MATRIX(SR,RR) += 1.0f;
} else if ((src_mask & STEREO) == STEREO) { } else if ((src_mask & STEREO) == STEREO) {
spa_log_debug(mix->log, "produce SIDE from STEREO"); spa_log_debug(mix->log, "produce SIDE from STEREO");
matrix[SL][FL] += 1.0f; _MATRIX(SL,FL) += 1.0f;
matrix[SR][FR] += 1.0f; _MATRIX(SR,FR) += 1.0f;
} }
} }
if (unassigned & REAR) { if (unassigned & REAR) {
if ((src_mask & SIDE) == SIDE) { if ((src_mask & SIDE) == SIDE) {
spa_log_debug(mix->log, "produce REAR from SIDE"); spa_log_debug(mix->log, "produce REAR from SIDE");
matrix[RL][SL] += 1.0f; _MATRIX(RL,SL) += 1.0f;
matrix[RR][SR] += 1.0f; _MATRIX(RR,SR) += 1.0f;
} else if ((src_mask & STEREO) == STEREO) { } else if ((src_mask & STEREO) == STEREO) {
spa_log_debug(mix->log, "produce REAR from STEREO"); spa_log_debug(mix->log, "produce REAR from STEREO");
matrix[RL][FL] += 1.0f; _MATRIX(RL,FL) += 1.0f;
matrix[RR][FR] += 1.0f; _MATRIX(RR,FR) += 1.0f;
} }
} }
done: done:
for (jc = 0, ic = 0, i = 0; i < NUM_CHAN; i++) { for (jc = 0, ic = 0, i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) {
float sum = 0.0f; float sum = 0.0f;
if ((dst_mask & (1UL << (i + 2))) == 0) if ((dst_mask & (1UL << i)) == 0)
continue; continue;
for (jc = 0, j = 0; j < NUM_CHAN; j++) { for (jc = 0, j = 0; j < SPA_AUDIO_MAX_CHANNELS; j++) {
if ((src_mask & (1UL << (j + 2))) == 0) if ((src_mask & (1UL << j)) == 0)
continue; continue;
mix->matrix_orig[ic][jc++] = matrix[i][j]; mix->matrix_orig[ic][jc++] = matrix[i][j];
sum += fabs(matrix[i][j]); sum += fabs(matrix[i][j]);
if (i == LFE) if (i == _CH(LFE))
lr4_set(&mix->lr4[ic], BQ_LOWPASS, mix->lfe_cutoff / mix->freq); lr4_set(&mix->lr4[ic], BQ_LOWPASS, mix->lfe_cutoff / mix->freq);
} }
maxsum = SPA_MAX(maxsum, sum); maxsum = SPA_MAX(maxsum, sum);