mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-03-31 07:11:14 -04:00
channelmix: make up/downmix levels configurable
Add channelmix.center-level, channelmix.surround-level and channelmix.lfe-level to control how center, surround and LFE is up/downmixed. Fixes #5198
This commit is contained in:
parent
03f894bab0
commit
18c97222c4
12 changed files with 119 additions and 25 deletions
|
|
@ -80,6 +80,9 @@ stream.properties = {
|
|||
#channelmix.fc-cutoff = 12000.0
|
||||
#channelmix.rear-delay = 12.0
|
||||
#channelmix.stereo-widen = 0.0
|
||||
#channelmix.center-level = 0.707106781
|
||||
#channelmix.surround-level = 0.707106781
|
||||
#channelmix.lfe-level = 0.5
|
||||
#channelmix.hilbert-taps = 0
|
||||
#dither.noise = 0
|
||||
#dither.method = none # rectangular, triangular, triangular-hf, wannamaker3, shaped5
|
||||
|
|
|
|||
|
|
@ -783,6 +783,15 @@ more to the center speaker and leaves the ambient sound in the stereo channels.
|
|||
This is only active when up-mix is enabled and a Front Center channel is mixed.
|
||||
\endparblock
|
||||
|
||||
@PAR@ node-prop channelmix.center-level = 0.707106781
|
||||
The level of the center channel when up/downmixing.
|
||||
|
||||
@PAR@ node-prop channelmix.surround-level = 0.707106781
|
||||
The level of the surround channels when up/downmixing.
|
||||
|
||||
@PAR@ node-prop channelmix.lfe-level = 0.5
|
||||
The level of the LFE channel when up/downmixing.
|
||||
|
||||
@PAR@ node-prop channelmix.hilbert-taps = 0
|
||||
\parblock
|
||||
This option will apply a 90 degree phase shift to the rear channels to improve specialization.
|
||||
|
|
|
|||
|
|
@ -93,6 +93,9 @@ stream.properties = {
|
|||
#channelmix.fc-cutoff = 12000.0
|
||||
#channelmix.rear-delay = 12.0
|
||||
#channelmix.stereo-widen = 0.0
|
||||
#channelmix.center-level = 0.707106781
|
||||
#channelmix.surround-level = 0.707106781
|
||||
#channelmix.lfe-level = 0.5
|
||||
#channelmix.hilbert-taps = 0
|
||||
#dither.noise = 0
|
||||
#dither.method = none # rectangular, triangular, triangular-hf, wannamaker3, shaped5
|
||||
|
|
|
|||
|
|
@ -724,6 +724,34 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
|
|||
SPA_PROP_INFO_params, SPA_POD_Bool(true));
|
||||
break;
|
||||
case 19:
|
||||
*param = spa_pod_builder_add_object(b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_name, SPA_POD_String("channelmix.center-level"),
|
||||
SPA_PROP_INFO_description, SPA_POD_String("Center up/downmix level"),
|
||||
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(
|
||||
this->mix.center_level, 0.0, 10.0),
|
||||
SPA_PROP_INFO_params, SPA_POD_Bool(true));
|
||||
break;
|
||||
case 20:
|
||||
*param = spa_pod_builder_add_object(b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_name, SPA_POD_String("channelmix.surround-level"),
|
||||
SPA_PROP_INFO_description, SPA_POD_String("Surround up/downmix level"),
|
||||
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(
|
||||
this->mix.surround_level, 0.0, 10.0),
|
||||
SPA_PROP_INFO_params, SPA_POD_Bool(true));
|
||||
break;
|
||||
case 21:
|
||||
*param = spa_pod_builder_add_object(b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_name, SPA_POD_String("channelmix.lfe-level"),
|
||||
SPA_PROP_INFO_description, SPA_POD_String("LFE up/downmix level"),
|
||||
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(
|
||||
this->mix.lfe_level, 0.0, 10.0),
|
||||
SPA_PROP_INFO_params, SPA_POD_Bool(true));
|
||||
break;
|
||||
|
||||
case 22:
|
||||
*param = spa_pod_builder_add_object(b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_name, SPA_POD_String("channelmix.hilbert-taps"),
|
||||
|
|
@ -732,7 +760,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
|
|||
this->mix.hilbert_taps, 0, MAX_TAPS),
|
||||
SPA_PROP_INFO_params, SPA_POD_Bool(true));
|
||||
break;
|
||||
case 20:
|
||||
case 23:
|
||||
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"),
|
||||
|
|
@ -751,14 +779,14 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
|
|||
spa_pod_builder_pop(b, &f[1]);
|
||||
*param = spa_pod_builder_pop(b, &f[0]);
|
||||
break;
|
||||
case 21:
|
||||
case 24:
|
||||
*param = spa_pod_builder_add_object(b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_rate),
|
||||
SPA_PROP_INFO_description, SPA_POD_String("Rate scaler"),
|
||||
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Double(p->rate, 0.0, 10.0));
|
||||
break;
|
||||
case 22:
|
||||
case 25:
|
||||
*param = spa_pod_builder_add_object(b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_quality),
|
||||
|
|
@ -767,7 +795,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
|
|||
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->resample_quality, 0, 14),
|
||||
SPA_PROP_INFO_params, SPA_POD_Bool(true));
|
||||
break;
|
||||
case 23:
|
||||
case 26:
|
||||
*param = spa_pod_builder_add_object(b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_name, SPA_POD_String("resample.disable"),
|
||||
|
|
@ -775,7 +803,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
|
|||
SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->resample_disabled),
|
||||
SPA_PROP_INFO_params, SPA_POD_Bool(true));
|
||||
break;
|
||||
case 24:
|
||||
case 27:
|
||||
*param = spa_pod_builder_add_object(b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_name, SPA_POD_String("dither.noise"),
|
||||
|
|
@ -783,7 +811,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
|
|||
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(this->dir[1].conv.noise_bits, 0, 16),
|
||||
SPA_PROP_INFO_params, SPA_POD_Bool(true));
|
||||
break;
|
||||
case 25:
|
||||
case 28:
|
||||
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("dither.method"),
|
||||
|
|
@ -801,7 +829,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
|
|||
spa_pod_builder_pop(b, &f[1]);
|
||||
*param = spa_pod_builder_pop(b, &f[0]);
|
||||
break;
|
||||
case 26:
|
||||
case 29:
|
||||
*param = spa_pod_builder_add_object(b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_name, SPA_POD_String("debug.wav-path"),
|
||||
|
|
@ -809,7 +837,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
|
|||
SPA_PROP_INFO_type, SPA_POD_String(p->wav_path),
|
||||
SPA_PROP_INFO_params, SPA_POD_Bool(true));
|
||||
break;
|
||||
case 27:
|
||||
case 30:
|
||||
*param = spa_pod_builder_add_object(b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_name, SPA_POD_String("channelmix.lock-volumes"),
|
||||
|
|
@ -817,7 +845,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
|
|||
SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->lock_volumes),
|
||||
SPA_PROP_INFO_params, SPA_POD_Bool(true));
|
||||
break;
|
||||
case 28:
|
||||
case 31:
|
||||
*param = spa_pod_builder_add_object(b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_name, SPA_POD_String("audioconvert.filter-graph.disable"),
|
||||
|
|
@ -825,7 +853,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
|
|||
SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->filter_graph_disabled),
|
||||
SPA_PROP_INFO_params, SPA_POD_Bool(true));
|
||||
break;
|
||||
case 29:
|
||||
case 32:
|
||||
*param = spa_pod_builder_add_object(b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_name, SPA_POD_String("audioconvert.filter-graph.N"),
|
||||
|
|
@ -836,7 +864,7 @@ static int node_param_prop_info(struct impl *this, uint32_t id, uint32_t index,
|
|||
default:
|
||||
if (this->filter_graph[0] && this->filter_graph[0]->graph) {
|
||||
return spa_filter_graph_enum_prop_info(this->filter_graph[0]->graph,
|
||||
index - 30, b, param);
|
||||
index - 33, b, param);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -903,6 +931,12 @@ static int node_param_props(struct impl *this, uint32_t id, uint32_t index,
|
|||
spa_pod_builder_float(b, this->mix.rear_delay);
|
||||
spa_pod_builder_string(b, "channelmix.stereo-widen");
|
||||
spa_pod_builder_float(b, this->mix.widen);
|
||||
spa_pod_builder_string(b, "channelmix.center-level");
|
||||
spa_pod_builder_float(b, this->mix.center_level);
|
||||
spa_pod_builder_string(b, "channelmix.surround-level");
|
||||
spa_pod_builder_float(b, this->mix.surround_level);
|
||||
spa_pod_builder_string(b, "channelmix.lfe-level");
|
||||
spa_pod_builder_float(b, this->mix.lfe_level);
|
||||
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");
|
||||
|
|
@ -1490,6 +1524,12 @@ static int audioconvert_set_param(struct impl *this, const char *k, const char *
|
|||
spa_atof(s, &this->mix.rear_delay);
|
||||
else if (spa_streq(k, "channelmix.stereo-widen"))
|
||||
spa_atof(s, &this->mix.widen);
|
||||
else if (spa_streq(k, "channelmix.center-level"))
|
||||
spa_atof(s, &this->mix.center_level);
|
||||
else if (spa_streq(k, "channelmix.surround-level"))
|
||||
spa_atof(s, &this->mix.surround_level);
|
||||
else if (spa_streq(k, "channelmix.lfe-level"))
|
||||
spa_atof(s, &this->mix.lfe_level);
|
||||
else if (spa_streq(k, "channelmix.hilbert-taps"))
|
||||
spa_atou32(s, &this->mix.hilbert_taps, 0);
|
||||
else if (spa_streq(k, "channelmix.upmix-method"))
|
||||
|
|
@ -4320,13 +4360,7 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
this->rate_limit.interval = 2 * SPA_NSEC_PER_SEC;
|
||||
this->rate_limit.burst = 1;
|
||||
|
||||
this->mix.options = CHANNELMIX_OPTION_UPMIX | CHANNELMIX_OPTION_MIX_LFE;
|
||||
this->mix.upmix = CHANNELMIX_UPMIX_NONE;
|
||||
this->mix.log = this->log;
|
||||
this->mix.lfe_cutoff = 0.0f;
|
||||
this->mix.fc_cutoff = 0.0f;
|
||||
this->mix.rear_delay = 0.0f;
|
||||
this->mix.widen = 0.0f;
|
||||
channelmix_reset(&this->mix);
|
||||
|
||||
for (i = 0; info && i < info->n_items; i++) {
|
||||
const char *k = info->items[i].key;
|
||||
|
|
|
|||
|
|
@ -198,9 +198,9 @@ static int make_matrix(struct channelmix *mix)
|
|||
uint32_t dst_chan = mix->dst_chan;
|
||||
uint64_t unassigned, keep;
|
||||
uint32_t i, j, ic, jc, matrix_encoding = MATRIX_NORMAL;
|
||||
float clev = SQRT1_2;
|
||||
float slev = SQRT1_2;
|
||||
float llev = 0.5f;
|
||||
float clev = mix->center_level;
|
||||
float slev = mix->surround_level;
|
||||
float llev = mix->lfe_level;
|
||||
float maxsum = 0.0f;
|
||||
bool filter_fc = false, filter_lfe = false, matched = false, normalize;
|
||||
#define _MATRIX(s,d) matrix[_CH(s)][_CH(d)]
|
||||
|
|
@ -874,6 +874,21 @@ static void impl_channelmix_free(struct channelmix *mix)
|
|||
mix->process = NULL;
|
||||
}
|
||||
|
||||
void channelmix_reset(struct channelmix *mix)
|
||||
{
|
||||
spa_zero(*mix);
|
||||
mix->options = CHANNELMIX_DEFAULT_OPTIONS;
|
||||
mix->upmix = CHANNELMIX_DEFAULT_UPMIX;
|
||||
mix->lfe_cutoff = CHANNELMIX_DEFAULT_LFE_CUTOFF;
|
||||
mix->fc_cutoff = CHANNELMIX_DEFAULT_FC_CUTOFF;
|
||||
mix->rear_delay = CHANNELMIX_DEFAULT_REAR_DELAY;
|
||||
mix->center_level = CHANNELMIX_DEFAULT_CENTER_LEVEL;
|
||||
mix->surround_level = CHANNELMIX_DEFAULT_SURROUND_LEVEL;
|
||||
mix->lfe_level = CHANNELMIX_DEFAULT_LFE_LEVEL;
|
||||
mix->widen = CHANNELMIX_DEFAULT_WIDEN;
|
||||
mix->hilbert_taps = CHANNELMIX_DEFAULT_HILBERT_TAPS;
|
||||
}
|
||||
|
||||
int channelmix_init(struct channelmix *mix)
|
||||
{
|
||||
const struct channelmix_info *info;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,17 @@
|
|||
|
||||
#define CHANNELMIX_OPS_MAX_ALIGN 16
|
||||
|
||||
#define CHANNELMIX_DEFAULT_OPTIONS (CHANNELMIX_OPTION_UPMIX | CHANNELMIX_OPTION_MIX_LFE)
|
||||
#define CHANNELMIX_DEFAULT_UPMIX CHANNELMIX_UPMIX_NONE
|
||||
#define CHANNELMIX_DEFAULT_LFE_CUTOFF 0.0f
|
||||
#define CHANNELMIX_DEFAULT_FC_CUTOFF 0.0f
|
||||
#define CHANNELMIX_DEFAULT_REAR_DELAY 0.0f
|
||||
#define CHANNELMIX_DEFAULT_CENTER_LEVEL 0.707106781f
|
||||
#define CHANNELMIX_DEFAULT_SURROUND_LEVEL 0.707106781f
|
||||
#define CHANNELMIX_DEFAULT_LFE_LEVEL 0.5f
|
||||
#define CHANNELMIX_DEFAULT_WIDEN 0.0f
|
||||
#define CHANNELMIX_DEFAULT_HILBERT_TAPS 0
|
||||
|
||||
struct channelmix {
|
||||
uint32_t src_chan;
|
||||
uint32_t dst_chan;
|
||||
|
|
@ -60,6 +71,9 @@ struct channelmix {
|
|||
float fc_cutoff; /* in Hz, 0 is disabled */
|
||||
float rear_delay; /* in ms, 0 is disabled */
|
||||
float widen; /* stereo widen. 0 is disabled */
|
||||
float center_level; /* center down/upmix level, sqrt(1/2) */
|
||||
float lfe_level; /* lfe down/upmix level, 1/2 */
|
||||
float surround_level; /* surround down/upmix level, sqrt(1/2) */
|
||||
uint32_t hilbert_taps; /* to phase shift, 0 disabled */
|
||||
struct lr4 lr4[MAX_CHANNELS];
|
||||
|
||||
|
|
@ -80,6 +94,7 @@ struct channelmix {
|
|||
void *data;
|
||||
};
|
||||
|
||||
void channelmix_reset(struct channelmix *mix);
|
||||
int channelmix_init(struct channelmix *mix);
|
||||
|
||||
static const struct channelmix_upmix_info {
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ static int setup_context(struct context *ctx)
|
|||
size_t size;
|
||||
int res;
|
||||
struct spa_support support[1];
|
||||
struct spa_dict_item items[6];
|
||||
struct spa_dict_item items[9];
|
||||
const struct spa_handle_factory *factory;
|
||||
void *iface;
|
||||
|
||||
|
|
@ -76,10 +76,13 @@ static int setup_context(struct context *ctx)
|
|||
items[3] = SPA_DICT_ITEM_INIT("channelmix.lfe-cutoff", "150");
|
||||
items[4] = SPA_DICT_ITEM_INIT("channelmix.fc-cutoff", "12000");
|
||||
items[5] = SPA_DICT_ITEM_INIT("channelmix.rear-delay", "12.0");
|
||||
items[6] = SPA_DICT_ITEM_INIT("channelmix.center-level", "0.707106781");
|
||||
items[7] = SPA_DICT_ITEM_INIT("channelmix.surround-level", "0.707106781");
|
||||
items[8] = SPA_DICT_ITEM_INIT("channelmix.lfe-level", "0.5");
|
||||
|
||||
res = spa_handle_factory_init(factory,
|
||||
ctx->convert_handle,
|
||||
&SPA_DICT_INIT(items, 6),
|
||||
&SPA_DICT_INIT(items, 9),
|
||||
support, 1);
|
||||
spa_assert_se(res >= 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ static void test_mix(uint32_t src_chan, uint32_t src_mask, uint32_t dst_chan, ui
|
|||
|
||||
spa_log_debug(&logger.log, "start %d->%d (%08x -> %08x)", src_chan, dst_chan, src_mask, dst_mask);
|
||||
|
||||
spa_zero(mix);
|
||||
channelmix_reset(&mix);
|
||||
mix.options = options;
|
||||
mix.src_chan = src_chan;
|
||||
mix.dst_chan = dst_chan;
|
||||
|
|
@ -340,7 +340,7 @@ static void test_n_m_impl(void)
|
|||
src[i] = src_data[i];
|
||||
}
|
||||
|
||||
spa_zero(mix);
|
||||
channelmix_reset(&mix);
|
||||
mix.src_chan = 16;
|
||||
mix.dst_chan = 12;
|
||||
mix.log = &logger.log;
|
||||
|
|
|
|||
|
|
@ -101,6 +101,9 @@ stream.properties = {
|
|||
#channelmix.lfe-cutoff = 150
|
||||
#channelmix.fc-cutoff = 12000
|
||||
#channelmix.rear-delay = 12.0
|
||||
#channelmix.center-level = 0.707106781
|
||||
#channelmix.surround-level = 0.707106781
|
||||
#channelmix.lfe-level = 0.5
|
||||
#channelmix.stereo-widen = 0.0
|
||||
#channelmix.hilbert-taps = 0
|
||||
#dither.noise = 0
|
||||
|
|
|
|||
|
|
@ -332,6 +332,9 @@ context.objects = [
|
|||
#channelmix.fc-cutoff = 12000
|
||||
#channelmix.rear-delay = 12.0
|
||||
#channelmix.stereo-widen = 0.0
|
||||
#channelmix.center-level = 0.707106781
|
||||
#channelmix.surround-level = 0.707106781
|
||||
#channelmix.lfe-level = 0.5
|
||||
#channelmix.hilbert-taps = 0
|
||||
#channelmix.disable = false
|
||||
#dither.noise = 0
|
||||
|
|
|
|||
|
|
@ -60,6 +60,9 @@ stream.properties = {
|
|||
#channelmix.fc-cutoff = 6000
|
||||
#channelmix.rear-delay = 12.0
|
||||
#channelmix.stereo-widen = 0.1
|
||||
#channelmix.center-level = 0.707106781
|
||||
#channelmix.surround-level = 0.707106781
|
||||
#channelmix.lfe-level = 0.5
|
||||
#channelmix.hilbert-taps = 0
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,9 @@ stream.properties = {
|
|||
#channelmix.fc-cutoff = 12000
|
||||
#channelmix.rear-delay = 12.0
|
||||
#channelmix.stereo-widen = 0.0
|
||||
#channelmix.center-level = 0.707106781
|
||||
#channelmix.surround-level = 0.707106781
|
||||
#channelmix.lfe-level = 0.5
|
||||
#channelmix.hilbert-taps = 0
|
||||
#dither.noise = 0
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue