[pulseaudio-discuss] [PATCH 09/11] alsa-mixer: Attempt to fix hardware volumes on init and after port switch.

Colin Guthrie colin at mageia.org
Mon Jul 18 03:36:52 PDT 2011


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 a4cb7f9..ca63e9e 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,18 +1375,35 @@ static void sink_set_mute_cb(pa_sink *s) {
 
 static void mixer_volume_init(struct userdata *u) {
     pa_assert(u);
+    pa_assert(u->mixer_path);
+    pa_assert(u->mixer_handle);
+
 
     if (!u->mixer_path->has_volume) {
         pa_sink_set_get_volume_callback(u->sink, NULL);
         pa_sink_set_set_volume_callback(u->sink, NULL);
         pa_sink_set_write_volume_callback(u->sink, 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 {
+        pa_cvolume r;
+
         pa_sink_set_get_volume_callback(u->sink, sink_get_volume_cb);
         pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
         pa_sink_set_write_volume_callback(u->sink, 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) {
             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);
@@ -1851,7 +1869,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;
 
@@ -2158,18 +2176,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);
@@ -2177,6 +2200,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 7764fa2..bb18ab8 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,18 +1250,35 @@ static void source_set_mute_cb(pa_source *s) {
 
 static void mixer_volume_init(struct userdata *u) {
     pa_assert(u);
+    pa_assert(u->mixer_path);
+    pa_assert(u->mixer_handle);
+
 
     if (!u->mixer_path->has_volume) {
         pa_source_set_get_volume_callback(u->source, NULL);
         pa_source_set_set_volume_callback(u->source, NULL);
         pa_source_set_write_volume_callback(u->source, 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 {
+        pa_cvolume r;
+
         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_write_volume_callback(u->source, 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) {
             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);
@@ -1625,7 +1643,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;
 
@@ -1917,18 +1935,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);
@@ -1936,6 +1959,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



More information about the pulseaudio-discuss mailing list