]> git.sesse.net Git - nageru/commitdiff
Add support for ALSA devices that have no channel maps.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Thu, 18 Aug 2016 23:12:08 +0000 (01:12 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 19 Oct 2016 22:55:44 +0000 (00:55 +0200)
In particular, my Focusrite Scarlett 18i6 presents no such map,
and thus, we'll need to get number of channels some other way.
(For good measure, it refuses to capture unless you ask for at least
14 of the 18 channels.)

alsa_input.cpp
alsa_input.h

index bfadacb142734cd746fd6a1788d7838fbeb0e8f4..87adb4ab2427c705a1a1682b9fad24ba7e309abf 100644 (file)
@@ -6,21 +6,14 @@ using namespace std;
 ALSAInput::ALSAInput(const char *device, unsigned sample_rate, unsigned num_channels, audio_callback_t audio_callback)
        : device(device), sample_rate(sample_rate), num_channels(num_channels), audio_callback(audio_callback)
 {
-       die_on_error(device, snd_pcm_open(&pcm_handle,device, SND_PCM_STREAM_CAPTURE, 0));
+       die_on_error(device, snd_pcm_open(&pcm_handle, device, SND_PCM_STREAM_CAPTURE, 0));
 
        // Set format.
        snd_pcm_hw_params_t *hw_params;
        snd_pcm_hw_params_alloca(&hw_params);
-       die_on_error("snd_pcm_hw_params_any()", snd_pcm_hw_params_any(pcm_handle, hw_params));
-       die_on_error("snd_pcm_hw_params_set_access()", snd_pcm_hw_params_set_access(pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED));
-       snd_pcm_format_mask_t *format_mask;
-       snd_pcm_format_mask_alloca(&format_mask);
-       snd_pcm_format_mask_set(format_mask, SND_PCM_FORMAT_S16_LE);
-       snd_pcm_format_mask_set(format_mask, SND_PCM_FORMAT_S24_LE);
-       snd_pcm_format_mask_set(format_mask, SND_PCM_FORMAT_S32_LE);
-       die_on_error("snd_pcm_hw_params_set_format()", snd_pcm_hw_params_set_format_mask(pcm_handle, hw_params, format_mask));
-       die_on_error("snd_pcm_hw_params_set_rate_near()", snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params, &sample_rate, 0));
-       die_on_error("snd_pcm_hw_params_set_channels()", snd_pcm_hw_params_set_channels(pcm_handle, hw_params, num_channels));
+       if (!set_base_params(device, pcm_handle, hw_params, &sample_rate)) {
+               exit(1);
+       }
 
        die_on_error("snd_pcm_hw_params_set_channels()", snd_pcm_hw_params_set_channels(pcm_handle, hw_params, num_channels));
 
@@ -147,6 +140,37 @@ void ALSAInput::die_on_error(const char *func_name, int err)
        }
 }
 
+bool ALSAInput::set_base_params(const char *device_name, snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hw_params, unsigned *sample_rate)
+{
+       int err;
+       err = snd_pcm_hw_params_any(pcm_handle, hw_params);
+       if (err < 0) {
+               fprintf(stderr, "[%s] snd_pcm_hw_params_any(): %s\n", device_name, snd_strerror(err));
+               return false;
+       }
+       err = snd_pcm_hw_params_set_access(pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
+       if (err < 0) {
+               fprintf(stderr, "[%s] snd_pcm_hw_params_set_access(): %s\n", device_name, snd_strerror(err));
+               return false;
+       }
+       snd_pcm_format_mask_t *format_mask;
+       snd_pcm_format_mask_alloca(&format_mask);
+       snd_pcm_format_mask_set(format_mask, SND_PCM_FORMAT_S16_LE);
+       snd_pcm_format_mask_set(format_mask, SND_PCM_FORMAT_S24_LE);
+       snd_pcm_format_mask_set(format_mask, SND_PCM_FORMAT_S32_LE);
+       err = snd_pcm_hw_params_set_format_mask(pcm_handle, hw_params, format_mask);
+       if (err < 0) {
+               fprintf(stderr, "[%s] snd_pcm_hw_params_set_format_mask(): %s\n", device_name, snd_strerror(err));
+               return false;
+       }
+       err = snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params, sample_rate, 0);
+       if (err < 0) {
+               fprintf(stderr, "[%s] snd_pcm_hw_params_set_rate_near(): %s\n", device_name, snd_strerror(err));
+               return false;
+       }
+       return true;
+}
+
 vector<ALSAInput::Device> ALSAInput::enumerate_devices()
 {
        vector<Device> ret;
@@ -182,15 +206,43 @@ vector<ALSAInput::Device> ALSAInput::enumerate_devices()
 
                        snprintf(address, sizeof(address), "hw:%d,%d", card_index, dev_index);
 
+                       unsigned num_channels = 0;
+
                        // Find all channel maps for this device, and pick out the one
                        // with the most channels.
                        snd_pcm_chmap_query_t **cmaps = snd_pcm_query_chmaps_from_hw(card_index, dev_index, 0, SND_PCM_STREAM_CAPTURE);
-                       unsigned num_channels = 0;
-                       for (snd_pcm_chmap_query_t **ptr = cmaps; *ptr; ++ptr) {
-                               num_channels = max(num_channels, (*ptr)->map.channels);
+                       if (cmaps != nullptr) {
+                               for (snd_pcm_chmap_query_t **ptr = cmaps; *ptr; ++ptr) {
+                                       num_channels = max(num_channels, (*ptr)->map.channels);
+                               }
+                               snd_pcm_free_chmaps(cmaps);
+                       }
+                       if (num_channels == 0) {
+                               // Device had no channel maps. We need to open it to query.
+                               snd_pcm_t *pcm_handle;
+                               int err = snd_pcm_open(&pcm_handle, address, SND_PCM_STREAM_CAPTURE, 0);
+                               if (err < 0) {
+                                       // TODO: When we go to hotplug support, we should support some
+                                       // retry here, as the device could legitimately be busy.
+                                       printf("%s: %s\n", address, snd_strerror(err));
+                                       continue;
+                               }
+                               snd_pcm_hw_params_t *hw_params;
+                               snd_pcm_hw_params_alloca(&hw_params);
+                               unsigned sample_rate;
+                               if (!set_base_params(address, pcm_handle, hw_params, &sample_rate)) {
+                                       snd_pcm_close(pcm_handle);
+                                       continue;
+                               }
+                               err = snd_pcm_hw_params_get_channels_max(hw_params, &num_channels);
+                               if (err < 0) {
+                                       fprintf(stderr, "[%s] snd_pcm_hw_params_get_channels_max(): %s\n",
+                                               address, snd_strerror(err));
+                                       snd_pcm_close(pcm_handle);
+                                       continue;
+                               }
+                               snd_pcm_close(pcm_handle);
                        }
-
-                       snd_pcm_free_chmaps(cmaps);
 
                        if (num_channels == 0) {
                                printf("%s: No channel maps with channels\n", address);
index 895760fed8fef76737e341e44e188267db7c9692..84e46b0887707dd6b273419d29cf2fada00c43d7 100644 (file)
@@ -46,6 +46,7 @@ private:
        void capture_thread_func();
        int64_t frames_to_pts(uint64_t n) const;
        void die_on_error(const char *func_name, int err);
+       static bool set_base_params(const char *device, snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hw_params, unsigned *sample_rate);
 
        std::string device;
        unsigned sample_rate, num_channels, num_periods;