Allow a mapping to relax the exact channel restriction: exact-channels = yes | no # If no, and the exact number of channels is not supported, # allow device to be opened with another channel count Signed-off-by: David Henningsson <david.henningsson at canonical.com> --- src/modules/alsa/alsa-mixer.c | 43 +++++++++++++++++++++--- src/modules/alsa/alsa-mixer.h | 1 + src/modules/alsa/mixer/profile-sets/default.conf | 2 ++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index 58f9182..77c3c7e 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -3368,6 +3368,7 @@ pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name) m = pa_xnew0(pa_alsa_mapping, 1); m->profile_set = ps; + m->exact_channels = true; m->name = pa_xstrdup(name); pa_sample_spec_init(&m->sample_spec); pa_channel_map_init(&m->channel_map); @@ -3485,6 +3486,30 @@ static int mapping_parse_paths(pa_config_parser_state *state) { return 0; } +static int mapping_parse_exact_channels(pa_config_parser_state *state) { + pa_alsa_profile_set *ps; + pa_alsa_mapping *m; + int b; + + pa_assert(state); + + ps = state->userdata; + + if (!(m = pa_alsa_mapping_get(ps, state->section))) { + pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section); + return -1; + } + + if ((b = pa_parse_boolean(state->rvalue)) < 0) { + pa_log("[%s:%u] %s has invalid value '%s'", state->filename, state->lineno, state->lvalue, state->section); + return -1; + } + + m->exact_channels = b; + + return 0; +} + static int mapping_parse_element(pa_config_parser_state *state) { pa_alsa_profile_set *ps; pa_alsa_mapping *m; @@ -4156,6 +4181,7 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel { "element-input", mapping_parse_element, NULL, NULL }, { "element-output", mapping_parse_element, NULL, NULL }, { "direction", mapping_parse_direction, NULL, NULL }, + { "exact-channels", mapping_parse_exact_channels, NULL, NULL }, /* Shared by [Mapping ...] and [Profile ...] */ { "description", mapping_parse_description, NULL, NULL }, @@ -4264,10 +4290,12 @@ static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_p static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m, const pa_sample_spec *ss, const char *dev_id, + bool exact_channels, int mode, unsigned default_n_fragments, unsigned default_fragment_size_msec) { + snd_pcm_t* handle; pa_sample_spec try_ss = *ss; pa_channel_map try_map = m->channel_map; snd_pcm_uframes_t try_period_size, try_buffer_size; @@ -4279,10 +4307,17 @@ static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m, pa_frame_size(&try_ss); try_buffer_size = default_n_fragments * try_period_size; - return pa_alsa_open_by_template( + handle = pa_alsa_open_by_template( m->device_strings, dev_id, NULL, &try_ss, &try_map, mode, &try_period_size, - &try_buffer_size, 0, NULL, NULL, true); + &try_buffer_size, 0, NULL, NULL, exact_channels); + if (handle && !exact_channels && m->channel_map.channels != try_map.channels) { + char buf[PA_CHANNEL_MAP_SNPRINT_MAX]; + pa_log_debug("Channel map for mapping '%s' permanently changed to '%s'", m->name, + pa_channel_map_snprint(buf, sizeof(buf), &try_map)); + m->channel_map = try_map; + } + return handle; } static void paths_drop_unused(pa_hashmap* h, pa_hashmap *keep) { @@ -4365,7 +4400,7 @@ void pa_alsa_profile_set_probe( continue; pa_log_debug("Checking for playback on %s (%s)", m->description, m->name); - if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id, + if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id, m->exact_channels, SND_PCM_STREAM_PLAYBACK, default_n_fragments, default_fragment_size_msec))) { @@ -4386,7 +4421,7 @@ void pa_alsa_profile_set_probe( continue; pa_log_debug("Checking for recording on %s (%s)", m->description, m->name); - if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id, + if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id, m->exact_channels, SND_PCM_STREAM_CAPTURE, default_n_fragments, default_fragment_size_msec))) { diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h index 995a34b..d04c2bb 100644 --- a/src/modules/alsa/alsa-mixer.h +++ b/src/modules/alsa/alsa-mixer.h @@ -264,6 +264,7 @@ struct pa_alsa_mapping { pa_alsa_path_set *output_path_set; unsigned supported; + bool exact_channels:1; /* Temporarily used during probing */ snd_pcm_t *input_pcm; diff --git a/src/modules/alsa/mixer/profile-sets/default.conf b/src/modules/alsa/mixer/profile-sets/default.conf index 83cb425..77a4d76 100644 --- a/src/modules/alsa/mixer/profile-sets/default.conf +++ b/src/modules/alsa/mixer/profile-sets/default.conf @@ -55,6 +55,8 @@ ; priority = ... ; direction = any | input | output # Only useful for? ; +; exact-channels = yes | no # If no, and the exact number of channels is not supported, +; # allow device to be opened with another channel count ; [Profile id] ; input-mappings = ... # Lists mappings for sources on this profile, those mapping must be ; # defined in this file too -- 1.9.1