mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-29 05:40:23 -04:00
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:
parent
49dcf0940e
commit
fe8b10cc05
15 changed files with 152 additions and 159 deletions
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue