From 6c6b50d6a8bbd7fc7fb2cfb59a66183741567922 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sat, 2 Jul 2011 16:23:01 +0100 Subject: [PATCH] alsa: Reinitialise the mixer on port change. This allows us to flip from software to hardware volume control as the port's mixer path dictates. --- src/modules/alsa/alsa-sink.c | 115 ++++++++++++++++----------- src/modules/alsa/alsa-source.c | 114 +++++++++++++++----------- src/modules/echo-cancel/module-echo-cancel.c | 4 +- src/modules/module-equalizer-sink.c | 2 +- src/modules/module-ladspa-sink.c | 2 +- src/modules/module-virtual-sink.c | 11 ++- src/modules/module-virtual-source.c | 11 ++- src/pulsecore/sink.c | 87 ++++++++++++++++++-- src/pulsecore/sink.h | 1 + src/pulsecore/source.c | 87 ++++++++++++++++++-- src/pulsecore/source.h | 1 + 11 files changed, 315 insertions(+), 120 deletions(-) diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 0dd1840..6c22b55 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,55 @@ 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_assert(u); + + if (!u->mixer_path->has_volume) { + pa_sink_set_write_volume_callback(u->sink, NULL); + pa_sink_set_get_volume_callback(u->sink, NULL); + pa_sink_set_set_volume_callback(u->sink, NULL); + + pa_log_info("Driver does not support hardware volume control, falling back to software volume control."); + } else { + pa_sink_set_get_volume_callback(u->sink, sink_get_volume_cb); + pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb); + + if (u->mixer_path->has_dB && u->sync_volume) { + pa_sink_set_write_volume_callback(u->sink, sink_write_volume_cb); + pa_log_info("Successfully enabled synchronous volume."); + } else + pa_sink_set_write_volume_callback(u->sink, NULL); + + if (u->mixer_path->has_dB) { + pa_sink_enable_decibel_volume(u->sink, TRUE); + 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)); + } else { + pa_sink_enable_decibel_volume(u->sink, FALSE); + 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) { + pa_sink_set_get_mute_callback(u->sink, NULL); + pa_sink_set_set_mute_callback(u->sink, NULL); + pa_log_info("Driver does not support hardware mute control, falling back to software mute control."); + } else { + pa_sink_set_get_mute_callback(u->sink, sink_get_mute_cb); + pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb); + pa_log_info("Using hardware mute control."); + } +} + 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 +1434,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 +1764,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 +1803,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; } - - pa_sink_set_get_volume_callback(u->sink, sink_get_volume_cb); - pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb); - - if (u->mixer_path->has_dB) { - u->sink->flags |= PA_SINK_DECIBEL_VOLUME; - if (sync_volume) { - pa_sink_set_write_volume_callback(u->sink, 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 { - pa_sink_set_get_mute_callback(u->sink, sink_get_mute_cb); - pa_sink_set_set_mute_callback(u->sink, 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 +1927,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 +2152,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 941aacb..8b35ce1 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,55 @@ 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_assert(u); + + if (!u->mixer_path->has_volume) { + pa_source_set_write_volume_callback(u->source, NULL); + pa_source_set_get_volume_callback(u->source, NULL); + pa_source_set_set_volume_callback(u->source, NULL); + + pa_log_info("Driver does not support hardware volume control, falling back to software volume control."); + } else { + pa_source_set_get_volume_callback(u->source, source_get_volume_cb); + pa_source_set_set_volume_callback(u->source, source_set_volume_cb); + + if (u->mixer_path->has_dB && u->sync_volume) { + pa_source_set_write_volume_callback(u->source, source_write_volume_cb); + pa_log_info("Successfully enabled synchronous volume."); + } else + pa_source_set_write_volume_callback(u->source, NULL); + + if (u->mixer_path->has_dB) { + pa_source_enable_decibel_volume(u->source, TRUE); + 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_source_enable_decibel_volume(u->source, FALSE); + 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) { + pa_source_set_get_mute_callback(u->source, NULL); + pa_source_set_set_mute_callback(u->source, NULL); + pa_log_info("Driver does not support hardware mute control, falling back to software mute control."); + } else { + pa_source_set_get_mute_callback(u->source, source_get_mute_cb); + pa_source_set_set_mute_callback(u->source, source_set_mute_cb); + pa_log_info("Using hardware mute control."); + } +} + 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 +1309,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 +1539,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 +1577,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); - pa_source_set_get_volume_callback(u->source, source_get_volume_cb); - pa_source_set_set_volume_callback(u->source, 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) { - pa_source_set_write_volume_callback(u->source, 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 { - pa_source_set_get_mute_callback(u->source, source_get_mute_cb); - pa_source_set_set_mute_callback(u->source, 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 +1695,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 +1911,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/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c index 62b436b..961f289 100644 --- a/src/modules/echo-cancel/module-echo-cancel.c +++ b/src/modules/echo-cancel/module-echo-cancel.c @@ -1523,7 +1523,6 @@ int pa__init(pa_module*m) { } u->source = pa_source_new(m->core, &source_data, - PA_SOURCE_DECIBEL_VOLUME| (source_master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY))); pa_source_new_data_done(&source_data); @@ -1535,6 +1534,7 @@ int pa__init(pa_module*m) { u->source->parent.process_msg = source_process_msg_cb; u->source->set_state = source_set_state_cb; u->source->update_requested_latency = source_update_requested_latency_cb; + pa_source_enable_decibel_volume(u->source, TRUE); pa_source_set_get_volume_callback(u->source, source_get_volume_cb); pa_source_set_set_volume_callback(u->source, source_set_volume_cb); pa_source_set_get_mute_callback(u->source, source_get_mute_cb); @@ -1571,7 +1571,6 @@ int pa__init(pa_module*m) { } u->sink = pa_sink_new(m->core, &sink_data, - PA_SINK_DECIBEL_VOLUME| (sink_master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY))); pa_sink_new_data_done(&sink_data); @@ -1584,6 +1583,7 @@ int pa__init(pa_module*m) { u->sink->set_state = sink_set_state_cb; u->sink->update_requested_latency = sink_update_requested_latency_cb; u->sink->request_rewind = sink_request_rewind_cb; + pa_sink_enable_decibel_volume(u->sink, TRUE); pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb); pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb); u->sink->userdata = u; diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index d01d453..ee9b678 100644 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -1178,7 +1178,6 @@ int pa__init(pa_module*m) { } u->sink = pa_sink_new(m->core, &sink_data, - PA_SINK_DECIBEL_VOLUME| (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY))); pa_sink_new_data_done(&sink_data); @@ -1191,6 +1190,7 @@ int pa__init(pa_module*m) { u->sink->set_state = sink_set_state_cb; u->sink->update_requested_latency = sink_update_requested_latency_cb; u->sink->request_rewind = sink_request_rewind_cb; + pa_sink_enable_decibel_volume(u->sink, TRUE); pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb); pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb); u->sink->userdata = u; diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index b962b1b..9b4903a 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -883,7 +883,6 @@ int pa__init(pa_module*m) { } u->sink = pa_sink_new(m->core, &sink_data, - PA_SINK_DECIBEL_VOLUME| (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY))); pa_sink_new_data_done(&sink_data); @@ -896,6 +895,7 @@ int pa__init(pa_module*m) { u->sink->set_state = sink_set_state_cb; u->sink->update_requested_latency = sink_update_requested_latency_cb; u->sink->request_rewind = sink_request_rewind_cb; + pa_sink_enable_decibel_volume(u->sink, TRUE); pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb); pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb); u->sink->userdata = u; diff --git a/src/modules/module-virtual-sink.c b/src/modules/module-virtual-sink.c index a880df4..b52a29b 100644 --- a/src/modules/module-virtual-sink.c +++ b/src/modules/module-virtual-sink.c @@ -554,8 +554,7 @@ int pa__init(pa_module*m) { } u->sink = pa_sink_new(m->core, &sink_data, (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)) - | (use_volume_sharing ? PA_SINK_SHARE_VOLUME_WITH_MASTER : 0) - | (force_flat_volume ? PA_SINK_FLAT_VOLUME : 0)); + | (use_volume_sharing ? PA_SINK_SHARE_VOLUME_WITH_MASTER : 0)); pa_sink_new_data_done(&sink_data); if (!u->sink) { @@ -567,8 +566,14 @@ int pa__init(pa_module*m) { u->sink->set_state = sink_set_state_cb; u->sink->update_requested_latency = sink_update_requested_latency_cb; u->sink->request_rewind = sink_request_rewind_cb; - pa_sink_set_set_volume_callback(u->sink, use_volume_sharing ? NULL : sink_set_volume_cb); pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb); + if (use_volume_sharing) { + pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb); + pa_sink_enable_decibel_volume(u->sink, TRUE); + } + /* Normally this flag would be enabled automatically be we can force it. */ + if (force_flat_volume) + u->sink->flags |= PA_SINK_FLAT_VOLUME; u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); diff --git a/src/modules/module-virtual-source.c b/src/modules/module-virtual-source.c index f5c5e67..7bcecfa 100644 --- a/src/modules/module-virtual-source.c +++ b/src/modules/module-virtual-source.c @@ -581,8 +581,7 @@ int pa__init(pa_module*m) { } u->source = pa_source_new(m->core, &source_data, (master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY)) - | (use_volume_sharing ? PA_SOURCE_SHARE_VOLUME_WITH_MASTER : 0) - | (force_flat_volume ? PA_SOURCE_FLAT_VOLUME : 0)); + | (use_volume_sharing ? PA_SOURCE_SHARE_VOLUME_WITH_MASTER : 0)); pa_source_new_data_done(&source_data); @@ -594,8 +593,14 @@ int pa__init(pa_module*m) { u->source->parent.process_msg = source_process_msg_cb; u->source->set_state = source_set_state_cb; u->source->update_requested_latency = source_update_requested_latency_cb; - pa_source_set_set_volume_callback(u->source, use_volume_sharing ? NULL : source_set_volume_cb); pa_source_set_set_mute_callback(u->source, source_set_mute_cb); + if (use_volume_sharing) { + pa_source_set_set_volume_callback(u->source, source_set_volume_cb); + pa_source_enable_decibel_volume(u->source, TRUE); + } + /* Normally this flag would be enabled automatically be we can force it. */ + if (force_flat_volume) + u->source->flags |= PA_SOURCE_FLAT_VOLUME; u->source->userdata = u; pa_source_set_asyncmsgq(u->source, master->asyncmsgq); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index b51156f..77c665f 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -456,29 +456,49 @@ void pa_sink_set_get_volume_callback(pa_sink *s, pa_sink_cb_t cb) { } void pa_sink_set_set_volume_callback(pa_sink *s, pa_sink_cb_t cb) { - pa_assert(s); + pa_sink_flags_t flags; + pa_assert(s); pa_assert(!s->write_volume || cb); s->set_volume = cb; - if (cb) + /* Save the current flags so we can tell if they've changed */ + flags = s->flags; + + if (cb) { + /* The sink implementor is responsible for setting decibel volume support */ s->flags |= PA_SINK_HW_VOLUME_CTRL; - else + } else { s->flags &= ~PA_SINK_HW_VOLUME_CTRL; + /* See note below in pa_sink_put() about volume sharing and decibel volumes */ + pa_sink_enable_decibel_volume(s, !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)); + } + + /* If the flags have changed after init, let any clients know via a change event */ + if (s->state != PA_SINK_INIT && flags != s->flags) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } void pa_sink_set_write_volume_callback(pa_sink *s, pa_sink_cb_t cb) { - pa_assert(s); + pa_sink_flags_t flags; + pa_assert(s); pa_assert(!cb || s->set_volume); s->write_volume = cb; + /* Save the current flags so we can tell if they've changed */ + flags = s->flags; + if (cb) s->flags |= PA_SINK_SYNC_VOLUME; else s->flags &= ~PA_SINK_SYNC_VOLUME; + + /* If the flags have changed after init, let any clients know via a change event */ + if (s->state != PA_SINK_INIT && flags != s->flags) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_cb_t cb) { @@ -488,14 +508,65 @@ void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_cb_t cb) { } void pa_sink_set_set_mute_callback(pa_sink *s, pa_sink_cb_t cb) { + pa_sink_flags_t flags; + pa_assert(s); s->set_mute = cb; + /* Save the current flags so we can tell if they've changed */ + flags = s->flags; + if (cb) s->flags |= PA_SINK_HW_MUTE_CTRL; else s->flags &= ~PA_SINK_HW_MUTE_CTRL; + + /* If the flags have changed after init, let any clients know via a change event */ + if (s->state != PA_SINK_INIT && flags != s->flags) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +} + +static void enable_flat_volume(pa_sink *s, pa_bool_t enable) { + pa_sink_flags_t flags; + + pa_assert(s); + + /* Always follow the overall user preference here */ + enable = enable && s->core->flat_volumes; + + /* Save the current flags so we can tell if they've changed */ + flags = s->flags; + + if (enable) + s->flags |= PA_SINK_FLAT_VOLUME; + else + s->flags &= ~PA_SINK_FLAT_VOLUME; + + /* If the flags have changed after init, let any clients know via a change event */ + if (s->state != PA_SINK_INIT && flags != s->flags) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +} + +void pa_sink_enable_decibel_volume(pa_sink *s, pa_bool_t enable) { + pa_sink_flags_t flags; + + pa_assert(s); + + /* Save the current flags so we can tell if they've changed */ + flags = s->flags; + + if (enable) { + s->flags |= PA_SINK_DECIBEL_VOLUME; + enable_flat_volume(s, TRUE); + } else { + s->flags &= ~PA_SINK_DECIBEL_VOLUME; + enable_flat_volume(s, FALSE); + } + + /* If the flags have changed after init, let any clients know via a change event */ + if (s->state != PA_SINK_INIT && flags != s->flags) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } /* Called from main context */ @@ -536,10 +607,12 @@ void pa_sink_put(pa_sink* s) { * * Note: This flag can also change over the life time of the sink. */ if (!(s->flags & PA_SINK_HW_VOLUME_CTRL) && !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) - s->flags |= PA_SINK_DECIBEL_VOLUME; + pa_sink_enable_decibel_volume(s, TRUE); - if ((s->flags & PA_SINK_DECIBEL_VOLUME) && s->core->flat_volumes) - s->flags |= PA_SINK_FLAT_VOLUME; + /* If the sink implementor support DB volumes by itself, we should always + * try and enable flat volumes too */ + if ((s->flags & PA_SINK_DECIBEL_VOLUME)) + enable_flat_volume(s, TRUE); 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 669ce20..239936b 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -362,6 +362,7 @@ void pa_sink_set_set_volume_callback(pa_sink *s, pa_sink_cb_t cb); void pa_sink_set_write_volume_callback(pa_sink *s, pa_sink_cb_t cb); void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_cb_t cb); void pa_sink_set_set_mute_callback(pa_sink *s, pa_sink_cb_t cb); +void pa_sink_enable_decibel_volume(pa_sink *s, pa_bool_t enable); 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 120ecdd..9764a88 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -388,29 +388,49 @@ void pa_source_set_get_volume_callback(pa_source *s, pa_source_cb_t cb) { } void pa_source_set_set_volume_callback(pa_source *s, pa_source_cb_t cb) { - pa_assert(s); + pa_source_flags_t flags; + pa_assert(s); pa_assert(!s->write_volume || cb); s->set_volume = cb; - if (cb) + /* Save the current flags so we can tell if they've changed */ + flags = s->flags; + + if (cb) { + /* The source implementor is responsible for setting decibel volume support */ s->flags |= PA_SOURCE_HW_VOLUME_CTRL; - else + } else { s->flags &= ~PA_SOURCE_HW_VOLUME_CTRL; + /* See note below in pa_source_put() about volume sharing and decibel volumes */ + pa_source_enable_decibel_volume(s, !(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)); + } + + /* If the flags have changed after init, let any clients know via a change event */ + if (s->state != PA_SOURCE_INIT && flags != s->flags) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } void pa_source_set_write_volume_callback(pa_source *s, pa_source_cb_t cb) { - pa_assert(s); + pa_source_flags_t flags; + pa_assert(s); pa_assert(!cb || s->set_volume); s->write_volume = cb; + /* Save the current flags so we can tell if they've changed */ + flags = s->flags; + if (cb) s->flags |= PA_SOURCE_SYNC_VOLUME; else s->flags &= ~PA_SOURCE_SYNC_VOLUME; + + /* If the flags have changed after init, let any clients know via a change event */ + if (s->state != PA_SOURCE_INIT && flags != s->flags) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } void pa_source_set_get_mute_callback(pa_source *s, pa_source_cb_t cb) { @@ -420,14 +440,65 @@ void pa_source_set_get_mute_callback(pa_source *s, pa_source_cb_t cb) { } void pa_source_set_set_mute_callback(pa_source *s, pa_source_cb_t cb) { + pa_source_flags_t flags; + pa_assert(s); s->set_mute = cb; + /* Save the current flags so we can tell if they've changed */ + flags = s->flags; + if (cb) s->flags |= PA_SOURCE_HW_MUTE_CTRL; else s->flags &= ~PA_SOURCE_HW_MUTE_CTRL; + + /* If the flags have changed after init, let any clients know via a change event */ + if (s->state != PA_SOURCE_INIT && flags != s->flags) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +} + +static void enable_flat_volume(pa_source *s, pa_bool_t enable) { + pa_source_flags_t flags; + + pa_assert(s); + + /* Always follow the overall user preference here */ + enable = enable && s->core->flat_volumes; + + /* Save the current flags so we can tell if they've changed */ + flags = s->flags; + + if (enable) + s->flags |= PA_SOURCE_FLAT_VOLUME; + else + s->flags &= ~PA_SOURCE_FLAT_VOLUME; + + /* If the flags have changed after init, let any clients know via a change event */ + if (s->state != PA_SOURCE_INIT && flags != s->flags) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +} + +void pa_source_enable_decibel_volume(pa_source *s, pa_bool_t enable) { + pa_source_flags_t flags; + + pa_assert(s); + + /* Save the current flags so we can tell if they've changed */ + flags = s->flags; + + if (enable) { + s->flags |= PA_SOURCE_DECIBEL_VOLUME; + enable_flat_volume(s, TRUE); + } else { + s->flags &= ~PA_SOURCE_DECIBEL_VOLUME; + enable_flat_volume(s, FALSE); + } + + /* If the flags have changed after init, let any clients know via a change event */ + if (s->state != PA_SOURCE_INIT && flags != s->flags) + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } /* Called from main context */ @@ -468,10 +539,12 @@ void pa_source_put(pa_source *s) { * * Note: This flag can also change over the life time of the source. */ if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL) && !(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) - s->flags |= PA_SOURCE_DECIBEL_VOLUME; + pa_source_enable_decibel_volume(s, TRUE); - if ((s->flags & PA_SOURCE_DECIBEL_VOLUME) && s->core->flat_volumes) - s->flags |= PA_SOURCE_FLAT_VOLUME; + /* If the source implementor support DB volumes by itself, we should always + * try and enable flat volumes too */ + if ((s->flags & PA_SOURCE_DECIBEL_VOLUME)) + enable_flat_volume(s, TRUE); 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 52186cd..13d279d 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -293,6 +293,7 @@ void pa_source_set_set_volume_callback(pa_source *s, pa_source_cb_t cb); void pa_source_set_write_volume_callback(pa_source *s, pa_source_cb_t cb); void pa_source_set_get_mute_callback(pa_source *s, pa_source_cb_t cb); void pa_source_set_set_mute_callback(pa_source *s, pa_source_cb_t cb); +void pa_source_enable_decibel_volume(pa_source *s, pa_bool_t enable); void pa_source_put(pa_source *s); void pa_source_unlink(pa_source *s); -- 2.7.4