[PATCH 2/4] alsa: Reinitialise the mixer on port change.

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

 



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



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

  Powered by Linux