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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Linux Audio Users]     [AMD Graphics]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux