mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-26 07:00:07 -05:00
alsa: Implement reconfiguration of channel count/map
This is needed at the moment for passthrough support of high bitrate formats (such as DTS-HD and Dolby TrueHD), which, in addition to setting a higher sample rate also set channel count to 8. It would be clunky to require users to reset the HDMI profile based on the format being played, so for consistency we continue to only allow setting formats on the stereo profile, and reconfigure channels as needed for the duration of pakssthrough playback. We implement the source side as well for consistency.
This commit is contained in:
parent
4693028b9b
commit
628c0c0b74
4 changed files with 111 additions and 0 deletions
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue