This allows us to flip from software to hardware volume control as the port's mixer path dictates. --- src/modules/alsa/alsa-sink.c | 124 ++++++++++++++++++++++++--------------- src/modules/alsa/alsa-source.c | 123 ++++++++++++++++++++++++--------------- src/pulsecore/sink.c | 27 +++++---- src/pulsecore/sink.h | 1 + src/pulsecore/source.c | 27 +++++---- src/pulsecore/source.h | 1 + 6 files changed, 185 insertions(+), 118 deletions(-) diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 8f31901..bdcf702 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -130,7 +130,7 @@ struct userdata { char *device_name; /* name of the PCM device */ char *control_device; /* name of the control device */ - pa_bool_t use_mmap:1, use_tsched:1; + pa_bool_t use_mmap:1, use_tsched:1, sync_volume:1; pa_bool_t first, after_rewind; @@ -1372,6 +1372,64 @@ static void sink_set_mute_cb(pa_sink *s) { pa_alsa_path_set_mute(u->mixer_path, u->mixer_handle, s->muted); } +static void mixer_volume_init(struct userdata *u) { + pa_sink_flags_t flags; + pa_assert(u); + + /* Save the current flags so we can tell if they've changed */ + flags = u->sink->flags; + + if (!u->mixer_path->has_volume) { + u->sink->get_volume = NULL; + u->sink->set_volume = NULL; + u->sink->write_volume = NULL; + + pa_log_info("Driver does not support hardware volume control, falling back to software volume control."); + } else { + u->sink->get_volume = sink_get_volume_cb; + u->sink->set_volume = sink_set_volume_cb; + u->sink->write_volume = NULL; + + if (u->mixer_path->has_dB) { + u->sink->flags |= PA_SINK_DECIBEL_VOLUME; + pa_log_info("Hardware volume ranges from %0.2f dB to %0.2f dB.", u->mixer_path->min_dB, u->mixer_path->max_dB); + + u->sink->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB); + u->sink->n_volume_steps = PA_VOLUME_NORM+1; + + pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->sink->base_volume)); + + if (u->sync_volume) { + u->sink->write_volume = sink_write_volume_cb; + pa_log_info("Successfully enabled synchronous volume."); + } + } else { + u->sink->flags &= ~PA_SINK_DECIBEL_VOLUME; + pa_log_info("Hardware volume ranges from %li to %li.", u->mixer_path->min_volume, u->mixer_path->max_volume); + u->sink->base_volume = PA_VOLUME_NORM; + u->sink->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1; + } + + pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported"); + } + + if (!u->mixer_path->has_mute) { + u->sink->get_mute = NULL; + u->sink->set_mute = NULL; + pa_log_info("Driver does not support hardware mute control, falling back to software mute control."); + } else { + u->sink->get_mute = sink_get_mute_cb; + u->sink->set_mute = sink_set_mute_cb; + pa_log_info("Using hardware mute control."); + } + + pa_sink_set_flags_from_callbacks(u->sink); + + /* If the flags have changed, let any clients know via a change event */ + if (flags != u->sink->flags) + pa_subscription_post(u->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, u->sink->index); +} + static int sink_set_port_cb(pa_sink *s, pa_device_port *p) { struct userdata *u = s->userdata; pa_alsa_port_data *data; @@ -1385,15 +1443,7 @@ static int sink_set_port_cb(pa_sink *s, pa_device_port *p) { pa_assert_se(u->mixer_path = data->path); pa_alsa_path_select(u->mixer_path, u->mixer_handle); - if (u->mixer_path->has_volume && u->mixer_path->has_dB) { - s->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB); - s->n_volume_steps = PA_VOLUME_NORM+1; - - pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(s->base_volume)); - } else { - s->base_volume = PA_VOLUME_NORM; - s->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1; - } + mixer_volume_init(u); if (data->setting) pa_alsa_setting_select(data->setting, u->mixer_handle); @@ -1723,7 +1773,10 @@ fail: } } -static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_volume) { + +static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) { + pa_bool_t need_mixer_callback = FALSE; + pa_assert(u); if (!u->mixer_handle) @@ -1759,47 +1812,21 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_v return 0; } - if (!u->mixer_path->has_volume) { - pa_log_info("Driver does not support hardware volume control, falling back to software volume control."); - } else { + mixer_volume_init(u); - if (u->mixer_path->has_dB) { - pa_log_info("Hardware volume ranges from %0.2f dB to %0.2f dB.", u->mixer_path->min_dB, u->mixer_path->max_dB); + /* Will we need to register callbacks? */ + if (u->mixer_path_set && u->mixer_path_set->paths) { + pa_alsa_path *p; - u->sink->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB); - u->sink->n_volume_steps = PA_VOLUME_NORM+1; - - pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->sink->base_volume)); - - } else { - pa_log_info("Hardware volume ranges from %li to %li.", u->mixer_path->min_volume, u->mixer_path->max_volume); - u->sink->base_volume = PA_VOLUME_NORM; - u->sink->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1; + PA_LLIST_FOREACH(p, u->mixer_path_set->paths) { + if (p->has_volume || p->has_mute) + need_mixer_callback = TRUE; } - - u->sink->get_volume = sink_get_volume_cb; - u->sink->set_volume = sink_set_volume_cb; - - if (u->mixer_path->has_dB) { - u->sink->flags |= PA_SINK_DECIBEL_VOLUME; - if (sync_volume) { - u->sink->write_volume = sink_write_volume_cb; - pa_log_info("Successfully enabled synchronous volume."); - } - } - - pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported"); - } - - if (!u->mixer_path->has_mute) { - pa_log_info("Driver does not support hardware mute control, falling back to software mute control."); - } else { - u->sink->get_mute = sink_get_mute_cb; - u->sink->set_mute = sink_set_mute_cb; - pa_log_info("Using hardware mute control."); } + else if (u->mixer_path) + need_mixer_callback = u->mixer_path->has_volume || u->mixer_path->has_mute; - if (u->mixer_path->has_volume || u->mixer_path->has_mute) { + if (need_mixer_callback) { int (*mixer_callback)(snd_mixer_elem_t *, unsigned int); if (u->sink->flags & PA_SINK_SYNC_VOLUME) { u->mixer_pd = pa_alsa_mixer_pdata_new(); @@ -1909,6 +1936,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca u->module = m; u->use_mmap = use_mmap; u->use_tsched = use_tsched; + u->sync_volume = sync_volume; u->first = TRUE; u->rewind_safeguard = rewind_safeguard; u->rtpoll = pa_rtpoll_new(); @@ -2133,7 +2161,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca if (update_sw_params(u) < 0) goto fail; - if (setup_mixer(u, ignore_dB, sync_volume) < 0) + if (setup_mixer(u, ignore_dB) < 0) goto fail; pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 52dd65e..5feeec1 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -116,7 +116,7 @@ struct userdata { char *device_name; /* name of the PCM device */ char *control_device; /* name of the control device */ - pa_bool_t use_mmap:1, use_tsched:1; + pa_bool_t use_mmap:1, use_tsched:1, sync_volume:1; pa_bool_t first; @@ -1247,6 +1247,64 @@ static void source_set_mute_cb(pa_source *s) { pa_alsa_path_set_mute(u->mixer_path, u->mixer_handle, s->muted); } +static void mixer_volume_init(struct userdata *u) { + pa_source_flags_t flags; + pa_assert(u); + + /* Save the current flags so we can tell if they've changed */ + flags = u->source->flags; + + if (!u->mixer_path->has_volume) { + u->source->get_volume = NULL; + u->source->set_volume = NULL; + u->source->write_volume = NULL; + + pa_log_info("Driver does not support hardware volume control, falling back to software volume control."); + } else { + u->source->get_volume = source_get_volume_cb; + u->source->set_volume = source_set_volume_cb; + u->source->write_volume = NULL; + + if (u->mixer_path->has_dB) { + u->source->flags |= PA_SOURCE_DECIBEL_VOLUME; + pa_log_info("Hardware volume ranges from %0.2f dB to %0.2f dB.", u->mixer_path->min_dB, u->mixer_path->max_dB); + + u->source->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB); + u->source->n_volume_steps = PA_VOLUME_NORM+1; + + pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->source->base_volume)); + + if (u->sync_volume) { + u->source->write_volume = source_write_volume_cb; + pa_log_info("Successfully enabled synchronous volume."); + } + } else { + u->source->flags &= ~PA_SOURCE_DECIBEL_VOLUME; + pa_log_info("Hardware volume ranges from %li to %li.", u->mixer_path->min_volume, u->mixer_path->max_volume); + u->source->base_volume = PA_VOLUME_NORM; + u->source->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1; + } + + pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported"); + } + + if (!u->mixer_path->has_mute) { + u->source->get_mute = NULL; + u->source->set_mute = NULL; + pa_log_info("Driver does not support hardware mute control, falling back to software mute control."); + } else { + u->source->get_mute = source_get_mute_cb; + u->source->set_mute = source_set_mute_cb; + pa_log_info("Using hardware mute control."); + } + + pa_source_set_flags_from_callbacks(u->source); + + /* If the flags have changed, let any clients know via a change event */ + if (flags != u->source->flags) + pa_subscription_post(u->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, u->source->index); +} + static int source_set_port_cb(pa_source *s, pa_device_port *p) { struct userdata *u = s->userdata; pa_alsa_port_data *data; @@ -1260,15 +1318,7 @@ static int source_set_port_cb(pa_source *s, pa_device_port *p) { pa_assert_se(u->mixer_path = data->path); pa_alsa_path_select(u->mixer_path, u->mixer_handle); - if (u->mixer_path->has_volume && u->mixer_path->has_dB) { - s->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB); - s->n_volume_steps = PA_VOLUME_NORM+1; - - pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(s->base_volume)); - } else { - s->base_volume = PA_VOLUME_NORM; - s->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1; - } + mixer_volume_init(u); if (data->setting) pa_alsa_setting_select(data->setting, u->mixer_handle); @@ -1498,7 +1548,9 @@ fail: } } -static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_volume) { +static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) { + pa_bool_t need_mixer_callback = FALSE; + pa_assert(u); if (!u->mixer_handle) @@ -1534,47 +1586,21 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_v return 0; } - if (!u->mixer_path->has_volume) { - pa_log_info("Driver does not support hardware volume control, falling back to software volume control."); - } else { - - if (u->mixer_path->has_dB) { - pa_log_info("Hardware volume ranges from %0.2f dB to %0.2f dB.", u->mixer_path->min_dB, u->mixer_path->max_dB); - - u->source->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB); - u->source->n_volume_steps = PA_VOLUME_NORM+1; - - pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->source->base_volume)); - - } else { - pa_log_info("Hardware volume ranges from %li to %li.", u->mixer_path->min_volume, u->mixer_path->max_volume); - u->source->base_volume = PA_VOLUME_NORM; - u->source->n_volume_steps = u->mixer_path->max_volume - u->mixer_path->min_volume + 1; - } + mixer_volume_init(u); - u->source->get_volume = source_get_volume_cb; - u->source->set_volume = source_set_volume_cb; + /* Will we need to register callbacks? */ + if (u->mixer_path_set && u->mixer_path_set->paths) { + pa_alsa_path *p; - if (u->mixer_path->has_dB) { - u->source->flags |= PA_SOURCE_DECIBEL_VOLUME; - if (sync_volume) { - u->source->write_volume = source_write_volume_cb; - pa_log_info("Successfully enabled synchronous volume."); - } + PA_LLIST_FOREACH(p, u->mixer_path_set->paths) { + if (p->has_volume || p->has_mute) + need_mixer_callback = TRUE; } - - pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported"); - } - - if (!u->mixer_path->has_mute) { - pa_log_info("Driver does not support hardware mute control, falling back to software mute control."); - } else { - u->source->get_mute = source_get_mute_cb; - u->source->set_mute = source_set_mute_cb; - pa_log_info("Using hardware mute control."); } + else if (u->mixer_path) + need_mixer_callback = u->mixer_path->has_volume || u->mixer_path->has_mute; - if (u->mixer_path->has_volume || u->mixer_path->has_mute) { + if (need_mixer_callback) { int (*mixer_callback)(snd_mixer_elem_t *, unsigned int); if (u->source->flags & PA_SOURCE_SYNC_VOLUME) { u->mixer_pd = pa_alsa_mixer_pdata_new(); @@ -1678,6 +1704,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p u->module = m; u->use_mmap = use_mmap; u->use_tsched = use_tsched; + u->sync_volume = sync_volume; u->first = TRUE; u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); @@ -1893,7 +1920,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p if (update_sw_params(u) < 0) goto fail; - if (setup_mixer(u, ignore_dB, sync_volume) < 0) + if (setup_mixer(u, ignore_dB) < 0) goto fail; pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 15c07a5..df5cd78 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -449,17 +449,8 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { return 0; } -/* Called from main context */ -void pa_sink_put(pa_sink* s) { - pa_sink_assert_ref(s); - pa_assert_ctl_context(); - - pa_assert(s->state == PA_SINK_INIT); - pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || s->input_to_master); - - /* The following fields must be initialized properly when calling _put() */ - pa_assert(s->asyncmsgq); - pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency); +void pa_sink_set_flags_from_callbacks(pa_sink *s) { + pa_assert(s); /* Generally, flags should be initialized via pa_sink_new(). As a * special exception we allow some volume related flags to be set @@ -492,7 +483,21 @@ void pa_sink_put(pa_sink* s) { if ((s->flags & PA_SINK_DECIBEL_VOLUME) && s->core->flat_volumes) s->flags |= PA_SINK_FLAT_VOLUME; +} + +/* Called from main context */ +void pa_sink_put(pa_sink* s) { + pa_sink_assert_ref(s); + pa_assert_ctl_context(); + + pa_assert(s->state == PA_SINK_INIT); + pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || s->input_to_master); + + /* The following fields must be initialized properly when calling _put() */ + pa_assert(s->asyncmsgq); + pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency); + pa_sink_set_flags_from_callbacks(s); if (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) { pa_sink *root_sink = s->input_to_master->sink; diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 85c22ec..e4dd6e8 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -339,6 +339,7 @@ pa_sink* pa_sink_new( pa_sink_new_data *data, pa_sink_flags_t flags); +void pa_sink_set_flags_from_callbacks(pa_sink *s); void pa_sink_put(pa_sink *s); void pa_sink_unlink(pa_sink* s); diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 121c358..e74dcc2 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -381,17 +381,8 @@ static int source_set_state(pa_source *s, pa_source_state_t state) { return 0; } -/* Called from main context */ -void pa_source_put(pa_source *s) { - pa_source_assert_ref(s); - pa_assert_ctl_context(); - - pa_assert(s->state == PA_SOURCE_INIT); - pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) || s->output_from_master); - - /* The following fields must be initialized properly when calling _put() */ - pa_assert(s->asyncmsgq); - pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency); +void pa_source_set_flags_from_callbacks(pa_source *s) { + pa_assert(s); /* Generally, flags should be initialized via pa_source_new(). As a * special exception we allow some volume related flags to be set @@ -424,7 +415,21 @@ void pa_source_put(pa_source *s) { if ((s->flags & PA_SOURCE_DECIBEL_VOLUME) && s->core->flat_volumes) s->flags |= PA_SOURCE_FLAT_VOLUME; +} + +/* Called from main context */ +void pa_source_put(pa_source *s) { + pa_source_assert_ref(s); + pa_assert_ctl_context(); + + pa_assert(s->state == PA_SOURCE_INIT); + pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) || s->output_from_master); + + /* The following fields must be initialized properly when calling _put() */ + pa_assert(s->asyncmsgq); + pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency); + pa_source_set_flags_from_callbacks(s); if (s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) { pa_source *root_source = s->output_from_master->source; diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index b68dfd5..d2122b2 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -270,6 +270,7 @@ pa_source* pa_source_new( pa_source_new_data *data, pa_source_flags_t flags); +void pa_source_set_flags_from_callbacks(pa_source *s); void pa_source_put(pa_source *s); void pa_source_unlink(pa_source *s); -- 1.7.6