From: Jaroslav Kysela Date: Wed, 14 Oct 2020 20:20:37 +0000 (+0200) Subject: alsa: mixer - add support up 8 mixer channels X-Git-Tag: v14.99.1~236 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b530aa4681087bcb315890df31ec91b4eb3cd4cc;p=platform%2Fupstream%2Fpulseaudio.git alsa: mixer - add support up 8 mixer channels We have at least one USB hardware which supports the 8 channels in one mixer element: https://github.com/alsa-project/alsa-ucm-conf/pull/25 POSITION_MASK_CHANNELS define was added for the future extensions. The override_map variable was changed from bool to mask (unsigned int). The channel map override settings is handled for channels up to eight now. Also added missing override-map.3 .. override-map.8 to the configuration parser array. The driver channel position was added to the override mask arguments (syntax is driver:pulseaudio like left:all-left). If ommited, the ALSA's channel positions are guessed by index. Link: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/292 Signed-off-by: Jaroslav Kysela Part-of: --- diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index 3a29132..8cd17cc 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -689,6 +689,20 @@ static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_M [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN }; +static snd_mixer_selem_channel_id_t alsa_channel_positions[POSITION_MASK_CHANNELS] = { + SND_MIXER_SCHN_FRONT_LEFT, + SND_MIXER_SCHN_FRONT_RIGHT, + SND_MIXER_SCHN_REAR_LEFT, + SND_MIXER_SCHN_REAR_RIGHT, + SND_MIXER_SCHN_FRONT_CENTER, + SND_MIXER_SCHN_WOOFER, + SND_MIXER_SCHN_SIDE_LEFT, + SND_MIXER_SCHN_SIDE_RIGHT, +#if POSITION_MASK_CHANNELS > 8 +#error "Extend alsa_channel_positions[] array (9+)" +#endif +}; + static void setting_free(pa_alsa_setting *s) { pa_assert(s); @@ -1767,7 +1781,11 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) { if (is_mono) { e->n_channels = 1; - if (!e->override_map) { + if ((e->override_map & (1 << (e->n_channels-1))) && e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] == 0) { + pa_log_warn("Override map for mono element %s is invalid, ignoring override map", e->path->name); + e->override_map &= ~(1 << (e->n_channels-1)); + } + if (!(e->override_map & (1 << (e->n_channels-1)))) { for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) { if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN) continue; @@ -1794,24 +1812,25 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) { alsa_id_str(buf, sizeof(buf), &e->alsa_id); pa_log_warn("Volume element %s with no channels?", buf); return false; - } else if (e->n_channels > 2) { + } else if (e->n_channels > POSITION_MASK_CHANNELS) { /* FIXME: In some places code like this is used: * * e->masks[alsa_channel_ids[p]][e->n_channels-1] * * The definition of e->masks is * - * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2]; + * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][POSITION_MASK_CHANNELS]; * - * Since the array size is fixed at 2, we obviously - * don't support elements with more than two + * Since the array size is fixed at POSITION_MASK_CHANNELS, we obviously + * don't support elements with more than POSITION_MASK_CHANNELS * channels... */ alsa_id_str(buf, sizeof(buf), &e->alsa_id); pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", buf, e->n_channels); return false; } - if (!e->override_map) { +retry: + if (!(e->override_map & (1 << (e->n_channels-1)))) { for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) { bool has_channel; @@ -1834,6 +1853,17 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) { e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1]; } + + if (e->merged_mask == 0) { + if (!(e->override_map & (1 << (e->n_channels-1)))) { + pa_log_warn("Channel map for element %s is invalid", e->path->name); + return false; + } + pa_log_warn("Override map for element %s has empty result, ignoring override map", e->path->name); + e->override_map &= ~(1 << (e->n_channels-1)); + goto retry; + } + return true; } @@ -2427,6 +2457,16 @@ static int element_parse_volume_limit(pa_config_parser_state *state) { return 0; } +static unsigned int parse_channel_position(const char *m) +{ + pa_channel_position_t p; + + if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID) + return SND_MIXER_SCHN_UNKNOWN; + + return alsa_channel_ids[p]; +} + static pa_channel_position_mask_t parse_mask(const char *m) { pa_channel_position_mask_t v; @@ -2464,7 +2504,9 @@ static int element_parse_override_map(pa_config_parser_state *state) { pa_alsa_path *p; pa_alsa_element *e; const char *split_state = NULL; + char *s; unsigned i = 0; + int channel_count = 0; char *n; pa_assert(state); @@ -2476,31 +2518,60 @@ static int element_parse_override_map(pa_config_parser_state *state) { return -1; } + s = strstr(state->lvalue, "."); + if (s) { + pa_atoi(s + 1, &channel_count); + if (channel_count < 1 || channel_count > POSITION_MASK_CHANNELS) { + pa_log("[%s:%u] Override map index '%s' invalid in '%s'", state->filename, state->lineno, state->lvalue, state->section); + return 0; + } + } else { + pa_log("[%s:%u] Invalid override map syntax '%s' in '%s'", state->filename, state->lineno, state->lvalue, state->section); + return -1; + } + while ((n = pa_split(state->rvalue, ",", &split_state))) { pa_channel_position_mask_t m; + snd_mixer_selem_channel_id_t channel_position; + + if (i >= (unsigned)channel_count) { + pa_log("[%s:%u] Invalid override map size (>%d) in '%s'", state->filename, state->lineno, channel_count, state->section); + return -1; + } + channel_position = alsa_channel_positions[i]; if (!*n) m = 0; else { - if ((m = parse_mask(n)) == 0) { - pa_log("[%s:%u] Override map '%s' invalid in '%s'", state->filename, state->lineno, n, state->section); + s = strstr(n, ":"); + if (s) { + *s = '\0'; + s++; + channel_position = parse_channel_position(n); + if (channel_position == SND_MIXER_SCHN_UNKNOWN) { + pa_log("[%s:%u] Override map position '%s' invalid in '%s'", state->filename, state->lineno, n, state->section); + pa_xfree(n); + return -1; + } + } + if ((m = parse_mask(s ? s : n)) == 0) { + pa_log("[%s:%u] Override map '%s' invalid in '%s'", state->filename, state->lineno, s ? s : n, state->section); pa_xfree(n); return -1; } } - if (pa_streq(state->lvalue, "override-map.1")) - e->masks[i++][0] = m; - else - e->masks[i++][1] = m; - - /* Later on we might add override-map.3 and so on here ... */ - + if (e->masks[channel_position][channel_count-1]) { + pa_log("[%s:%u] Override map '%s' duplicate position '%s' in '%s'", state->filename, state->lineno, s ? s : n, snd_mixer_selem_channel_name(channel_position), state->section); + pa_xfree(n); + return -1; + } + e->override_map |= (1 << (channel_count - 1)); + e->masks[channel_position][channel_count-1] = m; pa_xfree(n); + i++; } - e->override_map = true; - return 0; } @@ -2856,6 +2927,15 @@ pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa { "enumeration", element_parse_enumeration, NULL, NULL }, { "override-map.1", element_parse_override_map, NULL, NULL }, { "override-map.2", element_parse_override_map, NULL, NULL }, + { "override-map.3", element_parse_override_map, NULL, NULL }, + { "override-map.4", element_parse_override_map, NULL, NULL }, + { "override-map.5", element_parse_override_map, NULL, NULL }, + { "override-map.6", element_parse_override_map, NULL, NULL }, + { "override-map.7", element_parse_override_map, NULL, NULL }, + { "override-map.8", element_parse_override_map, NULL, NULL }, +#if POSITION_MASK_CHANNELS > 8 +#error "Add override-map.9+ definitions" +#endif /* ... later on we might add override-map.3 and so on here ... */ { "required", element_parse_required, NULL, NULL }, { "required-any", element_parse_required, NULL, NULL }, @@ -3227,7 +3307,7 @@ void pa_alsa_element_dump(pa_alsa_element *e) { pa_assert(e); alsa_id_str(buf, sizeof(buf), &e->alsa_id); - pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s", + pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%02x", buf, e->direction, e->switch_use, @@ -3239,7 +3319,7 @@ void pa_alsa_element_dump(pa_alsa_element *e) { e->required_absent, (long long unsigned) e->merged_mask, e->n_channels, - pa_yes_no(e->override_map)); + e->override_map); PA_LLIST_FOREACH(o, e->options) pa_alsa_option_dump(o); diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h index 905e312..8151955 100644 --- a/src/modules/alsa/alsa-mixer.h +++ b/src/modules/alsa/alsa-mixer.h @@ -50,6 +50,8 @@ typedef struct pa_alsa_port_data pa_alsa_port_data; #include "alsa-util.h" #include "alsa-ucm.h" +#define POSITION_MASK_CHANNELS 8 + typedef enum pa_alsa_switch_use { PA_ALSA_SWITCH_IGNORE, PA_ALSA_SWITCH_MUTE, /* make this switch follow mute status */ @@ -152,7 +154,7 @@ struct pa_alsa_element { long constant_volume; - bool override_map:1; + unsigned int override_map; bool direction_try_other:1; bool has_dB:1; @@ -160,7 +162,7 @@ struct pa_alsa_element { long volume_limit; /* -1 for no configured limit */ double min_dB, max_dB; - pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2]; + pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][POSITION_MASK_CHANNELS]; unsigned n_channels; pa_channel_position_mask_t merged_mask;