spa: acp: allow also too few channels in SplitPCM configs

GoXLR Mini has different numbers of channels actually available (21, 23,
or 25) depending on its firmware/etc, but its UCM profile specifies
always 23. The count can then be bigger or smaller than what is actually
available.

Fail a bit more gracefully in the case of too few channels: create all
the split devices specified by the profile. The channels that aren't
actually available in HW just won't get routed anywhere.

ALSA upstream IIUC is saying that the channel counts should be fixed, so
spew warnings that say the UCM profiles are wrong if they look wrong.
This commit is contained in:
Pauli Virtanen 2025-03-12 18:40:14 +02:00 committed by Wim Taymans
parent e16c184228
commit ba3a36b3d1

View file

@ -353,6 +353,15 @@ static pa_alsa_ucm_split *ucm_get_split_channels(pa_alsa_ucm_device *device, snd
const char *device_name; const char *device_name;
int i; int i;
uint32_t hw_channels; uint32_t hw_channels;
const char *pcm_name;
const char *rule_name;
if (spa_streq(prefix, "Playback"))
pcm_name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SINK);
else
pcm_name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SOURCE);
if (!pcm_name)
pcm_name = "";
device_name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME); device_name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME);
if (!device_name) if (!device_name)
@ -373,15 +382,20 @@ static pa_alsa_ucm_split *ucm_get_split_channels(pa_alsa_ucm_device *device, snd
break; break;
if (idx >= hw_channels) if (idx >= hw_channels)
goto fail; pa_log_notice("Error in ALSA UCM profile for %s (%s): %sChannel%d=%d >= %sChannels=%d",
pcm_name, device_name, prefix, i, idx, prefix, hw_channels);
value = ucm_get_string(uc_mgr, "%sChannelPos%d/%s", prefix, i, device_name); value = ucm_get_string(uc_mgr, "%sChannelPos%d/%s", prefix, i, device_name);
if (!value) if (!value) {
rule_name = "ChannelPos";
goto fail; goto fail;
}
map = snd_pcm_chmap_parse_string(value); map = snd_pcm_chmap_parse_string(value);
if (!map) if (!map) {
rule_name = "ChannelPos value";
goto fail; goto fail;
}
if (map->channels == 1) { if (map->channels == 1) {
pa_log_debug("Split %s channel %d -> device %s channel %d: %s (%d)", pa_log_debug("Split %s channel %d -> device %s channel %d: %s (%d)",
@ -391,6 +405,7 @@ static pa_alsa_ucm_split *ucm_get_split_channels(pa_alsa_ucm_device *device, snd
free(map); free(map);
} else { } else {
free(map); free(map);
rule_name = "channel map parsing";
goto fail; goto fail;
} }
} }
@ -405,7 +420,7 @@ static pa_alsa_ucm_split *ucm_get_split_channels(pa_alsa_ucm_device *device, snd
return split; return split;
fail: fail:
pa_log_warn("Invalid SplitPCM ALSA UCM rule for device %s", device_name); pa_log_warn("Invalid SplitPCM ALSA UCM %s for device %s (%s)", rule_name, pcm_name, device_name);
pa_xfree(split); pa_xfree(split);
return NULL; return NULL;
} }
@ -2422,21 +2437,25 @@ static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m,
if (pcm) { if (pcm) {
if (m->split) { if (m->split) {
if (try_map.channels < m->split->hw_channels) { const char *mode_name = mode == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture";
pa_alsa_close(&pcm);
pa_logl((max_channels ? PA_LOG_WARN : PA_LOG_DEBUG), if (try_map.channels < m->split->hw_channels) {
"Too few channels in %s for ALSA UCM SplitPCM: avail %d < required %d", pa_logl((max_channels ? PA_LOG_NOTICE : PA_LOG_DEBUG),
m->device_strings[0], try_map.channels, m->split->hw_channels); "Error in ALSA UCM profile for %s (%s): %sChannels=%d > avail %d",
m->device_strings[0], m->name, mode_name, m->split->hw_channels, try_map.channels);
/* Retry with max channel count, in case ALSA rounded down */ /* Retry with max channel count, in case ALSA rounded down */
if (!max_channels) if (!max_channels) {
pa_alsa_close(&pcm);
return mapping_open_pcm(ucm, m, mode, true); return mapping_open_pcm(ucm, m, mode, true);
}
return NULL; /* Just accept whatever we got... Some of the routings won't get connected
* anywhere */
m->split->hw_channels = try_map.channels;
} else if (try_map.channels > m->split->hw_channels) { } else if (try_map.channels > m->split->hw_channels) {
pa_log_debug("Update split PCM channel count for %s: %d -> %d", pa_log_notice("Error in ALSA UCM profile for %s (%s): %sChannels=%d < avail %d",
m->device_strings[0], m->split->hw_channels, try_map.channels); m->device_strings[0], m->name, mode_name, m->split->hw_channels, try_map.channels);
m->split->hw_channels = try_map.channels; m->split->hw_channels = try_map.channels;
} }
} else if (!exact_channels) { } else if (!exact_channels) {