[PATCH 2/2] alsa: Add support for channel reconfiguration

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Linux Audio Users]     [AMD Graphics]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux