man/pulse-daemon.conf.5.xml.in | 8 ++++ src/daemon/daemon-conf.c | 4 ++ src/daemon/daemon-conf.h | 1 src/daemon/daemon.conf.in | 1 src/daemon/main.c | 1 src/pulsecore/core.c | 1 src/pulsecore/core.h | 1 src/pulsecore/resampler.c | 82 ++++++++++++++++++++++++++++++++++++----- src/pulsecore/resampler.h | 3 + src/pulsecore/sink-input.c | 2 + src/pulsecore/source-output.c | 2 + src/tests/remix-test.c | 39 ++++++++++++++++--- 12 files changed, 128 insertions(+), 17 deletions(-)
New commits: commit f02a9a463f3b71cb593f876a5d5e7d909894fda0 Author: David Mandelberg <[email protected]> Date: Wed Jan 4 11:55:49 2017 -0500 daemon-conf: add remixing-use-all-sink-channels option This option controls the PA_RESAMPLER_NO_FILL_SINK flag added in a previous commit. BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=62588 BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=94563 diff --git a/man/pulse-daemon.conf.5.xml.in b/man/pulse-daemon.conf.5.xml.in index ff5a293..883c4ea 100644 --- a/man/pulse-daemon.conf.5.xml.in +++ b/man/pulse-daemon.conf.5.xml.in @@ -130,6 +130,14 @@ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. </option> <option> + <p><opt>remixing-use-all-sink-channels=</opt> If enabled, use + all sink channels when remixing. Otherwise, remix to the minimal + set of sink channels needed to reproduce all of the source + channels. (This has no effect on LFE remixing.) Defaults to + <opt>yes</opt>.</p> + </option> + + <option> <p><opt>enable-lfe-remixing=</opt> If disabled when upmixing or downmixing ignore LFE channels. When this option is disabled the output LFE channel will only get a signal when an input LFE diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c index f0ed0b4..31e4b14 100644 --- a/src/daemon/daemon-conf.c +++ b/src/daemon/daemon-conf.c @@ -82,6 +82,7 @@ static const pa_daemon_conf default_conf = { .log_time = false, .resample_method = PA_RESAMPLER_AUTO, .disable_remixing = false, + .remixing_use_all_sink_channels = true, .disable_lfe_remixing = true, .lfe_crossover_freq = 0, .config_file = NULL, @@ -554,6 +555,8 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { { "nice-level", parse_nice_level, c, NULL }, { "disable-remixing", pa_config_parse_bool, &c->disable_remixing, NULL }, { "enable-remixing", pa_config_parse_not_bool, &c->disable_remixing, NULL }, + { "remixing-use-all-sink-channels", + pa_config_parse_bool, &c->remixing_use_all_sink_channels, NULL }, { "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 }, @@ -748,6 +751,7 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) { pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]); pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method)); pa_strbuf_printf(s, "enable-remixing = %s\n", pa_yes_no(!c->disable_remixing)); + pa_strbuf_printf(s, "remixing-use-all-sink-channels = %s\n", pa_yes_no(c->remixing_use_all_sink_channels)); pa_strbuf_printf(s, "enable-lfe-remixing = %s\n", pa_yes_no(!c->disable_lfe_remixing)); pa_strbuf_printf(s, "lfe-crossover-freq = %u\n", c->lfe_crossover_freq); pa_strbuf_printf(s, "default-sample-format = %s\n", pa_sample_format_to_string(c->default_sample_spec.format)); diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h index 82b619f..e61f67f 100644 --- a/src/daemon/daemon-conf.h +++ b/src/daemon/daemon-conf.h @@ -68,6 +68,7 @@ typedef struct pa_daemon_conf { disable_shm, disable_memfd, disable_remixing, + remixing_use_all_sink_channels, disable_lfe_remixing, load_default_script_file, disallow_exit, diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in index f3a1cc3..2d74afa 100644 --- a/src/daemon/daemon.conf.in +++ b/src/daemon/daemon.conf.in @@ -55,6 +55,7 @@ ifelse(@HAVE_DBUS@, 1, [dnl ; resample-method = speex-float-1 ; enable-remixing = yes +; remixing-use-all-sink-channels = yes ; enable-lfe-remixing = no ; lfe-crossover-freq = 0 diff --git a/src/daemon/main.c b/src/daemon/main.c index dc5e5bc..280252a 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -1037,6 +1037,7 @@ int main(int argc, char *argv[]) { c->realtime_priority = conf->realtime_priority; c->realtime_scheduling = conf->realtime_scheduling; 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->deferred_volume = conf->deferred_volume; c->running_as_daemon = conf->daemonize; diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index 2a96dfa..afdb0a4 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -142,6 +142,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t c->realtime_scheduling = false; c->realtime_priority = 5; c->disable_remixing = false; + c->remixing_use_all_sink_channels = true; c->disable_lfe_remixing = true; c->lfe_crossover_freq = 0; c->deferred_volume = true; diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 802111b..d2fe887 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -200,6 +200,7 @@ struct pa_core { bool running_as_daemon:1; bool realtime_scheduling:1; bool disable_remixing:1; + bool remixing_use_all_sink_channels:1; bool disable_lfe_remixing:1; bool deferred_volume:1; diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 0dda204..d988310 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -456,6 +456,7 @@ int pa_sink_input_new( ((data->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | ((data->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | (core->disable_remixing || (data->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | + (core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) | (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) { pa_log_warn("Unsupported resampling operation."); return -PA_ERR_NOTSUPPORTED; @@ -2259,6 +2260,7 @@ int pa_sink_input_update_rate(pa_sink_input *i) { ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | + (i->core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) | (i->core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)); if (!new_resampler) { diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 35ef1c5..351c399 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -401,6 +401,7 @@ int pa_source_output_new( ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | (core->disable_remixing || (data->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | + (core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) | (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) { pa_log_warn("Unsupported resampling operation."); return -PA_ERR_NOTSUPPORTED; @@ -1714,6 +1715,7 @@ int pa_source_output_update_rate(pa_source_output *o) { ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | + (o->core->remixing_use_all_sink_channels ? 0 : PA_RESAMPLER_NO_FILL_SINK) | (o->core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)); if (!new_resampler) { commit 1c7d3e60ccd17bf9a8e82651f86a269fc2f23433 Author: David Mandelberg <[email protected]> Date: Wed Jan 4 11:55:48 2017 -0500 remix-test: test the remixer with PA_RESAMPLER_NO_FILL_SINK set diff --git a/src/tests/remix-test.c b/src/tests/remix-test.c index 5b0c677..0dcc2f1 100644 --- a/src/tests/remix-test.c +++ b/src/tests/remix-test.c @@ -61,6 +61,8 @@ int main(int argc, char *argv[]) { RESAMPLE_FLAGS(PA_RESAMPLER_NO_REMAP), RESAMPLE_FLAGS(PA_RESAMPLER_NO_REMIX), RESAMPLE_FLAGS(PA_RESAMPLER_NO_LFE), + RESAMPLE_FLAGS(PA_RESAMPLER_NO_FILL_SINK), + RESAMPLE_FLAGS(PA_RESAMPLER_NO_LFE | PA_RESAMPLER_NO_FILL_SINK), { .str = NULL, .value = 0 }, }; commit 32291eaca21a2a1f5df9bca69a5fbd83d7d7c624 Author: David Mandelberg <[email protected]> Date: Wed Jan 4 11:55:47 2017 -0500 resampler: Flag for remixing to all sink channels. Add a flag PA_RESAMPLER_NO_FILL_SINK, which controls whether remixing should attempt to use all sink channels, versus only the ones needed to reproduce the source audio. BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=62588 BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=94563 Suggested-by: Alexander E. Patrakov <[email protected]> diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index ea22cd2..063c4c5 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -796,6 +796,64 @@ static int front_rear_side(pa_channel_position_t p) { return ON_OTHER; } +/* Fill a map of which output channels should get mono from input, not including + * LFE output channels. (The LFE output channels are mapped separately.) + */ +static void setup_oc_mono_map(const pa_resampler *r, float *oc_mono_map) { + unsigned oc; + unsigned n_oc; + bool found_oc_for_mono = false; + + pa_assert(r); + pa_assert(oc_mono_map); + + n_oc = r->o_ss.channels; + + if (!(r->flags & PA_RESAMPLER_NO_FILL_SINK)) { + /* Mono goes to all non-LFE output channels and we're done. */ + for (oc = 0; oc < n_oc; oc++) + oc_mono_map[oc] = on_lfe(r->o_cm.map[oc]) ? 0.0f : 1.0f; + return; + } else { + /* Initialize to all zero so we can select individual channels below. */ + for (oc = 0; oc < n_oc; oc++) + oc_mono_map[oc] = 0.0f; + } + + for (oc = 0; oc < n_oc; oc++) { + if (r->o_cm.map[oc] == PA_CHANNEL_POSITION_MONO) { + oc_mono_map[oc] = 1.0f; + found_oc_for_mono = true; + } + } + if (found_oc_for_mono) + return; + + for (oc = 0; oc < n_oc; oc++) { + if (r->o_cm.map[oc] == PA_CHANNEL_POSITION_FRONT_CENTER) { + oc_mono_map[oc] = 1.0f; + found_oc_for_mono = true; + } + } + if (found_oc_for_mono) + return; + + for (oc = 0; oc < n_oc; oc++) { + if (r->o_cm.map[oc] == PA_CHANNEL_POSITION_FRONT_LEFT || r->o_cm.map[oc] == PA_CHANNEL_POSITION_FRONT_RIGHT) { + oc_mono_map[oc] = 1.0f; + found_oc_for_mono = true; + } + } + if (found_oc_for_mono) + return; + + /* Give up on finding a suitable map for mono, and just send it to all + * non-LFE output channels. + */ + for (oc = 0; oc < n_oc; oc++) + oc_mono_map[oc] = on_lfe(r->o_cm.map[oc]) ? 0.0f : 1.0f; +} + static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed) { unsigned oc, ic; unsigned n_oc, n_ic; @@ -858,14 +916,14 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed) * 1) Connect all channels with matching names. * * 2) Mono Handling: - * S:Mono: Copy into all D:channels + * S:Mono: See setup_oc_mono_map(). * D:Mono: Avg all S:channels * - * 3) Mix D:Left, D:Right: + * 3) Mix D:Left, D:Right (if PA_RESAMPLER_NO_FILL_SINK is clear): * D:Left: If not connected, avg all S:Left * D:Right: If not connected, avg all S:Right * - * 4) Mix D:Center + * 4) Mix D:Center (if PA_RESAMPLER_NO_FILL_SINK is clear): * If not connected, avg all S:Center * If still not connected, avg all S:Left, S:Right * @@ -908,6 +966,7 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed) ic_unconnected_center = 0, ic_unconnected_lfe = 0; bool ic_unconnected_center_mixed_in = 0; + float oc_mono_map[PA_CHANNELS_MAX]; for (ic = 0; ic < n_ic; ic++) { if (on_left(r->i_cm.map[ic])) @@ -918,6 +977,8 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed) ic_center++; } + setup_oc_mono_map(r, oc_mono_map); + for (oc = 0; oc < n_oc; oc++) { bool oc_connected = false; pa_channel_position_t b = r->o_cm.map[oc]; @@ -925,14 +986,17 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed) for (ic = 0; ic < n_ic; ic++) { pa_channel_position_t a = r->i_cm.map[ic]; - if (a == b || a == PA_CHANNEL_POSITION_MONO) { + if (a == b) { m->map_table_f[oc][ic] = 1.0f; oc_connected = true; ic_connected[ic] = true; + } + else if (a == PA_CHANNEL_POSITION_MONO && oc_mono_map[oc] > 0.0f) { + m->map_table_f[oc][ic] = oc_mono_map[oc]; - if (a == PA_CHANNEL_POSITION_MONO && on_lfe(b) && !(r->flags & PA_RESAMPLER_NO_LFE)) - *lfe_remixed = true; + oc_connected = true; + ic_connected[ic] = true; } else if (b == PA_CHANNEL_POSITION_MONO) { m->map_table_f[oc][ic] = 1.0f / (float) n_ic; @@ -945,7 +1009,7 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed) if (!oc_connected) { /* Try to find matching input ports for this output port */ - if (on_left(b)) { + if (on_left(b) && !(r->flags & PA_RESAMPLER_NO_FILL_SINK)) { /* We are not connected and on the left side, let's * average all left side input channels. */ @@ -960,7 +1024,7 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed) /* We ignore the case where there is no left input channel. * Something is really wrong in this case anyway. */ - } else if (on_right(b)) { + } else if (on_right(b) && !(r->flags & PA_RESAMPLER_NO_FILL_SINK)) { /* We are not connected and on the right side, let's * average all right side input channels. */ @@ -976,7 +1040,7 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed) * channel. Something is really wrong in this case anyway. * */ - } else if (on_center(b)) { + } else if (on_center(b) && !(r->flags & PA_RESAMPLER_NO_FILL_SINK)) { if (ic_center > 0) { diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h index 4469022..5d3171f 100644 --- a/src/pulsecore/resampler.h +++ b/src/pulsecore/resampler.h @@ -68,7 +68,8 @@ typedef enum pa_resample_flags { PA_RESAMPLER_VARIABLE_RATE = 0x0001U, PA_RESAMPLER_NO_REMAP = 0x0002U, /* implies NO_REMIX */ PA_RESAMPLER_NO_REMIX = 0x0004U, - PA_RESAMPLER_NO_LFE = 0x0008U + PA_RESAMPLER_NO_LFE = 0x0008U, + PA_RESAMPLER_NO_FILL_SINK = 0x0010U, } pa_resample_flags_t; struct pa_resampler { commit bc77676ca0766951581f50c0b9863dc07d562e5e Author: David Mandelberg <[email protected]> Date: Wed Jan 4 11:55:46 2017 -0500 remix-test: test the remixer using different flags This will make it easier to see how the following commit affects the remixer. diff --git a/src/tests/remix-test.c b/src/tests/remix-test.c index 1e58418..5b0c677 100644 --- a/src/tests/remix-test.c +++ b/src/tests/remix-test.c @@ -27,6 +27,17 @@ #include <pulsecore/macro.h> #include <pulsecore/memblock.h> +struct resample_flags { + const char *str; + pa_resample_flags_t value; +}; + +/* Call like this to get an initializer for struct resample_flags: + * RESAMPLE_FLAGS(PA_RESAMPLER_NO_LFE) + */ +#define RESAMPLE_FLAGS(flags) { .str = #flags, .value = (flags) } + + int main(int argc, char *argv[]) { static const pa_channel_map maps[] = { @@ -45,7 +56,15 @@ int main(int argc, char *argv[]) { { 0, { 0 } } }; - unsigned i, j; + static const struct resample_flags flag_sets[] = { + RESAMPLE_FLAGS(0), + RESAMPLE_FLAGS(PA_RESAMPLER_NO_REMAP), + RESAMPLE_FLAGS(PA_RESAMPLER_NO_REMIX), + RESAMPLE_FLAGS(PA_RESAMPLER_NO_LFE), + { .str = NULL, .value = 0 }, + }; + + unsigned i, j, k; pa_mempool *pool; unsigned crossover_freq = 120; @@ -59,20 +78,24 @@ int main(int argc, char *argv[]) { pa_resampler *r; pa_sample_spec ss1, ss2; - pa_log_info("Converting from '%s' to '%s'.", pa_channel_map_snprint(a, sizeof(a), &maps[i]), pa_channel_map_snprint(b, sizeof(b), &maps[j])); - ss1.channels = maps[i].channels; ss2.channels = maps[j].channels; ss1.rate = ss2.rate = 44100; ss1.format = ss2.format = PA_SAMPLE_S16NE; - r = pa_resampler_new(pool, &ss1, &maps[i], &ss2, &maps[j], crossover_freq, PA_RESAMPLER_AUTO, 0); + for (k = 0; flag_sets[k].str; k++) { + pa_log_info("Converting from '%s' to '%s' with flags %s.", pa_channel_map_snprint(a, sizeof(a), &maps[i]), + pa_channel_map_snprint(b, sizeof(b), &maps[j]), flag_sets[k].str); + + r = pa_resampler_new(pool, &ss1, &maps[i], &ss2, &maps[j], crossover_freq, PA_RESAMPLER_AUTO, + flag_sets[k].value); - /* We don't really care for the resampler. We just want to - * see the remixing debug output. */ + /* We don't really care for the resampler. We just want to + * see the remixing debug output. */ - pa_resampler_free(r); + pa_resampler_free(r); + } } pa_mempool_unref(pool); _______________________________________________ pulseaudio-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/pulseaudio-commits
