diff --git a/spa/plugins/alsa/alsa-pcm-sink.c b/spa/plugins/alsa/alsa-pcm-sink.c index a79fe8bd4..88e407240 100644 --- a/spa/plugins/alsa/alsa-pcm-sink.c +++ b/spa/plugins/alsa/alsa-pcm-sink.c @@ -644,6 +644,7 @@ static int port_set_format(void *object, return 0; spa_log_debug(this->log, "clear format"); + this->card->format_ref--; spa_alsa_pause(this); clear_buffers(this); spa_alsa_close(this); @@ -1020,6 +1021,8 @@ impl_init(const struct spa_handle_factory *factory, this->disable_batch = spa_atob(s); } else if (spa_streq(k, "api.alsa.use-chmap")) { this->props.use_chmap = spa_atob(s); + } else if (spa_streq(k, "api.alsa.multi-rate")) { + this->multi_rate = spa_atob(s); } } diff --git a/spa/plugins/alsa/alsa-pcm-source.c b/spa/plugins/alsa/alsa-pcm-source.c index d5f440bf1..db12467b0 100644 --- a/spa/plugins/alsa/alsa-pcm-source.c +++ b/spa/plugins/alsa/alsa-pcm-source.c @@ -586,6 +586,8 @@ static int port_set_format(void *object, if (!this->have_format) return 0; + spa_log_debug(this->log, "clear format"); + this->card->format_ref--; spa_alsa_pause(this); clear_buffers(this); spa_alsa_close(this); @@ -960,6 +962,8 @@ impl_init(const struct spa_handle_factory *factory, this->disable_batch = spa_atob(s); } else if (spa_streq(k, "api.alsa.use-chmap")) { this->props.use_chmap = spa_atob(s); + } else if (spa_streq(k, "api.alsa.multi-rate")) { + this->multi_rate = spa_atob(s); } } return spa_alsa_init(this); diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index 1fc15c125..e39cadce3 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -16,14 +16,6 @@ static struct spa_list cards = SPA_LIST_INIT(&cards); -struct card { - struct spa_list link; - int ref; - uint32_t index; - snd_use_case_mgr_t *ucm; - char *ucm_prefix; -}; - static struct card *find_card(uint32_t index) { struct card *c; @@ -36,7 +28,7 @@ static struct card *find_card(uint32_t index) return NULL; } -static struct card *ensure_card(uint32_t index) +static struct card *ensure_card(uint32_t index, bool ucm) { struct card *c; char card_name[64]; @@ -50,25 +42,26 @@ static struct card *ensure_card(uint32_t index) c->ref = 1; c->index = index; - snprintf(card_name, sizeof(card_name), "hw:%i", index); - err = snd_use_case_mgr_open(&c->ucm, card_name); - if (err < 0) { - char *name; - err = snd_card_get_name(index, &name); - if (err < 0) - goto error; - - snprintf(card_name, sizeof(card_name), "%s", name); - free(name); - + if (ucm) { + snprintf(card_name, sizeof(card_name), "hw:%i", index); err = snd_use_case_mgr_open(&c->ucm, card_name); - if (err < 0) - goto error; - } - if ((snd_use_case_get(c->ucm, "_alibpref", &alibpref) != 0)) - alibpref = NULL; - c->ucm_prefix = (char*)alibpref; + if (err < 0) { + char *name; + err = snd_card_get_name(index, &name); + if (err < 0) + goto error; + snprintf(card_name, sizeof(card_name), "%s", name); + free(name); + + err = snd_use_case_mgr_open(&c->ucm, card_name); + if (err < 0) + goto error; + } + if ((snd_use_case_get(c->ucm, "_alibpref", &alibpref) != 0)) + alibpref = NULL; + c->ucm_prefix = (char*)alibpref; + } spa_list_append(&cards, &c->link); return c; @@ -88,8 +81,10 @@ static void release_card(uint32_t index) return; spa_list_remove(&c->link); - free(c->ucm_prefix); - snd_use_case_mgr_close(c->ucm); + if (c->ucm) { + free(c->ucm_prefix); + snd_use_case_mgr_close(c->ucm); + } free(c); } @@ -103,23 +98,18 @@ int spa_alsa_init(struct state *state) state->iec958_codecs |= 1ULL << SPA_AUDIO_IEC958_CODEC_PCM; } - if (state->open_ucm) { - struct card *c; - - c = ensure_card(state->card_index); - if (c == NULL) { - spa_log_error(state->log, "UCM not available for card %d", state->card_index); - return -errno; - } - state->ucm_prefix = c->ucm_prefix; + state->card = ensure_card(state->card_index, state->open_ucm); + if (state->card == NULL) { + spa_log_error(state->log, "can't create card %d", state->card_index); + return -errno; } return 0; } int spa_alsa_clear(struct state *state) { - state->ucm_prefix = NULL; release_card(state->card_index); + state->card = NULL; return 0; } @@ -138,7 +128,7 @@ int spa_alsa_open(struct state *state, const char *params) CHECK(snd_output_stdio_attach(&state->output, stderr, 0), "attach failed"); spa_scnprintf(device_name, sizeof(device_name), "%s%s%s", - state->ucm_prefix ? state->ucm_prefix : "", + state->card->ucm_prefix ? state->card->ucm_prefix : "", props->device, params ? params : ""); spa_log_info(state->log, "%p: ALSA device open '%s' %s", state, device_name, @@ -163,10 +153,10 @@ int spa_alsa_open(struct state *state, const char *params) /* we would love to use the sync_id but it always returns 0, so use the * card id for now */ - state->card = snd_pcm_info_get_card(pcminfo); + state->pcm_card = snd_pcm_info_get_card(pcminfo); if (state->clock) { snprintf(state->clock->name, sizeof(state->clock->name), - "api.alsa.%d", state->card); + "api.alsa.%d", state->pcm_card); } state->opened = true; state->sample_count = 0; @@ -385,11 +375,15 @@ static int add_rate(struct state *state, uint32_t scale, bool all, uint32_t inde CHECK(snd_pcm_hw_params_get_rate_min(params, &min, &dir), "get_rate_min"); CHECK(snd_pcm_hw_params_get_rate_max(params, &max, &dir), "get_rate_max"); - if (state->default_rate != 0 && !all) { - if (min < state->default_rate) - min = state->default_rate; - if (max > state->default_rate) - max = state->default_rate; + rate = state->default_rate; + if (!state->multi_rate && state->card->format_ref > 0) + rate = state->card->rate; + + if (rate != 0 && !all) { + if (min < rate) + min = rate; + if (max > rate) + max = rate; } spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0); @@ -997,6 +991,14 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_ match = false; } + if (!state->multi_rate && + state->card->format_ref > 0 && + state->card->rate != rrate) { + spa_log_error(state->log, "%p: card already opened at rate:%i", + state, state->card->rate); + return -EINVAL; + } + /* set the stream rate */ val = rrate; CHECK(snd_pcm_hw_params_set_rate_near(hndl, params, &val, 0), "set_rate_near"); @@ -1020,6 +1022,9 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_ else state->frame_size *= rchannels; + if (state->card->format_ref++ == 0) + state->card->rate = rrate; + dir = 0; period_size = state->default_period_size ? state->default_period_size : 1024; is_batch = snd_pcm_hw_params_is_batch(params) && @@ -1351,9 +1356,9 @@ static int setup_matching(struct state *state) if (state->position == NULL) return -ENOTSUP; - spa_log_debug(state->log, "clock:%s card:%d", state->position->clock.name, state->card); + spa_log_debug(state->log, "clock:%s card:%d", state->position->clock.name, state->pcm_card); if (sscanf(state->position->clock.name, "api.alsa.%d", &card) == 1 && - card == state->card) { + card == state->pcm_card) { state->matching = false; } state->resample = ((uint32_t)state->rate != state->rate_denom) || state->matching; diff --git a/spa/plugins/alsa/alsa-pcm.h b/spa/plugins/alsa/alsa-pcm.h index d2a2210d6..b1f294a03 100644 --- a/spa/plugins/alsa/alsa-pcm.h +++ b/spa/plugins/alsa/alsa-pcm.h @@ -88,6 +88,18 @@ struct channel_map { uint32_t channels; uint32_t pos[SPA_AUDIO_MAX_CHANNELS]; }; + + +struct card { + struct spa_list link; + int ref; + uint32_t index; + snd_use_case_mgr_t *ucm; + char *ucm_prefix; + int format_ref; + uint32_t rate; +}; + struct state { struct spa_handle handle; struct spa_node node; @@ -97,6 +109,7 @@ struct state { struct spa_loop *data_loop; int card_index; + struct card *card; snd_pcm_stream_t stream; snd_output_t *output; @@ -115,7 +128,7 @@ struct state { bool opened; snd_pcm_t *hndl; - int card; + int pcm_card; bool have_format; struct spa_audio_info current_format; @@ -188,6 +201,7 @@ struct state { unsigned int open_ucm:1; unsigned int is_iec958:1; unsigned int is_hdmi:1; + unsigned int multi_rate:1; uint64_t iec958_codecs; @@ -205,8 +219,6 @@ struct state { struct spa_latency_info latency[2]; struct spa_process_latency_info process_latency; - - const char *ucm_prefix; }; int