channelmix: normalize volumes

Normalize the volumes.
Don't mix in LFE by default but add an option
Move some booleans to flags
Improve some checks for fastpaths.
This commit is contained in:
Wim Taymans 2020-09-03 13:20:36 +02:00
parent 289a8e86ca
commit f7d8fef070
5 changed files with 89 additions and 81 deletions

View file

@ -32,11 +32,11 @@ channelmix_copy_c(struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRICT ds
float **d = (float **)dst;
const float **s = (const float **)src;
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
for (i = 0; i < n_dst; i++)
memset(d[i], 0, n_samples * sizeof(float));
}
else if (mix->identity) {
else if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_IDENTITY)) {
for (i = 0; i < n_dst; i++)
spa_memcpy(d[i], s[i], n_samples * sizeof(float));
}
@ -81,19 +81,18 @@ channelmix_f32_1_2_c(struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRICT
const float v0 = mix->matrix[0][0];
const float v1 = mix->matrix[1][0];
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
memset(d[0], 0, n_samples * sizeof(float));
memset(d[1], 0, n_samples * sizeof(float));
}
else if (mix->norm) {
for (n = 0; n < n_samples; n++)
d[0][n] = d[1][n] = s[0][n];
}
else if (mix->equal) {
for (n = 0; n < n_samples; n++)
d[0][n] = d[1][n] = s[0][n] * v0;
}
else {
} else if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_EQUAL)) {
if (v0 == 1.0f) {
for (n = 0; n < n_samples; n++)
d[0][n] = d[1][n] = s[0][n];
} else {
for (n = 0; n < n_samples; n++)
d[0][n] = d[1][n] = s[0][n] * v0;
}
} else {
for (n = 0; n < n_samples; n++) {
d[0][n] = s[0][n] * v0;
d[1][n] = s[0][n] * v1;
@ -111,10 +110,9 @@ channelmix_f32_2_1_c(struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRICT
const float v0 = mix->matrix[0][0];
const float v1 = mix->matrix[0][1];
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
memset(d[0], 0, n_samples * sizeof(float));
}
else if (mix->equal) {
} else if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_EQUAL)) {
for (n = 0; n < n_samples; n++)
d[0][n] = (s[0][n] + s[1][n]) * v0;
}
@ -136,10 +134,10 @@ channelmix_f32_4_1_c(struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRICT
const float v2 = mix->matrix[0][2];
const float v3 = mix->matrix[0][3];
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
memset(d[0], 0, n_samples * sizeof(float));
}
else if (mix->equal) {
else if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_EQUAL)) {
for (n = 0; n < n_samples; n++)
d[0][n] = (s[0][n] + s[1][n] + s[2][n] + s[3][n]) * v0;
}
@ -161,10 +159,10 @@ channelmix_f32_3p1_1_c(struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRI
const float v1 = mix->matrix[0][1];
const float v2 = mix->matrix[0][2];
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
memset(d[0], 0, n_samples * sizeof(float));
}
else if (mix->equal) {
else if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_EQUAL)) {
for (n = 0; n < n_samples; n++)
d[0][n] = (s[0][n] + s[1][n] + s[2][n] + s[3][n]) * v0;
}
@ -189,20 +187,21 @@ channelmix_f32_2_4_c(struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRICT
const float v2 = mix->matrix[2][0];
const float v3 = mix->matrix[3][1];
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
for (i = 0; i < n_dst; i++)
memset(d[i], 0, n_samples * sizeof(float));
}
else if (mix->norm) {
for (n = 0; n < n_samples; n++) {
d[0][n] = d[2][n] = s[0][n];
d[1][n] = d[3][n] = s[1][n];
}
}
else if (v0 == v2 && v1 == v3) {
for (n = 0; n < n_samples; n++) {
d[0][n] = d[2][n] = s[0][n] * v0;
d[1][n] = d[3][n] = s[1][n] * v1;
if (v0 == 1.0f && v1 == 1.0f) {
for (n = 0; n < n_samples; n++) {
d[0][n] = d[2][n] = s[0][n];
d[1][n] = d[3][n] = s[1][n];
}
} else {
for (n = 0; n < n_samples; n++) {
d[0][n] = d[2][n] = s[0][n] * v0;
d[1][n] = d[3][n] = s[1][n] * v1;
}
}
}
else {
@ -226,11 +225,11 @@ channelmix_f32_2_3p1_c(struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRI
const float v0 = mix->matrix[0][0];
const float v1 = mix->matrix[1][1];
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
for (i = 0; i < n_dst; i++)
memset(d[i], 0, n_samples * sizeof(float));
}
else if (mix->norm) {
else if (v0 == 1.0f && v1 == 1.0f) {
for (n = 0; n < n_samples; n++) {
d[0][n] = s[0][n];
d[1][n] = s[1][n];
@ -261,11 +260,11 @@ channelmix_f32_2_5p1_c(struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRI
const float v4 = mix->matrix[4][0];
const float v5 = mix->matrix[5][1];
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
for (i = 0; i < n_dst; i++)
memset(d[i], 0, n_samples * sizeof(float));
}
else if (mix->norm) {
else if (v0 == 1.0f && v1 == 1.0f && v4 == 1.0f && v5 == 1.0f) {
for (n = 0; n < n_samples; n++) {
d[0][n] = d[4][n] = s[0][n];
d[1][n] = d[5][n] = s[1][n];
@ -300,7 +299,7 @@ channelmix_f32_5p1_2_c(struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRI
const float slev0 = mix->matrix[0][4];
const float slev1 = mix->matrix[1][5];
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
memset(d[0], 0, n_samples * sizeof(float));
memset(d[1], 0, n_samples * sizeof(float));
}
@ -328,7 +327,7 @@ channelmix_f32_5p1_3p1_c(struct channelmix *mix, uint32_t n_dst, void * SPA_REST
const float v4 = mix->matrix[0][4];
const float v5 = mix->matrix[1][5];
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
for (i = 0; i < n_dst; i++)
memset(d[i], 0, n_samples * sizeof(float));
}
@ -357,7 +356,7 @@ channelmix_f32_5p1_4_c(struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRI
const float v4 = mix->matrix[2][4];
const float v5 = mix->matrix[3][5];
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
for (i = 0; i < n_dst; i++)
memset(d[i], 0, n_samples * sizeof(float));
}
@ -391,7 +390,7 @@ channelmix_f32_7p1_2_c(struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRI
const float rlev0 = mix->matrix[0][6];
const float rlev1 = mix->matrix[1][7];
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
memset(d[0], 0, n_samples * sizeof(float));
memset(d[1], 0, n_samples * sizeof(float));
}
@ -419,7 +418,7 @@ channelmix_f32_7p1_3p1_c(struct channelmix *mix, uint32_t n_dst, void * SPA_REST
const float v4 = (mix->matrix[0][4] + mix->matrix[0][6]) * 0.5f;
const float v5 = (mix->matrix[1][5] + mix->matrix[1][6]) * 0.5f;
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
for (i = 0; i < n_dst; i++)
memset(d[i], 0, n_samples * sizeof(float));
}
@ -450,7 +449,7 @@ channelmix_f32_7p1_4_c(struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRI
const float rlev0 = mix->matrix[0][6];
const float rlev1 = mix->matrix[1][7];
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
for (i = 0; i < n_dst; i++)
memset(d[i], 0, n_samples * sizeof(float));
}

View file

@ -33,11 +33,11 @@ void channelmix_copy_sse(struct channelmix *mix, uint32_t n_dst, void * SPA_REST
float **d = (float **)dst;
const float **s = (const float **)src;
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
for (i = 0; i < n_dst; i++)
memset(d[i], 0, n_samples * sizeof(float));
}
else if (mix->norm) {
else if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_IDENTITY)) {
for (i = 0; i < n_dst; i++)
spa_memcpy(d[i], s[i], n_samples * sizeof(float));
}
@ -77,8 +77,8 @@ channelmix_f32_2_4_sse(struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRI
uint32_t i, n, unrolled;
float **d = (float **)dst;
const float **s = (const float **)src;
const __m128 v0 = _mm_set1_ps(mix->matrix[0][0]);
const __m128 v1 = _mm_set1_ps(mix->matrix[1][1]);
const float m00 = mix->matrix[0][0];
const float m11 = mix->matrix[1][1];
__m128 in;
const float *sFL = s[0], *sFR = s[1];
float *dFL = d[0], *dFR = d[1], *dRL = d[2], *dRR = d[3];
@ -93,11 +93,11 @@ channelmix_f32_2_4_sse(struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRI
else
unrolled = 0;
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
for (i = 0; i < n_dst; i++)
memset(d[i], 0, n_samples * sizeof(float));
}
else if (mix->norm) {
else if (m00 == 1.0f && m11 == 1.0f) {
for(n = 0; n < unrolled; n += 4) {
in = _mm_load_ps(&sFL[n]);
_mm_store_ps(&dFL[n], in);
@ -116,6 +116,8 @@ channelmix_f32_2_4_sse(struct channelmix *mix, uint32_t n_dst, void * SPA_RESTRI
}
}
else {
const __m128 v0 = _mm_set1_ps(m00);
const __m128 v1 = _mm_set1_ps(m11);
for(n = 0; n < unrolled; n += 4) {
in = _mm_mul_ps(_mm_load_ps(&sFL[n]), v0);
_mm_store_ps(&dFL[n], in);
@ -143,8 +145,8 @@ channelmix_f32_5p1_2_sse(struct channelmix *mix, uint32_t n_dst, void * SPA_REST
uint32_t n, unrolled;
float **d = (float **) dst;
const float **s = (const float **) src;
const __m128 v0 = _mm_set1_ps(mix->matrix[0][0]);
const __m128 v1 = _mm_set1_ps(mix->matrix[1][1]);
const float m00 = mix->matrix[0][0];
const float m11 = mix->matrix[1][1];
const __m128 clev = _mm_set1_ps((mix->matrix[0][2] + mix->matrix[1][2]) * 0.5f);
const __m128 llev = _mm_set1_ps((mix->matrix[0][3] + mix->matrix[1][3]) * 0.5f);
const __m128 slev0 = _mm_set1_ps(mix->matrix[0][4]);
@ -165,11 +167,11 @@ channelmix_f32_5p1_2_sse(struct channelmix *mix, uint32_t n_dst, void * SPA_REST
else
unrolled = 0;
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
memset(dFL, 0, n_samples * sizeof(float));
memset(dFR, 0, n_samples * sizeof(float));
}
else if (mix->norm) {
else if (m00 == 1.0f && m11 == 1.0f) {
for(n = 0; n < unrolled; n += 4) {
ctr = _mm_mul_ps(_mm_load_ps(&sFC[n]), clev);
ctr = _mm_add_ps(ctr, _mm_mul_ps(_mm_load_ps(&sLFE[n]), llev));
@ -196,6 +198,8 @@ channelmix_f32_5p1_2_sse(struct channelmix *mix, uint32_t n_dst, void * SPA_REST
}
}
else {
const __m128 v0 = _mm_set1_ps(m00);
const __m128 v1 = _mm_set1_ps(m11);
for(n = 0; n < unrolled; n += 4) {
ctr = _mm_mul_ps(_mm_load_ps(&sFC[n]), clev);
ctr = _mm_add_ps(ctr, _mm_mul_ps(_mm_load_ps(&sLFE[n]), llev));
@ -259,7 +263,7 @@ channelmix_f32_5p1_3p1_sse(struct channelmix *mix, uint32_t n_dst, void * SPA_RE
else
unrolled = 0;
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
for (i = 0; i < n_dst; i++)
memset(d[i], 0, n_samples * sizeof(float));
}
@ -315,10 +319,10 @@ channelmix_f32_5p1_4_sse(struct channelmix *mix, uint32_t n_dst, void * SPA_REST
const float **s = (const float **) src;
const __m128 clev = _mm_set1_ps(mix->matrix[0][2]);
const __m128 llev = _mm_set1_ps(mix->matrix[0][3]);
const __m128 v0 = _mm_set1_ps(mix->matrix[0][0]);
const __m128 v1 = _mm_set1_ps(mix->matrix[1][1]);
const __m128 v4 = _mm_set1_ps(mix->matrix[2][4]);
const __m128 v5 = _mm_set1_ps(mix->matrix[3][5]);
const float m00 = mix->matrix[0][0];
const float m11 = mix->matrix[1][1];
const float m24 = mix->matrix[2][4];
const float m35 = mix->matrix[3][5];
__m128 ctr;
const float *sFL = s[0], *sFR = s[1], *sFC = s[2], *sLFE = s[3], *sSL = s[4], *sSR = s[5];
float *dFL = d[0], *dFR = d[1], *dRL = d[2], *dRR = d[3];
@ -337,11 +341,11 @@ channelmix_f32_5p1_4_sse(struct channelmix *mix, uint32_t n_dst, void * SPA_REST
else
unrolled = 0;
if (mix->zero) {
if (SPA_FLAG_IS_SET(mix->flags, CHANNELMIX_FLAG_ZERO)) {
for (i = 0; i < n_dst; i++)
memset(d[i], 0, n_samples * sizeof(float));
}
else if (mix->norm) {
else if (m00 == 1.0f && m11 == 1.0f && m24 == 1.0f && m35 == 1.0f) {
for(n = 0; n < unrolled; n += 4) {
ctr = _mm_mul_ps(_mm_load_ps(&sFC[n]), clev);
ctr = _mm_add_ps(ctr, _mm_mul_ps(_mm_load_ps(&sLFE[n]), llev));
@ -360,6 +364,11 @@ channelmix_f32_5p1_4_sse(struct channelmix *mix, uint32_t n_dst, void * SPA_REST
}
}
else {
const __m128 v0 = _mm_set1_ps(m00);
const __m128 v1 = _mm_set1_ps(m11);
const __m128 v4 = _mm_set1_ps(m24);
const __m128 v5 = _mm_set1_ps(m35);
for(n = 0; n < unrolled; n += 4) {
ctr = _mm_mul_ps(_mm_load_ps(&sFC[n]), clev);
ctr = _mm_add_ps(ctr, _mm_mul_ps(_mm_load_ps(&sLFE[n]), llev));

View file

@ -318,7 +318,8 @@ static int make_matrix(struct channelmix *mix)
spa_log_warn(mix->log, "can't assign FLC");
}
}
if (unassigned & _MASK(LFE)) {
if (unassigned & _MASK(LFE) &&
SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_MIX_LFE)) {
if (dst_mask & _MASK(FC)) {
matrix[FC][LFE] += llev;
} else if (dst_mask & _MASK(FL)) {
@ -338,11 +339,9 @@ static int make_matrix(struct channelmix *mix)
mix->matrix_orig[ic][jc++] = matrix[i][j];
sum += fabs(matrix[i][j]);
}
#if 0
if (sum > 1.0f)
for (j = 0; j < jc; j++)
mix->matrix_orig[ic][j] /= sum;
#endif
ic++;
}
return 0;
@ -358,13 +357,10 @@ static void impl_channelmix_set_volume(struct channelmix *mix, float volume, boo
uint32_t dst_chan = mix->dst_chan;
/** apply global volume to channels */
mix->norm = true;
for (i = 0; i < n_channel_volumes; i++) {
for (i = 0; i < n_channel_volumes; i++)
volumes[i] = channel_volumes[i] * vol;
if (volumes[i] != 1.0f)
mix->norm = false;
}
/** apply volumes per channel */
if (n_channel_volumes == src_chan) {
for (i = 0; i < dst_chan; i++) {
for (j = 0; j < src_chan; j++) {
@ -379,11 +375,11 @@ static void impl_channelmix_set_volume(struct channelmix *mix, float volume, boo
}
}
mix->zero = true;
mix->equal = true;
mix->identity = dst_chan == src_chan;
t = 0.0;
SPA_FLAG_SET(mix->flags, CHANNELMIX_FLAG_ZERO);
SPA_FLAG_SET(mix->flags, CHANNELMIX_FLAG_EQUAL);
SPA_FLAG_UPDATE(mix->flags, CHANNELMIX_FLAG_IDENTITY, dst_chan == src_chan);
t = 0.0;
for (i = 0; i < dst_chan; i++) {
for (j = 0; j < src_chan; j++) {
float v = mix->matrix[i][j];
@ -391,15 +387,15 @@ static void impl_channelmix_set_volume(struct channelmix *mix, float volume, boo
if (i == 0 && j == 0)
t = v;
else if (t != v)
mix->equal = false;
SPA_FLAG_CLEAR(mix->flags, CHANNELMIX_FLAG_EQUAL);
if (v != 0.0)
mix->zero = false;
SPA_FLAG_CLEAR(mix->flags, CHANNELMIX_FLAG_ZERO);
if ((i == j && v != 1.0f) ||
(i != j && v != 0.0f))
mix->identity = false;
SPA_FLAG_CLEAR(mix->flags, CHANNELMIX_FLAG_IDENTITY);
}
}
spa_log_debug(mix->log, "zero:%d norm:%d identity:%d", mix->zero, mix->norm, mix->identity);
spa_log_debug(mix->log, "flags:%08x", mix->flags);
}
static void impl_channelmix_free(struct channelmix *mix)

View file

@ -46,13 +46,15 @@ struct channelmix {
uint64_t src_mask;
uint64_t dst_mask;
uint32_t cpu_flags;
#define CHANNELMIX_OPTION_MIX_LFE (1<<0) /**< mix LFE */
uint32_t options;
struct spa_log *log;
unsigned int zero:1; /* all zero components */
unsigned int identity:1; /* identity matrix */
unsigned int norm:1; /* all normal values */
unsigned int equal:1; /* all values are equal */
#define CHANNELMIX_FLAG_ZERO (1<<0) /**< all zero components */
#define CHANNELMIX_FLAG_IDENTITY (1<<1) /**< identity matrix */
#define CHANNELMIX_FLAG_EQUAL (1<<2) /**< all values are equal */
uint32_t flags;
float matrix_orig[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS];
float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS];

View file

@ -256,11 +256,11 @@ static int setup_convert(struct impl *this,
emit_params_changed(this);
spa_log_debug(this->log, NAME " %p: got channelmix features %08x:%08x identity:%d",
spa_log_debug(this->log, NAME " %p: got channelmix features %08x:%08x flags:%08x",
this, this->cpu_flags, this->mix.cpu_flags,
this->mix.identity);
this->mix.flags);
this->is_passthrough = this->mix.identity;
this->is_passthrough = SPA_FLAG_IS_SET(this->mix.flags, CHANNELMIX_FLAG_IDENTITY);
return 0;
}
@ -1057,7 +1057,9 @@ static int impl_node_process(void *object)
void *dst_datas[n_dst_datas];
bool is_passthrough;
is_passthrough = this->is_passthrough && this->mix.identity && ctrlport->ctrl == NULL;
is_passthrough = this->is_passthrough &&
SPA_FLAG_IS_SET(this->mix.flags, CHANNELMIX_FLAG_IDENTITY) &&
ctrlport->ctrl == NULL;
n_samples = sb->datas[0].chunk->size / inport->stride;