channelmix: add simple upmix method

Add a property to select the upmix method. PSD is enabled by default
but a new simple upmixing algorithm is available that duplicates
channels and avoids lowpass filter on the FC and disables widening.

Fixes #861
This commit is contained in:
Wim Taymans 2022-03-14 15:33:07 +01:00
parent cec8898740
commit bc5b486cb9
8 changed files with 98 additions and 1 deletions

View file

@ -246,6 +246,16 @@ channelmix_f32_2_3p1_c(struct channelmix *mix, void * SPA_RESTRICT dst[],
for (i = 0; i < n_dst; i++)
memset(d[i], 0, n_samples * sizeof(float));
}
else if (mix->upmix == CHANNELMIX_UPMIX_SIMPLE) {
for (n = 0; n < n_samples; n++) {
float c = s[0][n] + s[1][n];
d[0][n] = s[0][n] * v0;
d[1][n] = s[1][n] * v1;
d[2][n] = c * v2;
d[3][n] = c;
}
lr4_process(&mix->lr4[3], d[3], d[3], v3, n_samples);
}
else if (v0 == 1.0f && v1 == 1.0f) {
for (n = 0; n < n_samples; n++) {
float c = s[0][n] + s[1][n];
@ -289,6 +299,18 @@ channelmix_f32_2_5p1_c(struct channelmix *mix, void * SPA_RESTRICT dst[],
for (i = 0; i < n_dst; i++)
memset(d[i], 0, n_samples * sizeof(float));
}
else if (mix->upmix == CHANNELMIX_UPMIX_SIMPLE) {
for (n = 0; n < n_samples; n++) {
float c = s[0][n] + s[1][n];
d[0][n] = s[0][n] * v0;
d[1][n] = s[1][n] * v1;
d[2][n] = c * v2;
d[3][n] = c;
d[4][n] = s[0][n] * v4;
d[5][n] = s[1][n] * v5;
}
lr4_process(&mix->lr4[3], d[3], d[3], v3, n_samples);
}
else if (v0 == 1.0f && v1 == 1.0f) {
for (n = 0; n < n_samples; n++) {
float c = s[0][n] + s[1][n];
@ -347,6 +369,20 @@ channelmix_f32_2_7p1_c(struct channelmix *mix, void * SPA_RESTRICT dst[],
for (i = 0; i < n_dst; i++)
memset(d[i], 0, n_samples * sizeof(float));
}
else if (mix->upmix == CHANNELMIX_UPMIX_SIMPLE) {
for (n = 0; n < n_samples; n++) {
float c = s[0][n] + s[1][n];
d[0][n] = s[0][n] * v0;
d[1][n] = s[1][n] * v1;
d[2][n] = c * v2;
d[3][n] = c;
d[4][n] = s[0][n] * v4;
d[5][n] = s[1][n] * v5;
d[6][n] = s[0][n] * v6;
d[7][n] = s[1][n] * v7;
}
lr4_process(&mix->lr4[3], d[3], d[3], v3, n_samples);
}
else if (v0 == 1.0f && v1 == 1.0f && v4 == 1.0f && v5 == 1.0f) {
for (n = 0; n < n_samples; n++) {
float c = s[0][n] + s[1][n];

View file

@ -210,7 +210,8 @@ static int make_matrix(struct channelmix *mix)
unassigned = src_mask & ~dst_mask;
keep = dst_mask & ~src_mask;
if (!SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_UPMIX))
if (!SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_UPMIX) ||
mix->upmix == CHANNELMIX_UPMIX_NONE)
keep = 0;
keep |= FRONT;

View file

@ -26,6 +26,7 @@
#include <stdio.h>
#include <spa/utils/defs.h>
#include <spa/utils/string.h>
#include <spa/param/audio/raw.h>
#undef SPA_LOG_TOPIC_DEFAULT
@ -61,6 +62,10 @@ struct channelmix {
#define CHANNELMIX_OPTION_NORMALIZE (1<<1) /**< normalize volumes */
#define CHANNELMIX_OPTION_UPMIX (1<<2) /**< do simple upmixing */
uint32_t options;
#define CHANNELMIX_UPMIX_NONE (0) /**< disable upmixing */
#define CHANNELMIX_UPMIX_SIMPLE (1) /**< simple upmixing */
#define CHANNELMIX_UPMIX_PSD (2) /**< Passive Surround Decoding upmixing */
uint32_t upmix;
struct spa_log *log;
@ -97,6 +102,26 @@ struct channelmix {
int channelmix_init(struct channelmix *mix);
static const struct channelmix_upmix_info {
const char *label;
const char *description;
uint32_t upmix;
} channelmix_upmix_info[] = {
[CHANNELMIX_UPMIX_NONE] = { "none", "Disabled", CHANNELMIX_UPMIX_NONE },
[CHANNELMIX_UPMIX_SIMPLE] = { "simple", "Simple upmixing", CHANNELMIX_UPMIX_SIMPLE },
[CHANNELMIX_UPMIX_PSD] = { "psd", "Passive Surround Decoding", CHANNELMIX_UPMIX_PSD }
};
static inline uint32_t channelmix_upmix_from_label(const char *label)
{
uint32_t i;
for (i = 0; i < SPA_N_ELEMENTS(channelmix_upmix_info); i++) {
if (spa_streq(channelmix_upmix_info[i].label, label))
return channelmix_upmix_info[i].upmix;
}
return CHANNELMIX_UPMIX_NONE;
}
#define channelmix_process(mix,...) (mix)->process(mix, __VA_ARGS__)
#define channelmix_set_volume(mix,...) (mix)->set_volume(mix, __VA_ARGS__)
#define channelmix_free(mix) (mix)->free(mix)

View file

@ -546,6 +546,31 @@ static int impl_node_enum_params(void *object, int seq,
this->mix.hilbert_taps, 0, MAX_TAPS),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 17:
{
struct spa_pod_frame f[2];
uint32_t i;
spa_pod_builder_push_object(&b, &f[0],
SPA_TYPE_OBJECT_PropInfo, id);
spa_pod_builder_add(&b,
SPA_PROP_INFO_name, SPA_POD_String("channelmix.upmix-method"),
SPA_PROP_INFO_description, SPA_POD_String("Upmix Method to use"),
SPA_PROP_INFO_type, SPA_POD_String(
channelmix_upmix_info[this->mix.upmix].label),
0);
spa_pod_builder_prop(&b, SPA_PROP_INFO_labels, 0);
spa_pod_builder_push_struct(&b, &f[1]);
for (i = 0; i < SPA_N_ELEMENTS(channelmix_upmix_info); i++) {
spa_pod_builder_string(&b, channelmix_upmix_info[i].label);
spa_pod_builder_string(&b, channelmix_upmix_info[i].description);
}
spa_pod_builder_pop(&b, &f[1]);
spa_pod_builder_add(&b,
SPA_PROP_INFO_params, SPA_POD_Bool(true),
0);
param = spa_pod_builder_pop(&b, &f[0]);
break;
}
default:
return 0;
}
@ -605,6 +630,8 @@ static int impl_node_enum_params(void *object, int seq,
spa_pod_builder_float(&b, this->mix.widen);
spa_pod_builder_string(&b, "channelmix.hilbert-taps");
spa_pod_builder_int(&b, this->mix.hilbert_taps);
spa_pod_builder_string(&b, "channelmix.upmix-method");
spa_pod_builder_string(&b, channelmix_upmix_info[this->mix.upmix].label);
spa_pod_builder_pop(&b, &f[1]);
param = spa_pod_builder_pop(&b, &f[0]);
break;
@ -648,6 +675,8 @@ static int channelmix_set_param(struct impl *this, const char *k, const char *s)
spa_atof(s, &this->mix.widen);
else if (spa_streq(k, "channelmix.hilbert-taps"))
spa_atou32(s, &this->mix.hilbert_taps, 0);
else if (spa_streq(k, "channelmix.upmix-method"))
this->mix.upmix = channelmix_upmix_from_label(s);
else
return 0;
return 1;
@ -1625,6 +1654,7 @@ impl_init(const struct spa_handle_factory *factory,
props_reset(&this->props);
this->mix.options = CHANNELMIX_OPTION_UPMIX;
this->mix.upmix = CHANNELMIX_UPMIX_PSD;
this->mix.log = this->log;
this->mix.lfe_cutoff = 120.0f;
this->mix.fc_cutoff = 6000.0f;

View file

@ -84,6 +84,7 @@ stream.properties = {
#channelmix.normalize = false
#channelmix.mix-lfe = true
#channelmix.upmix = true
#channelmix.upmix-method = psd # none, simple
#channelmix.lfe-cutoff = 120
#channelmix.fc-cutoff = 6000
#channelmix.rear-delay = 12.0

View file

@ -74,6 +74,7 @@ stream.properties = {
#channelmix.normalize = false
#channelmix.mix-lfe = false
#channelmix.upmix = true
#channelmix.upmix-method = psd # none, simple
#channelmix.lfe-cutoff = 120
#channelmix.fc-cutoff = 6000
#channelmix.rear-delay = 12.0

View file

@ -205,6 +205,7 @@ context.objects = [
#channelmix.normalize = false
#channelmix.mix-lfe = false
#channelmix.upmix = true
#channelmix.upmix-method = psd # none, simple
#channelmix.lfe-cutoff = 120
#channelmix.fc-cutoff = 6000
#channelmix.rear-delay = 12.0
@ -264,6 +265,7 @@ context.objects = [
#channelmix.normalize = false
#channelmix.mix-lfe = false
#channelmix.upmix = true
#channelmix.upmix-method = psd # none, simple
#channelmix.lfe-cutoff = 120
#channelmix.fc-cutoff = 6000
#channelmix.rear-delay = 12.0

View file

@ -60,6 +60,7 @@ stream.properties = {
#channelmix.normalize = false
#channelmix.mix-lfe = false
#channelmix.upmix = true
#channelmix.upmix-method = psd # none, simple
#channelmix.lfe-cutoff = 120
#channelmix.fc-cutoff = 6000
#channelmix.rear-delay = 12.0