Added a new configuration option: remix-non-lfe-channels (default: yes), which results in the non-lfe channels being highpass-filtered. When set to false, the lfe channel is lowpass-filtered and the other channels stay untouched. Removing the low frequencies from speakers that support low frequencies sounds awful. The current behavior is the default, since this case is less likely than speakers not playing well with low frequencies. --- src/daemon/daemon-conf.c | 2 + src/daemon/daemon-conf.h | 1 + src/daemon/daemon.conf.in | 1 + src/daemon/main.c | 1 + src/modules/module-virtual-surround-sink.c | 2 +- src/pulsecore/core.c | 1 + src/pulsecore/core.h | 1 + src/pulsecore/filter/biquad.c | 9 ++ src/pulsecore/filter/biquad.h | 2 + src/pulsecore/filter/crossover.c | 128 +++++++++++++++-------------- src/pulsecore/filter/lfe-filter.c | 7 +- src/pulsecore/filter/lfe-filter.h | 2 +- src/pulsecore/resampler.c | 5 +- src/pulsecore/resampler.h | 3 +- src/pulsecore/sink-input.c | 2 + src/pulsecore/source-output.c | 2 + 16 files changed, 100 insertions(+), 69 deletions(-) diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c index 98831267..cf14d0db 100644 --- a/src/daemon/daemon-conf.c +++ b/src/daemon/daemon-conf.c @@ -86,6 +86,7 @@ static const pa_daemon_conf default_conf = { .remixing_use_all_sink_channels = true, .disable_lfe_remixing = true, .lfe_crossover_freq = 0, + .remix_non_lfe_channels = true, .config_file = NULL, .use_pid_file = true, .system_instance = false, @@ -562,6 +563,7 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { { "disable-lfe-remixing", pa_config_parse_bool, &c->disable_lfe_remixing, NULL }, { "enable-lfe-remixing", pa_config_parse_not_bool, &c->disable_lfe_remixing, NULL }, { "lfe-crossover-freq", pa_config_parse_unsigned, &c->lfe_crossover_freq, NULL }, + { "remix-non-lfe-channels", pa_config_parse_bool, &c->remix_non_lfe_channels, NULL }, { "load-default-script-file", pa_config_parse_bool, &c->load_default_script_file, NULL }, { "shm-size-bytes", pa_config_parse_size, &c->shm_size, NULL }, { "log-meta", pa_config_parse_bool, &c->log_meta, NULL }, diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h index 953ea33a..d9ccb8b5 100644 --- a/src/daemon/daemon-conf.h +++ b/src/daemon/daemon-conf.h @@ -71,6 +71,7 @@ typedef struct pa_daemon_conf { disable_remixing, remixing_use_all_sink_channels, disable_lfe_remixing, + remix_non_lfe_channels, load_default_script_file, disallow_exit, log_meta, diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in index a9555238..0202a2c9 100644 --- a/src/daemon/daemon.conf.in +++ b/src/daemon/daemon.conf.in @@ -58,6 +58,7 @@ ifelse(@HAVE_DBUS@, 1, [dnl ; enable-remixing = yes ; remixing-use-all-sink-channels = yes ; enable-lfe-remixing = no +; remix-non-lfe-channels = yes ; lfe-crossover-freq = 0 ; flat-volumes = yes diff --git a/src/daemon/main.c b/src/daemon/main.c index f35252d0..12f27754 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -1040,6 +1040,7 @@ int main(int argc, char *argv[]) { c->disable_remixing = conf->disable_remixing; c->remixing_use_all_sink_channels = conf->remixing_use_all_sink_channels; c->disable_lfe_remixing = conf->disable_lfe_remixing; + c->remix_non_lfe_channels = conf->remix_non_lfe_channels; c->deferred_volume = conf->deferred_volume; c->running_as_daemon = conf->daemonize; c->disallow_exit = conf->disallow_exit; diff --git a/src/modules/module-virtual-surround-sink.c b/src/modules/module-virtual-surround-sink.c index 09c5e6dd..a879a06d 100644 --- a/src/modules/module-virtual-surround-sink.c +++ b/src/modules/module-virtual-surround-sink.c @@ -787,7 +787,7 @@ int pa__init(pa_module*m) { /* resample hrir */ resampler = pa_resampler_new(u->sink->core->mempool, &hrir_temp_ss, &hrir_map, &hrir_ss, &hrir_map, u->sink->core->lfe_crossover_freq, - PA_RESAMPLER_SRC_SINC_BEST_QUALITY, PA_RESAMPLER_NO_REMAP); + u->sink->core->remix_non_lfe_channels,PA_RESAMPLER_SRC_SINC_BEST_QUALITY, PA_RESAMPLER_NO_REMAP); u->hrir_samples = hrir_temp_chunk.length / pa_frame_size(&hrir_temp_ss) * hrir_ss.rate / hrir_temp_ss.rate; if (u->hrir_samples > 64) { diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index e01677d5..395939ce 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -144,6 +144,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t c->disable_remixing = false; c->remixing_use_all_sink_channels = true; c->disable_lfe_remixing = true; + c->remix_non_lfe_channels = true; c->lfe_crossover_freq = 0; c->deferred_volume = true; c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 1; diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 79a095d2..8fa83a36 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -217,6 +217,7 @@ struct pa_core { bool disable_remixing:1; bool remixing_use_all_sink_channels:1; bool disable_lfe_remixing:1; + bool remix_non_lfe_channels:1; bool deferred_volume:1; pa_resample_method_t resample_method; diff --git a/src/pulsecore/filter/biquad.c b/src/pulsecore/filter/biquad.c index 3205e7c2..a7e1b626 100644 --- a/src/pulsecore/filter/biquad.c +++ b/src/pulsecore/filter/biquad.c @@ -37,6 +37,11 @@ static void set_coefficient(struct biquad *bq, double b0, double b1, double b2, bq->a2 = a2 * a0_inv; } +static void set_active(struct biquad *bq, bool active) +{ + bq->active = active; +} + static void biquad_lowpass(struct biquad *bq, double cutoff) { /* Limit cutoff to 0 to 1. */ @@ -108,10 +113,14 @@ void biquad_set(struct biquad *bq, enum biquad_type type, double freq) switch (type) { case BQ_LOWPASS: + set_active(bq, true); biquad_lowpass(bq, freq); break; case BQ_HIGHPASS: + set_active(bq, true); biquad_highpass(bq, freq); break; + case BQ_NONE: + set_active(bq, false); } } diff --git a/src/pulsecore/filter/biquad.h b/src/pulsecore/filter/biquad.h index bb8f2fb9..cda345bc 100644 --- a/src/pulsecore/filter/biquad.h +++ b/src/pulsecore/filter/biquad.h @@ -21,12 +21,14 @@ extern "C" { struct biquad { float b0, b1, b2; float a1, a2; + bool active; }; /* The type of the biquad filters */ enum biquad_type { BQ_LOWPASS, BQ_HIGHPASS, + BQ_NONE, }; /* Initialize a biquad filter parameters from its type and parameters. diff --git a/src/pulsecore/filter/crossover.c b/src/pulsecore/filter/crossover.c index dab34afc..652b858d 100644 --- a/src/pulsecore/filter/crossover.c +++ b/src/pulsecore/filter/crossover.c @@ -24,74 +24,78 @@ void lr4_set(struct lr4 *lr4, enum biquad_type type, float freq) void lr4_process_float32(struct lr4 *lr4, int samples, int channels, float *src, float *dest) { - float lx1 = lr4->x1; - float lx2 = lr4->x2; - float ly1 = lr4->y1; - float ly2 = lr4->y2; - float lz1 = lr4->z1; - float lz2 = lr4->z2; - float lb0 = lr4->bq.b0; - float lb1 = lr4->bq.b1; - float lb2 = lr4->bq.b2; - float la1 = lr4->bq.a1; - float la2 = lr4->bq.a2; + if(lr4->bq.active == true) { + float lx1 = lr4->x1; + float lx2 = lr4->x2; + float ly1 = lr4->y1; + float ly2 = lr4->y2; + float lz1 = lr4->z1; + float lz2 = lr4->z2; + float lb0 = lr4->bq.b0; + float lb1 = lr4->bq.b1; + float lb2 = lr4->bq.b2; + float la1 = lr4->bq.a1; + float la2 = lr4->bq.a2; - int i; - for (i = 0; i < samples * channels; i += channels) { - float x, y, z; - x = src[i]; - y = lb0*x + lb1*lx1 + lb2*lx2 - la1*ly1 - la2*ly2; - z = lb0*y + lb1*ly1 + lb2*ly2 - la1*lz1 - la2*lz2; - lx2 = lx1; - lx1 = x; - ly2 = ly1; - ly1 = y; - lz2 = lz1; - lz1 = z; - dest[i] = z; - } + int i; + for (i = 0; i < samples * channels; i += channels) { + float x, y, z; + x = src[i]; + y = lb0*x + lb1*lx1 + lb2*lx2 - la1*ly1 - la2*ly2; + z = lb0*y + lb1*ly1 + lb2*ly2 - la1*lz1 - la2*lz2; + lx2 = lx1; + lx1 = x; + ly2 = ly1; + ly1 = y; + lz2 = lz1; + lz1 = z; + dest[i] = z; + } - lr4->x1 = lx1; - lr4->x2 = lx2; - lr4->y1 = ly1; - lr4->y2 = ly2; - lr4->z1 = lz1; - lr4->z2 = lz2; + lr4->x1 = lx1; + lr4->x2 = lx2; + lr4->y1 = ly1; + lr4->y2 = ly2; + lr4->z1 = lz1; + lr4->z2 = lz2; + } } void lr4_process_s16(struct lr4 *lr4, int samples, int channels, short *src, short *dest) { - float lx1 = lr4->x1; - float lx2 = lr4->x2; - float ly1 = lr4->y1; - float ly2 = lr4->y2; - float lz1 = lr4->z1; - float lz2 = lr4->z2; - float lb0 = lr4->bq.b0; - float lb1 = lr4->bq.b1; - float lb2 = lr4->bq.b2; - float la1 = lr4->bq.a1; - float la2 = lr4->bq.a2; + if(lr4->bq.active == true) { + float lx1 = lr4->x1; + float lx2 = lr4->x2; + float ly1 = lr4->y1; + float ly2 = lr4->y2; + float lz1 = lr4->z1; + float lz2 = lr4->z2; + float lb0 = lr4->bq.b0; + float lb1 = lr4->bq.b1; + float lb2 = lr4->bq.b2; + float la1 = lr4->bq.a1; + float la2 = lr4->bq.a2; - int i; - for (i = 0; i < samples * channels; i += channels) { - float x, y, z; - x = src[i]; - y = lb0*x + lb1*lx1 + lb2*lx2 - la1*ly1 - la2*ly2; - z = lb0*y + lb1*ly1 + lb2*ly2 - la1*lz1 - la2*lz2; - lx2 = lx1; - lx1 = x; - ly2 = ly1; - ly1 = y; - lz2 = lz1; - lz1 = z; - dest[i] = PA_CLAMP_UNLIKELY((int) z, -0x8000, 0x7fff); - } + int i; + for (i = 0; i < samples * channels; i += channels) { + float x, y, z; + x = src[i]; + y = lb0*x + lb1*lx1 + lb2*lx2 - la1*ly1 - la2*ly2; + z = lb0*y + lb1*ly1 + lb2*ly2 - la1*lz1 - la2*lz2; + lx2 = lx1; + lx1 = x; + ly2 = ly1; + ly1 = y; + lz2 = lz1; + lz1 = z; + dest[i] = PA_CLAMP_UNLIKELY((int) z, -0x8000, 0x7fff); + } - lr4->x1 = lx1; - lr4->x2 = lx2; - lr4->y1 = ly1; - lr4->y2 = ly2; - lr4->z1 = lz1; - lr4->z2 = lz2; + lr4->x1 = lx1; + lr4->x2 = lx2; + lr4->y1 = ly1; + lr4->y2 = ly2; + lr4->z1 = lz1; + lr4->z2 = lz2; + } } diff --git a/src/pulsecore/filter/lfe-filter.c b/src/pulsecore/filter/lfe-filter.c index c0b1eb07..21292cd7 100644 --- a/src/pulsecore/filter/lfe-filter.c +++ b/src/pulsecore/filter/lfe-filter.c @@ -49,6 +49,7 @@ struct pa_lfe_filter { int64_t index; PA_LLIST_HEAD(struct saved_state, saved); float crossover; + bool remix_non_lfe_channels; pa_channel_map cm; pa_sample_spec ss; size_t maxrewind; @@ -62,10 +63,11 @@ static void remove_state(pa_lfe_filter_t *f, struct saved_state *s) { pa_xfree(s); } -pa_lfe_filter_t * pa_lfe_filter_new(const pa_sample_spec* ss, const pa_channel_map* cm, float crossover_freq, size_t maxrewind) { +pa_lfe_filter_t * pa_lfe_filter_new(const pa_sample_spec* ss, const pa_channel_map* cm, float crossover_freq, bool remix_non_lfe_channels, size_t maxrewind) { pa_lfe_filter_t *f = pa_xnew0(struct pa_lfe_filter, 1); f->crossover = crossover_freq; + f->remix_non_lfe_channels = remix_non_lfe_channels; f->cm = *cm; f->ss = *ss; f->maxrewind = maxrewind; @@ -148,6 +150,7 @@ pa_memchunk * pa_lfe_filter_process(pa_lfe_filter_t *f, pa_memchunk *buf) { void pa_lfe_filter_update_rate(pa_lfe_filter_t *f, uint32_t new_rate) { int i; float biquad_freq = f->crossover / (new_rate / 2); + enum biquad_type other_channel_biquad = (f->remix_non_lfe_channels) ? BQ_HIGHPASS : BQ_NONE; while (f->saved) remove_state(f, f->saved); @@ -161,7 +164,7 @@ void pa_lfe_filter_update_rate(pa_lfe_filter_t *f, uint32_t new_rate) { } for (i = 0; i < f->cm.channels; i++) - lr4_set(&f->lr4[i], f->cm.map[i] == PA_CHANNEL_POSITION_LFE ? BQ_LOWPASS : BQ_HIGHPASS, biquad_freq); + lr4_set(&f->lr4[i], f->cm.map[i] == PA_CHANNEL_POSITION_LFE ? BQ_LOWPASS : other_channel_biquad, biquad_freq); f->active = true; } diff --git a/src/pulsecore/filter/lfe-filter.h b/src/pulsecore/filter/lfe-filter.h index 54d695b9..2fc21365 100644 --- a/src/pulsecore/filter/lfe-filter.h +++ b/src/pulsecore/filter/lfe-filter.h @@ -29,7 +29,7 @@ typedef struct pa_lfe_filter pa_lfe_filter_t; -pa_lfe_filter_t * pa_lfe_filter_new(const pa_sample_spec* ss, const pa_channel_map* cm, float crossover_freq, size_t maxrewind); +pa_lfe_filter_t * pa_lfe_filter_new(const pa_sample_spec* ss, const pa_channel_map* cm, float crossover_freq, bool remix_non_lfe_channels, size_t maxrewind); void pa_lfe_filter_free(pa_lfe_filter_t *); void pa_lfe_filter_reset(pa_lfe_filter_t *); void pa_lfe_filter_rewind(pa_lfe_filter_t *, size_t amount); diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index 063c4c5a..4cf754e7 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -320,7 +320,8 @@ pa_resampler* pa_resampler_new( const pa_channel_map *am, const pa_sample_spec *b, const pa_channel_map *bm, - unsigned crossover_freq, + unsigned crossover_freq, + bool remix_non_lfe_channels, pa_resample_method_t method, pa_resample_flags_t flags) { @@ -420,7 +421,7 @@ pa_resampler* pa_resampler_new( pa_sample_spec wss = r->o_ss; wss.format = r->work_format; /* FIXME: For now just hardcode maxrewind to 3 seconds */ - r->lfe_filter = pa_lfe_filter_new(&wss, &r->o_cm, (float)crossover_freq, b->rate * 3); + r->lfe_filter = pa_lfe_filter_new(&wss, &r->o_cm, (float)crossover_freq, remix_non_lfe_channels, b->rate * 3); pa_log_debug(" lfe filter activated (LR4 type), the crossover_freq = %uHz", crossover_freq); } diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h index 5d3171f6..95e4a481 100644 --- a/src/pulsecore/resampler.h +++ b/src/pulsecore/resampler.h @@ -119,7 +119,8 @@ pa_resampler* pa_resampler_new( const pa_channel_map *am, const pa_sample_spec *b, const pa_channel_map *bm, - unsigned crossover_freq, + unsigned crossover_freq, + bool remix_non_lfe_channels, pa_resample_method_t resample_method, pa_resample_flags_t flags); diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 4155b69a..6cf5b509 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -452,6 +452,7 @@ int pa_sink_input_new( &data->sample_spec, &data->channel_map, &data->sink->sample_spec, &data->sink->channel_map, core->lfe_crossover_freq, + core->remix_non_lfe_channels, data->resample_method, ((data->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | ((data->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | @@ -2301,6 +2302,7 @@ int pa_sink_input_update_rate(pa_sink_input *i) { &i->sample_spec, &i->channel_map, &i->sink->sample_spec, &i->sink->channel_map, i->core->lfe_crossover_freq, + i->core->remix_non_lfe_channels, i->requested_resample_method, ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index a4c99af0..fab54693 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -397,6 +397,7 @@ int pa_source_output_new( &data->source->sample_spec, &data->source->channel_map, &data->sample_spec, &data->channel_map, core->lfe_crossover_freq, + core->remix_non_lfe_channels, data->resample_method, ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | @@ -1742,6 +1743,7 @@ int pa_source_output_update_rate(pa_source_output *o) { &o->source->sample_spec, &o->source->channel_map, &o->sample_spec, &o->channel_map, o->core->lfe_crossover_freq, + o->core->remix_non_lfe_channels, o->requested_resample_method, ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | -- 2.13.3