[pulseaudio-discuss] [PATCH 2/2] alsa: Add support for channel reconfiguration
Arun Raghavan
arun at arunraghavan.net
Sun May 20 06:28:59 UTC 2018
This is needed for supporting high-bitrate formats in passthrough mode.
The alsa-source reconfiguration path is untested at the moment as I do
not have hardware that can support multiple channel configurations for
capture.
This patch is not complete, because setting a channel count that results
in a frame size that does not exactly divide the h/w buffer size results
in a failure during unsuspend() -- the buffer and period count that we
get are necessarily lower than the total buffer / period size.
---
src/modules/alsa/alsa-sink.c | 43 ++++++++++++++++++++++++++++------
src/modules/alsa/alsa-source.c | 43 ++++++++++++++++++++++++++++------
src/modules/alsa/alsa-util.c | 38 ++++++++++++++++++++++++++++++
src/modules/alsa/alsa-util.h | 1 +
4 files changed, 111 insertions(+), 14 deletions(-)
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index ed9e0a51c..56d4feafd 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -112,6 +112,7 @@ struct userdata {
pa_cvolume hardware_volume;
unsigned int *rates;
+ unsigned int *channels;
size_t
frame_size,
@@ -1670,30 +1671,49 @@ static bool sink_set_formats(pa_sink *s, pa_idxset *formats) {
return true;
}
-static int sink_reconfigure_cb(pa_sink *s, pa_sample_spec *spec, bool passthrough) {
+static int sink_reconfigure_cb(pa_sink *s, pa_sample_spec *spec, pa_channel_map *map, bool passthrough) {
struct userdata *u = s->userdata;
int i;
- bool supported = false;
-
- /* FIXME: we only update rate for now */
+ bool supported_rate = false, supported_channels = false;
pa_assert(u);
for (i = 0; u->rates[i]; i++) {
if (u->rates[i] == spec->rate) {
- supported = true;
+ supported_rate = true;
break;
}
}
- if (!supported) {
+ if (!supported_rate) {
pa_log_info("Sink does not support sample rate of %d Hz", spec->rate);
return -1;
}
+ for (i = 0; u->channels[i]; i++) {
+ if (u->channels[i] == spec->channels) {
+ supported_channels = true;
+ break;
+ }
+ }
+
+ if (!supported_channels) {
+ pa_log_info("Sink does not support channels: %d", spec->channels);
+ return -1;
+ }
+
if (!PA_SINK_IS_OPENED(s->state)) {
- pa_log_info("Updating rate for device %s, new rate is %d", u->device_name, spec->rate);
+ pa_log_info("Updating rate and channels for device %s, %d Hz, %d channels", u->device_name, spec->rate, spec->channels);
+
u->sink->sample_spec.rate = spec->rate;
+ u->sink->sample_spec.channels = spec->channels;
+ if (map)
+ u->sink->channel_map = *map;
+ else
+ pa_channel_map_init_auto(&u->sink->channel_map, u->sink->sample_spec.channels, PA_CHANNEL_MAP_ALSA);
+
+ u->frame_size = pa_frame_size(&u->sink->sample_spec);
+
return 0;
}
@@ -2345,6 +2365,12 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
goto fail;
}
+ u->channels = pa_alsa_get_supported_channels(u->pcm_handle, ss.channels);
+ if (!u->channels) {
+ pa_log_error("Failed to find any supported sample rates.");
+ goto fail;
+ }
+
/* ALSA might tweak the sample spec, so recalculate the frame size */
frame_size = pa_frame_size(&ss);
@@ -2616,6 +2642,9 @@ static void userdata_free(struct userdata *u) {
if (u->rates)
pa_xfree(u->rates);
+ if (u->channels)
+ pa_xfree(u->channels);
+
reserve_done(u);
monitor_done(u);
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 31d5bb321..fc8ddaf07 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -100,6 +100,7 @@ struct userdata {
pa_cvolume hardware_volume;
unsigned int *rates;
+ unsigned int *channels;
size_t
frame_size,
@@ -1466,30 +1467,49 @@ static void source_update_requested_latency_cb(pa_source *s) {
update_sw_params(u);
}
-static int source_reconfigure_cb(pa_source *s, pa_sample_spec *spec, bool passthrough) {
+static int source_reconfigure_cb(pa_source *s, pa_sample_spec *spec, pa_channel_map *map, bool passthrough) {
struct userdata *u = s->userdata;
int i;
- bool supported = false;
-
- /* FIXME: we only update rate for now */
+ bool supported_rate = false, supported_channels = false;
pa_assert(u);
for (i = 0; u->rates[i]; i++) {
if (u->rates[i] == spec->rate) {
- supported = true;
+ supported_rate = true;
break;
}
}
- if (!supported) {
+ if (!supported_rate) {
pa_log_info("Source does not support sample rate of %d Hz", spec->rate);
return -1;
}
+ for (i = 0; u->channels[i]; i++) {
+ if (u->channels[i] == spec->channels) {
+ supported_channels = true;
+ break;
+ }
+ }
+
+ if (!supported_channels) {
+ pa_log_info("Source does not support channels: %d", spec->channels);
+ return -1;
+ }
+
if (!PA_SOURCE_IS_OPENED(s->state)) {
- pa_log_info("Updating rate for device %s, new rate is %d", u->device_name, spec->rate);
+ pa_log_info("Updating rate and channels for device %s, %d Hz, %d channels", u->device_name, spec->rate, spec->channels);
+
u->source->sample_spec.rate = spec->rate;
+ u->source->sample_spec.channels = spec->channels;
+ if (map)
+ u->source->channel_map = *map;
+ else
+ pa_channel_map_init_auto(&u->source->channel_map, u->source->sample_spec.channels, PA_CHANNEL_MAP_ALSA);
+
+ u->frame_size = pa_frame_size(&u->source->sample_spec);
+
return 0;
}
@@ -2022,6 +2042,12 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
goto fail;
}
+ u->channels = pa_alsa_get_supported_channels(u->pcm_handle, ss.channels);
+ if (!u->channels) {
+ pa_log_error("Failed to find any supported sample rates.");
+ goto fail;
+ }
+
/* ALSA might tweak the sample spec, so recalculate the frame size */
frame_size = pa_frame_size(&ss);
@@ -2247,6 +2273,9 @@ static void userdata_free(struct userdata *u) {
if (u->rates)
pa_xfree(u->rates);
+ if (u->channels)
+ pa_xfree(u->channels);
+
reserve_done(u);
monitor_done(u);
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 61fb4903c..ea5743046 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1417,6 +1417,44 @@ unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm, unsigned int fallback_
return rates;
}
+unsigned int *pa_alsa_get_supported_channels(snd_pcm_t *pcm, unsigned int fallback_channels) {
+ snd_pcm_hw_params_t *hwparams;
+ unsigned int min, max;
+ unsigned int *channels;
+ unsigned int n, i;
+ 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;
+ }
+
+ if (snd_pcm_hw_params_get_channels_min(hwparams, &min) < 0 ||
+ snd_pcm_hw_params_get_channels_max(hwparams, &max) < 0 ||
+ min - max + 1 <= 0) {
+
+ pa_log_debug("Could not probe channel range");
+ return NULL;
+ }
+
+ channels = pa_xnew0(unsigned int, max - min + 1);
+
+ for (i = min, n = 0; i <= max; i++) {
+ if (snd_pcm_hw_params_test_channels(pcm, hwparams, i) == 0) {
+ pa_log_debug("Supported channel count: %d", i);
+ channels[n] = i;
+ n++;
+ }
+ }
+
+ if (n == 0)
+ channels[0] = fallback_channels;
+
+ 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 8345a0ba5..1fa2e3766 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -132,6 +132,7 @@ char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm);
char *pa_alsa_get_reserve_name(const char *device);
unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm, unsigned int fallback_rate);
+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);
--
2.17.0
More information about the pulseaudio-discuss
mailing list