[pulseaudio-discuss] [PATCH 2/8] alsa: Reinitialise the mixer on port change.
Colin Guthrie
colin at mageia.org
Thu Jul 7 02:55:51 PDT 2011
This allows us to flip from software to hardware volume control as the port's
mixer path dictates.
---
src/modules/alsa/alsa-sink.c | 124 ++++++++++++++++++++++++---------------
src/modules/alsa/alsa-source.c | 123 ++++++++++++++++++++++++---------------
src/pulsecore/sink.c | 27 +++++----
src/pulsecore/sink.h | 1 +
src/pulsecore/source.c | 27 +++++----
src/pulsecore/source.h | 1 +
6 files changed, 185 insertions(+), 118 deletions(-)
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 8f31901..bdcf702 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,64 @@ 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_sink_flags_t flags;
+ pa_assert(u);
+
+ /* Save the current flags so we can tell if they've changed */
+ flags = u->sink->flags;
+
+ 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 {
+ u->sink->get_volume = sink_get_volume_cb;
+ u->sink->set_volume = sink_set_volume_cb;
+ u->sink->write_volume = NULL;
+
+ 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);
+
+ 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));
+
+ if (u->sync_volume) {
+ u->sink->write_volume = sink_write_volume_cb;
+ pa_log_info("Successfully enabled synchronous volume.");
+ }
+ } else {
+ u->sink->flags &= ~PA_SINK_DECIBEL_VOLUME;
+ 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;
+ }
+
+ pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported");
+ }
+
+ 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.");
+ }
+
+ pa_sink_set_flags_from_callbacks(u->sink);
+
+ /* If the flags have changed, let any clients know via a change event */
+ if (flags != u->sink->flags)
+ pa_subscription_post(u->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, u->sink->index);
+}
+
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 +1443,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 +1773,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,47 +1812,21 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_v
return 0;
}
- if (!u->mixer_path->has_volume) {
- pa_log_info("Driver does not support hardware volume control, falling back to software volume control.");
- } else {
+ mixer_volume_init(u);
- 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);
+ /* Will we need to register callbacks? */
+ if (u->mixer_path_set && u->mixer_path_set->paths) {
+ pa_alsa_path *p;
- 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;
+ PA_LLIST_FOREACH(p, u->mixer_path_set->paths) {
+ if (p->has_volume || p->has_mute)
+ need_mixer_callback = TRUE;
}
-
- u->sink->get_volume = sink_get_volume_cb;
- u->sink->set_volume = sink_set_volume_cb;
-
- if (u->mixer_path->has_dB) {
- u->sink->flags |= PA_SINK_DECIBEL_VOLUME;
- if (sync_volume) {
- u->sink->write_volume = sink_write_volume_cb;
- pa_log_info("Successfully enabled synchronous volume.");
- }
- }
-
- pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported");
- }
-
- if (!u->mixer_path->has_mute) {
- 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();
@@ -1909,6 +1936,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();
@@ -2133,7 +2161,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 52dd65e..5feeec1 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,64 @@ 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_source_flags_t flags;
+ pa_assert(u);
+
+ /* Save the current flags so we can tell if they've changed */
+ flags = u->source->flags;
+
+ 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 {
+ u->source->get_volume = source_get_volume_cb;
+ u->source->set_volume = source_set_volume_cb;
+ u->source->write_volume = NULL;
+
+ 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);
+
+ 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));
+
+ if (u->sync_volume) {
+ u->source->write_volume = source_write_volume_cb;
+ pa_log_info("Successfully enabled synchronous volume.");
+ }
+ } else {
+ u->source->flags &= ~PA_SOURCE_DECIBEL_VOLUME;
+ 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;
+ }
+
+ pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported");
+ }
+
+ 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.");
+ }
+
+ pa_source_set_flags_from_callbacks(u->source);
+
+ /* If the flags have changed, let any clients know via a change event */
+ if (flags != u->source->flags)
+ pa_subscription_post(u->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, u->source->index);
+}
+
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 +1318,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 +1548,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,47 +1586,21 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_v
return 0;
}
- if (!u->mixer_path->has_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;
- }
+ mixer_volume_init(u);
- u->source->get_volume = source_get_volume_cb;
- u->source->set_volume = source_set_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) {
- u->source->flags |= PA_SOURCE_DECIBEL_VOLUME;
- if (sync_volume) {
- u->source->write_volume = source_write_volume_cb;
- 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");
- }
-
- if (!u->mixer_path->has_mute) {
- 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();
@@ -1678,6 +1704,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);
@@ -1893,7 +1920,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);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 15c07a5..df5cd78 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -449,17 +449,8 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
return 0;
}
-/* Called from main context */
-void pa_sink_put(pa_sink* s) {
- pa_sink_assert_ref(s);
- pa_assert_ctl_context();
-
- pa_assert(s->state == PA_SINK_INIT);
- pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || s->input_to_master);
-
- /* The following fields must be initialized properly when calling _put() */
- pa_assert(s->asyncmsgq);
- pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
+void pa_sink_set_flags_from_callbacks(pa_sink *s) {
+ pa_assert(s);
/* Generally, flags should be initialized via pa_sink_new(). As a
* special exception we allow some volume related flags to be set
@@ -492,7 +483,21 @@ void pa_sink_put(pa_sink* s) {
if ((s->flags & PA_SINK_DECIBEL_VOLUME) && s->core->flat_volumes)
s->flags |= PA_SINK_FLAT_VOLUME;
+}
+
+/* Called from main context */
+void pa_sink_put(pa_sink* s) {
+ pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+
+ pa_assert(s->state == PA_SINK_INIT);
+ pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || s->input_to_master);
+
+ /* The following fields must be initialized properly when calling _put() */
+ pa_assert(s->asyncmsgq);
+ pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
+ pa_sink_set_flags_from_callbacks(s);
if (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) {
pa_sink *root_sink = s->input_to_master->sink;
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 85c22ec..e4dd6e8 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -339,6 +339,7 @@ pa_sink* pa_sink_new(
pa_sink_new_data *data,
pa_sink_flags_t flags);
+void pa_sink_set_flags_from_callbacks(pa_sink *s);
void pa_sink_put(pa_sink *s);
void pa_sink_unlink(pa_sink* s);
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 121c358..e74dcc2 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -381,17 +381,8 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
return 0;
}
-/* Called from main context */
-void pa_source_put(pa_source *s) {
- pa_source_assert_ref(s);
- pa_assert_ctl_context();
-
- pa_assert(s->state == PA_SOURCE_INIT);
- pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) || s->output_from_master);
-
- /* The following fields must be initialized properly when calling _put() */
- pa_assert(s->asyncmsgq);
- pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
+void pa_source_set_flags_from_callbacks(pa_source *s) {
+ pa_assert(s);
/* Generally, flags should be initialized via pa_source_new(). As a
* special exception we allow some volume related flags to be set
@@ -424,7 +415,21 @@ void pa_source_put(pa_source *s) {
if ((s->flags & PA_SOURCE_DECIBEL_VOLUME) && s->core->flat_volumes)
s->flags |= PA_SOURCE_FLAT_VOLUME;
+}
+
+/* Called from main context */
+void pa_source_put(pa_source *s) {
+ pa_source_assert_ref(s);
+ pa_assert_ctl_context();
+
+ pa_assert(s->state == PA_SOURCE_INIT);
+ pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) || s->output_from_master);
+
+ /* The following fields must be initialized properly when calling _put() */
+ pa_assert(s->asyncmsgq);
+ pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
+ pa_source_set_flags_from_callbacks(s);
if (s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) {
pa_source *root_source = s->output_from_master->source;
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index b68dfd5..d2122b2 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -270,6 +270,7 @@ pa_source* pa_source_new(
pa_source_new_data *data,
pa_source_flags_t flags);
+void pa_source_set_flags_from_callbacks(pa_source *s);
void pa_source_put(pa_source *s);
void pa_source_unlink(pa_source *s);
--
1.7.6
More information about the pulseaudio-discuss
mailing list