From 5892403b01ad66413f5225766aab9eea648692cc Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 16 Feb 2021 12:26:51 +0100 Subject: [PATCH] acp: add a Pro Audio profile The Pro Audio profile exposes all devices and subdevices with maximum channel count and no channel layout. It also have no hardware volume and is more suited for Pro Audio usage. See #731 #704 #57 --- spa/plugins/alsa/acp/acp.c | 127 ++++++++++++++++++++++++++++++ spa/plugins/alsa/acp/channelmap.h | 11 +++ 2 files changed, 138 insertions(+) diff --git a/spa/plugins/alsa/acp/acp.c b/spa/plugins/alsa/acp/acp.c index bc5cfeb55..816875e8c 100644 --- a/spa/plugins/alsa/acp/acp.c +++ b/spa/plugins/alsa/acp/acp.c @@ -266,6 +266,131 @@ static void profile_free(void *data) } } +static int add_pro_profile(pa_card *impl, uint32_t index) +{ + snd_ctl_t *ctl_hndl; + int err, dev; + pa_alsa_profile *ap; + pa_alsa_profile_set *ps = impl->profile_set; + pa_alsa_mapping *m; + char *device; + snd_pcm_info_t *pcminfo; + pa_sample_spec ss; + snd_pcm_uframes_t try_period_size, try_buffer_size; + + ss.format = PA_SAMPLE_S32LE; + ss.rate = 48000; + ss.channels = 64; + + ap = pa_xnew0(pa_alsa_profile, 1); + ap->profile_set = ps; + ap->profile.name = ap->name = pa_xstrdup("pro-audio"); + ap->profile.description = ap->description = pa_xstrdup(_("Pro Audio")); + ap->profile.available = ACP_AVAILABLE_YES; + ap->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + ap->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + pa_hashmap_put(ps->profiles, ap->name, ap); + + ap->output_name = pa_xstrdup("pro-output"); + ap->input_name = pa_xstrdup("pro-input"); + ap->priority = 1; + + asprintf(&device, "hw:%d", index); + + if ((err = snd_ctl_open(&ctl_hndl, device, 0)) < 0) { + pa_log_error("can't open control for card %s: %s", + device, snd_strerror(err)); + return err; + } + + snd_pcm_info_alloca(&pcminfo); + + dev = -1; + while (1) { + if ((err = snd_ctl_pcm_next_device(ctl_hndl, &dev)) < 0) { + pa_log_error("error iterating devices: %s", snd_strerror(err)); + break; + } + if (dev < 0) + break; + + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + + snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK); + if ((err = snd_ctl_pcm_info(ctl_hndl, pcminfo)) < 0) { + if (err != -ENOENT) + pa_log_error("error pcm info: %s", snd_strerror(err)); + } + if (err >= 0) { + char *devstr, *name, *desc; + asprintf(&devstr, "hw:%d,%d", index, dev); + asprintf(&name, "Mapping pro-output-%d", dev); + asprintf(&desc, "Pro Output %d", dev); + m = pa_alsa_mapping_get(ps, name); + m->description = desc; + m->device_strings = pa_split_spaces_strv(devstr); + + try_period_size = 1024; + try_buffer_size = 1024 * 64; + m->sample_spec = ss; + + if ((m->output_pcm = pa_alsa_open_by_template(m->device_strings, + devstr, NULL, &m->sample_spec, + &m->channel_map, SND_PCM_STREAM_PLAYBACK, + &try_period_size, &try_buffer_size, + 0, NULL, NULL, false))) { + pa_alsa_init_proplist_pcm(NULL, m->output_proplist, m->output_pcm); + snd_pcm_close(m->output_pcm); + m->output_pcm = NULL; + m->supported = true; + pa_channel_map_init_pro(&m->channel_map, m->sample_spec.channels); + } + pa_idxset_put(ap->output_mappings, m, NULL); + free(name); + free(devstr); + } + + snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_CAPTURE); + if ((err = snd_ctl_pcm_info(ctl_hndl, pcminfo)) < 0) { + if (err != -ENOENT) + pa_log_error("error pcm info: %s", snd_strerror(err)); + } + if (err >= 0) { + char *devstr, *name, *desc; + asprintf(&devstr, "hw:%d,%d", index, dev); + asprintf(&name, "Mapping pro-input-%d", dev); + asprintf(&desc, "Mapping Pro Input %d", dev); + m = pa_alsa_mapping_get(ps, name); + m->description = desc; + m->device_strings = pa_split_spaces_strv(devstr); + + try_period_size = 1024; + try_buffer_size = 1024 * 64; + m->sample_spec = ss; + + if ((m->input_pcm = pa_alsa_open_by_template(m->device_strings, + devstr, NULL, &m->sample_spec, + &m->channel_map, SND_PCM_STREAM_CAPTURE, + &try_period_size, &try_buffer_size, + 0, NULL, NULL, false))) { + pa_alsa_init_proplist_pcm(NULL, m->input_proplist, m->input_pcm); + snd_pcm_close(m->input_pcm); + m->input_pcm = NULL; + m->supported = true; + pa_channel_map_init_pro(&m->channel_map, m->sample_spec.channels); + } + pa_idxset_put(ap->input_mappings, m, NULL); + free(devstr); + free(name); + } + } + snd_ctl_close(ctl_hndl); + + return 0; +} + + static void add_profiles(pa_card *impl) { pa_alsa_profile *ap; @@ -286,6 +411,8 @@ static void add_profiles(pa_card *impl) ap->profile.flags = ACP_PROFILE_OFF; pa_hashmap_put(impl->profiles, ap->name, ap); + add_pro_profile(impl, impl->card.index); + PA_HASHMAP_FOREACH(ap, impl->profile_set->profiles, state) { pa_alsa_mapping *m; diff --git a/spa/plugins/alsa/acp/channelmap.h b/spa/plugins/alsa/acp/channelmap.h index bb713b5d3..47f964aa5 100644 --- a/spa/plugins/alsa/acp/channelmap.h +++ b/spa/plugins/alsa/acp/channelmap.h @@ -201,6 +201,17 @@ static inline pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, return NULL; } +static inline pa_channel_map* pa_channel_map_init_pro(pa_channel_map *m, + unsigned channels) +{ + unsigned i; + pa_channel_map_init(m); + for (i = 0; i < channels; i++) + m->map[i] = PA_CHANNEL_POSITION_INVALID; + m->channels = (uint8_t) channels; + return m; +} + typedef uint64_t pa_channel_position_mask_t; #define PA_CHANNEL_POSITION_MASK(f) ((pa_channel_position_mask_t) (1ULL << (f)))