summaryrefslogtreecommitdiffstats
path: root/src/modules
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/alsa/alsa-source.c206
-rw-r--r--src/modules/alsa/module-alsa-source.c8
-rw-r--r--src/modules/bluetooth/module-bluetooth-device.c4
-rw-r--r--src/modules/dbus/iface-device.c2
-rw-r--r--src/modules/echo-cancel/module-echo-cancel.c18
-rw-r--r--src/modules/module-virtual-source.c74
-rw-r--r--src/modules/oss/module-oss.c8
7 files changed, 214 insertions, 106 deletions
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 3355fbdc..adec8f64 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -93,6 +93,7 @@ struct userdata {
snd_pcm_t *pcm_handle;
pa_alsa_fdlist *mixer_fdl;
+ pa_alsa_mixer_pdata *mixer_pd;
snd_mixer_t *mixer_handle;
pa_alsa_path_set *mixer_path_set;
pa_alsa_path *mixer_path;
@@ -114,8 +115,8 @@ struct userdata {
pa_usec_t watermark_dec_not_before;
- char *device_name;
- char *control_device;
+ 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;
@@ -185,10 +186,10 @@ static int reserve_init(struct userdata *u, const char *dname) {
if (pa_in_system_mode())
return 0;
- /* We are resuming, try to lock the device */
if (!(rname = pa_alsa_get_reserve_name(dname)))
return 0;
+ /* We are resuming, try to lock the device */
u->reserve = pa_reserve_wrapper_get(u->core, rname);
pa_xfree(rname);
@@ -238,10 +239,10 @@ static int reserve_monitor_init(struct userdata *u, const char *dname) {
if (pa_in_system_mode())
return 0;
- /* We are resuming, try to lock the device */
if (!(rname = pa_alsa_get_reserve_name(dname)))
return 0;
+ /* We are resuming, try to lock the device */
u->monitor = pa_reserve_monitor_wrapper_get(u->core, rname);
pa_xfree(rname);
@@ -256,6 +257,7 @@ static int reserve_monitor_init(struct userdata *u, const char *dname) {
static void fix_min_sleep_wakeup(struct userdata *u) {
size_t max_use, max_use_2;
+
pa_assert(u);
pa_assert(u->use_tsched);
@@ -350,7 +352,7 @@ restart:
u->watermark_dec_not_before = now + TSCHED_WATERMARK_VERIFY_AFTER_USEC;
}
-static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
+static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
pa_usec_t wm, usec;
pa_assert(sleep_usec);
@@ -378,8 +380,6 @@ static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_use
(unsigned long) (*sleep_usec / PA_USEC_PER_MSEC),
(unsigned long) (*process_usec / PA_USEC_PER_MSEC));
#endif
-
- return usec;
}
static int try_recover(struct userdata *u, const char *call, int err) {
@@ -444,9 +444,9 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t
else if (left_to_record > u->watermark_dec_threshold) {
reset_not_before = FALSE;
- /* We decrease the watermark only if have actually been
- * woken up by a timeout. If something else woke us up
- * it's too easy to fulfill the deadlines... */
+ /* We decrease the watermark only if have actually
+ * been woken up by a timeout. If something else woke
+ * us up it's too easy to fulfill the deadlines... */
if (on_timeout)
decrease_watermark(u);
@@ -521,6 +521,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
break;
}
+
if (++j > 10) {
#ifdef DEBUG_TIMING
pa_log_debug("Not filling up, because already too many iterations.");
@@ -536,15 +537,14 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
#endif
for (;;) {
+ pa_memchunk chunk;
+ void *p;
int err;
const snd_pcm_channel_area_t *areas;
snd_pcm_uframes_t offset, frames;
- pa_memchunk chunk;
- void *p;
snd_pcm_sframes_t sframes;
frames = (snd_pcm_uframes_t) (n_bytes / u->frame_size);
-
/* pa_log_debug("%lu frames to read", (unsigned long) frames); */
if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
@@ -822,6 +822,7 @@ static int build_pollfd(struct userdata *u) {
return 0;
}
+/* Called from IO context */
static int suspend(struct userdata *u) {
pa_assert(u);
pa_assert(u->pcm_handle);
@@ -842,6 +843,7 @@ static int suspend(struct userdata *u) {
return 0;
}
+/* Called from IO context */
static int update_sw_params(struct userdata *u) {
snd_pcm_uframes_t avail_min;
int err;
@@ -894,6 +896,7 @@ static int update_sw_params(struct userdata *u) {
return 0;
}
+/* Called from IO context */
static int unsuspend(struct userdata *u) {
pa_sample_spec ss;
int err;
@@ -971,6 +974,7 @@ fail:
return -PA_ERR_IO;
}
+/* Called from IO context */
static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SOURCE(o)->userdata;
@@ -993,6 +997,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
case PA_SOURCE_SUSPENDED: {
int r;
+
pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
if ((r = suspend(u)) < 0)
@@ -1049,7 +1054,7 @@ static int source_set_state_cb(pa_source *s, pa_source_state_t new_state) {
return 0;
}
-static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
+static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
struct userdata *u = snd_mixer_elem_get_callback_private(elem);
pa_assert(u);
@@ -1069,6 +1074,24 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
return 0;
}
+static int io_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
+ struct userdata *u = snd_mixer_elem_get_callback_private(elem);
+
+ pa_assert(u);
+ pa_assert(u->mixer_handle);
+
+ if (mask == SND_CTL_EVENT_MASK_REMOVE)
+ return 0;
+
+ if (u->source->suspend_cause & PA_SUSPEND_SESSION)
+ return 0;
+
+ if (mask & SND_CTL_EVENT_MASK_VALUE)
+ pa_source_update_volume_and_mute(u->source);
+
+ return 0;
+}
+
static void source_get_volume_cb(pa_source *s) {
struct userdata *u = s->userdata;
pa_cvolume r;
@@ -1095,7 +1118,7 @@ static void source_get_volume_cb(pa_source *s) {
if (pa_cvolume_equal(&u->hardware_volume, &r))
return;
- s->volume = u->hardware_volume = r;
+ s->real_volume = u->hardware_volume = r;
/* Hmm, so the hardware volume changed, let's reset our software volume */
if (u->mixer_path->has_dB)
@@ -1106,15 +1129,16 @@ static void source_set_volume_cb(pa_source *s) {
struct userdata *u = s->userdata;
pa_cvolume r;
char vol_str_pcnt[PA_CVOLUME_SNPRINT_MAX];
+ pa_bool_t write_to_hw = (s->flags & PA_SOURCE_SYNC_VOLUME) ? FALSE : TRUE;
pa_assert(u);
pa_assert(u->mixer_path);
pa_assert(u->mixer_handle);
/* Shift up by the base volume */
- pa_sw_cvolume_divide_scalar(&r, &s->volume, s->base_volume);
+ pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume);
- if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, TRUE) < 0)
+ if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, write_to_hw) < 0)
return;
/* Shift down by the base volume, so that 0dB becomes maximum volume */
@@ -1128,7 +1152,7 @@ static void source_set_volume_cb(pa_source *s) {
char vol_str_db[PA_SW_CVOLUME_SNPRINT_DB_MAX];
/* Match exactly what the user requested by software */
- pa_sw_cvolume_divide(&new_soft_volume, &s->volume, &u->hardware_volume);
+ pa_sw_cvolume_divide(&new_soft_volume, &s->real_volume, &u->hardware_volume);
/* If the adjustment to do in software is only minimal we
* can skip it. That saves us CPU at the expense of a bit of
@@ -1137,8 +1161,8 @@ static void source_set_volume_cb(pa_source *s) {
(pa_cvolume_min(&new_soft_volume) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) &&
(pa_cvolume_max(&new_soft_volume) <= (PA_VOLUME_NORM + VOLUME_ACCURACY));
- pa_log_debug("Requested volume: %s", pa_cvolume_snprint(vol_str_pcnt, sizeof(vol_str_pcnt), &s->volume));
- pa_log_debug(" in dB: %s", pa_sw_cvolume_snprint_dB(vol_str_db, sizeof(vol_str_db), &s->volume));
+ pa_log_debug("Requested volume: %s", pa_cvolume_snprint(vol_str_pcnt, sizeof(vol_str_pcnt), &s->real_volume));
+ pa_log_debug(" in dB: %s", pa_sw_cvolume_snprint_dB(vol_str_db, sizeof(vol_str_db), &s->real_volume));
pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(vol_str_pcnt, sizeof(vol_str_pcnt), &u->hardware_volume));
pa_log_debug(" in dB: %s", pa_sw_cvolume_snprint_dB(vol_str_db, sizeof(vol_str_db), &u->hardware_volume));
pa_log_debug("Calculated software volume: %s (accurate-enough=%s)",
@@ -1155,7 +1179,49 @@ static void source_set_volume_cb(pa_source *s) {
/* We can't match exactly what the user requested, hence let's
* at least tell the user about it */
- s->volume = r;
+ s->real_volume = r;
+ }
+}
+
+static void source_write_volume_cb(pa_source *s) {
+ struct userdata *u = s->userdata;
+ pa_cvolume hw_vol = s->thread_info.current_hw_volume;
+
+ pa_assert(u);
+ pa_assert(u->mixer_path);
+ pa_assert(u->mixer_handle);
+ pa_assert(s->flags & PA_SOURCE_SYNC_VOLUME);
+
+ /* Shift up by the base volume */
+ pa_sw_cvolume_divide_scalar(&hw_vol, &hw_vol, s->base_volume);
+
+ if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, TRUE) < 0)
+ pa_log_error("Writing HW volume failed");
+ else {
+ pa_cvolume tmp_vol;
+ pa_bool_t accurate_enough;
+
+ /* Shift down by the base volume, so that 0dB becomes maximum volume */
+ pa_sw_cvolume_multiply_scalar(&hw_vol, &hw_vol, s->base_volume);
+
+ pa_sw_cvolume_divide(&tmp_vol, &hw_vol, &s->thread_info.current_hw_volume);
+ accurate_enough =
+ (pa_cvolume_min(&tmp_vol) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) &&
+ (pa_cvolume_max(&tmp_vol) <= (PA_VOLUME_NORM + VOLUME_ACCURACY));
+
+ if (!accurate_enough) {
+ union {
+ char db[2][PA_SW_CVOLUME_SNPRINT_DB_MAX];
+ char pcnt[2][PA_CVOLUME_SNPRINT_MAX];
+ } vol;
+
+ pa_log_debug("Written HW volume did not match with the request: %s (request) != %s",
+ pa_cvolume_snprint(vol.pcnt[0], sizeof(vol.pcnt[0]), &s->thread_info.current_hw_volume),
+ pa_cvolume_snprint(vol.pcnt[1], sizeof(vol.pcnt[1]), &hw_vol));
+ pa_log_debug(" in dB: %s (request) != %s",
+ pa_sw_cvolume_snprint_dB(vol.db[0], sizeof(vol.db[0]), &s->thread_info.current_hw_volume),
+ pa_sw_cvolume_snprint_dB(vol.db[1], sizeof(vol.db[1]), &hw_vol));
+ }
}
}
@@ -1220,7 +1286,9 @@ static int source_set_port_cb(pa_source *s, pa_device_port *p) {
static void source_update_requested_latency_cb(pa_source *s) {
struct userdata *u = s->userdata;
pa_assert(u);
- pa_assert(u->use_tsched);
+ pa_assert(u->use_tsched); /* only when timer scheduling is used
+ * we can dynamically adjust the
+ * latency */
if (!u->pcm_handle)
return;
@@ -1243,6 +1311,7 @@ static void thread_func(void *userdata) {
for (;;) {
int ret;
+ pa_usec_t rtpoll_sleep = 0;
#ifdef DEBUG_TIMING
pa_log_debug("Loop");
@@ -1291,17 +1360,29 @@ static void thread_func(void *userdata) {
/* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
/* We don't trust the conversion, so we wake up whatever comes first */
- pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(sleep_usec, cusec));
+ rtpoll_sleep = PA_MIN(sleep_usec, cusec);
}
- } else if (u->use_tsched)
+ }
+
+ if (u->source->flags & PA_SOURCE_SYNC_VOLUME) {
+ pa_usec_t volume_sleep;
+ pa_source_volume_change_apply(u->source, &volume_sleep);
+ if (volume_sleep > 0)
+ rtpoll_sleep = PA_MIN(volume_sleep, rtpoll_sleep);
+ }
- /* OK, we're in an invalid state, let's disable our timers */
+ if (rtpoll_sleep > 0)
+ pa_rtpoll_set_timer_relative(u->rtpoll, rtpoll_sleep);
+ else
pa_rtpoll_set_timer_disabled(u->rtpoll);
/* Hmm, nothing to do. Let's sleep */
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
goto fail;
+ if (u->source->flags & PA_SOURCE_SYNC_VOLUME)
+ pa_source_volume_change_apply(u->source, NULL);
+
if (ret == 0)
goto finish;
@@ -1419,7 +1500,7 @@ fail:
}
}
-static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
+static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_volume) {
pa_assert(u);
if (!u->mixer_handle)
@@ -1475,8 +1556,17 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
u->source->get_volume = source_get_volume_cb;
u->source->set_volume = source_set_volume_cb;
+ u->source->write_volume = source_write_volume_cb;
+
+ u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL;
+ if (u->mixer_path->has_dB) {
+ u->source->flags |= PA_SOURCE_DECIBEL_VOLUME;
+ if (sync_volume) {
+ u->source->flags |= PA_SOURCE_SYNC_VOLUME;
+ pa_log_info("Successfully enabled synchronous volume.");
+ }
+ }
- u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->mixer_path->has_dB ? PA_SOURCE_DECIBEL_VOLUME : 0);
pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported");
}
@@ -1489,17 +1579,31 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
pa_log_info("Using hardware mute control.");
}
- u->mixer_fdl = pa_alsa_fdlist_new();
+ if (u->source->flags & (PA_SOURCE_HW_VOLUME_CTRL|PA_SOURCE_HW_MUTE_CTRL)) {
+ 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();
+ mixer_callback = io_mixer_callback;
- if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, u->core->mainloop) < 0) {
- pa_log("Failed to initialize file descriptor monitoring");
- return -1;
- }
+ if (pa_alsa_set_mixer_rtpoll(u->mixer_pd, u->mixer_handle, u->rtpoll) < 0) {
+ pa_log("Failed to initialize file descriptor monitoring");
+ return -1;
+ }
+ } else {
+ u->mixer_fdl = pa_alsa_fdlist_new();
+ mixer_callback = ctl_mixer_callback;
- if (u->mixer_path_set)
- pa_alsa_path_set_set_callback(u->mixer_path_set, u->mixer_handle, mixer_callback, u);
- else
- pa_alsa_path_set_callback(u->mixer_path, u->mixer_handle, mixer_callback, u);
+ if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, u->core->mainloop) < 0) {
+ pa_log("Failed to initialize file descriptor monitoring");
+ return -1;
+ }
+ }
+
+ if (u->mixer_path_set)
+ pa_alsa_path_set_set_callback(u->mixer_path_set, u->mixer_handle, mixer_callback, u);
+ else
+ pa_alsa_path_set_callback(u->mixer_path, u->mixer_handle, mixer_callback, u);
+ }
return 0;
}
@@ -1513,7 +1617,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;
+ pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE, namereg_fail = FALSE, sync_volume = FALSE;
pa_source_new_data data;
pa_alsa_profile_set *profile_set = NULL;
@@ -1523,7 +1627,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
ss = m->core->default_sample_spec;
map = m->core->default_channel_map;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
- pa_log("Failed to parse sample specification");
+ pa_log("Failed to parse sample specification and channel map");
goto fail;
}
@@ -1557,7 +1661,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
}
if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
- pa_log("Failed to parse timer_scheduling argument.");
+ pa_log("Failed to parse tsched argument.");
goto fail;
}
@@ -1566,6 +1670,12 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
goto fail;
}
+ sync_volume = m->core->sync_volume;
+ if (pa_modargs_get_value_boolean(ma, "sync_volume", &sync_volume) < 0) {
+ pa_log("Failed to parse sync_volume argument.");
+ goto fail;
+ }
+
use_tsched = pa_alsa_may_tsched(use_tsched);
u = pa_xnew0(struct userdata, 1);
@@ -1728,6 +1838,18 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
goto fail;
}
+ if (pa_modargs_get_value_u32(ma, "sync_volume_safety_margin",
+ &u->source->thread_info.volume_change_safety_margin) < 0) {
+ pa_log("Failed to parse sync_volume_safety_margin parameter");
+ goto fail;
+ }
+
+ if (pa_modargs_get_value_s32(ma, "sync_volume_extra_delay",
+ &u->source->thread_info.volume_change_extra_delay) < 0) {
+ pa_log("Failed to parse sync_volume_extra_delay parameter");
+ goto fail;
+ }
+
u->source->parent.process_msg = source_process_msg;
if (u->use_tsched)
u->source->update_requested_latency = source_update_requested_latency_cb;
@@ -1776,7 +1898,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) < 0)
+ if (setup_mixer(u, ignore_dB, sync_volume) < 0)
goto fail;
pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
@@ -1785,6 +1907,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
pa_log("Failed to create thread.");
goto fail;
}
+
/* Get initial mixer settings */
if (data.volume_is_set) {
if (u->source->set_volume)
@@ -1836,6 +1959,9 @@ static void userdata_free(struct userdata *u) {
if (u->source)
pa_source_unref(u->source);
+ if (u->mixer_pd)
+ pa_alsa_mixer_pdata_free(u->mixer_pd);
+
if (u->alsa_rtpoll_item)
pa_rtpoll_item_free(u->alsa_rtpoll_item);
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
index 90ffea57..478a2e8c 100644
--- a/src/modules/alsa/module-alsa-source.c
+++ b/src/modules/alsa/module-alsa-source.c
@@ -79,7 +79,10 @@ PA_MODULE_USAGE(
"tsched_buffer_size=<buffer size when using timer based scheduling> "
"tsched_buffer_watermark=<upper fill watermark> "
"ignore_dB=<ignore dB information from the device?> "
- "control=<name of mixer control>");
+ "control=<name of mixer control>"
+ "sync_volume=<syncronize sw and hw voluchanges in IO-thread?> "
+ "sync_volume_safety_margin=<usec adjustment depending on volume direction> "
+ "sync_volume_extra_delay=<usec adjustment to HW volume changes>");
static const char* const valid_modargs[] = {
"name",
@@ -100,6 +103,9 @@ static const char* const valid_modargs[] = {
"tsched_buffer_watermark",
"ignore_dB",
"control",
+ "sync_volume",
+ "sync_volume_safety_margin",
+ "sync_volume_extra_delay",
NULL
};
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 1ff752a9..0639afd6 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1868,7 +1868,7 @@ static void source_set_volume_cb(pa_source *s) {
pa_assert(u->source == s);
pa_assert(u->profile == PROFILE_HSP);
- gain = (pa_cvolume_max(&s->volume) * HSP_MAX_GAIN) / PA_VOLUME_NORM;
+ gain = (pa_cvolume_max(&s->real_volume) * HSP_MAX_GAIN) / PA_VOLUME_NORM;
if (gain > HSP_MAX_GAIN)
gain = HSP_MAX_GAIN;
@@ -1879,7 +1879,7 @@ static void source_set_volume_cb(pa_source *s) {
if (volume < PA_VOLUME_NORM)
volume++;
- pa_cvolume_set(&s->volume, u->sample_spec.channels, volume);
+ pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetMicrophoneGain"));
pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID));
diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
index c5ba88e0..652790f0 100644
--- a/src/modules/dbus/iface-device.c
+++ b/src/modules/dbus/iface-device.c
@@ -447,7 +447,7 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessag
if (d->type == DEVICE_TYPE_SINK)
pa_sink_set_volume(d->sink, &new_vol, TRUE, TRUE);
else
- pa_source_set_volume(d->source, &new_vol, TRUE);
+ pa_source_set_volume(d->source, &new_vol, TRUE, TRUE);
pa_dbus_send_empty_reply(conn, msg);
}
diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
index 3d22ef84..e5ee5b13 100644
--- a/src/modules/echo-cancel/module-echo-cancel.c
+++ b/src/modules/echo-cancel/module-echo-cancel.c
@@ -526,8 +526,7 @@ static void source_set_volume_cb(pa_source *s) {
!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
return;
- /* FIXME, no volume control in source_output, set volume at the master */
- pa_source_set_volume(u->source_output->source, &s->volume, TRUE);
+ pa_source_output_set_volume(u->source_output, &s->real_volume, s->save_volume, TRUE);
}
/* Called from main context */
@@ -546,6 +545,7 @@ static void sink_set_volume_cb(pa_sink *s) {
static void source_get_volume_cb(pa_source *s) {
struct userdata *u;
+ pa_cvolume v;
pa_source_assert_ref(s);
pa_assert_se(u = s->userdata);
@@ -554,18 +554,16 @@ static void source_get_volume_cb(pa_source *s) {
!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
return;
- /* FIXME, no volume control in source_output, get the info from the master */
- pa_source_get_volume(u->source_output->source, TRUE);
+ pa_source_output_get_volume(u->source_output, &v, TRUE);
- if (pa_cvolume_equal(&s->volume,&u->source_output->source->volume))
+ if (pa_cvolume_equal(&s->real_volume, &v))
/* no change */
return;
- s->volume = u->source_output->source->volume;
+ s->real_volume = v;
pa_source_set_soft_volume(s, NULL);
}
-
/* Called from main context */
static void source_set_mute_cb(pa_source *s) {
struct userdata *u;
@@ -577,8 +575,7 @@ static void source_set_mute_cb(pa_source *s) {
!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
return;
- /* FIXME, no volume control in source_output, set mute at the master */
- pa_source_set_mute(u->source_output->source, TRUE, TRUE);
+ pa_source_output_set_mute(u->source_output, s->muted, s->save_muted);
}
/* Called from main context */
@@ -606,8 +603,7 @@ static void source_get_mute_cb(pa_source *s) {
!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
return;
- /* FIXME, no volume control in source_output, get the info from the master */
- pa_source_get_mute(u->source_output->source, TRUE);
+ pa_source_output_get_mute(u->source_output);
}
/* must be called from the input thread context */
diff --git a/src/modules/module-virtual-source.c b/src/modules/module-virtual-source.c
index 170fa4e0..680e71a1 100644
--- a/src/modules/module-virtual-source.c
+++ b/src/modules/module-virtual-source.c
@@ -61,6 +61,8 @@ PA_MODULE_USAGE(
"rate=<sample rate> "
"channels=<number of channels> "
"channel_map=<channel map> "
+ "use_volume_sharing=<yes or no> "
+ "force_flat_volume=<yes or no> "
));
#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
@@ -96,6 +98,8 @@ static const char* const valid_modargs[] = {
"rate",
"channels",
"channel_map",
+ "use_volume_sharing",
+ "force_flat_volume",
NULL
};
@@ -241,32 +245,9 @@ static void source_set_volume_cb(pa_source *s) {
!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
return;
- /* FIXME, no volume control in source_output, set volume at the master */
- pa_source_set_volume(u->source_output->source, &s->volume, TRUE);
+ pa_source_output_set_volume(u->source_output, &s->real_volume, s->save_volume, TRUE);
}
-static void source_get_volume_cb(pa_source *s) {
- struct userdata *u;
-
- pa_source_assert_ref(s);
- pa_assert_se(u = s->userdata);
-
- if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
- !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
- return;
-
- /* FIXME, no volume control in source_output, get the info from the master */
- pa_source_get_volume(u->source_output->source, TRUE);
-
- if (pa_cvolume_equal(&s->volume,&u->source_output->source->volume))
- /* no change */
- return;
-
- s->volume = u->source_output->source->volume;
- pa_source_set_soft_volume(s, NULL);
-}
-
-
/* Called from main context */
static void source_set_mute_cb(pa_source *s) {
struct userdata *u;
@@ -278,23 +259,7 @@ static void source_set_mute_cb(pa_source *s) {
!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
return;
- /* FIXME, no volume control in source_output, set mute at the master */
- pa_source_set_mute(u->source_output->source, TRUE, TRUE);
-}
-
-/* Called from main context */
-static void source_get_mute_cb(pa_source *s) {
- struct userdata *u;
-
- pa_source_assert_ref(s);
- pa_assert_se(u = s->userdata);
-
- if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
- !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
- return;
-
- /* FIXME, no volume control in source_output, get the info from the master */
- pa_source_get_mute(u->source_output->source, TRUE);
+ pa_source_output_set_mute(u->source_output, s->muted, s->save_muted);
}
/* Called from input thread context */
@@ -538,6 +503,8 @@ int pa__init(pa_module*m) {
pa_source *master=NULL;
pa_source_output_new_data source_output_data;
pa_source_new_data source_data;
+ pa_bool_t use_volume_sharing = FALSE;
+ pa_bool_t force_flat_volume = FALSE;
/* optional for uplink_sink */
pa_sink_new_data sink_data;
@@ -565,6 +532,20 @@ int pa__init(pa_module*m) {
goto fail;
}
+ if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) {
+ pa_log("use_volume_sharing= expects a boolean argument");
+ goto fail;
+ }
+
+ if (pa_modargs_get_value_boolean(ma, "force_flat_volume", &force_flat_volume) < 0) {
+ pa_log("force_flat_volume= expects a boolean argument");
+ goto fail;
+ }
+
+ if (use_volume_sharing && force_flat_volume) {
+ pa_log("Flat volume can't be forced when using volume sharing.");
+ goto fail;
+ }
u = pa_xnew0(struct userdata, 1);
if (!u) {
@@ -605,9 +586,10 @@ int pa__init(pa_module*m) {
pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Virtual Source %s on %s", source_data.name, z ? z : master->name);
}
- u->source = pa_source_new(m->core, &source_data,
- PA_SOURCE_HW_MUTE_CTRL|PA_SOURCE_HW_VOLUME_CTRL|PA_SOURCE_DECIBEL_VOLUME|
- (master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY)));
+ u->source = pa_source_new(m->core, &source_data, (master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY))
+ | (use_volume_sharing ? PA_SOURCE_SHARE_VOLUME_WITH_MASTER : 0)
+ | (force_flat_volume ? PA_SOURCE_FLAT_VOLUME : 0));
+
pa_source_new_data_done(&source_data);
if (!u->source) {
@@ -618,10 +600,8 @@ int pa__init(pa_module*m) {
u->source->parent.process_msg = source_process_msg_cb;
u->source->set_state = source_set_state_cb;
u->source->update_requested_latency = source_update_requested_latency_cb;
- u->source->set_volume = source_set_volume_cb;
+ u->source->set_volume = use_volume_sharing ? NULL : source_set_volume_cb;
u->source->set_mute = source_set_mute_cb;
- u->source->get_volume = source_get_volume_cb;
- u->source->get_mute = source_get_mute_cb;
u->source->userdata = u;
pa_source_set_asyncmsgq(u->source, master->asyncmsgq);
diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c
index 84dbbdaf..dea5628f 100644
--- a/src/modules/oss/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -848,11 +848,11 @@ static void source_get_volume(pa_source *s) {
pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
if (u->mixer_devmask & SOUND_MASK_IGAIN)
- if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->volume) >= 0)
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->real_volume) >= 0)
return;
if (u->mixer_devmask & SOUND_MASK_RECLEV)
- if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->volume) >= 0)
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->real_volume) >= 0)
return;
pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
@@ -866,11 +866,11 @@ static void source_set_volume(pa_source *s) {
pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
if (u->mixer_devmask & SOUND_MASK_IGAIN)
- if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->volume) >= 0)
+ if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->real_volume) >= 0)
return;
if (u->mixer_devmask & SOUND_MASK_RECLEV)
- if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->volume) >= 0)
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->real_volume) >= 0)
return;
pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));