summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2009-08-19 02:55:02 +0200
committerLennart Poettering <lennart@poettering.net>2009-08-19 02:55:02 +0200
commit8c31974f56ebbbfc1a4978150026acf77c32689e (patch)
tree3078845a45638377a5d181695ed352dd32bf44cb /src
parent5207e191424675df74059aaf30f9b1292a05cb5d (diff)
sink: volume handling rework, new flat volume logic
- We now implement a logic where the sink maintains two distinct volumes: the 'reference' volume which is shown to the users, and the 'real' volume, which is configured to the hardware. The latter is configured to the max of all streams. Volume changes on sinks are propagated back to the streams proportional to the reference volume change. Volume changes on sink inputs are forwarded to the sink by 'pushing' the volume if necessary. This renames the old 'virtual_volume' to 'real_volume'. The 'reference_volume' is now the one exposed to users. By this logic the sink volume visible to the user, will always be the "upper" boundary for everything that is played. Saved/restored stream volumes are measured relative to this boundary, the factor here is always < 1.0. - introduce accuracy for sink volumes, similar to the accuracy we already have for source volumes. - other cleanups.
Diffstat (limited to 'src')
-rw-r--r--src/modules/alsa/alsa-sink.c2
-rw-r--r--src/modules/module-device-restore.c2
-rw-r--r--src/modules/module-lirc.c2
-rw-r--r--src/modules/module-match.c2
-rw-r--r--src/modules/module-mmkbd-evdev.c6
-rw-r--r--src/modules/module-tunnel.c4
-rw-r--r--src/modules/oss/module-oss.c8
-rw-r--r--src/modules/raop/module-raop-sink.c6
-rw-r--r--src/pulsecore/cli-command.c4
-rw-r--r--src/pulsecore/cli-text.c6
-rw-r--r--src/pulsecore/core.h1
-rw-r--r--src/pulsecore/protocol-native.c4
-rw-r--r--src/pulsecore/sink-input.c164
-rw-r--r--src/pulsecore/sink-input.h15
-rw-r--r--src/pulsecore/sink.c381
-rw-r--r--src/pulsecore/sink.h12
16 files changed, 333 insertions, 286 deletions
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 12538368..e3707ae7 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1009,7 +1009,7 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
return 0;
if (mask & SND_CTL_EVENT_MASK_VALUE) {
- pa_sink_get_volume(u->sink, TRUE, FALSE);
+ pa_sink_get_volume(u->sink, TRUE);
pa_sink_get_mute(u->sink, TRUE);
}
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index 120b762c..da6c9666 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -218,7 +218,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
if (sink->save_volume) {
entry.channel_map = sink->channel_map;
- entry.volume = *pa_sink_get_volume(sink, FALSE, TRUE);
+ entry.volume = *pa_sink_get_volume(sink, FALSE);
entry.volume_valid = TRUE;
}
diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c
index 2bb8014d..fdfdc797 100644
--- a/src/modules/module-lirc.c
+++ b/src/modules/module-lirc.c
@@ -120,7 +120,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
pa_log("Failed to get sink '%s'", u->sink_name);
else {
int i;
- pa_cvolume cv = *pa_sink_get_volume(s, FALSE, FALSE);
+ pa_cvolume cv = *pa_sink_get_volume(s, FALSE);
#define DELTA (PA_VOLUME_NORM/20)
diff --git a/src/modules/module-match.c b/src/modules/module-match.c
index 14e01127..0bd781d2 100644
--- a/src/modules/module-match.c
+++ b/src/modules/module-match.c
@@ -216,7 +216,7 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v
pa_cvolume cv;
pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
- pa_sink_input_set_volume(si, &cv, TRUE, TRUE);
+ pa_sink_input_set_volume(si, &cv, TRUE, FALSE);
}
}
}
diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c
index b30fae51..7be48700 100644
--- a/src/modules/module-mmkbd-evdev.c
+++ b/src/modules/module-mmkbd-evdev.c
@@ -102,7 +102,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
pa_log("Failed to get sink '%s'", u->sink_name);
else {
int i;
- pa_cvolume cv = *pa_sink_get_volume(s, FALSE, FALSE);
+ pa_cvolume cv = *pa_sink_get_volume(s, FALSE);
#define DELTA (PA_VOLUME_NORM/20)
@@ -115,7 +115,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
cv.values[i] = PA_VOLUME_MAX;
}
- pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE);
+ pa_sink_set_volume(s, &cv, TRUE, TRUE);
break;
case DOWN:
@@ -126,7 +126,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
cv.values[i] = PA_VOLUME_MUTED;
}
- pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE);
+ pa_sink_set_volume(s, &cv, TRUE, TRUE);
break;
case MUTE_TOGGLE:
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index eaccea4e..5ccb81d0 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -1162,7 +1162,7 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag
pa_assert(u->sink);
if ((u->version < 11 || !!mute == !!u->sink->muted) &&
- pa_cvolume_equal(&volume, &u->sink->virtual_volume))
+ pa_cvolume_equal(&volume, &u->sink->real_volume))
return;
pa_sink_volume_changed(u->sink, &volume);
@@ -1763,7 +1763,7 @@ static void sink_set_volume(pa_sink *sink) {
pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);
pa_tagstruct_putu32(t, tag = u->ctag++);
pa_tagstruct_putu32(t, u->device_index);
- pa_tagstruct_put_cvolume(t, &sink->virtual_volume);
+ pa_tagstruct_put_cvolume(t, &sink->real_volume);
pa_pstream_send_tagstruct(u->pstream, t);
}
diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c
index 0848d43b..71536260 100644
--- a/src/modules/oss/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -812,11 +812,11 @@ static void sink_get_volume(pa_sink *s) {
pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
if (u->mixer_devmask & SOUND_MASK_VOLUME)
- if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->virtual_volume) >= 0)
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->real_volume) >= 0)
return;
if (u->mixer_devmask & SOUND_MASK_PCM)
- if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->virtual_volume) >= 0)
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->real_volume) >= 0)
return;
pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
@@ -830,11 +830,11 @@ static void sink_set_volume(pa_sink *s) {
pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
if (u->mixer_devmask & SOUND_MASK_VOLUME)
- if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->virtual_volume) >= 0)
+ if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->real_volume) >= 0)
return;
if (u->mixer_devmask & SOUND_MASK_PCM)
- if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->virtual_volume) >= 0)
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->real_volume) >= 0)
return;
pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c
index 9699132d..ac48ab10 100644
--- a/src/modules/raop/module-raop-sink.c
+++ b/src/modules/raop/module-raop-sink.c
@@ -283,15 +283,15 @@ static void sink_set_volume_cb(pa_sink *s) {
/* Calculate the max volume of all channels.
We'll use this as our (single) volume on the APEX device and emulate
any variation in channel volumes in software */
- v = pa_cvolume_max(&s->virtual_volume);
+ v = pa_cvolume_max(&s->real_volume);
/* Create a pa_cvolume version of our single value */
pa_cvolume_set(&hw, s->sample_spec.channels, v);
/* Perform any software manipulation of the volume needed */
- pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &hw);
+ pa_sw_cvolume_divide(&s->soft_volume, &s->real_volume, &hw);
- pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume));
+ pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->real_volume));
pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &hw));
pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume));
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index e2c3c066..6ec74647 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -530,7 +530,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
}
pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
- pa_sink_set_volume(sink, &cvolume, TRUE, TRUE, TRUE, TRUE);
+ pa_sink_set_volume(sink, &cvolume, TRUE, TRUE);
return 0;
}
@@ -1586,7 +1586,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
nl = 1;
}
- pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, FALSE, TRUE)));
+ pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, FALSE)));
pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink, FALSE)));
pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));
}
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index a5530991..c7a178d6 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -262,10 +262,10 @@ char *pa_sink_list_to_string(pa_core *c) {
sink->suspend_cause & PA_SUSPEND_APPLICATION ? "APPLICATION " : "",
sink->suspend_cause & PA_SUSPEND_IDLE ? "IDLE " : "",
sink->suspend_cause & PA_SUSPEND_SESSION ? "SESSION" : "",
- pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE, FALSE)),
+ pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)),
sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "",
- sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE, FALSE)) : "",
- pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE, FALSE), &sink->channel_map),
+ sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE)) : "",
+ pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE), &sink->channel_map),
pa_volume_snprint(v, sizeof(v), sink->base_volume),
sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "",
sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), sink->base_volume) : "",
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index e7abd61b..f6ec7122 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -83,7 +83,6 @@ typedef enum pa_core_hook {
PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL,
PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
- PA_CORE_HOOK_SINK_INPUT_SET_VOLUME,
PA_CORE_HOOK_SINK_INPUT_SEND_EVENT,
PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 280707e2..b1285e15 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -2840,7 +2840,7 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
PA_TAG_SAMPLE_SPEC, &fixed_ss,
PA_TAG_CHANNEL_MAP, &sink->channel_map,
PA_TAG_U32, sink->module ? sink->module->index : PA_INVALID_INDEX,
- PA_TAG_CVOLUME, pa_sink_get_volume(sink, FALSE, FALSE),
+ PA_TAG_CVOLUME, pa_sink_get_volume(sink, FALSE),
PA_TAG_BOOLEAN, pa_sink_get_mute(sink, FALSE),
PA_TAG_U32, sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
PA_TAG_STRING, sink->monitor_source ? sink->monitor_source->name : NULL,
@@ -3388,7 +3388,7 @@ static void command_set_volume(
if (sink) {
pa_log_debug("Client %s changes volume of sink %s.", client_name, sink->name);
- pa_sink_set_volume(sink, &volume, TRUE, TRUE, TRUE, TRUE);
+ pa_sink_set_volume(sink, &volume, TRUE, TRUE);
} else if (source) {
pa_log_debug("Client %s changes volume of sink %s.", client_name, source->name);
pa_source_set_volume(source, &volume, TRUE);
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index f6d9ac73..a29334f9 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -47,6 +47,7 @@
static PA_DEFINE_CHECK_TYPE(pa_sink_input, pa_msgobject);
static void sink_input_free(pa_object *o);
+static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v);
pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data) {
pa_assert(data);
@@ -270,18 +271,20 @@ int pa_sink_input_new(
i->channel_map = data->channel_map;
if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !data->volume_is_absolute) {
+ pa_cvolume remapped;
+
/* When the 'absolute' bool is not set then we'll treat the volume
* as relative to the sink volume even in flat volume mode */
-
- pa_cvolume v = data->sink->reference_volume;
- pa_cvolume_remap(&v, &data->sink->channel_map, &data->channel_map);
- pa_sw_cvolume_multiply(&i->virtual_volume, &data->volume, &v);
+ remapped = data->sink->reference_volume;
+ pa_cvolume_remap(&remapped, &data->sink->channel_map, &data->channel_map);
+ pa_sw_cvolume_multiply(&i->volume, &data->volume, &remapped);
} else
- i->virtual_volume = data->volume;
+ i->volume = data->volume;
i->volume_factor = data->volume_factor;
- pa_cvolume_init(&i->soft_volume);
- memset(i->relative_volume, 0, sizeof(i->relative_volume));
+ i->real_ratio = i->reference_ratio = data->volume;
+ pa_cvolume_reset(&i->soft_volume, i->sample_spec.channels);
+ pa_cvolume_reset(&i->real_ratio, i->sample_spec.channels);
i->save_volume = data->save_volume;
i->save_sink = data->save_sink;
i->save_muted = data->save_muted;
@@ -445,11 +448,8 @@ void pa_sink_input_unlink(pa_sink_input *i) {
if (linked && i->sink) {
/* We might need to update the sink's volume if we are in flat volume mode. */
- if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume new_volume;
- pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
- }
+ if (i->sink->flags & PA_SINK_FLAT_VOLUME)
+ pa_sink_set_volume(i->sink, NULL, FALSE, FALSE);
if (i->sink->asyncmsgq)
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0);
@@ -526,12 +526,10 @@ void pa_sink_input_put(pa_sink_input *i) {
i->state = state;
/* We might need to update the sink's volume if we are in flat volume mode. */
- if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume new_volume;
- pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
- } else
- pa_sink_input_set_relative_volume(i, &i->virtual_volume);
+ if (i->sink->flags & PA_SINK_FLAT_VOLUME)
+ pa_sink_set_volume(i->sink, NULL, FALSE, i->save_volume);
+ else
+ set_real_ratio(i, &i->volume);
i->thread_info.soft_volume = i->soft_volume;
i->thread_info.muted = i->muted;
@@ -910,6 +908,27 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
}
/* Called from main context */
+static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) {
+ pa_sink_input_assert_ref(i);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+ pa_assert(!v || pa_cvolume_compatible(v, &i->sample_spec));
+
+ /* This basically calculates:
+ *
+ * i->real_ratio := v
+ * i->soft_volume := i->real_ratio * i->volume_factor */
+
+ if (v)
+ i->real_ratio = *v;
+ else
+ pa_cvolume_reset(&i->real_ratio, i->sample_spec.channels);
+
+ pa_sw_cvolume_multiply(&i->soft_volume, &i->real_ratio, &i->volume_factor);
+ /* We don't copy the data to the thread_info data. That's left for someone else to do */
+}
+
+/* Called from main context */
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) {
pa_cvolume v;
@@ -926,29 +945,24 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
volume = pa_sw_cvolume_multiply(&v, &v, volume);
}
- if (pa_cvolume_equal(volume, &i->virtual_volume))
+ if (pa_cvolume_equal(volume, &i->volume)) {
+ i->save_volume = i->save_volume || save;
return;
+ }
- i->virtual_volume = *volume;
+ i->volume = *volume;
i->save_volume = save;
- if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume new_volume;
-
+ if (i->sink->flags & PA_SINK_FLAT_VOLUME)
/* We are in flat volume mode, so let's update all sink input
* volumes and update the flat volume of the sink */
- pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE, FALSE, FALSE);
-
- } else {
+ pa_sink_set_volume(i->sink, NULL, TRUE, save);
+ else {
/* OK, we are in normal volume mode. The volume only affects
* ourselves */
- pa_sink_input_set_relative_volume(i, volume);
-
- /* Hooks have the ability to play games with i->soft_volume */
- pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+ set_real_ratio(i, volume);
/* Copy the new soft_volume to the thread_info struct */
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
@@ -964,68 +978,15 @@ pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bo
pa_assert_ctl_context();
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) {
- pa_cvolume v = i->sink->reference_volume;
- pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
- pa_sw_cvolume_divide(volume, &i->virtual_volume, &v);
- } else
- *volume = i->virtual_volume;
+ if (absolute || !(i->sink->flags & PA_SINK_FLAT_VOLUME))
+ *volume = i->volume;
+ else
+ *volume = i->reference_ratio;
return volume;
}
/* Called from main context */
-pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v) {
- unsigned c;
-
- pa_sink_input_assert_ref(i);
- pa_assert_ctl_context();
- pa_assert(v);
- pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
-
- /* This always returns the relative volume. Converts the float
- * version into a pa_cvolume */
-
- v->channels = i->sample_spec.channels;
-
- for (c = 0; c < v->channels; c++)
- v->values[c] = pa_sw_volume_from_linear(i->relative_volume[c]);
-
- return v;
-}
-
-/* Called from main context */
-void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v) {
- unsigned c;
- pa_cvolume _v;
-
- pa_sink_input_assert_ref(i);
- pa_assert_ctl_context();
- pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- pa_assert(!v || pa_cvolume_compatible(v, &i->sample_spec));
-
- if (!v)
- v = pa_cvolume_reset(&_v, i->sample_spec.channels);
-
- /* This basically calculates:
- *
- * i->relative_volume := v
- * i->soft_volume := i->relative_volume * i->volume_factor */
-
- i->soft_volume.channels = i->sample_spec.channels;
-
- for (c = 0; c < i->sample_spec.channels; c++) {
- i->relative_volume[c] = pa_sw_volume_to_linear(v->values[c]);
-
- i->soft_volume.values[c] = pa_sw_volume_from_linear(
- i->relative_volume[c] *
- pa_sw_volume_to_linear(i->volume_factor.values[c]));
- }
-
- /* We don't copy the data to the thread_info data. That's left for someone else to do */
-}
-
-/* Called from main context */
void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save) {
pa_sink_input_assert_ref(i);
pa_assert_ctl_context();
@@ -1198,20 +1159,10 @@ int pa_sink_input_start_move(pa_sink_input *i) {
if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
pa_assert_se(i->sink->n_corked-- >= 1);
- if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume new_volume;
-
- /* Make the virtual volume relative */
- pa_sink_input_get_relative_volume(i, &i->virtual_volume);
-
- /* And reset the the relative volume */
- pa_sink_input_set_relative_volume(i, NULL);
-
+ if (i->sink->flags & PA_SINK_FLAT_VOLUME)
/* We might need to update the sink's volume if we are in flat
* volume mode. */
- pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
- }
+ pa_sink_set_volume(i->sink, NULL, FALSE, FALSE);
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
@@ -1295,16 +1246,15 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
pa_sink_update_status(dest);
if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume new_volume;
+ pa_cvolume remapped;
- /* Make relative volume absolute again */
- pa_cvolume t = dest->reference_volume;
- pa_cvolume_remap(&t, &dest->channel_map, &i->channel_map);
- pa_sw_cvolume_multiply(&i->virtual_volume, &i->virtual_volume, &t);
+ /* Make relative volumes absolute */
+ remapped = dest->reference_volume;
+ pa_cvolume_remap(&remapped, &dest->channel_map, &i->channel_map);
+ pa_sw_cvolume_multiply(&i->volume, &i->reference_ratio, &remapped);
/* We might need to update the sink's volume if we are in flat volume mode. */
- pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
+ pa_sink_set_volume(i->sink, NULL, FALSE, i->save_volume);
}
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index b5502c45..ea0f8c0e 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -94,10 +94,12 @@ struct pa_sink_input {
pa_sink_input *sync_prev, *sync_next;
/* Also see http://pulseaudio.org/wiki/InternalVolumes */
- pa_cvolume virtual_volume; /* The volume clients are informed about */
- pa_cvolume volume_factor; /* An internally used volume factor that can be used by modules to apply effects and suchlike without having that visible to the outside */
- double relative_volume[PA_CHANNELS_MAX]; /* The calculated volume relative to the sink volume as linear factors. */
- pa_cvolume soft_volume; /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as relative_volume * volume_factor */
+ pa_cvolume volume; /* The volume clients are informed about */
+ pa_cvolume reference_ratio; /* The ratio of the stream's volume to the sink's reference volume */
+ pa_cvolume real_ratio; /* The ratio of the stream's volume to the sink's real volume */
+ pa_cvolume volume_factor; /* An internally used volume factor that can be used by modules to apply effects and suchlike without having that visible to the outside */
+ pa_cvolume soft_volume; /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as real_ratio * volume_factor */
+
pa_bool_t muted:1;
/* if TRUE then the source we are connected to and/or the volume
@@ -325,8 +327,6 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute);
pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute);
-pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v);
-
void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save);
pa_bool_t pa_sink_input_get_mute(pa_sink_input *i);
@@ -369,9 +369,6 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i);
pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
-/* To be used by sink.c only */
-void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v);
-
#define pa_sink_input_assert_io_context(s) \
pa_assert(pa_thread_mq_get() || !PA_SINK_INPUT_IS_LINKED((s)->state))
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 717584f2..1cce8e6b 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -212,7 +212,7 @@ pa_sink* pa_sink_new(
pa_cvolume_reset(&data->volume, data->sample_spec.channels);
pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
- pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
+ pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));
if (!data->muted_is_set)
data->muted = FALSE;
@@ -249,7 +249,7 @@ pa_sink* pa_sink_new(
s->inputs = pa_idxset_new(NULL, NULL);
s->n_corked = 0;
- s->reference_volume = s->virtual_volume = data->volume;
+ s->reference_volume = s->real_volume = data->volume;
pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
s->base_volume = PA_VOLUME_NORM;
s->n_volume_steps = PA_VOLUME_NORM+1;
@@ -434,6 +434,11 @@ void pa_sink_put(pa_sink* s) {
if ((s->flags & PA_SINK_DECIBEL_VOLUME) && s->core->flat_volumes)
s->flags |= PA_SINK_FLAT_VOLUME;
+ /* We assume that if the sink implementor changed the default
+ * volume he did so in real_volume, because that is the usual
+ * place where he is supposed to place his changes. */
+ s->reference_volume = s->real_volume;
+
s->thread_info.soft_volume = s->soft_volume;
s->thread_info.soft_muted = s->muted;
@@ -1212,105 +1217,144 @@ pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
return usec;
}
-static void compute_new_soft_volume(pa_sink_input *i, const pa_cvolume *new_volume) {
- unsigned c;
+/* Called from main context */
+static void compute_reference_ratios(pa_sink *s) {
+ uint32_t idx;
+ pa_sink_input *i;
+
+ pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+ pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
- pa_sink_input_assert_ref(i);
- pa_assert(new_volume->channels == i->sample_spec.channels);
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ unsigned c;
+ pa_cvolume remapped;
- /*
- * This basically calculates:
- *
- * i->relative_volume := i->virtual_volume / new_volume
- * i->soft_volume := i->relative_volume * i->volume_factor
- */
+ /*
+ * Calculates the reference volume from the sink's reference
+ * volume. This basically calculates:
+ *
+ * i->reference_ratio = i->volume / s->reference_volume
+ */
- /* The new sink volume passed in here must already be remapped to
- * the sink input's channel map! */
+ remapped = s->reference_volume;
+ pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
- i->soft_volume.channels = i->sample_spec.channels;
+ i->reference_ratio.channels = i->sample_spec.channels;
- for (c = 0; c < i->sample_spec.channels; c++)
+ for (c = 0; c < i->sample_spec.channels; c++) {
- if (new_volume->values[c] <= PA_VOLUME_MUTED)
- /* We leave i->relative_volume untouched */
- i->soft_volume.values[c] = PA_VOLUME_MUTED;
- else {
- i->relative_volume[c] =
- pa_sw_volume_to_linear(i->virtual_volume.values[c]) /
- pa_sw_volume_to_linear(new_volume->values[c]);
+ /* We don't update when the sink volume is 0 anyway */
+ if (remapped.values[c] <= PA_VOLUME_MUTED)
+ continue;
- i->soft_volume.values[c] = pa_sw_volume_from_linear(
- i->relative_volume[c] *
- pa_sw_volume_to_linear(i->volume_factor.values[c]));
+ /* Don't update the reference ratio unless necessary */
+ if (pa_sw_volume_multiply(
+ i->reference_ratio.values[c],
+ remapped.values[c]) == i->volume.values[c])
+ continue;
+
+ i->reference_ratio.values[c] = pa_sw_volume_divide(
+ i->volume.values[c],
+ remapped.values[c]);
}
+ }
+}
+
+/* Called from main context */
+static void compute_real_ratios(pa_sink *s) {
+ pa_sink_input *i;
+ uint32_t idx;
- /* Hooks have the ability to play games with i->soft_volume */
- pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+ pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+ pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
- /* We don't copy the soft_volume to the thread_info data
- * here. That must be done by the caller */
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ unsigned c;
+ pa_cvolume remapped;
+
+ /*
+ * This basically calculates:
+ *
+ * i->real_ratio := i->volume / s->real_volume
+ * i->soft_volume := i->real_ratio * i->volume_factor
+ */
+
+ remapped = s->real_volume;
+ pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
+
+ i->real_ratio.channels = i->sample_spec.channels;
+ i->soft_volume.channels = i->sample_spec.channels;
+
+ for (c = 0; c < i->sample_spec.channels; c++) {
+
+ if (remapped.values[c] <= PA_VOLUME_MUTED) {
+ /* We leave i->real_ratio untouched */
+ i->soft_volume.values[c] = PA_VOLUME_MUTED;
+ continue;
+ }
+
+ /* Don't lose accuracy unless necessary */
+ if (pa_sw_volume_multiply(
+ i->real_ratio.values[c],
+ remapped.values[c]) != i->volume.values[c])
+
+ i->real_ratio.values[c] = pa_sw_volume_divide(
+ i->volume.values[c],
+ remapped.values[c]);
+
+ i->soft_volume.values[c] = pa_sw_volume_multiply(
+ i->real_ratio.values[c],
+ i->volume_factor.values[c]);
+ }
+
+ /* We don't copy the soft_volume to the thread_info data
+ * here. That must be done by the caller */
+ }
}
/* Called from main thread */
-void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
+static void compute_real_volume(pa_sink *s) {
pa_sink_input *i;
uint32_t idx;
pa_sink_assert_ref(s);
pa_assert_ctl_context();
- pa_assert(new_volume);
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
- /* This is called whenever a sink input volume changes or a sink
- * input is added/removed and we might need to fix up the sink
- * volume accordingly. Please note that we don't actually update
- * the sinks volume here, we only return how it needs to be
- * updated. The caller should then call pa_sink_set_volume().*/
+ /* This determines the maximum volume of all streams and sets
+ * s->real_volume accordingly. */
if (pa_idxset_isempty(s->inputs)) {
/* In the special case that we have no sink input we leave the
* volume unmodified. */
- *new_volume = s->reference_volume;
+ s->real_volume = s->reference_volume;
return;
}
- pa_cvolume_mute(new_volume, s->channel_map.channels);
+ pa_cvolume_mute(&s->real_volume, s->channel_map.channels);
/* First let's determine the new maximum volume of all inputs
* connected to this sink */
- for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
- unsigned c;
- pa_cvolume remapped_volume;
-
- remapped_volume = i->virtual_volume;
- pa_cvolume_remap(&remapped_volume, &i->channel_map, &s->channel_map);
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ pa_cvolume remapped;
- for (c = 0; c < new_volume->channels; c++)
- if (remapped_volume.values[c] > new_volume->values[c])
- new_volume->values[c] = remapped_volume.values[c];
+ remapped = i->volume;
+ pa_cvolume_remap(&remapped, &i->channel_map, &s->channel_map);
+ pa_cvolume_merge(&s->real_volume, &s->real_volume, &remapped);
}
- /* Then, let's update the soft volumes of all inputs connected
- * to this sink */
- for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
- pa_cvolume remapped_new_volume;
-
- remapped_new_volume = *new_volume;
- pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
- compute_new_soft_volume(i, &remapped_new_volume);
-
- /* We don't copy soft_volume to the thread_info data here
- * (i.e. issue PA_SINK_INPUT_MESSAGE_SET_VOLUME) because we
- * want the update to be atomically with the sink volume
- * update, hence we do it within the pa_sink_set_volume() call
- * below */
- }
+ /* Then, let's update the real ratios/soft volumes of all inputs
+ * connected to this sink */
+ compute_real_ratios(s);
}
/* Called from main thread */
-void pa_sink_propagate_flat_volume(pa_sink *s) {
+static void propagate_reference_volume(pa_sink *s) {
pa_sink_input *i;
uint32_t idx;
@@ -1323,64 +1367,77 @@ void pa_sink_propagate_flat_volume(pa_sink *s) {
* caused by a sink input volume change. We need to fix up the
* sink input volumes accordingly */
- for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
- pa_cvolume sink_volume, new_virtual_volume;
- unsigned c;
-
- /* This basically calculates i->virtual_volume := i->relative_volume * s->virtual_volume */
-
- sink_volume = s->virtual_volume;
- pa_cvolume_remap(&sink_volume, &s->channel_map, &i->channel_map);
-
- for (c = 0; c < i->sample_spec.channels; c++)
- new_virtual_volume.values[c] = pa_sw_volume_from_linear(
- i->relative_volume[c] *
- pa_sw_volume_to_linear(sink_volume.values[c]));
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ pa_cvolume old_volume, remapped;
- new_virtual_volume.channels = i->sample_spec.channels;
+ old_volume = i->volume;
- if (!pa_cvolume_equal(&new_virtual_volume, &i->virtual_volume)) {
- i->virtual_volume = new_virtual_volume;
+ /* This basically calculates:
+ *
+ * i->volume := s->reference_volume * i->reference_ratio */
- /* Hmm, the soft volume might no longer actually match
- * what has been chosen as new virtual volume here,
- * especially when the old volume was
- * PA_VOLUME_MUTED. Hence let's recalculate the soft
- * volumes here. */
- compute_new_soft_volume(i, &sink_volume);
+ remapped = s->reference_volume;
+ pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
+ pa_sw_cvolume_multiply(&i->volume, &remapped, &i->reference_ratio);
- /* The virtual volume changed, let's tell people so */
+ /* The reference volume changed, let's tell people so */
+ if (!pa_cvolume_equal(&old_volume, &i->volume))
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
- }
}
-
- /* If the soft_volume of any of the sink inputs got changed, let's
- * make sure the thread copies are synced up. */
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SYNC_VOLUMES, NULL, 0, NULL) == 0);
}
/* Called from main thread */
-void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference, pa_bool_t save) {
- pa_bool_t virtual_volume_changed;
+void pa_sink_set_volume(
+ pa_sink *s,
+ const pa_cvolume *volume,
+ pa_bool_t sendmsg,
+ pa_bool_t save) {
+
+ pa_cvolume old_reference_volume;
+ pa_bool_t reference_changed;
pa_sink_assert_ref(s);
pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
- pa_assert(volume);
- pa_assert(pa_cvolume_valid(volume));
- pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
+ pa_assert(!volume || pa_cvolume_valid(volume));
+ pa_assert(!volume || pa_cvolume_compatible(volume, &s->sample_spec));
+ pa_assert(volume || (s->flags & PA_SINK_FLAT_VOLUME));
+
+ /* If volume is NULL we synchronize the sink's real and reference
+ * volumes with the stream volumes. If it is not NULL we update
+ * the reference_volume with it. */
+
+ old_reference_volume = s->reference_volume;
+
+ if (volume) {
+
+ s->reference_volume = *volume;
+
+ if (s->flags & PA_SINK_FLAT_VOLUME) {
+ /* OK, propagate this volume change back to the inputs */
+ propagate_reference_volume(s);
+
+ /* And now recalculate the real volume */
+ compute_real_volume(s);
+ } else
+ s->real_volume = s->reference_volume;
+
+ } else {
+ pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
+
+ /* Ok, let's determine the new real volume */
+ compute_real_volume(s);
- virtual_volume_changed = !pa_cvolume_equal(volume, &s->virtual_volume);
- s->virtual_volume = *volume;
- s->save_volume = (!virtual_volume_changed && s->save_volume) || save;
+ /* Let's 'push' the reference volume if necessary */
+ pa_cvolume_merge(&s->reference_volume, &s->reference_volume, &s->real_volume);
- if (become_reference)
- s->reference_volume = s->virtual_volume;
+ /* We need to fix the reference ratios of all streams now that
+ * we changed the reference volume */
+ compute_reference_ratios(s);
+ }
- /* Propagate this volume change back to the inputs */
- if (virtual_volume_changed)
- if (propagate && (s->flags & PA_SINK_FLAT_VOLUME))
- pa_sink_propagate_flat_volume(s);
+ reference_changed = !pa_cvolume_equal(&old_reference_volume, &s->reference_volume);
+ s->save_volume = (!reference_changed && s->save_volume) || save;
if (s->set_volume) {
/* If we have a function set_volume(), then we do not apply a
@@ -1393,13 +1450,13 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat
} else
/* If we have no function set_volume(), then the soft volume
* becomes the virtual volume */
- s->soft_volume = s->virtual_volume;
+ s->soft_volume = s->real_volume;
/* This tells the sink that soft and/or virtual volume changed */
if (sendmsg)
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
- if (virtual_volume_changed)
+ if (reference_changed)
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
@@ -1407,67 +1464,114 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat
void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
pa_sink_assert_ref(s);
pa_assert_ctl_context();
- pa_assert(volume);
- s->soft_volume = *volume;
+ if (!volume)
+ pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
+ else
+ s->soft_volume = *volume;
if (PA_SINK_IS_LINKED(s->state))
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
else
- s->thread_info.soft_volume = *volume;
+ s->thread_info.soft_volume = s->soft_volume;
}
-/* Called from main thread */
-const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh, pa_bool_t reference) {
+static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume) {
+ pa_sink_input *i;
+ uint32_t idx;
+ pa_cvolume old_reference_volume;
+
pa_sink_assert_ref(s);
pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
- if (s->refresh_volume || force_refresh) {
- struct pa_cvolume old_virtual_volume = s->virtual_volume;
+ /* This is called when the hardware's real volume changes due to
+ * some external event. We copy the real volume into our
+ * reference volume and then rebuild the stream volumes based on
+ * i->real_ratio which should stay fixed. */
- if (s->get_volume)
- s->get_volume(s);
+ if (pa_cvolume_equal(old_real_volume, &s->real_volume))
+ return;
- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
+ old_reference_volume = s->reference_volume;
- if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) {
+ /* 1. Make the real volume the reference volume */
+ s->reference_volume = s->real_volume;
- s->reference_volume = s->virtual_volume;
+ if (s->flags & PA_SINK_FLAT_VOLUME) {
- /* Something got changed in the hardware. It probably
- * makes sense to save changed hw settings given that hw
- * volume changes not triggered by PA are almost certainly
- * done by the user. */
- s->save_volume = TRUE;
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ pa_cvolume old_volume, remapped;
- if (s->flags & PA_SINK_FLAT_VOLUME)
- pa_sink_propagate_flat_volume(s);
+ old_volume = i->volume;
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ /* 2. Since the sink's reference and real volumes are equal
+ * now our ratios should be too. */
+ i->reference_ratio = i->real_ratio;
+
+ /* 3. Recalculate the new stream reference volume based on the
+ * reference ratio and the sink's reference volume.
+ *
+ * This basically calculates:
+ *
+ * i->volume = s->reference_volume * i->reference_ratio
+ *
+ * This is identical to propagate_reference_volume() */
+ remapped = s->reference_volume;
+ pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
+ pa_sw_cvolume_multiply(&i->volume, &remapped, &i->reference_ratio);
+
+ /* Notify if something changed */
+ if (!pa_cvolume_equal(&old_volume, &i->volume))
+ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
}
- return reference ? &s->reference_volume : &s->virtual_volume;
+ /* Something got changed in the hardware. It probably makes sense
+ * to save changed hw settings given that hw volume changes not
+ * triggered by PA are almost certainly done by the user. */
+ s->save_volume = TRUE;
+
+ if (!pa_cvolume_equal(&old_reference_volume, &s->reference_volume))
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
/* Called from main thread */
-void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume) {
+const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
pa_sink_assert_ref(s);
pa_assert_ctl_context();
pa_assert(PA_SINK_IS_LINKED(s->state));
- /* The sink implementor may call this if the volume changed to make sure everyone is notified */
- if (pa_cvolume_equal(&s->virtual_volume, new_volume))
- return;
+ if (s->refresh_volume || force_refresh) {
+ struct pa_cvolume old_real_volume;
- s->reference_volume = s->virtual_volume = *new_volume;
- s->save_volume = TRUE;
+ old_real_volume = s->real_volume;
- if (s->flags & PA_SINK_FLAT_VOLUME)
- pa_sink_propagate_flat_volume(s);
+ if (s->get_volume)
+ s->get_volume(s);
- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
+
+ propagate_real_volume(s, &old_real_volume);
+ }
+
+ return &s->reference_volume;
+}
+
+/* Called from main thread */
+void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_real_volume) {
+ pa_cvolume old_real_volume;
+
+ pa_sink_assert_ref(s);
+ pa_assert_ctl_context();
+ pa_assert(PA_SINK_IS_LINKED(s->state));
+
+ /* The sink implementor may call this if the volume changed to make sure everyone is notified */
+
+ old_real_volume = s->real_volume;
+ s->real_volume = *new_real_volume;
+
+ propagate_real_volume(s, &old_real_volume);
}
/* Called from main thread */
@@ -1516,7 +1620,6 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
}
}
-
return s->muted;
}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 3cd7e59d..936d1c2a 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -90,9 +90,10 @@ struct pa_sink {
unsigned n_volume_steps; /* shall be constant */
/* Also see http://pulseaudio.org/wiki/InternalVolumes */
- pa_cvolume virtual_volume; /* The volume clients are informed about */
- pa_cvolume reference_volume; /* The volume taken as refernce base for relative sink input volumes */
+ pa_cvolume reference_volume; /* The volume exported and taken as reference base for relative sink input volumes */
+ pa_cvolume real_volume; /* The volume that the hardware is configured to */
pa_cvolume soft_volume; /* The internal software volume we apply to all PCM data while it passes through */
+
pa_bool_t muted:1;
pa_bool_t refresh_volume:1;
@@ -303,11 +304,8 @@ int pa_sink_update_status(pa_sink*s);
int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause);
int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause);
-void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume);
-void pa_sink_propagate_flat_volume(pa_sink *s);
-
-void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference, pa_bool_t save);
-const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh, pa_bool_t reference);
+void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t sendmsg, pa_bool_t save);
+const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);
void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute, pa_bool_t save);
pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh);