This commit attempts to fix the initial volume at startup. Due to the sync volume stuff, there are several complications with regards to ensuring the actual h/w has the volume. This change mostly fixes things. Changing ports allows the volume to be reset to the current value (this is ultimately something we likely want to avoid - with device-restore keeping separate volumes per port - but this is another issue) and the initial startup volume is set also. Sadly sometimes the hardware does not seem to see the volume after a port change. On these occasions, just touching the alsa volume up or down makes it jump to the right value, so the overall volume in PA is obviously 'correct' even if it's not correctly distributed. On other occasions, the h/w volume jumps to 100%... still not worked that one out. --- src/modules/alsa/alsa-sink.c | 40 ++++++++++++++++++++++++++++++++++++---- src/modules/alsa/alsa-source.c | 40 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 6c2f69a..588fd21 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1293,6 +1293,7 @@ static void sink_set_volume_cb(pa_sink *s) { pa_yes_no(accurate_enough)); pa_log_debug(" in dB: %s", pa_sw_cvolume_snprint_dB(vol_str_db, sizeof(vol_str_db), &new_soft_volume)); + /* NB, the soft volume will already have been reset in pa_sink_set_volume() */ if (!accurate_enough) s->soft_volume = new_soft_volume; @@ -1374,7 +1375,12 @@ static void sink_set_mute_cb(pa_sink *s) { static void mixer_volume_init(struct userdata *u) { pa_sink_flags_t flags; + pa_cvolume r; + pa_assert(u); + pa_assert(u->mixer_path); + pa_assert(u->mixer_handle); + /* Save the current flags so we can tell if they've changed */ flags = u->sink->flags; @@ -1383,6 +1389,8 @@ static void mixer_volume_init(struct userdata *u) { u->sink->get_volume = NULL; u->sink->set_volume = NULL; u->sink->write_volume = NULL; + pa_cvolume_mute(&u->hardware_volume, u->sink->sample_spec.channels); + pa_cvolume_mute(&u->sink->thread_info.current_hw_volume, u->sink->sample_spec.channels); pa_log_info("Driver does not support hardware volume control, falling back to software volume control."); } else { @@ -1390,6 +1398,16 @@ static void mixer_volume_init(struct userdata *u) { u->sink->set_volume = sink_set_volume_cb; u->sink->write_volume = NULL; + /* Read the current volume so we can properly compare it for later writing. + * This is needed on init and on port changes where the mixer_path is + * totally different */ + if (pa_alsa_path_get_volume(u->mixer_path, u->mixer_handle, &u->sink->channel_map, &r) >= 0) { + /* Shift down by the base volume, so that 0dB becomes maximum volume */ + pa_sw_cvolume_multiply_scalar(&r, &r, u->sink->base_volume); + + u->sink->thread_info.current_hw_volume = u->hardware_volume = r; + } + 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); @@ -1861,7 +1879,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark, rewind_safeguard; snd_pcm_uframes_t period_frames, buffer_frames, tsched_frames; size_t frame_size; - pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE, namereg_fail = FALSE, sync_volume = FALSE; + pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE, namereg_fail = FALSE, sync_volume = FALSE, init_volume = FALSE; pa_sink_new_data data; pa_alsa_profile_set *profile_set = NULL; @@ -2168,18 +2186,23 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca goto fail; } - /* Get initial mixer settings */ + + /* Get or set initial mixer settings */ if (data.volume_is_set) { - if (u->sink->set_volume) + if (!u->sink->write_volume && u->sink->set_volume) u->sink->set_volume(u->sink); + else if (u->sink->write_volume) + init_volume = TRUE; } else { if (u->sink->get_volume) u->sink->get_volume(u->sink); } if (data.muted_is_set) { - if (u->sink->set_mute) + if (!u->sink->write_volume && u->sink->set_mute) u->sink->set_mute(u->sink); + else if (u->sink->write_volume) + init_volume = TRUE; } else { if (u->sink->get_mute) u->sink->get_mute(u->sink); @@ -2187,6 +2210,15 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca pa_sink_put(u->sink); + /* With sync volumes enabled, we have to push the change to the h/w */ + if (init_volume) { + /* Set the current h/w volume such that we detect the a "change" + * if we need to push to h/w... */ + u->sink->thread_info.current_hw_volume = u->hardware_volume; + + pa_assert_se(pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_SET_VOLUME_SYNCED, NULL, 0, NULL) == 0); + } + if (profile_set) pa_alsa_profile_set_free(profile_set); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 00b1fc7..2b2f403 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -1168,6 +1168,7 @@ static void source_set_volume_cb(pa_source *s) { pa_yes_no(accurate_enough)); pa_log_debug(" in dB: %s", pa_sw_cvolume_snprint_dB(vol_str_db, sizeof(vol_str_db), &new_soft_volume)); + /* NB, the soft volume will already have been reset in pa_source_set_volume() */ if (!accurate_enough) s->soft_volume = new_soft_volume; @@ -1249,7 +1250,12 @@ static void source_set_mute_cb(pa_source *s) { static void mixer_volume_init(struct userdata *u) { pa_source_flags_t flags; + pa_cvolume r; + pa_assert(u); + pa_assert(u->mixer_path); + pa_assert(u->mixer_handle); + /* Save the current flags so we can tell if they've changed */ flags = u->source->flags; @@ -1258,6 +1264,8 @@ static void mixer_volume_init(struct userdata *u) { u->source->get_volume = NULL; u->source->set_volume = NULL; u->source->write_volume = NULL; + pa_cvolume_mute(&u->hardware_volume, u->source->sample_spec.channels); + pa_cvolume_mute(&u->source->thread_info.current_hw_volume, u->source->sample_spec.channels); pa_log_info("Driver does not support hardware volume control, falling back to software volume control."); } else { @@ -1265,6 +1273,16 @@ static void mixer_volume_init(struct userdata *u) { u->source->set_volume = source_set_volume_cb; u->source->write_volume = NULL; + /* Read the current volume so we can properly compare it for later writing. + * This is needed on init and on port changes where the mixer_path is + * totally different */ + if (pa_alsa_path_get_volume(u->mixer_path, u->mixer_handle, &u->source->channel_map, &r) >= 0) { + /* Shift down by the base volume, so that 0dB becomes maximum volume */ + pa_sw_cvolume_multiply_scalar(&r, &r, u->source->base_volume); + + u->source->thread_info.current_hw_volume = u->hardware_volume = r; + } + 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); @@ -1635,7 +1653,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark; snd_pcm_uframes_t period_frames, buffer_frames, tsched_frames; size_t frame_size; - pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE, namereg_fail = FALSE, sync_volume = FALSE; + pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE, namereg_fail = FALSE, sync_volume = FALSE, init_volume = FALSE; pa_source_new_data data; pa_alsa_profile_set *profile_set = NULL; @@ -1927,18 +1945,23 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p goto fail; } - /* Get initial mixer settings */ + + /* Get or set initial mixer settings */ if (data.volume_is_set) { - if (u->source->set_volume) + if (!u->source->write_volume && u->source->set_volume) u->source->set_volume(u->source); + else if (u->source->write_volume) + init_volume = TRUE; } else { if (u->source->get_volume) u->source->get_volume(u->source); } if (data.muted_is_set) { - if (u->source->set_mute) + if (!u->source->write_volume && u->source->set_mute) u->source->set_mute(u->source); + else if (u->source->write_volume) + init_volume = TRUE; } else { if (u->source->get_mute) u->source->get_mute(u->source); @@ -1946,6 +1969,15 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p pa_source_put(u->source); + /* With sync volumes enabled, we have to push the change to the h/w */ + if (init_volume) { + /* Set the current h/w volume such that we detect the a "change" + * if we need to push to h/w... */ + u->source->thread_info.current_hw_volume = u->hardware_volume; + + pa_assert_se(pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), PA_SOURCE_MESSAGE_SET_VOLUME_SYNCED, NULL, 0, NULL) == 0); + } + if (profile_set) pa_alsa_profile_set_free(profile_set); -- 1.7.6