diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 6ffdf6ea7..6f9a71888 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -124,6 +124,7 @@ struct userdata { pa_sample_spec verified_sample_spec; pa_sample_format_t *supported_formats; unsigned int *supported_rates; + unsigned int *supported_channels; struct { size_t fragment_size; size_t nfrags; @@ -1822,6 +1823,7 @@ static int sink_reconfigure_cb(pa_sink *s, pa_sample_spec *spec, pa_channel_map int i; bool format_supported = false; bool rate_supported = false; + bool channels_supported = false; #ifdef USE_SMOOTHER_2 pa_sample_spec effective_spec; #endif @@ -1875,6 +1877,27 @@ static int sink_reconfigure_cb(pa_sink *s, pa_sample_spec *spec, pa_channel_map pa_smoother_2_set_sample_spec(u->smoother, pa_rtclock_now(), &effective_spec); #endif + for (i = 0; u->supported_channels[i]; i++) { + if (u->supported_channels[i] == spec->channels) { + pa_sink_set_channels(u->sink, spec->channels); + channels_supported = true; + break; + } + } + + if (!channels_supported) { + pa_log_info("Sink does not support %u channels, set it to a verified value", spec->channels); + pa_sink_set_channels(u->sink, u->verified_sample_spec.channels); + } + + if (map) { + pa_sink_set_channel_map(s, map); + } else { + pa_channel_map def_map; + pa_channel_map_init_auto(&def_map, spec->channels, PA_CHANNEL_MAP_DEFAULT); + pa_sink_set_channel_map(s, &def_map); + } + /* Passthrough status change is handled during unsuspend */ return 0; @@ -2614,6 +2637,12 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca goto fail; } + u->supported_channels = pa_alsa_get_supported_channels(u->pcm_handle, ss.channels); + if (!u->supported_channels) { + pa_log_error("Failed to find any supported channel counts."); + goto fail; + } + /* ALSA might tweak the sample spec, so recalculate the frame size */ frame_size = pa_frame_size(&ss); @@ -2936,6 +2965,9 @@ static void userdata_free(struct userdata *u) { if (u->supported_rates) pa_xfree(u->supported_rates); + if (u->supported_channels) + pa_xfree(u->supported_channels); + reserve_done(u); monitor_done(u); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index ee2d04d9a..9be79d37d 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -112,6 +112,7 @@ struct userdata { pa_sample_spec verified_sample_spec; pa_sample_format_t *supported_formats; unsigned int *supported_rates; + unsigned int *supported_channels; struct { size_t fragment_size; size_t nfrags; @@ -1637,6 +1638,7 @@ static int source_reconfigure_cb(pa_source *s, pa_sample_spec *spec, pa_channel_ int i; bool format_supported = false; bool rate_supported = false; + bool channels_supported = false; #ifdef USE_SMOOTHER_2 pa_sample_spec effective_spec; #endif @@ -1690,6 +1692,27 @@ static int source_reconfigure_cb(pa_source *s, pa_sample_spec *spec, pa_channel_ pa_smoother_2_set_sample_spec(u->smoother, pa_rtclock_now(), &effective_spec); #endif + for (i = 0; u->supported_channels[i]; i++) { + if (u->supported_channels[i] == spec->channels) { + pa_source_set_channels(u->source, spec->channels); + channels_supported = true; + break; + } + } + + if (!channels_supported) { + pa_log_info("Sink does not support %u channels, set it to a verified value", spec->channels); + pa_source_set_channels(u->source, u->verified_sample_spec.channels); + } + + if (map) { + pa_source_set_channel_map(s, map); + } else { + pa_channel_map def_map; + pa_channel_map_init_auto(&def_map, spec->channels, PA_CHANNEL_MAP_DEFAULT); + pa_source_set_channel_map(s, &def_map); + } + return 0; } @@ -2294,6 +2317,12 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p goto fail; } + u->supported_channels = pa_alsa_get_supported_channels(u->pcm_handle, ss.channels); + if (!u->supported_channels) { + pa_log_error("Failed to find any supported channel counts."); + goto fail; + } + /* ALSA might tweak the sample spec, so recalculate the frame size */ frame_size = pa_frame_size(&ss); @@ -2555,6 +2584,9 @@ static void userdata_free(struct userdata *u) { if (u->supported_rates) pa_xfree(u->supported_rates); + if (u->supported_channels) + pa_xfree(u->supported_channels); + reserve_done(u); monitor_done(u); diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 9d464a8ac..d9aa8e329 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -1535,6 +1535,52 @@ pa_sample_format_t *pa_alsa_get_supported_formats(snd_pcm_t *pcm, pa_sample_form return formats; } +unsigned int *pa_alsa_get_supported_channels(snd_pcm_t *pcm, unsigned int fallback_channels) { + /* Index 0 is unused as "no channels" is meaningless */ + bool supported[PA_CHANNELS_MAX + 1] = { false, }; + snd_pcm_hw_params_t *hwparams; + unsigned int i, j, n, *channels = NULL; + int ret; + + snd_pcm_hw_params_alloca(&hwparams); + + if ((ret = snd_pcm_hw_params_any(pcm, hwparams)) < 0) { + pa_log_debug("snd_pcm_hw_params_any() failed: %s", pa_alsa_strerror(ret)); + return NULL; + } + + for (i = 1, n = 0; i <= PA_CHANNELS_MAX; i++) { + if (snd_pcm_hw_params_test_channels(pcm, hwparams, i) == 0) { + supported[i] = true; + n++; + } + } + + if (n > 0) { + channels = pa_xnew(unsigned int, n + 1); + + for (i = 1, j = 0; i <= PA_CHANNELS_MAX; i++) { + if (supported[i]) + channels[j++] = i; + } + + channels[j] = 0; + } else { + channels = pa_xnew(unsigned int, 2); + + channels[0] = fallback_channels; + if ((ret = snd_pcm_hw_params_set_channels_near(pcm, hwparams, &channels[0])) < 0) { + pa_log_debug("snd_pcm_hw_params_set_channels_near() failed: %s", pa_alsa_strerror(ret)); + pa_xfree(channels); + return NULL; + } + + channels[1] = 0; + } + + return channels; +} + bool pa_alsa_pcm_is_hw(snd_pcm_t *pcm) { snd_pcm_info_t* info; snd_pcm_info_alloca(&info); diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h index 2eed3eac3..50a049adc 100644 --- a/src/modules/alsa/alsa-util.h +++ b/src/modules/alsa/alsa-util.h @@ -140,6 +140,7 @@ char *pa_alsa_get_reserve_name(const char *device); unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm, unsigned int fallback_rate); pa_sample_format_t *pa_alsa_get_supported_formats(snd_pcm_t *pcm, pa_sample_format_t fallback_format); +unsigned int *pa_alsa_get_supported_channels(snd_pcm_t *pcm, unsigned int fallback_channels); bool pa_alsa_pcm_is_hw(snd_pcm_t *pcm); bool pa_alsa_pcm_is_modem(snd_pcm_t *pcm);