/* Called from IO context */
static int suspend(struct userdata *u) {
- const char *mod_name;
-
pa_assert(u);
pa_assert(u->pcm_handle);
snd_pcm_close(u->pcm_handle);
u->pcm_handle = NULL;
- if ((mod_name = pa_proplist_gets(u->sink->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
- pa_log_info("Disable ucm modifier %s", mod_name);
-
- if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_dismod", mod_name) < 0)
- pa_log("Failed to disable ucm modifier %s", mod_name);
- }
-
if (u->alsa_rtpoll_item) {
pa_rtpoll_item_free(u->alsa_rtpoll_item);
u->alsa_rtpoll_item = NULL;
pa_bool_t b, d;
snd_pcm_uframes_t period_size, buffer_size;
char *device_name = NULL;
- const char *mod_name;
pa_assert(u);
pa_assert(!u->pcm_handle);
pa_log_info("Trying resume...");
- if ((mod_name = pa_proplist_gets(u->sink->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
- pa_log_info("Enable ucm modifier %s", mod_name);
-
- if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_enamod", mod_name) < 0)
- pa_log("Failed to enable ucm modifier %s", mod_name);
- }
-
if ((is_iec958(u) || is_hdmi(u)) && pa_sink_is_passthrough(u->sink)) {
/* Need to open device in NONAUDIO mode */
int len = strlen(u->device_name) + 8;
/* Called from IO context */
static int suspend(struct userdata *u) {
- const char *mod_name;
-
pa_assert(u);
pa_assert(u->pcm_handle);
snd_pcm_close(u->pcm_handle);
u->pcm_handle = NULL;
- if ((mod_name = pa_proplist_gets(u->source->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
- pa_log_info("Disable ucm modifier %s", mod_name);
-
- if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_dismod", mod_name) < 0)
- pa_log("Failed to disable ucm modifier %s", mod_name);
- }
-
if (u->alsa_rtpoll_item) {
pa_rtpoll_item_free(u->alsa_rtpoll_item);
u->alsa_rtpoll_item = NULL;
int err;
pa_bool_t b, d;
snd_pcm_uframes_t period_size, buffer_size;
- const char *mod_name;
pa_assert(u);
pa_assert(!u->pcm_handle);
pa_log_info("Trying resume...");
- if ((mod_name = pa_proplist_gets(u->source->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
- pa_log_info("Enable ucm modifier %s", mod_name);
-
- if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_enamod", mod_name) < 0)
- pa_log("Failed to enable ucm modifier %s", mod_name);
- }
-
if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE,
SND_PCM_NONBLOCK|
SND_PCM_NO_AUTO_RESAMPLE|
pa_idxset_free(context->ucm_modifiers, NULL, NULL);
}
}
+
+/* Enable the modifier when the first stream with matched role starts */
+void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
+ pa_alsa_ucm_modifier *mod;
+
+ if (!ucm->active_verb)
+ return;
+
+ PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
+ if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
+ if (mod->enabled_counter == 0) {
+ const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
+
+ pa_log_info("Enable ucm modifier %s", mod_name);
+ if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) {
+ pa_log("Failed to enable ucm modifier %s", mod_name);
+ }
+ }
+
+ mod->enabled_counter++;
+ break;
+ }
+ }
+}
+
+/* Disable the modifier when the last stream with matched role ends */
+void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
+ pa_alsa_ucm_modifier *mod;
+
+ if (!ucm->active_verb)
+ return;
+
+ PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
+ if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
+
+ mod->enabled_counter--;
+ if (mod->enabled_counter == 0) {
+ const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
+
+ pa_log_info("Disable ucm modifier %s", mod_name);
+ if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) {
+ pa_log("Failed to disable ucm modifier %s", mod_name);
+ }
+ }
+
+ break;
+ }
+ }
+}
void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm);
void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context);
+void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir);
+void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir);
+
/* UCM - Use Case Manager is available on some audio cards */
struct pa_alsa_ucm_device {
/* Non-NULL if the modifier has its own PlaybackPCM/CapturePCM */
pa_alsa_mapping *playback_mapping;
pa_alsa_mapping *capture_mapping;
+
+ /* Count how many role matched streams are running */
+ int enabled_counter;
};
struct pa_alsa_ucm_verb {
/* ucm stuffs */
pa_bool_t use_ucm;
pa_alsa_ucm_config ucm;
+
+ /* hooks for modifier action */
+ pa_hook_slot
+ *sink_input_put_hook_slot,
+ *source_output_put_hook_slot,
+ *sink_input_unlink_hook_slot,
+ *source_output_unlink_hook_slot;
};
struct profile_data {
pa_xfree(t);
}
+static pa_hook_result_t sink_input_put_hook_callback(pa_core *c, pa_sink_input *sink_input, struct userdata *u) {
+ const char *role;
+ pa_sink *sink = sink_input->sink;
+
+ pa_assert(sink);
+
+ role = pa_proplist_gets(sink_input->proplist, PA_PROP_MEDIA_ROLE);
+
+ /* new sink input linked to sink of this card */
+ if (role && sink->card == u->card)
+ pa_alsa_ucm_roled_stream_begin(&u->ucm, role, PA_DIRECTION_OUTPUT);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_put_hook_callback(pa_core *c, pa_source_output *source_output, struct userdata *u) {
+ const char *role;
+ pa_source *source = source_output->source;
+
+ pa_assert(source);
+
+ role = pa_proplist_gets(source_output->proplist, PA_PROP_MEDIA_ROLE);
+
+ /* new source output linked to source of this card */
+ if (role && source->card == u->card)
+ pa_alsa_ucm_roled_stream_begin(&u->ucm, role, PA_DIRECTION_INPUT);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_unlink_hook_callback(pa_core *c, pa_sink_input *sink_input, struct userdata *u) {
+ const char *role;
+ pa_sink *sink = sink_input->sink;
+
+ pa_assert(sink);
+
+ role = pa_proplist_gets(sink_input->proplist, PA_PROP_MEDIA_ROLE);
+
+ /* new sink input unlinked from sink of this card */
+ if (role && sink->card == u->card)
+ pa_alsa_ucm_roled_stream_end(&u->ucm, role, PA_DIRECTION_OUTPUT);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_unlink_hook_callback(pa_core *c, pa_source_output *source_output, struct userdata *u) {
+ const char *role;
+ pa_source *source = source_output->source;
+
+ pa_assert(source);
+
+ role = pa_proplist_gets(source_output->proplist, PA_PROP_MEDIA_ROLE);
+
+ /* new source output unlinked from source of this card */
+ if (role && source->card == u->card)
+ pa_alsa_ucm_roled_stream_end(&u->ucm, role, PA_DIRECTION_INPUT);
+
+ return PA_HOOK_OK;
+}
+
int pa__init(pa_module *m) {
pa_card_new_data data;
pa_modargs *ma;
pa_log_info("Found UCM profiles");
u->profile_set = pa_alsa_ucm_add_profile_set(&u->ucm, &u->core->default_channel_map);
+
+ /* hook start of sink input/source output to enable modifiers */
+ /* A little bit later than module-role-cork */
+ u->sink_input_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE+10,
+ (pa_hook_cb_t) sink_input_put_hook_callback, u);
+ u->source_output_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_LATE+10,
+ (pa_hook_cb_t) source_output_put_hook_callback, u);
+
+ /* hook end of sink input/source output to disable modifiers */
+ /* A little bit later than module-role-cork */
+ u->sink_input_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE+10,
+ (pa_hook_cb_t) sink_input_unlink_hook_callback, u);
+ u->source_output_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], PA_HOOK_LATE+10,
+ (pa_hook_cb_t) source_output_unlink_hook_callback, u);
}
else {
u->use_ucm = FALSE;
if (!(u = m->userdata))
goto finish;
+ if (u->sink_input_put_hook_slot)
+ pa_hook_slot_free(u->sink_input_put_hook_slot);
+
+ if (u->sink_input_unlink_hook_slot)
+ pa_hook_slot_free(u->sink_input_unlink_hook_slot);
+
+ if (u->source_output_put_hook_slot)
+ pa_hook_slot_free(u->source_output_put_hook_slot);
+
+ if (u->source_output_unlink_hook_slot)
+ pa_hook_slot_free(u->source_output_unlink_hook_slot);
+
if (u->mixer_fdl)
pa_alsa_fdlist_free(u->mixer_fdl);
if (u->mixer_handle)