This allows us to flip from software to hardware volume control as the port's mixer path dictates. --- src/modules/alsa/alsa-sink.c | 126 ++++++++++++++++++++++----------------- src/modules/alsa/alsa-source.c | 125 ++++++++++++++++++++++----------------- 2 files changed, 141 insertions(+), 110 deletions(-) diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 0ad8f12..84f2904 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,59 @@ 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); + + pa_proplist_sets(u->sink->proplist, PA_PROP_DEVICE_HW_VOLUME, pa_yes_no(u->mixer_path->has_volume)); + pa_proplist_sets(u->sink->proplist, PA_PROP_DEVICE_DECIBEL, pa_yes_no(u->mixer_path->has_volume && u->mixer_path->has_dB)); + if (!u->mixer_path->has_volume) { + u->sink->get_volume = NULL; + u->sink->set_volume = NULL; + u->sink->write_volume = NULL; + u->sink->flags &= ~PA_SINK_SYNC_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->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; + } + + u->sink->get_volume = sink_get_volume_cb; + u->sink->set_volume = sink_set_volume_cb; + u->sink->write_volume = sink_write_volume_cb; + + if (u->mixer_path->has_dB && u->sync_volume) { + u->sink->flags |= PA_SINK_SYNC_VOLUME; + pa_log_info("Successfully enabled synchronous volume."); + } else + u->sink->flags &= ~PA_SINK_SYNC_VOLUME; + + pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported"); + } + + pa_proplist_sets(u->sink->proplist, PA_PROP_DEVICE_HW_MUTE, pa_yes_no(u->mixer_path->has_mute)); + 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."); + } +} + 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 +1438,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 +1768,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,54 +1807,21 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_v return 0; } - pa_proplist_sets(u->sink->proplist, PA_PROP_DEVICE_HW_VOLUME, pa_yes_no(u->mixer_path->has_volume)); - pa_proplist_sets(u->sink->proplist, PA_PROP_DEVICE_DECIBEL, pa_yes_no(u->mixer_path->has_volume && u->mixer_path->has_dB)); - 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 { - - 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->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; - } + mixer_volume_init(u); - u->sink->get_volume = sink_get_volume_cb; - u->sink->set_volume = sink_set_volume_cb; - u->sink->write_volume = sink_write_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 && sync_volume) { - u->sink->flags |= PA_SINK_SYNC_VOLUME; - 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"); - } - - pa_proplist_sets(u->sink->proplist, PA_PROP_DEVICE_HW_MUTE, pa_yes_no(u->mixer_path->has_mute)); - 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."); } + 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(); @@ -1916,6 +1931,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(); @@ -2140,7 +2156,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 33ed330..f65f070 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,59 @@ 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); + + pa_proplist_sets(u->source->proplist, PA_PROP_DEVICE_HW_VOLUME, pa_yes_no(u->mixer_path->has_volume)); + pa_proplist_sets(u->source->proplist, PA_PROP_DEVICE_DECIBEL, pa_yes_no(u->mixer_path->has_volume && u->mixer_path->has_dB)); + if (!u->mixer_path->has_volume) { + u->source->get_volume = NULL; + u->source->set_volume = NULL; + u->source->write_volume = NULL; + u->source->flags &= ~PA_SOURCE_SYNC_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; + } + + u->source->get_volume = source_get_volume_cb; + u->source->set_volume = source_set_volume_cb; + u->source->write_volume = source_write_volume_cb; + + if (u->mixer_path->has_dB && u->sync_volume) { + u->source->flags |= PA_SOURCE_SYNC_VOLUME; + pa_log_info("Successfully enabled synchronous volume."); + } else + u->source->flags &= ~PA_SOURCE_SYNC_VOLUME; + + pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported"); + } + + pa_proplist_sets(u->source->proplist, PA_PROP_DEVICE_HW_MUTE, pa_yes_no(u->mixer_path->has_mute)); + 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."); + } +} + 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 +1313,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 +1543,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,54 +1581,21 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_v return 0; } - pa_proplist_sets(u->source->proplist, PA_PROP_DEVICE_HW_VOLUME, pa_yes_no(u->mixer_path->has_volume)); - pa_proplist_sets(u->source->proplist, PA_PROP_DEVICE_DECIBEL, pa_yes_no(u->mixer_path->has_volume && u->mixer_path->has_dB)); - 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 { - - 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; + mixer_volume_init(u); - pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->source->base_volume)); + /* Will we need to register callbacks? */ + if (u->mixer_path_set && u->mixer_path_set->paths) { + pa_alsa_path *p; - } 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; - } - - u->source->get_volume = source_get_volume_cb; - u->source->set_volume = source_set_volume_cb; - u->source->write_volume = source_write_volume_cb; - - if (u->mixer_path->has_dB && sync_volume) { - u->source->flags |= PA_SOURCE_SYNC_VOLUME; - 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"); - } - - pa_proplist_sets(u->source->proplist, PA_PROP_DEVICE_HW_MUTE, pa_yes_no(u->mixer_path->has_mute)); - 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."); } + 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(); @@ -1685,6 +1699,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); @@ -1900,7 +1915,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); -- 1.7.6