core: introduce new 'reference' volume for sinks

The reference volume is to be used as reference volume for stored stream
volumes. Previously if a new stream was created the relative volume was
taken relatively to the virtual device volume. Due to the flat volume
logic this could then be fed back to the virtual device volume.
Repeating the whole story over and over would result in a device volume
that would go lower, and lower and lower.

This patch introduces a 'reference' volume for each sink which stays
unmodified by stream volume changes even if flat volumes are used. It is
only modified if the sink volumes are modified directly by the user.

For further explanations see http://pulseaudio.org/wiki/InternalVolumes
This commit is contained in:
Lennart Poettering 2009-04-13 22:50:24 +02:00
parent 49dcf0940e
commit fe8b10cc05
15 changed files with 152 additions and 159 deletions

View file

@ -931,7 +931,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);
pa_sink_get_volume(u->sink, TRUE, FALSE);
pa_sink_get_mute(u->sink, TRUE);
}

View file

@ -191,7 +191,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
name = pa_sprintf_malloc("sink:%s", sink->name);
entry.channel_map = sink->channel_map;
entry.volume = *pa_sink_get_volume(sink, FALSE);
entry.volume = *pa_sink_get_volume(sink, FALSE, TRUE);
entry.muted = pa_sink_get_mute(sink, FALSE);
} else {

View file

@ -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);
pa_cvolume cv = *pa_sink_get_volume(s, FALSE, FALSE);
#define DELTA (PA_VOLUME_NORM/20)
@ -133,7 +133,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);
pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE);
break;
case DOWN:
@ -144,7 +144,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);
pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE);
break;
case MUTE:

View file

@ -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);
pa_sink_input_set_volume(si, &cv, TRUE, TRUE);
}
}
}

View file

@ -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);
pa_cvolume cv = *pa_sink_get_volume(s, FALSE, 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);
pa_sink_set_volume(s, &cv, TRUE, 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);
pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE);
break;
case MUTE_TOGGLE:

View file

@ -91,15 +91,14 @@ struct userdata {
pa_idxset *subscribed;
};
#define ENTRY_VERSION 1
#define ENTRY_VERSION 2
struct entry {
uint8_t version;
pa_bool_t muted_valid:1, relative_volume_valid:1, absolute_volume_valid:1, device_valid:1;
pa_bool_t muted_valid:1, volume_valid:1, device_valid:1;
pa_bool_t muted:1;
pa_channel_map channel_map;
pa_cvolume relative_volume;
pa_cvolume absolute_volume;
pa_cvolume volume;
char device[PA_NAME_MAX];
} PA_GCC_PACKED;
@ -192,13 +191,12 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
goto fail;
}
if ((e->relative_volume_valid || e->absolute_volume_valid) && !pa_channel_map_valid(&e->channel_map)) {
if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) {
pa_log_warn("Invalid channel map stored in database for stream %s", name);
goto fail;
}
if ((e->relative_volume_valid && (!pa_cvolume_valid(&e->relative_volume) || e->relative_volume.channels != e->channel_map.channels)) ||
(e->absolute_volume_valid && (!pa_cvolume_valid(&e->absolute_volume) || e->absolute_volume.channels != e->channel_map.channels))) {
if (e->volume_valid && (!pa_cvolume_valid(&e->volume) || !pa_cvolume_compatible_with_channel_map(&e->volume, &e->channel_map))) {
pa_log_warn("Invalid volume stored in database for stream %s", name);
goto fail;
}
@ -251,14 +249,9 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
(a->muted_valid && (a->muted != b->muted)))
return FALSE;
t = b->relative_volume;
if (a->relative_volume_valid != b->relative_volume_valid ||
(a->relative_volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->relative_volume)))
return FALSE;
t = b->absolute_volume;
if (a->absolute_volume_valid != b->absolute_volume_valid ||
(a->absolute_volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->absolute_volume)))
t = b->volume;
if (a->volume_valid != b->volume_valid ||
(a->volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->volume)))
return FALSE;
return TRUE;
@ -291,22 +284,24 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
if (!(name = get_name(sink_input->proplist, "sink-input")))
return;
entry.channel_map = sink_input->channel_map;
if ((old = read_entry(u, name)))
entry = *old;
pa_sink_input_get_relative_volume(sink_input, &entry.relative_volume);
entry.relative_volume_valid = sink_input->save_volume;
if (sink_input->save_volume) {
entry.channel_map = sink_input->channel_map;
pa_sink_input_get_volume(sink_input, &entry.volume, FALSE);
entry.volume_valid = TRUE;
}
if (sink_input->sink->flags & PA_SINK_FLAT_VOLUME) {
entry.absolute_volume = *pa_sink_input_get_volume(sink_input);
entry.absolute_volume_valid = sink_input->save_volume;
} else
entry.absolute_volume_valid = FALSE;
if (sink_input->save_muted) {
entry.muted = pa_sink_input_get_mute(sink_input);
entry.muted_valid = TRUE;
}
entry.muted = pa_sink_input_get_mute(sink_input);
entry.muted_valid = sink_input->save_muted;
pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device));
entry.device_valid = sink_input->save_sink;
if (sink_input->save_sink) {
pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device));
entry.device_valid = TRUE;
}
} else {
pa_source_output *source_output;
@ -319,13 +314,16 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
if (!(name = get_name(source_output->proplist, "source-output")))
return;
entry.channel_map = source_output->channel_map;
if ((old = read_entry(u, name)))
entry = *old;
pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device));
entry.device_valid = source_output->save_source;
if (source_output->save_source) {
pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device));
entry.device_valid = source_output->save_source;
}
}
if ((old = read_entry(u, name))) {
if (old) {
if (entries_equal(old, &entry)) {
pa_xfree(old);
@ -400,42 +398,24 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
if ((e = read_entry(u, name))) {
if (u->restore_volume) {
if (u->restore_volume && e->volume_valid) {
if (!new_data->volume_is_set) {
pa_cvolume v;
pa_cvolume_init(&v);
if (new_data->sink->flags & PA_SINK_FLAT_VOLUME) {
pa_log_info("Restoring volume for sink input %s.", name);
v = e->volume;
pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
pa_sink_input_new_data_set_volume(new_data, &v);
/* We don't check for e->device_valid here because
that bit marks whether it is a good choice for
restoring, not just if the data is filled in. */
if (e->absolute_volume_valid &&
(e->device[0] == 0 || pa_streq(new_data->sink->name, e->device))) {
v = e->absolute_volume;
new_data->volume_is_absolute = TRUE;
} else if (e->relative_volume_valid) {
v = e->relative_volume;
new_data->volume_is_absolute = FALSE;
}
} else if (e->relative_volume_valid) {
v = e->relative_volume;
new_data->volume_is_absolute = FALSE;
}
if (v.channels > 0) {
pa_log_info("Restoring volume for sink input %s.", name);
pa_sink_input_new_data_set_volume(new_data, pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map));
new_data->save_volume = TRUE;
}
new_data->volume_is_absolute = FALSE;
new_data->save_volume = FALSE;
} else
pa_log_debug("Not restoring volume for sink input %s, because already set.", name);
}
if (u->restore_muted && e->muted_valid) {
if (!new_data->muted_is_set) {
pa_log_info("Restoring mute state for sink input %s.", name);
pa_sink_input_new_data_set_muted(new_data, e->muted);
@ -532,30 +512,15 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
}
pa_xfree(n);
if (u->restore_volume) {
if (u->restore_volume && e->volume_valid) {
pa_cvolume v;
pa_cvolume_init(&v);
if (si->sink->flags & PA_SINK_FLAT_VOLUME) {
if (e->absolute_volume_valid &&
(e->device[0] == 0 || pa_streq(e->device, si->sink->name)))
v = e->absolute_volume;
else if (e->relative_volume_valid) {
pa_cvolume t = *pa_sink_get_volume(si->sink, FALSE);
pa_sw_cvolume_multiply(&v, &e->relative_volume, pa_cvolume_remap(&t, &si->sink->channel_map, &e->channel_map));
}
} else if (e->relative_volume_valid)
v = e->relative_volume;
if (v.channels > 0) {
pa_log_info("Restoring volume for sink input %s.", name);
pa_sink_input_set_volume(si, pa_cvolume_remap(&v, &e->channel_map, &si->channel_map), TRUE);
}
v = e->volume;
pa_log_info("Restoring volume for sink input %s.", name);
pa_sink_input_set_volume(si, pa_cvolume_remap(&v, &e->channel_map, &si->channel_map), FALSE, FALSE);
}
if (u->restore_muted &&
e->muted_valid) {
if (u->restore_muted && e->muted_valid) {
pa_log_info("Restoring mute state for sink input %s.", name);
pa_sink_input_set_mute(si, e->muted, TRUE);
}
@ -610,10 +575,10 @@ static void dump_database(struct userdata *u) {
if ((e = read_entry(u, name))) {
char t[256];
pa_log("name=%s", name);
pa_log("device=%s", e->device);
pa_log("device=%s %s", e->device, pa_yes_no(e->device_valid));
pa_log("channel_map=%s", pa_channel_map_snprint(t, sizeof(t), &e->channel_map));
pa_log("volume=%s", pa_cvolume_snprint(t, sizeof(t), &e->volume));
pa_log("mute=%s", pa_yes_no(e->muted));
pa_log("volume=%s %s", pa_cvolume_snprint(t, sizeof(t), &e->volume), pa_yes_no(e->volume_valid));
pa_log("mute=%s %s", pa_yes_no(e->muted), pa_yes_no(e->volume_valid));
pa_xfree(e);
}
@ -674,8 +639,8 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
pa_channel_map cm;
pa_tagstruct_puts(reply, name);
pa_tagstruct_put_channel_map(reply, (e->relative_volume_valid || e->absolute_volume_valid) ? &e->channel_map : pa_channel_map_init(&cm));
pa_tagstruct_put_cvolume(reply, e->absolute_volume_valid ? &e->absolute_volume : (e->relative_volume_valid ? &e->relative_volume : pa_cvolume_init(&r)));
pa_tagstruct_put_channel_map(reply, e->volume_valid ? &e->channel_map : pa_channel_map_init(&cm));
pa_tagstruct_put_cvolume(reply, e->volume_valid ? &e->volume : pa_cvolume_init(&r));
pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL);
pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE);
@ -718,7 +683,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_get_channel_map(t, &entry.channel_map) ||
pa_tagstruct_get_cvolume(t, &entry.absolute_volume) < 0 ||
pa_tagstruct_get_cvolume(t, &entry.volume) < 0 ||
pa_tagstruct_gets(t, &device) < 0 ||
pa_tagstruct_get_boolean(t, &muted) < 0)
goto fail;
@ -726,11 +691,10 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
if (!name || !*name)
goto fail;
entry.relative_volume = entry.absolute_volume;
entry.absolute_volume_valid = entry.relative_volume_valid = entry.relative_volume.channels > 0;
entry.volume_valid = entry.volume.channels > 0;
if (entry.relative_volume_valid)
if (!pa_cvolume_compatible_with_channel_map(&entry.relative_volume, &entry.channel_map))
if (entry.volume_valid)
if (!pa_cvolume_compatible_with_channel_map(&entry.volume, &entry.channel_map))
goto fail;
entry.muted = muted;

View file

@ -617,7 +617,7 @@ static int unsuspend(struct userdata *u) {
build_pollfd(u);
if (u->sink)
pa_sink_get_volume(u->sink, TRUE);
pa_sink_get_volume(u->sink, TRUE, FALSE);
if (u->source)
pa_source_get_volume(u->source, TRUE);

View file

@ -524,7 +524,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);
pa_sink_set_volume(sink, &cvolume, TRUE, TRUE, TRUE);
return 0;
}
@ -566,7 +566,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
}
pa_cvolume_set(&cvolume, si->sample_spec.channels, volume);
pa_sink_input_set_volume(si, &cvolume, TRUE);
pa_sink_input_set_volume(si, &cvolume, TRUE, TRUE);
return 0;
}
@ -1516,7 +1516,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)));
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-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));
}

View file

@ -258,10 +258,10 @@ char *pa_sink_list_to_string(pa_core *c) {
sink->flags & PA_SINK_FLAT_VOLUME ? "FLAT_VOLUME " : "",
sink->flags & PA_SINK_DYNAMIC_LATENCY ? "DYNAMIC_LATENCY" : "",
sink_state_to_string(pa_sink_get_state(sink)),
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)),
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE, 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)) : "",
pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE), &sink->channel_map),
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),
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) : "",
@ -507,6 +507,9 @@ char *pa_sink_input_list_to_string(pa_core *c) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
pa_usec_t cl;
const char *cmn;
pa_cvolume v;
pa_sink_input_get_volume(i, &v, TRUE);
cmn = pa_channel_map_to_pretty_name(&i->channel_map);
@ -547,9 +550,9 @@ char *pa_sink_input_list_to_string(pa_core *c) {
i->flags & PA_SINK_INPUT_FAIL_ON_SUSPEND ? "FAIL_ON_SUSPEND " : "",
state_table[pa_sink_input_get_state(i)],
i->sink->index, i->sink->name,
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_input_get_volume(i)),
pa_cvolume_get_balance(pa_sink_input_get_volume(i), &i->channel_map),
pa_cvolume_snprint(cv, sizeof(cv), &v),
pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &v),
pa_cvolume_get_balance(&v, &i->channel_map),
pa_yes_no(pa_sink_input_get_mute(i)),
(double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC,
clt,

View file

@ -638,7 +638,8 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
pa_assert(t >= k*2+s);
if (conn->sink_input) {
pa_cvolume volume = *pa_sink_input_get_volume(conn->sink_input);
pa_cvolume volume;
pa_sink_input_get_volume(conn->sink_input, &volume, TRUE);
rate = (int32_t) conn->sink_input->sample_spec.rate;
lvolume = (int32_t) ((volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
rvolume = (int32_t) ((volume.values[volume.channels == 2 ? 1 : 0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
@ -778,7 +779,7 @@ static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *
volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
volume.channels = conn->sink_input->sample_spec.channels;
pa_sink_input_set_volume(conn->sink_input, &volume, TRUE);
pa_sink_input_set_volume(conn->sink_input, &volume, TRUE, TRUE);
ok = 1;
} else
ok = 0;

View file

@ -2819,7 +2819,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),
PA_TAG_CVOLUME, pa_sink_get_volume(sink, FALSE, 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,
@ -2943,6 +2943,7 @@ static void module_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_m
static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) {
pa_sample_spec fixed_ss;
pa_usec_t sink_latency;
pa_cvolume v;
pa_assert(t);
pa_sink_input_assert_ref(s);
@ -2956,7 +2957,7 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t,
pa_tagstruct_putu32(t, s->sink->index);
pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &s->channel_map);
pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s));
pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s, &v, TRUE));
pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency));
pa_tagstruct_put_usec(t, sink_latency);
pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
@ -3321,11 +3322,11 @@ static void command_set_volume(
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
if (sink)
pa_sink_set_volume(sink, &volume, TRUE, TRUE);
pa_sink_set_volume(sink, &volume, TRUE, TRUE, TRUE);
else if (source)
pa_source_set_volume(source, &volume);
else if (si)
pa_sink_input_set_volume(si, &volume, TRUE);
pa_sink_input_set_volume(si, &volume, TRUE, TRUE);
pa_pstream_send_simple_ack(c->pstream, tag);
}

View file

@ -176,16 +176,8 @@ int pa_sink_input_new(
pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
if (!data->volume_is_set) {
if (data->sink->flags & PA_SINK_FLAT_VOLUME) {
data->volume = *pa_sink_get_volume(data->sink, FALSE);
pa_cvolume_remap(&data->volume, &data->sink->channel_map, &data->channel_map);
data->volume_is_absolute = TRUE;
} else {
pa_cvolume_reset(&data->volume, data->sample_spec.channels);
data->volume_is_absolute = FALSE;
}
pa_cvolume_reset(&data->volume, data->sample_spec.channels);
data->volume_is_absolute = FALSE;
data->save_volume = FALSE;
}
@ -279,10 +271,9 @@ int pa_sink_input_new(
/* 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 t = *pa_sink_get_volume(data->sink, FALSE);
pa_cvolume_remap(&t, &data->sink->channel_map, &data->channel_map);
pa_sw_cvolume_multiply(&i->virtual_volume, &data->volume, &t);
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);
} else
i->virtual_volume = data->volume;
@ -451,7 +442,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
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);
pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE);
}
if (i->sink->asyncmsgq)
@ -529,7 +520,7 @@ void pa_sink_input_put(pa_sink_input *i) {
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);
pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE);
} else
pa_sink_input_set_relative_volume(i, &i->virtual_volume);
@ -881,13 +872,21 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
}
/* Called from main context */
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save) {
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;
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_assert(volume);
pa_assert(pa_cvolume_valid(volume));
pa_assert(pa_cvolume_compatible(volume, &i->sample_spec));
if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) {
v = i->sink->reference_volume;
pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
volume = pa_sw_cvolume_multiply(&v, &v, volume);
}
if (pa_cvolume_equal(volume, &i->virtual_volume))
return;
@ -901,7 +900,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
* 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);
pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE, FALSE);
} else {
@ -921,11 +920,18 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
}
/* Called from main context */
const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute) {
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
return &i->virtual_volume;
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;
return volume;
}
/* Called from main context */
@ -936,7 +942,8 @@ pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v) {
pa_assert(v);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
/* This always returns a relative volume, even in flat volume mode */
/* This always returns the relative volume. Converts the float
* version into a pa_cvolume */
v->channels = i->sample_spec.channels;
@ -1152,7 +1159,7 @@ int pa_sink_input_start_move(pa_sink_input *i) {
/* 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);
pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE);
}
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
@ -1239,13 +1246,13 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
pa_cvolume new_volume;
/* Make relative volume absolute again */
pa_cvolume t = dest->virtual_volume;
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);
/* 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);
pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE);
}
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);

View file

@ -91,6 +91,7 @@ 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. */
@ -309,11 +310,14 @@ void pa_sink_input_kill(pa_sink_input*i);
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);
const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i);
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);
void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p);
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);

View file

@ -202,7 +202,7 @@ pa_sink* pa_sink_new(
s->inputs = pa_idxset_new(NULL, NULL);
s->n_corked = 0;
s->virtual_volume = data->volume;
s->reference_volume = s->virtual_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;
@ -351,11 +351,12 @@ void pa_sink_put(pa_sink* s) {
if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) {
s->flags |= PA_SINK_DECIBEL_VOLUME;
s->thread_info.soft_volume = s->soft_volume;
s->thread_info.soft_muted = s->muted;
s->base_volume = PA_VOLUME_NORM;
}
s->thread_info.soft_volume = s->soft_volume;
s->thread_info.soft_muted = s->muted;
if (s->flags & PA_SINK_DECIBEL_VOLUME)
s->n_volume_steps = PA_VOLUME_NORM+1;
@ -1046,16 +1047,16 @@ void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *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 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 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().*/
if (pa_idxset_isempty(s->inputs)) {
/* In the special case that we have no sink input we leave the
* volume unmodified. */
*new_volume = s->virtual_volume;
*new_volume = s->reference_volume;
return;
}
@ -1142,7 +1143,7 @@ void pa_sink_propagate_flat_volume(pa_sink *s) {
}
/* Called from main thread */
void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg) {
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 virtual_volume_changed;
pa_sink_assert_ref(s);
@ -1154,6 +1155,9 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat
virtual_volume_changed = !pa_cvolume_equal(volume, &s->virtual_volume);
s->virtual_volume = *volume;
if (become_reference)
s->reference_volume = s->virtual_volume;
/* Propagate this volume change back to the inputs */
if (virtual_volume_changed)
if (propagate && (s->flags & PA_SINK_FLAT_VOLUME))
@ -1161,8 +1165,8 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat
if (s->set_volume) {
/* If we have a function set_volume(), then we do not apply a
* soft volume by default. However, set_volume() is apply one
* to s->soft_volume */
* soft volume by default. However, set_volume() is free to
* apply one to s->soft_volume */
pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
s->set_volume(s);
@ -1194,7 +1198,7 @@ void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
}
/* Called from main thread */
const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh, pa_bool_t reference) {
pa_sink_assert_ref(s);
if (s->refresh_volume || force_refresh) {
@ -1207,6 +1211,8 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) {
s->reference_volume = s->virtual_volume;
if (s->flags & PA_SINK_FLAT_VOLUME)
pa_sink_propagate_flat_volume(s);
@ -1214,7 +1220,7 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
}
}
return &s->virtual_volume;
return reference ? &s->reference_volume : &s->virtual_volume;
}
/* Called from main thread */
@ -1226,7 +1232,11 @@ void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume) {
if (pa_cvolume_equal(&s->virtual_volume, new_volume))
return;
s->virtual_volume = *new_volume;
s->reference_volume = s->virtual_volume = *new_volume;
if (s->flags & PA_SINK_FLAT_VOLUME)
pa_sink_propagate_flat_volume(s);
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}

View file

@ -74,8 +74,10 @@ struct pa_sink {
pa_volume_t base_volume; /* shall be constant */
unsigned n_volume_steps; /* shall be constant */
pa_cvolume virtual_volume; /* The volume clients are informed about */
pa_cvolume soft_volume; /* The internal software volume we apply to all PCM data while it passes through */
/* 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 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;
@ -255,8 +257,9 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend);
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);
const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);
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);
const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh, pa_bool_t reference);
void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute);
pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh);