153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2004-2006 Lennart Poettering
553a5a1b3Sopenharmony_ci  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
653a5a1b3Sopenharmony_ci
753a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
853a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
953a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
1053a5a1b3Sopenharmony_ci  or (at your option) any later version.
1153a5a1b3Sopenharmony_ci
1253a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1353a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1453a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1553a5a1b3Sopenharmony_ci  General Public License for more details.
1653a5a1b3Sopenharmony_ci
1753a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1853a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1953a5a1b3Sopenharmony_ci***/
2053a5a1b3Sopenharmony_ci
2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2253a5a1b3Sopenharmony_ci#include <config.h>
2353a5a1b3Sopenharmony_ci#endif
2453a5a1b3Sopenharmony_ci
2553a5a1b3Sopenharmony_ci#include <stdio.h>
2653a5a1b3Sopenharmony_ci#include <stdlib.h>
2753a5a1b3Sopenharmony_ci#include <string.h>
2853a5a1b3Sopenharmony_ci
2953a5a1b3Sopenharmony_ci#include <pulse/introspect.h>
3053a5a1b3Sopenharmony_ci#include <pulse/format.h>
3153a5a1b3Sopenharmony_ci#include <pulse/utf8.h>
3253a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
3353a5a1b3Sopenharmony_ci#include <pulse/timeval.h>
3453a5a1b3Sopenharmony_ci#include <pulse/util.h>
3553a5a1b3Sopenharmony_ci#include <pulse/rtclock.h>
3653a5a1b3Sopenharmony_ci#include <pulse/internal.h>
3753a5a1b3Sopenharmony_ci
3853a5a1b3Sopenharmony_ci#include <pulsecore/i18n.h>
3953a5a1b3Sopenharmony_ci#include <pulsecore/sink-input.h>
4053a5a1b3Sopenharmony_ci#include <pulsecore/namereg.h>
4153a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
4253a5a1b3Sopenharmony_ci#include <pulsecore/sample-util.h>
4353a5a1b3Sopenharmony_ci#include <pulsecore/stream-util.h>
4453a5a1b3Sopenharmony_ci#include <pulsecore/mix.h>
4553a5a1b3Sopenharmony_ci#include <pulsecore/core-subscribe.h>
4653a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
4753a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
4853a5a1b3Sopenharmony_ci#include <pulsecore/play-memblockq.h>
4953a5a1b3Sopenharmony_ci#include <pulsecore/flist.h>
5053a5a1b3Sopenharmony_ci
5153a5a1b3Sopenharmony_ci#include "sink.h"
5253a5a1b3Sopenharmony_ci
5353a5a1b3Sopenharmony_ci#define MAX_MIX_CHANNELS 32
5453a5a1b3Sopenharmony_ci#define MIX_BUFFER_LENGTH (pa_page_size())
5553a5a1b3Sopenharmony_ci#define ABSOLUTE_MIN_LATENCY (500)
5653a5a1b3Sopenharmony_ci#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
5753a5a1b3Sopenharmony_ci#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
5853a5a1b3Sopenharmony_ci
5953a5a1b3Sopenharmony_ciPA_DEFINE_PUBLIC_CLASS(pa_sink, pa_msgobject);
6053a5a1b3Sopenharmony_ci
6153a5a1b3Sopenharmony_cistruct pa_sink_volume_change {
6253a5a1b3Sopenharmony_ci    pa_usec_t at;
6353a5a1b3Sopenharmony_ci    pa_cvolume hw_volume;
6453a5a1b3Sopenharmony_ci
6553a5a1b3Sopenharmony_ci    PA_LLIST_FIELDS(pa_sink_volume_change);
6653a5a1b3Sopenharmony_ci};
6753a5a1b3Sopenharmony_ci
6853a5a1b3Sopenharmony_cistruct set_state_data {
6953a5a1b3Sopenharmony_ci    pa_sink_state_t state;
7053a5a1b3Sopenharmony_ci    pa_suspend_cause_t suspend_cause;
7153a5a1b3Sopenharmony_ci};
7253a5a1b3Sopenharmony_ci
7353a5a1b3Sopenharmony_cistatic void sink_free(pa_object *s);
7453a5a1b3Sopenharmony_ci
7553a5a1b3Sopenharmony_cistatic void pa_sink_volume_change_push(pa_sink *s);
7653a5a1b3Sopenharmony_cistatic void pa_sink_volume_change_flush(pa_sink *s);
7753a5a1b3Sopenharmony_cistatic void pa_sink_volume_change_rewind(pa_sink *s, size_t nbytes);
7853a5a1b3Sopenharmony_ci
7953a5a1b3Sopenharmony_cipa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) {
8053a5a1b3Sopenharmony_ci    pa_assert(data);
8153a5a1b3Sopenharmony_ci
8253a5a1b3Sopenharmony_ci    pa_zero(*data);
8353a5a1b3Sopenharmony_ci    data->proplist = pa_proplist_new();
8453a5a1b3Sopenharmony_ci    data->ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_device_port_unref);
8553a5a1b3Sopenharmony_ci
8653a5a1b3Sopenharmony_ci    return data;
8753a5a1b3Sopenharmony_ci}
8853a5a1b3Sopenharmony_ci
8953a5a1b3Sopenharmony_civoid pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name) {
9053a5a1b3Sopenharmony_ci    pa_assert(data);
9153a5a1b3Sopenharmony_ci
9253a5a1b3Sopenharmony_ci    pa_xfree(data->name);
9353a5a1b3Sopenharmony_ci    data->name = pa_xstrdup(name);
9453a5a1b3Sopenharmony_ci}
9553a5a1b3Sopenharmony_ci
9653a5a1b3Sopenharmony_civoid pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec) {
9753a5a1b3Sopenharmony_ci    pa_assert(data);
9853a5a1b3Sopenharmony_ci
9953a5a1b3Sopenharmony_ci    if ((data->sample_spec_is_set = !!spec))
10053a5a1b3Sopenharmony_ci        data->sample_spec = *spec;
10153a5a1b3Sopenharmony_ci}
10253a5a1b3Sopenharmony_ci
10353a5a1b3Sopenharmony_civoid pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map) {
10453a5a1b3Sopenharmony_ci    pa_assert(data);
10553a5a1b3Sopenharmony_ci
10653a5a1b3Sopenharmony_ci    if ((data->channel_map_is_set = !!map))
10753a5a1b3Sopenharmony_ci        data->channel_map = *map;
10853a5a1b3Sopenharmony_ci}
10953a5a1b3Sopenharmony_ci
11053a5a1b3Sopenharmony_civoid pa_sink_new_data_set_alternate_sample_rate(pa_sink_new_data *data, const uint32_t alternate_sample_rate) {
11153a5a1b3Sopenharmony_ci    pa_assert(data);
11253a5a1b3Sopenharmony_ci
11353a5a1b3Sopenharmony_ci    data->alternate_sample_rate_is_set = true;
11453a5a1b3Sopenharmony_ci    data->alternate_sample_rate = alternate_sample_rate;
11553a5a1b3Sopenharmony_ci}
11653a5a1b3Sopenharmony_ci
11753a5a1b3Sopenharmony_civoid pa_sink_new_data_set_avoid_resampling(pa_sink_new_data *data, bool avoid_resampling) {
11853a5a1b3Sopenharmony_ci    pa_assert(data);
11953a5a1b3Sopenharmony_ci
12053a5a1b3Sopenharmony_ci    data->avoid_resampling_is_set = true;
12153a5a1b3Sopenharmony_ci    data->avoid_resampling = avoid_resampling;
12253a5a1b3Sopenharmony_ci}
12353a5a1b3Sopenharmony_ci
12453a5a1b3Sopenharmony_civoid pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume) {
12553a5a1b3Sopenharmony_ci    pa_assert(data);
12653a5a1b3Sopenharmony_ci
12753a5a1b3Sopenharmony_ci    if ((data->volume_is_set = !!volume))
12853a5a1b3Sopenharmony_ci        data->volume = *volume;
12953a5a1b3Sopenharmony_ci}
13053a5a1b3Sopenharmony_ci
13153a5a1b3Sopenharmony_civoid pa_sink_new_data_set_muted(pa_sink_new_data *data, bool mute) {
13253a5a1b3Sopenharmony_ci    pa_assert(data);
13353a5a1b3Sopenharmony_ci
13453a5a1b3Sopenharmony_ci    data->muted_is_set = true;
13553a5a1b3Sopenharmony_ci    data->muted = mute;
13653a5a1b3Sopenharmony_ci}
13753a5a1b3Sopenharmony_ci
13853a5a1b3Sopenharmony_civoid pa_sink_new_data_set_port(pa_sink_new_data *data, const char *port) {
13953a5a1b3Sopenharmony_ci    pa_assert(data);
14053a5a1b3Sopenharmony_ci
14153a5a1b3Sopenharmony_ci    pa_xfree(data->active_port);
14253a5a1b3Sopenharmony_ci    data->active_port = pa_xstrdup(port);
14353a5a1b3Sopenharmony_ci}
14453a5a1b3Sopenharmony_ci
14553a5a1b3Sopenharmony_civoid pa_sink_new_data_done(pa_sink_new_data *data) {
14653a5a1b3Sopenharmony_ci    pa_assert(data);
14753a5a1b3Sopenharmony_ci
14853a5a1b3Sopenharmony_ci    pa_proplist_free(data->proplist);
14953a5a1b3Sopenharmony_ci
15053a5a1b3Sopenharmony_ci    if (data->ports)
15153a5a1b3Sopenharmony_ci        pa_hashmap_free(data->ports);
15253a5a1b3Sopenharmony_ci
15353a5a1b3Sopenharmony_ci    pa_xfree(data->name);
15453a5a1b3Sopenharmony_ci    pa_xfree(data->active_port);
15553a5a1b3Sopenharmony_ci}
15653a5a1b3Sopenharmony_ci
15753a5a1b3Sopenharmony_ci/* Called from main context */
15853a5a1b3Sopenharmony_cistatic void reset_callbacks(pa_sink *s) {
15953a5a1b3Sopenharmony_ci    pa_assert(s);
16053a5a1b3Sopenharmony_ci
16153a5a1b3Sopenharmony_ci    s->set_state_in_main_thread = NULL;
16253a5a1b3Sopenharmony_ci    s->set_state_in_io_thread = NULL;
16353a5a1b3Sopenharmony_ci    s->get_volume = NULL;
16453a5a1b3Sopenharmony_ci    s->set_volume = NULL;
16553a5a1b3Sopenharmony_ci    s->write_volume = NULL;
16653a5a1b3Sopenharmony_ci    s->get_mute = NULL;
16753a5a1b3Sopenharmony_ci    s->set_mute = NULL;
16853a5a1b3Sopenharmony_ci    s->request_rewind = NULL;
16953a5a1b3Sopenharmony_ci    s->update_requested_latency = NULL;
17053a5a1b3Sopenharmony_ci    s->set_port = NULL;
17153a5a1b3Sopenharmony_ci    s->get_formats = NULL;
17253a5a1b3Sopenharmony_ci    s->set_formats = NULL;
17353a5a1b3Sopenharmony_ci    s->reconfigure = NULL;
17453a5a1b3Sopenharmony_ci}
17553a5a1b3Sopenharmony_ci
17653a5a1b3Sopenharmony_ci/* Called from main context */
17753a5a1b3Sopenharmony_cipa_sink* pa_sink_new(
17853a5a1b3Sopenharmony_ci        pa_core *core,
17953a5a1b3Sopenharmony_ci        pa_sink_new_data *data,
18053a5a1b3Sopenharmony_ci        pa_sink_flags_t flags) {
18153a5a1b3Sopenharmony_ci
18253a5a1b3Sopenharmony_ci    pa_sink *s;
18353a5a1b3Sopenharmony_ci    const char *name;
18453a5a1b3Sopenharmony_ci    char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
18553a5a1b3Sopenharmony_ci    pa_source_new_data source_data;
18653a5a1b3Sopenharmony_ci    const char *dn;
18753a5a1b3Sopenharmony_ci    char *pt;
18853a5a1b3Sopenharmony_ci
18953a5a1b3Sopenharmony_ci    pa_assert(core);
19053a5a1b3Sopenharmony_ci    pa_assert(data);
19153a5a1b3Sopenharmony_ci    pa_assert(data->name);
19253a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
19353a5a1b3Sopenharmony_ci
19453a5a1b3Sopenharmony_ci    s = pa_msgobject_new(pa_sink);
19553a5a1b3Sopenharmony_ci
19653a5a1b3Sopenharmony_ci    if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SINK, s, data->namereg_fail))) {
19753a5a1b3Sopenharmony_ci        pa_log_debug("Failed to register name %s.", data->name);
19853a5a1b3Sopenharmony_ci        pa_xfree(s);
19953a5a1b3Sopenharmony_ci        return NULL;
20053a5a1b3Sopenharmony_ci    }
20153a5a1b3Sopenharmony_ci
20253a5a1b3Sopenharmony_ci    pa_sink_new_data_set_name(data, name);
20353a5a1b3Sopenharmony_ci
20453a5a1b3Sopenharmony_ci    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_NEW], data) < 0) {
20553a5a1b3Sopenharmony_ci        pa_xfree(s);
20653a5a1b3Sopenharmony_ci        pa_namereg_unregister(core, name);
20753a5a1b3Sopenharmony_ci        return NULL;
20853a5a1b3Sopenharmony_ci    }
20953a5a1b3Sopenharmony_ci
21053a5a1b3Sopenharmony_ci    /* FIXME, need to free s here on failure */
21153a5a1b3Sopenharmony_ci
21253a5a1b3Sopenharmony_ci    pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
21353a5a1b3Sopenharmony_ci    pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
21453a5a1b3Sopenharmony_ci
21553a5a1b3Sopenharmony_ci    pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
21653a5a1b3Sopenharmony_ci
21753a5a1b3Sopenharmony_ci    if (!data->channel_map_is_set)
21853a5a1b3Sopenharmony_ci        pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
21953a5a1b3Sopenharmony_ci
22053a5a1b3Sopenharmony_ci    pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
22153a5a1b3Sopenharmony_ci    pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
22253a5a1b3Sopenharmony_ci
22353a5a1b3Sopenharmony_ci    /* FIXME: There should probably be a general function for checking whether
22453a5a1b3Sopenharmony_ci     * the sink volume is allowed to be set, like there is for sink inputs. */
22553a5a1b3Sopenharmony_ci    pa_assert(!data->volume_is_set || !(flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
22653a5a1b3Sopenharmony_ci
22753a5a1b3Sopenharmony_ci    if (!data->volume_is_set) {
22853a5a1b3Sopenharmony_ci        pa_cvolume_reset(&data->volume, data->sample_spec.channels);
22953a5a1b3Sopenharmony_ci        data->save_volume = false;
23053a5a1b3Sopenharmony_ci    }
23153a5a1b3Sopenharmony_ci
23253a5a1b3Sopenharmony_ci    pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
23353a5a1b3Sopenharmony_ci    pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));
23453a5a1b3Sopenharmony_ci
23553a5a1b3Sopenharmony_ci    if (!data->muted_is_set)
23653a5a1b3Sopenharmony_ci        data->muted = false;
23753a5a1b3Sopenharmony_ci
23853a5a1b3Sopenharmony_ci    if (data->card)
23953a5a1b3Sopenharmony_ci        pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist);
24053a5a1b3Sopenharmony_ci
24153a5a1b3Sopenharmony_ci    pa_device_init_description(data->proplist, data->card);
24253a5a1b3Sopenharmony_ci    pa_device_init_icon(data->proplist, true);
24353a5a1b3Sopenharmony_ci    pa_device_init_intended_roles(data->proplist);
24453a5a1b3Sopenharmony_ci
24553a5a1b3Sopenharmony_ci    if (!data->active_port) {
24653a5a1b3Sopenharmony_ci        pa_device_port *p = pa_device_port_find_best(data->ports);
24753a5a1b3Sopenharmony_ci        if (p)
24853a5a1b3Sopenharmony_ci            pa_sink_new_data_set_port(data, p->name);
24953a5a1b3Sopenharmony_ci    }
25053a5a1b3Sopenharmony_ci
25153a5a1b3Sopenharmony_ci    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
25253a5a1b3Sopenharmony_ci        pa_xfree(s);
25353a5a1b3Sopenharmony_ci        pa_namereg_unregister(core, name);
25453a5a1b3Sopenharmony_ci        return NULL;
25553a5a1b3Sopenharmony_ci    }
25653a5a1b3Sopenharmony_ci
25753a5a1b3Sopenharmony_ci    s->parent.parent.free = sink_free;
25853a5a1b3Sopenharmony_ci    s->parent.process_msg = pa_sink_process_msg;
25953a5a1b3Sopenharmony_ci
26053a5a1b3Sopenharmony_ci    s->core = core;
26153a5a1b3Sopenharmony_ci    s->state = PA_SINK_INIT;
26253a5a1b3Sopenharmony_ci    s->flags = flags;
26353a5a1b3Sopenharmony_ci    s->priority = 0;
26453a5a1b3Sopenharmony_ci    s->suspend_cause = data->suspend_cause;
26553a5a1b3Sopenharmony_ci    s->name = pa_xstrdup(name);
26653a5a1b3Sopenharmony_ci    s->proplist = pa_proplist_copy(data->proplist);
26753a5a1b3Sopenharmony_ci    s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
26853a5a1b3Sopenharmony_ci    s->module = data->module;
26953a5a1b3Sopenharmony_ci    s->card = data->card;
27053a5a1b3Sopenharmony_ci
27153a5a1b3Sopenharmony_ci    s->priority = pa_device_init_priority(s->proplist);
27253a5a1b3Sopenharmony_ci
27353a5a1b3Sopenharmony_ci    s->sample_spec = data->sample_spec;
27453a5a1b3Sopenharmony_ci    s->channel_map = data->channel_map;
27553a5a1b3Sopenharmony_ci    s->default_sample_rate = s->sample_spec.rate;
27653a5a1b3Sopenharmony_ci
27753a5a1b3Sopenharmony_ci    if (data->alternate_sample_rate_is_set)
27853a5a1b3Sopenharmony_ci        s->alternate_sample_rate = data->alternate_sample_rate;
27953a5a1b3Sopenharmony_ci    else
28053a5a1b3Sopenharmony_ci        s->alternate_sample_rate = s->core->alternate_sample_rate;
28153a5a1b3Sopenharmony_ci
28253a5a1b3Sopenharmony_ci    if (data->avoid_resampling_is_set)
28353a5a1b3Sopenharmony_ci        s->avoid_resampling = data->avoid_resampling;
28453a5a1b3Sopenharmony_ci    else
28553a5a1b3Sopenharmony_ci        s->avoid_resampling = s->core->avoid_resampling;
28653a5a1b3Sopenharmony_ci
28753a5a1b3Sopenharmony_ci    s->inputs = pa_idxset_new(NULL, NULL);
28853a5a1b3Sopenharmony_ci    s->n_corked = 0;
28953a5a1b3Sopenharmony_ci    s->input_to_master = NULL;
29053a5a1b3Sopenharmony_ci
29153a5a1b3Sopenharmony_ci    s->reference_volume = s->real_volume = data->volume;
29253a5a1b3Sopenharmony_ci    pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
29353a5a1b3Sopenharmony_ci    s->base_volume = PA_VOLUME_NORM;
29453a5a1b3Sopenharmony_ci    s->n_volume_steps = PA_VOLUME_NORM+1;
29553a5a1b3Sopenharmony_ci    s->muted = data->muted;
29653a5a1b3Sopenharmony_ci    s->refresh_volume = s->refresh_muted = false;
29753a5a1b3Sopenharmony_ci
29853a5a1b3Sopenharmony_ci    reset_callbacks(s);
29953a5a1b3Sopenharmony_ci    s->userdata = NULL;
30053a5a1b3Sopenharmony_ci
30153a5a1b3Sopenharmony_ci    s->asyncmsgq = NULL;
30253a5a1b3Sopenharmony_ci
30353a5a1b3Sopenharmony_ci    /* As a minor optimization we just steal the list instead of
30453a5a1b3Sopenharmony_ci     * copying it here */
30553a5a1b3Sopenharmony_ci    s->ports = data->ports;
30653a5a1b3Sopenharmony_ci    data->ports = NULL;
30753a5a1b3Sopenharmony_ci
30853a5a1b3Sopenharmony_ci    s->active_port = NULL;
30953a5a1b3Sopenharmony_ci    s->save_port = false;
31053a5a1b3Sopenharmony_ci
31153a5a1b3Sopenharmony_ci    if (data->active_port)
31253a5a1b3Sopenharmony_ci        if ((s->active_port = pa_hashmap_get(s->ports, data->active_port)))
31353a5a1b3Sopenharmony_ci            s->save_port = data->save_port;
31453a5a1b3Sopenharmony_ci
31553a5a1b3Sopenharmony_ci    /* Hopefully the active port has already been assigned in the previous call
31653a5a1b3Sopenharmony_ci       to pa_device_port_find_best, but better safe than sorry */
31753a5a1b3Sopenharmony_ci    if (!s->active_port)
31853a5a1b3Sopenharmony_ci        s->active_port = pa_device_port_find_best(s->ports);
31953a5a1b3Sopenharmony_ci
32053a5a1b3Sopenharmony_ci    if (s->active_port)
32153a5a1b3Sopenharmony_ci        s->port_latency_offset = s->active_port->latency_offset;
32253a5a1b3Sopenharmony_ci    else
32353a5a1b3Sopenharmony_ci        s->port_latency_offset = 0;
32453a5a1b3Sopenharmony_ci
32553a5a1b3Sopenharmony_ci    s->save_volume = data->save_volume;
32653a5a1b3Sopenharmony_ci    s->save_muted = data->save_muted;
32753a5a1b3Sopenharmony_ci
32853a5a1b3Sopenharmony_ci    pa_silence_memchunk_get(
32953a5a1b3Sopenharmony_ci            &core->silence_cache,
33053a5a1b3Sopenharmony_ci            core->mempool,
33153a5a1b3Sopenharmony_ci            &s->silence,
33253a5a1b3Sopenharmony_ci            &s->sample_spec,
33353a5a1b3Sopenharmony_ci            0);
33453a5a1b3Sopenharmony_ci
33553a5a1b3Sopenharmony_ci    s->thread_info.rtpoll = NULL;
33653a5a1b3Sopenharmony_ci    s->thread_info.inputs = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
33753a5a1b3Sopenharmony_ci                                                (pa_free_cb_t) pa_sink_input_unref);
33853a5a1b3Sopenharmony_ci    s->thread_info.soft_volume =  s->soft_volume;
33953a5a1b3Sopenharmony_ci    s->thread_info.soft_muted = s->muted;
34053a5a1b3Sopenharmony_ci    s->thread_info.state = s->state;
34153a5a1b3Sopenharmony_ci    s->thread_info.rewind_nbytes = 0;
34253a5a1b3Sopenharmony_ci    s->thread_info.last_rewind_nbytes = 0;
34353a5a1b3Sopenharmony_ci    s->thread_info.rewind_requested = false;
34453a5a1b3Sopenharmony_ci    s->thread_info.max_rewind = 0;
34553a5a1b3Sopenharmony_ci    s->thread_info.max_request = 0;
34653a5a1b3Sopenharmony_ci    s->thread_info.requested_latency_valid = false;
34753a5a1b3Sopenharmony_ci    s->thread_info.requested_latency = 0;
34853a5a1b3Sopenharmony_ci    s->thread_info.min_latency = ABSOLUTE_MIN_LATENCY;
34953a5a1b3Sopenharmony_ci    s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
35053a5a1b3Sopenharmony_ci    s->thread_info.fixed_latency = flags & PA_SINK_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
35153a5a1b3Sopenharmony_ci
35253a5a1b3Sopenharmony_ci    PA_LLIST_HEAD_INIT(pa_sink_volume_change, s->thread_info.volume_changes);
35353a5a1b3Sopenharmony_ci    s->thread_info.volume_changes_tail = NULL;
35453a5a1b3Sopenharmony_ci    pa_sw_cvolume_divide(&s->thread_info.current_hw_volume, &s->real_volume, &s->soft_volume);
35553a5a1b3Sopenharmony_ci    s->thread_info.volume_change_safety_margin = core->deferred_volume_safety_margin_usec;
35653a5a1b3Sopenharmony_ci    s->thread_info.volume_change_extra_delay = core->deferred_volume_extra_delay_usec;
35753a5a1b3Sopenharmony_ci    s->thread_info.port_latency_offset = s->port_latency_offset;
35853a5a1b3Sopenharmony_ci
35953a5a1b3Sopenharmony_ci    /* FIXME: This should probably be moved to pa_sink_put() */
36053a5a1b3Sopenharmony_ci    pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
36153a5a1b3Sopenharmony_ci
36253a5a1b3Sopenharmony_ci    if (s->card)
36353a5a1b3Sopenharmony_ci        pa_assert_se(pa_idxset_put(s->card->sinks, s, NULL) >= 0);
36453a5a1b3Sopenharmony_ci
36553a5a1b3Sopenharmony_ci    pt = pa_proplist_to_string_sep(s->proplist, "\n    ");
36653a5a1b3Sopenharmony_ci    pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s\n    %s",
36753a5a1b3Sopenharmony_ci                s->index,
36853a5a1b3Sopenharmony_ci                s->name,
36953a5a1b3Sopenharmony_ci                pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
37053a5a1b3Sopenharmony_ci                pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map),
37153a5a1b3Sopenharmony_ci                pt);
37253a5a1b3Sopenharmony_ci    pa_xfree(pt);
37353a5a1b3Sopenharmony_ci
37453a5a1b3Sopenharmony_ci    pa_source_new_data_init(&source_data);
37553a5a1b3Sopenharmony_ci    pa_source_new_data_set_sample_spec(&source_data, &s->sample_spec);
37653a5a1b3Sopenharmony_ci    pa_source_new_data_set_channel_map(&source_data, &s->channel_map);
37753a5a1b3Sopenharmony_ci    pa_source_new_data_set_alternate_sample_rate(&source_data, s->alternate_sample_rate);
37853a5a1b3Sopenharmony_ci    pa_source_new_data_set_avoid_resampling(&source_data, s->avoid_resampling);
37953a5a1b3Sopenharmony_ci    source_data.name = pa_sprintf_malloc("%s.monitor", name);
38053a5a1b3Sopenharmony_ci    source_data.driver = data->driver;
38153a5a1b3Sopenharmony_ci    source_data.module = data->module;
38253a5a1b3Sopenharmony_ci    source_data.card = data->card;
38353a5a1b3Sopenharmony_ci
38453a5a1b3Sopenharmony_ci    dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
38553a5a1b3Sopenharmony_ci    pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name);
38653a5a1b3Sopenharmony_ci    pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor");
38753a5a1b3Sopenharmony_ci
38853a5a1b3Sopenharmony_ci    s->monitor_source = pa_source_new(core, &source_data,
38953a5a1b3Sopenharmony_ci                                      ((flags & PA_SINK_LATENCY) ? PA_SOURCE_LATENCY : 0) |
39053a5a1b3Sopenharmony_ci                                      ((flags & PA_SINK_DYNAMIC_LATENCY) ? PA_SOURCE_DYNAMIC_LATENCY : 0));
39153a5a1b3Sopenharmony_ci
39253a5a1b3Sopenharmony_ci    pa_source_new_data_done(&source_data);
39353a5a1b3Sopenharmony_ci
39453a5a1b3Sopenharmony_ci    if (!s->monitor_source) {
39553a5a1b3Sopenharmony_ci        pa_sink_unlink(s);
39653a5a1b3Sopenharmony_ci        pa_sink_unref(s);
39753a5a1b3Sopenharmony_ci        return NULL;
39853a5a1b3Sopenharmony_ci    }
39953a5a1b3Sopenharmony_ci
40053a5a1b3Sopenharmony_ci    s->monitor_source->monitor_of = s;
40153a5a1b3Sopenharmony_ci
40253a5a1b3Sopenharmony_ci    pa_source_set_latency_range(s->monitor_source, s->thread_info.min_latency, s->thread_info.max_latency);
40353a5a1b3Sopenharmony_ci    pa_source_set_fixed_latency(s->monitor_source, s->thread_info.fixed_latency);
40453a5a1b3Sopenharmony_ci    pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
40553a5a1b3Sopenharmony_ci
40653a5a1b3Sopenharmony_ci    return s;
40753a5a1b3Sopenharmony_ci}
40853a5a1b3Sopenharmony_ci
40953a5a1b3Sopenharmony_ci/* Called from main context */
41053a5a1b3Sopenharmony_cistatic int sink_set_state(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t suspend_cause) {
41153a5a1b3Sopenharmony_ci    int ret = 0;
41253a5a1b3Sopenharmony_ci    bool state_changed;
41353a5a1b3Sopenharmony_ci    bool suspend_cause_changed;
41453a5a1b3Sopenharmony_ci    bool suspending;
41553a5a1b3Sopenharmony_ci    bool resuming;
41653a5a1b3Sopenharmony_ci    pa_sink_state_t old_state;
41753a5a1b3Sopenharmony_ci    pa_suspend_cause_t old_suspend_cause;
41853a5a1b3Sopenharmony_ci
41953a5a1b3Sopenharmony_ci    pa_assert(s);
42053a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
42153a5a1b3Sopenharmony_ci
42253a5a1b3Sopenharmony_ci    state_changed = state != s->state;
42353a5a1b3Sopenharmony_ci    suspend_cause_changed = suspend_cause != s->suspend_cause;
42453a5a1b3Sopenharmony_ci
42553a5a1b3Sopenharmony_ci    if (!state_changed && !suspend_cause_changed)
42653a5a1b3Sopenharmony_ci        return 0;
42753a5a1b3Sopenharmony_ci
42853a5a1b3Sopenharmony_ci    suspending = PA_SINK_IS_OPENED(s->state) && state == PA_SINK_SUSPENDED;
42953a5a1b3Sopenharmony_ci    resuming = s->state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state);
43053a5a1b3Sopenharmony_ci
43153a5a1b3Sopenharmony_ci    /* If we are resuming, suspend_cause must be 0. */
43253a5a1b3Sopenharmony_ci    pa_assert(!resuming || !suspend_cause);
43353a5a1b3Sopenharmony_ci
43453a5a1b3Sopenharmony_ci    /* Here's something to think about: what to do with the suspend cause if
43553a5a1b3Sopenharmony_ci     * resuming the sink fails? The old suspend cause will be incorrect, so we
43653a5a1b3Sopenharmony_ci     * can't use that. On the other hand, if we set no suspend cause (as is the
43753a5a1b3Sopenharmony_ci     * case currently), then it looks strange to have a sink suspended without
43853a5a1b3Sopenharmony_ci     * any cause. It might be a good idea to add a new "resume failed" suspend
43953a5a1b3Sopenharmony_ci     * cause, or it might just add unnecessary complexity, given that the
44053a5a1b3Sopenharmony_ci     * current approach of not setting any suspend cause works well enough. */
44153a5a1b3Sopenharmony_ci
44253a5a1b3Sopenharmony_ci    if (s->set_state_in_main_thread) {
44353a5a1b3Sopenharmony_ci        if ((ret = s->set_state_in_main_thread(s, state, suspend_cause)) < 0) {
44453a5a1b3Sopenharmony_ci            /* set_state_in_main_thread() is allowed to fail only when resuming. */
44553a5a1b3Sopenharmony_ci            pa_assert(resuming);
44653a5a1b3Sopenharmony_ci
44753a5a1b3Sopenharmony_ci            /* If resuming fails, we set the state to SUSPENDED and
44853a5a1b3Sopenharmony_ci             * suspend_cause to 0. */
44953a5a1b3Sopenharmony_ci            state = PA_SINK_SUSPENDED;
45053a5a1b3Sopenharmony_ci            suspend_cause = 0;
45153a5a1b3Sopenharmony_ci            state_changed = false;
45253a5a1b3Sopenharmony_ci            suspend_cause_changed = suspend_cause != s->suspend_cause;
45353a5a1b3Sopenharmony_ci            resuming = false;
45453a5a1b3Sopenharmony_ci
45553a5a1b3Sopenharmony_ci            /* We know the state isn't changing. If the suspend cause isn't
45653a5a1b3Sopenharmony_ci             * changing either, then there's nothing more to do. */
45753a5a1b3Sopenharmony_ci            if (!suspend_cause_changed)
45853a5a1b3Sopenharmony_ci                return ret;
45953a5a1b3Sopenharmony_ci        }
46053a5a1b3Sopenharmony_ci    }
46153a5a1b3Sopenharmony_ci
46253a5a1b3Sopenharmony_ci    if (s->asyncmsgq) {
46353a5a1b3Sopenharmony_ci        struct set_state_data data = { .state = state, .suspend_cause = suspend_cause };
46453a5a1b3Sopenharmony_ci
46553a5a1b3Sopenharmony_ci        if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, &data, 0, NULL)) < 0) {
46653a5a1b3Sopenharmony_ci            /* SET_STATE is allowed to fail only when resuming. */
46753a5a1b3Sopenharmony_ci            pa_assert(resuming);
46853a5a1b3Sopenharmony_ci
46953a5a1b3Sopenharmony_ci            if (s->set_state_in_main_thread)
47053a5a1b3Sopenharmony_ci                s->set_state_in_main_thread(s, PA_SINK_SUSPENDED, 0);
47153a5a1b3Sopenharmony_ci
47253a5a1b3Sopenharmony_ci            /* If resuming fails, we set the state to SUSPENDED and
47353a5a1b3Sopenharmony_ci             * suspend_cause to 0. */
47453a5a1b3Sopenharmony_ci            state = PA_SINK_SUSPENDED;
47553a5a1b3Sopenharmony_ci            suspend_cause = 0;
47653a5a1b3Sopenharmony_ci            state_changed = false;
47753a5a1b3Sopenharmony_ci            suspend_cause_changed = suspend_cause != s->suspend_cause;
47853a5a1b3Sopenharmony_ci            resuming = false;
47953a5a1b3Sopenharmony_ci
48053a5a1b3Sopenharmony_ci            /* We know the state isn't changing. If the suspend cause isn't
48153a5a1b3Sopenharmony_ci             * changing either, then there's nothing more to do. */
48253a5a1b3Sopenharmony_ci            if (!suspend_cause_changed)
48353a5a1b3Sopenharmony_ci                return ret;
48453a5a1b3Sopenharmony_ci        }
48553a5a1b3Sopenharmony_ci    }
48653a5a1b3Sopenharmony_ci
48753a5a1b3Sopenharmony_ci    old_suspend_cause = s->suspend_cause;
48853a5a1b3Sopenharmony_ci    if (suspend_cause_changed) {
48953a5a1b3Sopenharmony_ci        char old_cause_buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE];
49053a5a1b3Sopenharmony_ci        char new_cause_buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE];
49153a5a1b3Sopenharmony_ci
49253a5a1b3Sopenharmony_ci        pa_log_debug("%s: suspend_cause: %s -> %s", s->name, pa_suspend_cause_to_string(s->suspend_cause, old_cause_buf),
49353a5a1b3Sopenharmony_ci                     pa_suspend_cause_to_string(suspend_cause, new_cause_buf));
49453a5a1b3Sopenharmony_ci        s->suspend_cause = suspend_cause;
49553a5a1b3Sopenharmony_ci    }
49653a5a1b3Sopenharmony_ci
49753a5a1b3Sopenharmony_ci    old_state = s->state;
49853a5a1b3Sopenharmony_ci    if (state_changed) {
49953a5a1b3Sopenharmony_ci        pa_log_debug("%s: state: %s -> %s", s->name, pa_sink_state_to_string(s->state), pa_sink_state_to_string(state));
50053a5a1b3Sopenharmony_ci        s->state = state;
50153a5a1b3Sopenharmony_ci
50253a5a1b3Sopenharmony_ci        /* If we enter UNLINKED state, then we don't send change notifications.
50353a5a1b3Sopenharmony_ci         * pa_sink_unlink() will send unlink notifications instead. */
50453a5a1b3Sopenharmony_ci        if (state != PA_SINK_UNLINKED) {
50553a5a1b3Sopenharmony_ci            pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
50653a5a1b3Sopenharmony_ci            pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
50753a5a1b3Sopenharmony_ci        }
50853a5a1b3Sopenharmony_ci    }
50953a5a1b3Sopenharmony_ci
51053a5a1b3Sopenharmony_ci    if (suspending || resuming || suspend_cause_changed) {
51153a5a1b3Sopenharmony_ci        pa_sink_input *i;
51253a5a1b3Sopenharmony_ci        uint32_t idx;
51353a5a1b3Sopenharmony_ci
51453a5a1b3Sopenharmony_ci        /* We're suspending or resuming, tell everyone about it */
51553a5a1b3Sopenharmony_ci
51653a5a1b3Sopenharmony_ci        PA_IDXSET_FOREACH(i, s->inputs, idx)
51753a5a1b3Sopenharmony_ci            if (s->state == PA_SINK_SUSPENDED &&
51853a5a1b3Sopenharmony_ci                (i->flags & PA_SINK_INPUT_KILL_ON_SUSPEND))
51953a5a1b3Sopenharmony_ci                pa_sink_input_kill(i);
52053a5a1b3Sopenharmony_ci            else if (i->suspend)
52153a5a1b3Sopenharmony_ci                i->suspend(i, old_state, old_suspend_cause);
52253a5a1b3Sopenharmony_ci    }
52353a5a1b3Sopenharmony_ci
52453a5a1b3Sopenharmony_ci    if ((suspending || resuming || suspend_cause_changed) && s->monitor_source && state != PA_SINK_UNLINKED)
52553a5a1b3Sopenharmony_ci        pa_source_sync_suspend(s->monitor_source);
52653a5a1b3Sopenharmony_ci
52753a5a1b3Sopenharmony_ci    return ret;
52853a5a1b3Sopenharmony_ci}
52953a5a1b3Sopenharmony_ci
53053a5a1b3Sopenharmony_civoid pa_sink_set_get_volume_callback(pa_sink *s, pa_sink_cb_t cb) {
53153a5a1b3Sopenharmony_ci    pa_assert(s);
53253a5a1b3Sopenharmony_ci
53353a5a1b3Sopenharmony_ci    s->get_volume = cb;
53453a5a1b3Sopenharmony_ci}
53553a5a1b3Sopenharmony_ci
53653a5a1b3Sopenharmony_civoid pa_sink_set_set_volume_callback(pa_sink *s, pa_sink_cb_t cb) {
53753a5a1b3Sopenharmony_ci    pa_sink_flags_t flags;
53853a5a1b3Sopenharmony_ci
53953a5a1b3Sopenharmony_ci    pa_assert(s);
54053a5a1b3Sopenharmony_ci    pa_assert(!s->write_volume || cb);
54153a5a1b3Sopenharmony_ci
54253a5a1b3Sopenharmony_ci    s->set_volume = cb;
54353a5a1b3Sopenharmony_ci
54453a5a1b3Sopenharmony_ci    /* Save the current flags so we can tell if they've changed */
54553a5a1b3Sopenharmony_ci    flags = s->flags;
54653a5a1b3Sopenharmony_ci
54753a5a1b3Sopenharmony_ci    if (cb) {
54853a5a1b3Sopenharmony_ci        /* The sink implementor is responsible for setting decibel volume support */
54953a5a1b3Sopenharmony_ci        s->flags |= PA_SINK_HW_VOLUME_CTRL;
55053a5a1b3Sopenharmony_ci    } else {
55153a5a1b3Sopenharmony_ci        s->flags &= ~PA_SINK_HW_VOLUME_CTRL;
55253a5a1b3Sopenharmony_ci        /* See note below in pa_sink_put() about volume sharing and decibel volumes */
55353a5a1b3Sopenharmony_ci        pa_sink_enable_decibel_volume(s, !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
55453a5a1b3Sopenharmony_ci    }
55553a5a1b3Sopenharmony_ci
55653a5a1b3Sopenharmony_ci    /* If the flags have changed after init, let any clients know via a change event */
55753a5a1b3Sopenharmony_ci    if (s->state != PA_SINK_INIT && flags != s->flags)
55853a5a1b3Sopenharmony_ci        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
55953a5a1b3Sopenharmony_ci}
56053a5a1b3Sopenharmony_ci
56153a5a1b3Sopenharmony_civoid pa_sink_set_write_volume_callback(pa_sink *s, pa_sink_cb_t cb) {
56253a5a1b3Sopenharmony_ci    pa_sink_flags_t flags;
56353a5a1b3Sopenharmony_ci
56453a5a1b3Sopenharmony_ci    pa_assert(s);
56553a5a1b3Sopenharmony_ci    pa_assert(!cb || s->set_volume);
56653a5a1b3Sopenharmony_ci
56753a5a1b3Sopenharmony_ci    s->write_volume = cb;
56853a5a1b3Sopenharmony_ci
56953a5a1b3Sopenharmony_ci    /* Save the current flags so we can tell if they've changed */
57053a5a1b3Sopenharmony_ci    flags = s->flags;
57153a5a1b3Sopenharmony_ci
57253a5a1b3Sopenharmony_ci    if (cb)
57353a5a1b3Sopenharmony_ci        s->flags |= PA_SINK_DEFERRED_VOLUME;
57453a5a1b3Sopenharmony_ci    else
57553a5a1b3Sopenharmony_ci        s->flags &= ~PA_SINK_DEFERRED_VOLUME;
57653a5a1b3Sopenharmony_ci
57753a5a1b3Sopenharmony_ci    /* If the flags have changed after init, let any clients know via a change event */
57853a5a1b3Sopenharmony_ci    if (s->state != PA_SINK_INIT && flags != s->flags)
57953a5a1b3Sopenharmony_ci        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
58053a5a1b3Sopenharmony_ci}
58153a5a1b3Sopenharmony_ci
58253a5a1b3Sopenharmony_civoid pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_get_mute_cb_t cb) {
58353a5a1b3Sopenharmony_ci    pa_assert(s);
58453a5a1b3Sopenharmony_ci
58553a5a1b3Sopenharmony_ci    s->get_mute = cb;
58653a5a1b3Sopenharmony_ci}
58753a5a1b3Sopenharmony_ci
58853a5a1b3Sopenharmony_civoid pa_sink_set_set_mute_callback(pa_sink *s, pa_sink_cb_t cb) {
58953a5a1b3Sopenharmony_ci    pa_sink_flags_t flags;
59053a5a1b3Sopenharmony_ci
59153a5a1b3Sopenharmony_ci    pa_assert(s);
59253a5a1b3Sopenharmony_ci
59353a5a1b3Sopenharmony_ci    s->set_mute = cb;
59453a5a1b3Sopenharmony_ci
59553a5a1b3Sopenharmony_ci    /* Save the current flags so we can tell if they've changed */
59653a5a1b3Sopenharmony_ci    flags = s->flags;
59753a5a1b3Sopenharmony_ci
59853a5a1b3Sopenharmony_ci    if (cb)
59953a5a1b3Sopenharmony_ci        s->flags |= PA_SINK_HW_MUTE_CTRL;
60053a5a1b3Sopenharmony_ci    else
60153a5a1b3Sopenharmony_ci        s->flags &= ~PA_SINK_HW_MUTE_CTRL;
60253a5a1b3Sopenharmony_ci
60353a5a1b3Sopenharmony_ci    /* If the flags have changed after init, let any clients know via a change event */
60453a5a1b3Sopenharmony_ci    if (s->state != PA_SINK_INIT && flags != s->flags)
60553a5a1b3Sopenharmony_ci        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
60653a5a1b3Sopenharmony_ci}
60753a5a1b3Sopenharmony_ci
60853a5a1b3Sopenharmony_cistatic void enable_flat_volume(pa_sink *s, bool enable) {
60953a5a1b3Sopenharmony_ci    pa_sink_flags_t flags;
61053a5a1b3Sopenharmony_ci
61153a5a1b3Sopenharmony_ci    pa_assert(s);
61253a5a1b3Sopenharmony_ci
61353a5a1b3Sopenharmony_ci    /* Always follow the overall user preference here */
61453a5a1b3Sopenharmony_ci    enable = enable && s->core->flat_volumes;
61553a5a1b3Sopenharmony_ci
61653a5a1b3Sopenharmony_ci    /* Save the current flags so we can tell if they've changed */
61753a5a1b3Sopenharmony_ci    flags = s->flags;
61853a5a1b3Sopenharmony_ci
61953a5a1b3Sopenharmony_ci    if (enable)
62053a5a1b3Sopenharmony_ci        s->flags |= PA_SINK_FLAT_VOLUME;
62153a5a1b3Sopenharmony_ci    else
62253a5a1b3Sopenharmony_ci        s->flags &= ~PA_SINK_FLAT_VOLUME;
62353a5a1b3Sopenharmony_ci
62453a5a1b3Sopenharmony_ci    /* If the flags have changed after init, let any clients know via a change event */
62553a5a1b3Sopenharmony_ci    if (s->state != PA_SINK_INIT && flags != s->flags)
62653a5a1b3Sopenharmony_ci        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
62753a5a1b3Sopenharmony_ci}
62853a5a1b3Sopenharmony_ci
62953a5a1b3Sopenharmony_civoid pa_sink_enable_decibel_volume(pa_sink *s, bool enable) {
63053a5a1b3Sopenharmony_ci    pa_sink_flags_t flags;
63153a5a1b3Sopenharmony_ci
63253a5a1b3Sopenharmony_ci    pa_assert(s);
63353a5a1b3Sopenharmony_ci
63453a5a1b3Sopenharmony_ci    /* Save the current flags so we can tell if they've changed */
63553a5a1b3Sopenharmony_ci    flags = s->flags;
63653a5a1b3Sopenharmony_ci
63753a5a1b3Sopenharmony_ci    if (enable) {
63853a5a1b3Sopenharmony_ci        s->flags |= PA_SINK_DECIBEL_VOLUME;
63953a5a1b3Sopenharmony_ci        enable_flat_volume(s, true);
64053a5a1b3Sopenharmony_ci    } else {
64153a5a1b3Sopenharmony_ci        s->flags &= ~PA_SINK_DECIBEL_VOLUME;
64253a5a1b3Sopenharmony_ci        enable_flat_volume(s, false);
64353a5a1b3Sopenharmony_ci    }
64453a5a1b3Sopenharmony_ci
64553a5a1b3Sopenharmony_ci    /* If the flags have changed after init, let any clients know via a change event */
64653a5a1b3Sopenharmony_ci    if (s->state != PA_SINK_INIT && flags != s->flags)
64753a5a1b3Sopenharmony_ci        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
64853a5a1b3Sopenharmony_ci}
64953a5a1b3Sopenharmony_ci
65053a5a1b3Sopenharmony_ci/* Called from main context */
65153a5a1b3Sopenharmony_civoid pa_sink_put(pa_sink* s) {
65253a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
65353a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
65453a5a1b3Sopenharmony_ci
65553a5a1b3Sopenharmony_ci    pa_assert(s->state == PA_SINK_INIT);
65653a5a1b3Sopenharmony_ci    pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || pa_sink_is_filter(s));
65753a5a1b3Sopenharmony_ci
65853a5a1b3Sopenharmony_ci    /* The following fields must be initialized properly when calling _put() */
65953a5a1b3Sopenharmony_ci    pa_assert(s->asyncmsgq);
66053a5a1b3Sopenharmony_ci    pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
66153a5a1b3Sopenharmony_ci
66253a5a1b3Sopenharmony_ci    /* Generally, flags should be initialized via pa_sink_new(). As a
66353a5a1b3Sopenharmony_ci     * special exception we allow some volume related flags to be set
66453a5a1b3Sopenharmony_ci     * between _new() and _put() by the callback setter functions above.
66553a5a1b3Sopenharmony_ci     *
66653a5a1b3Sopenharmony_ci     * Thus we implement a couple safeguards here which ensure the above
66753a5a1b3Sopenharmony_ci     * setters were used (or at least the implementor made manual changes
66853a5a1b3Sopenharmony_ci     * in a compatible way).
66953a5a1b3Sopenharmony_ci     *
67053a5a1b3Sopenharmony_ci     * Note: All of these flags set here can change over the life time
67153a5a1b3Sopenharmony_ci     * of the sink. */
67253a5a1b3Sopenharmony_ci    pa_assert(!(s->flags & PA_SINK_HW_VOLUME_CTRL) || s->set_volume);
67353a5a1b3Sopenharmony_ci    pa_assert(!(s->flags & PA_SINK_DEFERRED_VOLUME) || s->write_volume);
67453a5a1b3Sopenharmony_ci    pa_assert(!(s->flags & PA_SINK_HW_MUTE_CTRL) || s->set_mute);
67553a5a1b3Sopenharmony_ci
67653a5a1b3Sopenharmony_ci    /* XXX: Currently decibel volume is disabled for all sinks that use volume
67753a5a1b3Sopenharmony_ci     * sharing. When the master sink supports decibel volume, it would be good
67853a5a1b3Sopenharmony_ci     * to have the flag also in the filter sink, but currently we don't do that
67953a5a1b3Sopenharmony_ci     * so that the flags of the filter sink never change when it's moved from
68053a5a1b3Sopenharmony_ci     * a master sink to another. One solution for this problem would be to
68153a5a1b3Sopenharmony_ci     * remove user-visible volume altogether from filter sinks when volume
68253a5a1b3Sopenharmony_ci     * sharing is used, but the current approach was easier to implement... */
68353a5a1b3Sopenharmony_ci    /* We always support decibel volumes in software, otherwise we leave it to
68453a5a1b3Sopenharmony_ci     * the sink implementor to set this flag as needed.
68553a5a1b3Sopenharmony_ci     *
68653a5a1b3Sopenharmony_ci     * Note: This flag can also change over the life time of the sink. */
68753a5a1b3Sopenharmony_ci    if (!(s->flags & PA_SINK_HW_VOLUME_CTRL) && !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
68853a5a1b3Sopenharmony_ci        pa_sink_enable_decibel_volume(s, true);
68953a5a1b3Sopenharmony_ci        s->soft_volume = s->reference_volume;
69053a5a1b3Sopenharmony_ci    }
69153a5a1b3Sopenharmony_ci
69253a5a1b3Sopenharmony_ci    /* If the sink implementor support DB volumes by itself, we should always
69353a5a1b3Sopenharmony_ci     * try and enable flat volumes too */
69453a5a1b3Sopenharmony_ci    if ((s->flags & PA_SINK_DECIBEL_VOLUME))
69553a5a1b3Sopenharmony_ci        enable_flat_volume(s, true);
69653a5a1b3Sopenharmony_ci
69753a5a1b3Sopenharmony_ci    if (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) {
69853a5a1b3Sopenharmony_ci        pa_sink *root_sink = pa_sink_get_master(s);
69953a5a1b3Sopenharmony_ci
70053a5a1b3Sopenharmony_ci        pa_assert(root_sink);
70153a5a1b3Sopenharmony_ci
70253a5a1b3Sopenharmony_ci        s->reference_volume = root_sink->reference_volume;
70353a5a1b3Sopenharmony_ci        pa_cvolume_remap(&s->reference_volume, &root_sink->channel_map, &s->channel_map);
70453a5a1b3Sopenharmony_ci
70553a5a1b3Sopenharmony_ci        s->real_volume = root_sink->real_volume;
70653a5a1b3Sopenharmony_ci        pa_cvolume_remap(&s->real_volume, &root_sink->channel_map, &s->channel_map);
70753a5a1b3Sopenharmony_ci    } else
70853a5a1b3Sopenharmony_ci        /* We assume that if the sink implementor changed the default
70953a5a1b3Sopenharmony_ci         * volume they did so in real_volume, because that is the usual
71053a5a1b3Sopenharmony_ci         * place where they are supposed to place their changes.  */
71153a5a1b3Sopenharmony_ci        s->reference_volume = s->real_volume;
71253a5a1b3Sopenharmony_ci
71353a5a1b3Sopenharmony_ci    s->thread_info.soft_volume = s->soft_volume;
71453a5a1b3Sopenharmony_ci    s->thread_info.soft_muted = s->muted;
71553a5a1b3Sopenharmony_ci    pa_sw_cvolume_divide(&s->thread_info.current_hw_volume, &s->real_volume, &s->soft_volume);
71653a5a1b3Sopenharmony_ci
71753a5a1b3Sopenharmony_ci    pa_assert((s->flags & PA_SINK_HW_VOLUME_CTRL)
71853a5a1b3Sopenharmony_ci              || (s->base_volume == PA_VOLUME_NORM
71953a5a1b3Sopenharmony_ci                  && ((s->flags & PA_SINK_DECIBEL_VOLUME || (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)))));
72053a5a1b3Sopenharmony_ci    pa_assert(!(s->flags & PA_SINK_DECIBEL_VOLUME) || s->n_volume_steps == PA_VOLUME_NORM+1);
72153a5a1b3Sopenharmony_ci    pa_assert(!(s->flags & PA_SINK_DYNAMIC_LATENCY) == !(s->thread_info.fixed_latency == 0));
72253a5a1b3Sopenharmony_ci    pa_assert(!(s->flags & PA_SINK_LATENCY) == !(s->monitor_source->flags & PA_SOURCE_LATENCY));
72353a5a1b3Sopenharmony_ci    pa_assert(!(s->flags & PA_SINK_DYNAMIC_LATENCY) == !(s->monitor_source->flags & PA_SOURCE_DYNAMIC_LATENCY));
72453a5a1b3Sopenharmony_ci
72553a5a1b3Sopenharmony_ci    pa_assert(s->monitor_source->thread_info.fixed_latency == s->thread_info.fixed_latency);
72653a5a1b3Sopenharmony_ci    pa_assert(s->monitor_source->thread_info.min_latency == s->thread_info.min_latency);
72753a5a1b3Sopenharmony_ci    pa_assert(s->monitor_source->thread_info.max_latency == s->thread_info.max_latency);
72853a5a1b3Sopenharmony_ci
72953a5a1b3Sopenharmony_ci    if (s->suspend_cause)
73053a5a1b3Sopenharmony_ci        pa_assert_se(sink_set_state(s, PA_SINK_SUSPENDED, s->suspend_cause) == 0);
73153a5a1b3Sopenharmony_ci    else
73253a5a1b3Sopenharmony_ci        pa_assert_se(sink_set_state(s, PA_SINK_IDLE, 0) == 0);
73353a5a1b3Sopenharmony_ci
73453a5a1b3Sopenharmony_ci    pa_source_put(s->monitor_source);
73553a5a1b3Sopenharmony_ci
73653a5a1b3Sopenharmony_ci    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
73753a5a1b3Sopenharmony_ci    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s);
73853a5a1b3Sopenharmony_ci
73953a5a1b3Sopenharmony_ci    /* It's good to fire the SINK_PUT hook before updating the default sink,
74053a5a1b3Sopenharmony_ci     * because module-switch-on-connect will set the new sink as the default
74153a5a1b3Sopenharmony_ci     * sink, and if we were to call pa_core_update_default_sink() before that,
74253a5a1b3Sopenharmony_ci     * the default sink might change twice, causing unnecessary stream moving. */
74353a5a1b3Sopenharmony_ci
74453a5a1b3Sopenharmony_ci    pa_core_update_default_sink(s->core);
74553a5a1b3Sopenharmony_ci
74653a5a1b3Sopenharmony_ci    pa_core_move_streams_to_newly_available_preferred_sink(s->core, s);
74753a5a1b3Sopenharmony_ci}
74853a5a1b3Sopenharmony_ci
74953a5a1b3Sopenharmony_ci/* Called from main context */
75053a5a1b3Sopenharmony_civoid pa_sink_unlink(pa_sink* s) {
75153a5a1b3Sopenharmony_ci    bool linked;
75253a5a1b3Sopenharmony_ci    pa_sink_input *i, PA_UNUSED *j = NULL;
75353a5a1b3Sopenharmony_ci
75453a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
75553a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
75653a5a1b3Sopenharmony_ci
75753a5a1b3Sopenharmony_ci    /* Please note that pa_sink_unlink() does more than simply
75853a5a1b3Sopenharmony_ci     * reversing pa_sink_put(). It also undoes the registrations
75953a5a1b3Sopenharmony_ci     * already done in pa_sink_new()! */
76053a5a1b3Sopenharmony_ci
76153a5a1b3Sopenharmony_ci    if (s->unlink_requested)
76253a5a1b3Sopenharmony_ci        return;
76353a5a1b3Sopenharmony_ci
76453a5a1b3Sopenharmony_ci    s->unlink_requested = true;
76553a5a1b3Sopenharmony_ci
76653a5a1b3Sopenharmony_ci    linked = PA_SINK_IS_LINKED(s->state);
76753a5a1b3Sopenharmony_ci
76853a5a1b3Sopenharmony_ci    if (linked)
76953a5a1b3Sopenharmony_ci        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s);
77053a5a1b3Sopenharmony_ci
77153a5a1b3Sopenharmony_ci    if (s->state != PA_SINK_UNLINKED)
77253a5a1b3Sopenharmony_ci        pa_namereg_unregister(s->core, s->name);
77353a5a1b3Sopenharmony_ci    pa_idxset_remove_by_data(s->core->sinks, s, NULL);
77453a5a1b3Sopenharmony_ci
77553a5a1b3Sopenharmony_ci    pa_core_update_default_sink(s->core);
77653a5a1b3Sopenharmony_ci
77753a5a1b3Sopenharmony_ci    if (linked && s->core->rescue_streams)
77853a5a1b3Sopenharmony_ci	pa_sink_move_streams_to_default_sink(s->core, s, false);
77953a5a1b3Sopenharmony_ci
78053a5a1b3Sopenharmony_ci    if (s->card)
78153a5a1b3Sopenharmony_ci        pa_idxset_remove_by_data(s->card->sinks, s, NULL);
78253a5a1b3Sopenharmony_ci
78353a5a1b3Sopenharmony_ci    while ((i = pa_idxset_first(s->inputs, NULL))) {
78453a5a1b3Sopenharmony_ci        pa_assert(i != j);
78553a5a1b3Sopenharmony_ci        pa_sink_input_kill(i);
78653a5a1b3Sopenharmony_ci        j = i;
78753a5a1b3Sopenharmony_ci    }
78853a5a1b3Sopenharmony_ci
78953a5a1b3Sopenharmony_ci    /* Unlink monitor source before unlinking the sink */
79053a5a1b3Sopenharmony_ci    if (s->monitor_source)
79153a5a1b3Sopenharmony_ci        pa_source_unlink(s->monitor_source);
79253a5a1b3Sopenharmony_ci
79353a5a1b3Sopenharmony_ci    if (linked)
79453a5a1b3Sopenharmony_ci        /* It's important to keep the suspend cause unchanged when unlinking,
79553a5a1b3Sopenharmony_ci         * because if we remove the SESSION suspend cause here, the alsa sink
79653a5a1b3Sopenharmony_ci         * will sync its volume with the hardware while another user is
79753a5a1b3Sopenharmony_ci         * active, messing up the volume for that other user. */
79853a5a1b3Sopenharmony_ci        sink_set_state(s, PA_SINK_UNLINKED, s->suspend_cause);
79953a5a1b3Sopenharmony_ci    else
80053a5a1b3Sopenharmony_ci        s->state = PA_SINK_UNLINKED;
80153a5a1b3Sopenharmony_ci
80253a5a1b3Sopenharmony_ci    reset_callbacks(s);
80353a5a1b3Sopenharmony_ci
80453a5a1b3Sopenharmony_ci    if (linked) {
80553a5a1b3Sopenharmony_ci        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
80653a5a1b3Sopenharmony_ci        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], s);
80753a5a1b3Sopenharmony_ci    }
80853a5a1b3Sopenharmony_ci}
80953a5a1b3Sopenharmony_ci
81053a5a1b3Sopenharmony_ci/* Called from main context */
81153a5a1b3Sopenharmony_cistatic void sink_free(pa_object *o) {
81253a5a1b3Sopenharmony_ci    pa_sink *s = PA_SINK(o);
81353a5a1b3Sopenharmony_ci
81453a5a1b3Sopenharmony_ci    pa_assert(s);
81553a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
81653a5a1b3Sopenharmony_ci    pa_assert(pa_sink_refcnt(s) == 0);
81753a5a1b3Sopenharmony_ci    pa_assert(!PA_SINK_IS_LINKED(s->state));
81853a5a1b3Sopenharmony_ci
81953a5a1b3Sopenharmony_ci    pa_log_info("Freeing sink %u \"%s\"", s->index, s->name);
82053a5a1b3Sopenharmony_ci
82153a5a1b3Sopenharmony_ci    pa_sink_volume_change_flush(s);
82253a5a1b3Sopenharmony_ci
82353a5a1b3Sopenharmony_ci    if (s->monitor_source) {
82453a5a1b3Sopenharmony_ci        pa_source_unref(s->monitor_source);
82553a5a1b3Sopenharmony_ci        s->monitor_source = NULL;
82653a5a1b3Sopenharmony_ci    }
82753a5a1b3Sopenharmony_ci
82853a5a1b3Sopenharmony_ci    pa_idxset_free(s->inputs, NULL);
82953a5a1b3Sopenharmony_ci    pa_hashmap_free(s->thread_info.inputs);
83053a5a1b3Sopenharmony_ci
83153a5a1b3Sopenharmony_ci    if (s->silence.memblock)
83253a5a1b3Sopenharmony_ci        pa_memblock_unref(s->silence.memblock);
83353a5a1b3Sopenharmony_ci
83453a5a1b3Sopenharmony_ci    pa_xfree(s->name);
83553a5a1b3Sopenharmony_ci    pa_xfree(s->driver);
83653a5a1b3Sopenharmony_ci
83753a5a1b3Sopenharmony_ci    if (s->proplist)
83853a5a1b3Sopenharmony_ci        pa_proplist_free(s->proplist);
83953a5a1b3Sopenharmony_ci
84053a5a1b3Sopenharmony_ci    if (s->ports)
84153a5a1b3Sopenharmony_ci        pa_hashmap_free(s->ports);
84253a5a1b3Sopenharmony_ci
84353a5a1b3Sopenharmony_ci    pa_xfree(s);
84453a5a1b3Sopenharmony_ci}
84553a5a1b3Sopenharmony_ci
84653a5a1b3Sopenharmony_ci/* Called from main context, and not while the IO thread is active, please */
84753a5a1b3Sopenharmony_civoid pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
84853a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
84953a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
85053a5a1b3Sopenharmony_ci
85153a5a1b3Sopenharmony_ci    s->asyncmsgq = q;
85253a5a1b3Sopenharmony_ci
85353a5a1b3Sopenharmony_ci    if (s->monitor_source)
85453a5a1b3Sopenharmony_ci        pa_source_set_asyncmsgq(s->monitor_source, q);
85553a5a1b3Sopenharmony_ci}
85653a5a1b3Sopenharmony_ci
85753a5a1b3Sopenharmony_ci/* Called from main context, and not while the IO thread is active, please */
85853a5a1b3Sopenharmony_civoid pa_sink_update_flags(pa_sink *s, pa_sink_flags_t mask, pa_sink_flags_t value) {
85953a5a1b3Sopenharmony_ci    pa_sink_flags_t old_flags;
86053a5a1b3Sopenharmony_ci    pa_sink_input *input;
86153a5a1b3Sopenharmony_ci    uint32_t idx;
86253a5a1b3Sopenharmony_ci
86353a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
86453a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
86553a5a1b3Sopenharmony_ci
86653a5a1b3Sopenharmony_ci    /* For now, allow only a minimal set of flags to be changed. */
86753a5a1b3Sopenharmony_ci    pa_assert((mask & ~(PA_SINK_DYNAMIC_LATENCY|PA_SINK_LATENCY)) == 0);
86853a5a1b3Sopenharmony_ci
86953a5a1b3Sopenharmony_ci    old_flags = s->flags;
87053a5a1b3Sopenharmony_ci    s->flags = (s->flags & ~mask) | (value & mask);
87153a5a1b3Sopenharmony_ci
87253a5a1b3Sopenharmony_ci    if (s->flags == old_flags)
87353a5a1b3Sopenharmony_ci        return;
87453a5a1b3Sopenharmony_ci
87553a5a1b3Sopenharmony_ci    if ((s->flags & PA_SINK_LATENCY) != (old_flags & PA_SINK_LATENCY))
87653a5a1b3Sopenharmony_ci        pa_log_debug("Sink %s: LATENCY flag %s.", s->name, (s->flags & PA_SINK_LATENCY) ? "enabled" : "disabled");
87753a5a1b3Sopenharmony_ci
87853a5a1b3Sopenharmony_ci    if ((s->flags & PA_SINK_DYNAMIC_LATENCY) != (old_flags & PA_SINK_DYNAMIC_LATENCY))
87953a5a1b3Sopenharmony_ci        pa_log_debug("Sink %s: DYNAMIC_LATENCY flag %s.",
88053a5a1b3Sopenharmony_ci                     s->name, (s->flags & PA_SINK_DYNAMIC_LATENCY) ? "enabled" : "disabled");
88153a5a1b3Sopenharmony_ci
88253a5a1b3Sopenharmony_ci    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
88353a5a1b3Sopenharmony_ci    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_FLAGS_CHANGED], s);
88453a5a1b3Sopenharmony_ci
88553a5a1b3Sopenharmony_ci    if (s->monitor_source)
88653a5a1b3Sopenharmony_ci        pa_source_update_flags(s->monitor_source,
88753a5a1b3Sopenharmony_ci                               ((mask & PA_SINK_LATENCY) ? PA_SOURCE_LATENCY : 0) |
88853a5a1b3Sopenharmony_ci                               ((mask & PA_SINK_DYNAMIC_LATENCY) ? PA_SOURCE_DYNAMIC_LATENCY : 0),
88953a5a1b3Sopenharmony_ci                               ((value & PA_SINK_LATENCY) ? PA_SOURCE_LATENCY : 0) |
89053a5a1b3Sopenharmony_ci                               ((value & PA_SINK_DYNAMIC_LATENCY) ? PA_SOURCE_DYNAMIC_LATENCY : 0));
89153a5a1b3Sopenharmony_ci
89253a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(input, s->inputs, idx) {
89353a5a1b3Sopenharmony_ci        if (input->origin_sink)
89453a5a1b3Sopenharmony_ci            pa_sink_update_flags(input->origin_sink, mask, value);
89553a5a1b3Sopenharmony_ci    }
89653a5a1b3Sopenharmony_ci}
89753a5a1b3Sopenharmony_ci
89853a5a1b3Sopenharmony_ci/* Called from IO context, or before _put() from main context */
89953a5a1b3Sopenharmony_civoid pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
90053a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
90153a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
90253a5a1b3Sopenharmony_ci
90353a5a1b3Sopenharmony_ci    s->thread_info.rtpoll = p;
90453a5a1b3Sopenharmony_ci
90553a5a1b3Sopenharmony_ci    if (s->monitor_source)
90653a5a1b3Sopenharmony_ci        pa_source_set_rtpoll(s->monitor_source, p);
90753a5a1b3Sopenharmony_ci}
90853a5a1b3Sopenharmony_ci
90953a5a1b3Sopenharmony_ci/* Called from main context */
91053a5a1b3Sopenharmony_ciint pa_sink_update_status(pa_sink*s) {
91153a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
91253a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
91353a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
91453a5a1b3Sopenharmony_ci
91553a5a1b3Sopenharmony_ci    if (s->state == PA_SINK_SUSPENDED)
91653a5a1b3Sopenharmony_ci        return 0;
91753a5a1b3Sopenharmony_ci
91853a5a1b3Sopenharmony_ci    return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE, 0);
91953a5a1b3Sopenharmony_ci}
92053a5a1b3Sopenharmony_ci
92153a5a1b3Sopenharmony_ci/* Called from main context */
92253a5a1b3Sopenharmony_ciint pa_sink_suspend(pa_sink *s, bool suspend, pa_suspend_cause_t cause) {
92353a5a1b3Sopenharmony_ci    pa_suspend_cause_t merged_cause;
92453a5a1b3Sopenharmony_ci
92553a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
92653a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
92753a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
92853a5a1b3Sopenharmony_ci    pa_assert(cause != 0);
92953a5a1b3Sopenharmony_ci
93053a5a1b3Sopenharmony_ci    if (suspend)
93153a5a1b3Sopenharmony_ci        merged_cause = s->suspend_cause | cause;
93253a5a1b3Sopenharmony_ci    else
93353a5a1b3Sopenharmony_ci        merged_cause = s->suspend_cause & ~cause;
93453a5a1b3Sopenharmony_ci
93553a5a1b3Sopenharmony_ci    if (merged_cause)
93653a5a1b3Sopenharmony_ci        return sink_set_state(s, PA_SINK_SUSPENDED, merged_cause);
93753a5a1b3Sopenharmony_ci    else
93853a5a1b3Sopenharmony_ci        return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE, 0);
93953a5a1b3Sopenharmony_ci}
94053a5a1b3Sopenharmony_ci
94153a5a1b3Sopenharmony_ci/* Called from main context */
94253a5a1b3Sopenharmony_cipa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q) {
94353a5a1b3Sopenharmony_ci    pa_sink_input *i, *n;
94453a5a1b3Sopenharmony_ci    uint32_t idx;
94553a5a1b3Sopenharmony_ci
94653a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
94753a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
94853a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
94953a5a1b3Sopenharmony_ci
95053a5a1b3Sopenharmony_ci    if (!q)
95153a5a1b3Sopenharmony_ci        q = pa_queue_new();
95253a5a1b3Sopenharmony_ci
95353a5a1b3Sopenharmony_ci    for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) {
95453a5a1b3Sopenharmony_ci        n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx));
95553a5a1b3Sopenharmony_ci
95653a5a1b3Sopenharmony_ci        pa_sink_input_ref(i);
95753a5a1b3Sopenharmony_ci
95853a5a1b3Sopenharmony_ci        if (pa_sink_input_start_move(i) >= 0)
95953a5a1b3Sopenharmony_ci            pa_queue_push(q, i);
96053a5a1b3Sopenharmony_ci        else
96153a5a1b3Sopenharmony_ci            pa_sink_input_unref(i);
96253a5a1b3Sopenharmony_ci    }
96353a5a1b3Sopenharmony_ci
96453a5a1b3Sopenharmony_ci    return q;
96553a5a1b3Sopenharmony_ci}
96653a5a1b3Sopenharmony_ci
96753a5a1b3Sopenharmony_ci/* Called from main context */
96853a5a1b3Sopenharmony_civoid pa_sink_move_all_finish(pa_sink *s, pa_queue *q, bool save) {
96953a5a1b3Sopenharmony_ci    pa_sink_input *i;
97053a5a1b3Sopenharmony_ci
97153a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
97253a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
97353a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
97453a5a1b3Sopenharmony_ci    pa_assert(q);
97553a5a1b3Sopenharmony_ci
97653a5a1b3Sopenharmony_ci    while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
97753a5a1b3Sopenharmony_ci        if (PA_SINK_INPUT_IS_LINKED(i->state)) {
97853a5a1b3Sopenharmony_ci            if (pa_sink_input_finish_move(i, s, save) < 0)
97953a5a1b3Sopenharmony_ci                pa_sink_input_fail_move(i);
98053a5a1b3Sopenharmony_ci
98153a5a1b3Sopenharmony_ci        }
98253a5a1b3Sopenharmony_ci        pa_sink_input_unref(i);
98353a5a1b3Sopenharmony_ci    }
98453a5a1b3Sopenharmony_ci
98553a5a1b3Sopenharmony_ci    pa_queue_free(q, NULL);
98653a5a1b3Sopenharmony_ci}
98753a5a1b3Sopenharmony_ci
98853a5a1b3Sopenharmony_ci/* Called from main context */
98953a5a1b3Sopenharmony_civoid pa_sink_move_all_fail(pa_queue *q) {
99053a5a1b3Sopenharmony_ci    pa_sink_input *i;
99153a5a1b3Sopenharmony_ci
99253a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
99353a5a1b3Sopenharmony_ci    pa_assert(q);
99453a5a1b3Sopenharmony_ci
99553a5a1b3Sopenharmony_ci    while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
99653a5a1b3Sopenharmony_ci        pa_sink_input_fail_move(i);
99753a5a1b3Sopenharmony_ci        pa_sink_input_unref(i);
99853a5a1b3Sopenharmony_ci    }
99953a5a1b3Sopenharmony_ci
100053a5a1b3Sopenharmony_ci    pa_queue_free(q, NULL);
100153a5a1b3Sopenharmony_ci}
100253a5a1b3Sopenharmony_ci
100353a5a1b3Sopenharmony_ci /* Called from IO thread context */
100453a5a1b3Sopenharmony_cisize_t pa_sink_process_input_underruns(pa_sink *s, size_t left_to_play) {
100553a5a1b3Sopenharmony_ci    pa_sink_input *i;
100653a5a1b3Sopenharmony_ci    void *state = NULL;
100753a5a1b3Sopenharmony_ci    size_t result = 0;
100853a5a1b3Sopenharmony_ci
100953a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
101053a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
101153a5a1b3Sopenharmony_ci
101253a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
101353a5a1b3Sopenharmony_ci        size_t uf = i->thread_info.underrun_for_sink;
101453a5a1b3Sopenharmony_ci
101553a5a1b3Sopenharmony_ci        /* Propagate down the filter tree */
101653a5a1b3Sopenharmony_ci        if (i->origin_sink) {
101753a5a1b3Sopenharmony_ci            size_t filter_result, left_to_play_origin;
101853a5a1b3Sopenharmony_ci
101953a5a1b3Sopenharmony_ci            /* The combine sink sets i->origin sink but has a different threading model
102053a5a1b3Sopenharmony_ci             * than the filter sinks. Therefore the recursion below may not be executed
102153a5a1b3Sopenharmony_ci             * because pa_sink_process_input_underruns() was not called in the thread
102253a5a1b3Sopenharmony_ci             * context of the origin sink.
102353a5a1b3Sopenharmony_ci             * FIXME: It is unclear if some other kind of recursion would be necessary
102453a5a1b3Sopenharmony_ci             * for the combine sink. */
102553a5a1b3Sopenharmony_ci            if (!i->module || !pa_safe_streq(i->module->name, "module-combine-sink")) {
102653a5a1b3Sopenharmony_ci
102753a5a1b3Sopenharmony_ci                /* The recursive call works in the origin sink domain ... */
102853a5a1b3Sopenharmony_ci                left_to_play_origin = pa_convert_size(left_to_play, &i->sink->sample_spec, &i->origin_sink->sample_spec);
102953a5a1b3Sopenharmony_ci
103053a5a1b3Sopenharmony_ci                /* .. and returns the time to sleep before waking up. We need the
103153a5a1b3Sopenharmony_ci                 * underrun duration for comparisons, so we undo the subtraction on
103253a5a1b3Sopenharmony_ci                 * the return value... */
103353a5a1b3Sopenharmony_ci                filter_result = left_to_play_origin - pa_sink_process_input_underruns(i->origin_sink, left_to_play_origin);
103453a5a1b3Sopenharmony_ci
103553a5a1b3Sopenharmony_ci                /* ... and convert it back to the master sink domain */
103653a5a1b3Sopenharmony_ci                filter_result = pa_convert_size(filter_result, &i->origin_sink->sample_spec, &i->sink->sample_spec);
103753a5a1b3Sopenharmony_ci
103853a5a1b3Sopenharmony_ci                /* Remember the longest underrun so far */
103953a5a1b3Sopenharmony_ci                if (filter_result > result)
104053a5a1b3Sopenharmony_ci                    result = filter_result;
104153a5a1b3Sopenharmony_ci            }
104253a5a1b3Sopenharmony_ci        }
104353a5a1b3Sopenharmony_ci
104453a5a1b3Sopenharmony_ci        if (uf == 0) {
104553a5a1b3Sopenharmony_ci            /* No underrun here, move on */
104653a5a1b3Sopenharmony_ci            continue;
104753a5a1b3Sopenharmony_ci        } else if (uf >= left_to_play) {
104853a5a1b3Sopenharmony_ci            /* The sink has possibly consumed all the data the sink input provided */
104953a5a1b3Sopenharmony_ci            pa_sink_input_process_underrun(i);
105053a5a1b3Sopenharmony_ci        } else if (uf > result) {
105153a5a1b3Sopenharmony_ci            /* Remember the longest underrun so far */
105253a5a1b3Sopenharmony_ci            result = uf;
105353a5a1b3Sopenharmony_ci        }
105453a5a1b3Sopenharmony_ci    }
105553a5a1b3Sopenharmony_ci
105653a5a1b3Sopenharmony_ci    if (result > 0)
105753a5a1b3Sopenharmony_ci        pa_log_debug("%s: Found underrun %ld bytes ago (%ld bytes ahead in playback buffer)", s->name,
105853a5a1b3Sopenharmony_ci                (long) result, (long) left_to_play - result);
105953a5a1b3Sopenharmony_ci    return left_to_play - result;
106053a5a1b3Sopenharmony_ci}
106153a5a1b3Sopenharmony_ci
106253a5a1b3Sopenharmony_ci/* Called from IO thread context */
106353a5a1b3Sopenharmony_civoid pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
106453a5a1b3Sopenharmony_ci    pa_sink_input *i;
106553a5a1b3Sopenharmony_ci    void *state = NULL;
106653a5a1b3Sopenharmony_ci
106753a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
106853a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
106953a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
107053a5a1b3Sopenharmony_ci
107153a5a1b3Sopenharmony_ci    /* If nobody requested this and this is actually no real rewind
107253a5a1b3Sopenharmony_ci     * then we can short cut this. Please note that this means that
107353a5a1b3Sopenharmony_ci     * not all rewind requests triggered upstream will always be
107453a5a1b3Sopenharmony_ci     * translated in actual requests! */
107553a5a1b3Sopenharmony_ci    if (!s->thread_info.rewind_requested && nbytes <= 0)
107653a5a1b3Sopenharmony_ci        return;
107753a5a1b3Sopenharmony_ci
107853a5a1b3Sopenharmony_ci    s->thread_info.rewind_nbytes = 0;
107953a5a1b3Sopenharmony_ci    s->thread_info.rewind_requested = false;
108053a5a1b3Sopenharmony_ci
108153a5a1b3Sopenharmony_ci    if (nbytes > 0) {
108253a5a1b3Sopenharmony_ci        pa_log_debug("Processing rewind...");
108353a5a1b3Sopenharmony_ci        if (s->flags & PA_SINK_DEFERRED_VOLUME)
108453a5a1b3Sopenharmony_ci            pa_sink_volume_change_rewind(s, nbytes);
108553a5a1b3Sopenharmony_ci    }
108653a5a1b3Sopenharmony_ci
108753a5a1b3Sopenharmony_ci    /* Save rewind value */
108853a5a1b3Sopenharmony_ci    s->thread_info.last_rewind_nbytes = nbytes;
108953a5a1b3Sopenharmony_ci
109053a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
109153a5a1b3Sopenharmony_ci        pa_sink_input_assert_ref(i);
109253a5a1b3Sopenharmony_ci        pa_sink_input_process_rewind(i, nbytes);
109353a5a1b3Sopenharmony_ci    }
109453a5a1b3Sopenharmony_ci
109553a5a1b3Sopenharmony_ci    if (nbytes > 0) {
109653a5a1b3Sopenharmony_ci        if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state))
109753a5a1b3Sopenharmony_ci            pa_source_process_rewind(s->monitor_source, nbytes);
109853a5a1b3Sopenharmony_ci    }
109953a5a1b3Sopenharmony_ci}
110053a5a1b3Sopenharmony_ci
110153a5a1b3Sopenharmony_ci/* Called from IO thread context */
110253a5a1b3Sopenharmony_cistatic unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, unsigned maxinfo) {
110353a5a1b3Sopenharmony_ci    pa_sink_input *i;
110453a5a1b3Sopenharmony_ci    unsigned n = 0;
110553a5a1b3Sopenharmony_ci    void *state = NULL;
110653a5a1b3Sopenharmony_ci    size_t mixlength = *length;
110753a5a1b3Sopenharmony_ci
110853a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
110953a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
111053a5a1b3Sopenharmony_ci    pa_assert(info);
111153a5a1b3Sopenharmony_ci
111253a5a1b3Sopenharmony_ci    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) {
111353a5a1b3Sopenharmony_ci        pa_sink_input_assert_ref(i);
111453a5a1b3Sopenharmony_ci
111553a5a1b3Sopenharmony_ci        pa_sink_input_peek(i, *length, &info->chunk, &info->volume);
111653a5a1b3Sopenharmony_ci
111753a5a1b3Sopenharmony_ci        if (mixlength == 0 || info->chunk.length < mixlength)
111853a5a1b3Sopenharmony_ci            mixlength = info->chunk.length;
111953a5a1b3Sopenharmony_ci
112053a5a1b3Sopenharmony_ci        if (pa_memblock_is_silence(info->chunk.memblock)) {
112153a5a1b3Sopenharmony_ci            pa_memblock_unref(info->chunk.memblock);
112253a5a1b3Sopenharmony_ci            continue;
112353a5a1b3Sopenharmony_ci        }
112453a5a1b3Sopenharmony_ci
112553a5a1b3Sopenharmony_ci        info->userdata = pa_sink_input_ref(i);
112653a5a1b3Sopenharmony_ci
112753a5a1b3Sopenharmony_ci        pa_assert(info->chunk.memblock);
112853a5a1b3Sopenharmony_ci        pa_assert(info->chunk.length > 0);
112953a5a1b3Sopenharmony_ci
113053a5a1b3Sopenharmony_ci        info++;
113153a5a1b3Sopenharmony_ci        n++;
113253a5a1b3Sopenharmony_ci        maxinfo--;
113353a5a1b3Sopenharmony_ci    }
113453a5a1b3Sopenharmony_ci
113553a5a1b3Sopenharmony_ci    if (mixlength > 0)
113653a5a1b3Sopenharmony_ci        *length = mixlength;
113753a5a1b3Sopenharmony_ci
113853a5a1b3Sopenharmony_ci    return n;
113953a5a1b3Sopenharmony_ci}
114053a5a1b3Sopenharmony_ci
114153a5a1b3Sopenharmony_ci/* Called from IO thread context */
114253a5a1b3Sopenharmony_cistatic void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *result) {
114353a5a1b3Sopenharmony_ci    pa_sink_input *i;
114453a5a1b3Sopenharmony_ci    void *state;
114553a5a1b3Sopenharmony_ci    unsigned p = 0;
114653a5a1b3Sopenharmony_ci    unsigned n_unreffed = 0;
114753a5a1b3Sopenharmony_ci
114853a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
114953a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
115053a5a1b3Sopenharmony_ci    pa_assert(result);
115153a5a1b3Sopenharmony_ci    pa_assert(result->memblock);
115253a5a1b3Sopenharmony_ci    pa_assert(result->length > 0);
115353a5a1b3Sopenharmony_ci
115453a5a1b3Sopenharmony_ci    /* We optimize for the case where the order of the inputs has not changed */
115553a5a1b3Sopenharmony_ci
115653a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
115753a5a1b3Sopenharmony_ci        unsigned j;
115853a5a1b3Sopenharmony_ci        pa_mix_info* m = NULL;
115953a5a1b3Sopenharmony_ci
116053a5a1b3Sopenharmony_ci        pa_sink_input_assert_ref(i);
116153a5a1b3Sopenharmony_ci
116253a5a1b3Sopenharmony_ci        /* Let's try to find the matching entry info the pa_mix_info array */
116353a5a1b3Sopenharmony_ci        for (j = 0; j < n; j ++) {
116453a5a1b3Sopenharmony_ci
116553a5a1b3Sopenharmony_ci            if (info[p].userdata == i) {
116653a5a1b3Sopenharmony_ci                m = info + p;
116753a5a1b3Sopenharmony_ci                break;
116853a5a1b3Sopenharmony_ci            }
116953a5a1b3Sopenharmony_ci
117053a5a1b3Sopenharmony_ci            p++;
117153a5a1b3Sopenharmony_ci            if (p >= n)
117253a5a1b3Sopenharmony_ci                p = 0;
117353a5a1b3Sopenharmony_ci        }
117453a5a1b3Sopenharmony_ci
117553a5a1b3Sopenharmony_ci        /* Drop read data */
117653a5a1b3Sopenharmony_ci        pa_sink_input_drop(i, result->length);
117753a5a1b3Sopenharmony_ci
117853a5a1b3Sopenharmony_ci        if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state)) {
117953a5a1b3Sopenharmony_ci
118053a5a1b3Sopenharmony_ci            if (pa_hashmap_size(i->thread_info.direct_outputs) > 0) {
118153a5a1b3Sopenharmony_ci                void *ostate = NULL;
118253a5a1b3Sopenharmony_ci                pa_source_output *o;
118353a5a1b3Sopenharmony_ci                pa_memchunk c;
118453a5a1b3Sopenharmony_ci
118553a5a1b3Sopenharmony_ci                if (m && m->chunk.memblock) {
118653a5a1b3Sopenharmony_ci                    c = m->chunk;
118753a5a1b3Sopenharmony_ci                    pa_memblock_ref(c.memblock);
118853a5a1b3Sopenharmony_ci                    pa_assert(result->length <= c.length);
118953a5a1b3Sopenharmony_ci                    c.length = result->length;
119053a5a1b3Sopenharmony_ci
119153a5a1b3Sopenharmony_ci                    pa_memchunk_make_writable(&c, 0);
119253a5a1b3Sopenharmony_ci                    pa_volume_memchunk(&c, &s->sample_spec, &m->volume);
119353a5a1b3Sopenharmony_ci                } else {
119453a5a1b3Sopenharmony_ci                    c = s->silence;
119553a5a1b3Sopenharmony_ci                    pa_memblock_ref(c.memblock);
119653a5a1b3Sopenharmony_ci                    pa_assert(result->length <= c.length);
119753a5a1b3Sopenharmony_ci                    c.length = result->length;
119853a5a1b3Sopenharmony_ci                }
119953a5a1b3Sopenharmony_ci
120053a5a1b3Sopenharmony_ci                while ((o = pa_hashmap_iterate(i->thread_info.direct_outputs, &ostate, NULL))) {
120153a5a1b3Sopenharmony_ci                    pa_source_output_assert_ref(o);
120253a5a1b3Sopenharmony_ci                    pa_assert(o->direct_on_input == i);
120353a5a1b3Sopenharmony_ci                    pa_source_post_direct(s->monitor_source, o, &c);
120453a5a1b3Sopenharmony_ci                }
120553a5a1b3Sopenharmony_ci
120653a5a1b3Sopenharmony_ci                pa_memblock_unref(c.memblock);
120753a5a1b3Sopenharmony_ci            }
120853a5a1b3Sopenharmony_ci        }
120953a5a1b3Sopenharmony_ci
121053a5a1b3Sopenharmony_ci        if (m) {
121153a5a1b3Sopenharmony_ci            if (m->chunk.memblock) {
121253a5a1b3Sopenharmony_ci                pa_memblock_unref(m->chunk.memblock);
121353a5a1b3Sopenharmony_ci                pa_memchunk_reset(&m->chunk);
121453a5a1b3Sopenharmony_ci            }
121553a5a1b3Sopenharmony_ci
121653a5a1b3Sopenharmony_ci            pa_sink_input_unref(m->userdata);
121753a5a1b3Sopenharmony_ci            m->userdata = NULL;
121853a5a1b3Sopenharmony_ci
121953a5a1b3Sopenharmony_ci            n_unreffed += 1;
122053a5a1b3Sopenharmony_ci        }
122153a5a1b3Sopenharmony_ci    }
122253a5a1b3Sopenharmony_ci
122353a5a1b3Sopenharmony_ci    /* Now drop references to entries that are included in the
122453a5a1b3Sopenharmony_ci     * pa_mix_info array but don't exist anymore */
122553a5a1b3Sopenharmony_ci
122653a5a1b3Sopenharmony_ci    if (n_unreffed < n) {
122753a5a1b3Sopenharmony_ci        for (; n > 0; info++, n--) {
122853a5a1b3Sopenharmony_ci            if (info->userdata)
122953a5a1b3Sopenharmony_ci                pa_sink_input_unref(info->userdata);
123053a5a1b3Sopenharmony_ci            if (info->chunk.memblock)
123153a5a1b3Sopenharmony_ci                pa_memblock_unref(info->chunk.memblock);
123253a5a1b3Sopenharmony_ci        }
123353a5a1b3Sopenharmony_ci    }
123453a5a1b3Sopenharmony_ci
123553a5a1b3Sopenharmony_ci    if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state))
123653a5a1b3Sopenharmony_ci        pa_source_post(s->monitor_source, result);
123753a5a1b3Sopenharmony_ci}
123853a5a1b3Sopenharmony_ci
123953a5a1b3Sopenharmony_ci/* Called from IO thread context */
124053a5a1b3Sopenharmony_civoid pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
124153a5a1b3Sopenharmony_ci    pa_mix_info info[MAX_MIX_CHANNELS];
124253a5a1b3Sopenharmony_ci    unsigned n;
124353a5a1b3Sopenharmony_ci    size_t block_size_max;
124453a5a1b3Sopenharmony_ci
124553a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
124653a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
124753a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
124853a5a1b3Sopenharmony_ci    pa_assert(pa_frame_aligned(length, &s->sample_spec));
124953a5a1b3Sopenharmony_ci    pa_assert(result);
125053a5a1b3Sopenharmony_ci
125153a5a1b3Sopenharmony_ci    pa_assert(!s->thread_info.rewind_requested);
125253a5a1b3Sopenharmony_ci    pa_assert(s->thread_info.rewind_nbytes == 0);
125353a5a1b3Sopenharmony_ci
125453a5a1b3Sopenharmony_ci    if (s->thread_info.state == PA_SINK_SUSPENDED) {
125553a5a1b3Sopenharmony_ci        result->memblock = pa_memblock_ref(s->silence.memblock);
125653a5a1b3Sopenharmony_ci        result->index = s->silence.index;
125753a5a1b3Sopenharmony_ci        result->length = PA_MIN(s->silence.length, length);
125853a5a1b3Sopenharmony_ci        return;
125953a5a1b3Sopenharmony_ci    }
126053a5a1b3Sopenharmony_ci
126153a5a1b3Sopenharmony_ci    pa_sink_ref(s);
126253a5a1b3Sopenharmony_ci
126353a5a1b3Sopenharmony_ci    if (length <= 0)
126453a5a1b3Sopenharmony_ci        length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
126553a5a1b3Sopenharmony_ci
126653a5a1b3Sopenharmony_ci    block_size_max = pa_mempool_block_size_max(s->core->mempool);
126753a5a1b3Sopenharmony_ci    if (length > block_size_max)
126853a5a1b3Sopenharmony_ci        length = pa_frame_align(block_size_max, &s->sample_spec);
126953a5a1b3Sopenharmony_ci
127053a5a1b3Sopenharmony_ci    pa_assert(length > 0);
127153a5a1b3Sopenharmony_ci
127253a5a1b3Sopenharmony_ci    n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS);
127353a5a1b3Sopenharmony_ci
127453a5a1b3Sopenharmony_ci    if (n == 0) {
127553a5a1b3Sopenharmony_ci
127653a5a1b3Sopenharmony_ci        *result = s->silence;
127753a5a1b3Sopenharmony_ci        pa_memblock_ref(result->memblock);
127853a5a1b3Sopenharmony_ci
127953a5a1b3Sopenharmony_ci        if (result->length > length)
128053a5a1b3Sopenharmony_ci            result->length = length;
128153a5a1b3Sopenharmony_ci
128253a5a1b3Sopenharmony_ci    } else if (n == 1) {
128353a5a1b3Sopenharmony_ci        pa_cvolume volume;
128453a5a1b3Sopenharmony_ci
128553a5a1b3Sopenharmony_ci        *result = info[0].chunk;
128653a5a1b3Sopenharmony_ci        pa_memblock_ref(result->memblock);
128753a5a1b3Sopenharmony_ci
128853a5a1b3Sopenharmony_ci        if (result->length > length)
128953a5a1b3Sopenharmony_ci            result->length = length;
129053a5a1b3Sopenharmony_ci
129153a5a1b3Sopenharmony_ci        pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
129253a5a1b3Sopenharmony_ci
129353a5a1b3Sopenharmony_ci        if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) {
129453a5a1b3Sopenharmony_ci            pa_memblock_unref(result->memblock);
129553a5a1b3Sopenharmony_ci            pa_silence_memchunk_get(&s->core->silence_cache,
129653a5a1b3Sopenharmony_ci                                    s->core->mempool,
129753a5a1b3Sopenharmony_ci                                    result,
129853a5a1b3Sopenharmony_ci                                    &s->sample_spec,
129953a5a1b3Sopenharmony_ci                                    result->length);
130053a5a1b3Sopenharmony_ci        } else if (!pa_cvolume_is_norm(&volume)) {
130153a5a1b3Sopenharmony_ci            pa_memchunk_make_writable(result, 0);
130253a5a1b3Sopenharmony_ci            pa_volume_memchunk(result, &s->sample_spec, &volume);
130353a5a1b3Sopenharmony_ci        }
130453a5a1b3Sopenharmony_ci    } else {
130553a5a1b3Sopenharmony_ci        void *ptr;
130653a5a1b3Sopenharmony_ci        result->memblock = pa_memblock_new(s->core->mempool, length);
130753a5a1b3Sopenharmony_ci
130853a5a1b3Sopenharmony_ci        ptr = pa_memblock_acquire(result->memblock);
130953a5a1b3Sopenharmony_ci        result->length = pa_mix(info, n,
131053a5a1b3Sopenharmony_ci                                ptr, length,
131153a5a1b3Sopenharmony_ci                                &s->sample_spec,
131253a5a1b3Sopenharmony_ci                                &s->thread_info.soft_volume,
131353a5a1b3Sopenharmony_ci                                s->thread_info.soft_muted);
131453a5a1b3Sopenharmony_ci        pa_memblock_release(result->memblock);
131553a5a1b3Sopenharmony_ci
131653a5a1b3Sopenharmony_ci        result->index = 0;
131753a5a1b3Sopenharmony_ci    }
131853a5a1b3Sopenharmony_ci
131953a5a1b3Sopenharmony_ci    inputs_drop(s, info, n, result);
132053a5a1b3Sopenharmony_ci
132153a5a1b3Sopenharmony_ci    pa_sink_unref(s);
132253a5a1b3Sopenharmony_ci}
132353a5a1b3Sopenharmony_ci
132453a5a1b3Sopenharmony_ci/* Called from IO thread context */
132553a5a1b3Sopenharmony_civoid pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
132653a5a1b3Sopenharmony_ci    pa_mix_info info[MAX_MIX_CHANNELS];
132753a5a1b3Sopenharmony_ci    unsigned n;
132853a5a1b3Sopenharmony_ci    size_t length, block_size_max;
132953a5a1b3Sopenharmony_ci
133053a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
133153a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
133253a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
133353a5a1b3Sopenharmony_ci    pa_assert(target);
133453a5a1b3Sopenharmony_ci    pa_assert(target->memblock);
133553a5a1b3Sopenharmony_ci    pa_assert(target->length > 0);
133653a5a1b3Sopenharmony_ci    pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
133753a5a1b3Sopenharmony_ci
133853a5a1b3Sopenharmony_ci    pa_assert(!s->thread_info.rewind_requested);
133953a5a1b3Sopenharmony_ci    pa_assert(s->thread_info.rewind_nbytes == 0);
134053a5a1b3Sopenharmony_ci
134153a5a1b3Sopenharmony_ci    if (s->thread_info.state == PA_SINK_SUSPENDED) {
134253a5a1b3Sopenharmony_ci        pa_silence_memchunk(target, &s->sample_spec);
134353a5a1b3Sopenharmony_ci        return;
134453a5a1b3Sopenharmony_ci    }
134553a5a1b3Sopenharmony_ci
134653a5a1b3Sopenharmony_ci    pa_sink_ref(s);
134753a5a1b3Sopenharmony_ci
134853a5a1b3Sopenharmony_ci    length = target->length;
134953a5a1b3Sopenharmony_ci    block_size_max = pa_mempool_block_size_max(s->core->mempool);
135053a5a1b3Sopenharmony_ci    if (length > block_size_max)
135153a5a1b3Sopenharmony_ci        length = pa_frame_align(block_size_max, &s->sample_spec);
135253a5a1b3Sopenharmony_ci
135353a5a1b3Sopenharmony_ci    pa_assert(length > 0);
135453a5a1b3Sopenharmony_ci
135553a5a1b3Sopenharmony_ci    n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS);
135653a5a1b3Sopenharmony_ci
135753a5a1b3Sopenharmony_ci    if (n == 0) {
135853a5a1b3Sopenharmony_ci        if (target->length > length)
135953a5a1b3Sopenharmony_ci            target->length = length;
136053a5a1b3Sopenharmony_ci
136153a5a1b3Sopenharmony_ci        pa_silence_memchunk(target, &s->sample_spec);
136253a5a1b3Sopenharmony_ci    } else if (n == 1) {
136353a5a1b3Sopenharmony_ci        pa_cvolume volume;
136453a5a1b3Sopenharmony_ci
136553a5a1b3Sopenharmony_ci        if (target->length > length)
136653a5a1b3Sopenharmony_ci            target->length = length;
136753a5a1b3Sopenharmony_ci
136853a5a1b3Sopenharmony_ci        pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
136953a5a1b3Sopenharmony_ci
137053a5a1b3Sopenharmony_ci        if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
137153a5a1b3Sopenharmony_ci            pa_silence_memchunk(target, &s->sample_spec);
137253a5a1b3Sopenharmony_ci        else {
137353a5a1b3Sopenharmony_ci            pa_memchunk vchunk;
137453a5a1b3Sopenharmony_ci
137553a5a1b3Sopenharmony_ci            vchunk = info[0].chunk;
137653a5a1b3Sopenharmony_ci            pa_memblock_ref(vchunk.memblock);
137753a5a1b3Sopenharmony_ci
137853a5a1b3Sopenharmony_ci            if (vchunk.length > length)
137953a5a1b3Sopenharmony_ci                vchunk.length = length;
138053a5a1b3Sopenharmony_ci
138153a5a1b3Sopenharmony_ci            if (!pa_cvolume_is_norm(&volume)) {
138253a5a1b3Sopenharmony_ci                pa_memchunk_make_writable(&vchunk, 0);
138353a5a1b3Sopenharmony_ci                pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
138453a5a1b3Sopenharmony_ci            }
138553a5a1b3Sopenharmony_ci
138653a5a1b3Sopenharmony_ci            pa_memchunk_memcpy(target, &vchunk);
138753a5a1b3Sopenharmony_ci            pa_memblock_unref(vchunk.memblock);
138853a5a1b3Sopenharmony_ci        }
138953a5a1b3Sopenharmony_ci
139053a5a1b3Sopenharmony_ci    } else {
139153a5a1b3Sopenharmony_ci        void *ptr;
139253a5a1b3Sopenharmony_ci
139353a5a1b3Sopenharmony_ci        ptr = pa_memblock_acquire(target->memblock);
139453a5a1b3Sopenharmony_ci
139553a5a1b3Sopenharmony_ci        target->length = pa_mix(info, n,
139653a5a1b3Sopenharmony_ci                                (uint8_t*) ptr + target->index, length,
139753a5a1b3Sopenharmony_ci                                &s->sample_spec,
139853a5a1b3Sopenharmony_ci                                &s->thread_info.soft_volume,
139953a5a1b3Sopenharmony_ci                                s->thread_info.soft_muted);
140053a5a1b3Sopenharmony_ci
140153a5a1b3Sopenharmony_ci        pa_memblock_release(target->memblock);
140253a5a1b3Sopenharmony_ci    }
140353a5a1b3Sopenharmony_ci
140453a5a1b3Sopenharmony_ci    inputs_drop(s, info, n, target);
140553a5a1b3Sopenharmony_ci
140653a5a1b3Sopenharmony_ci    pa_sink_unref(s);
140753a5a1b3Sopenharmony_ci}
140853a5a1b3Sopenharmony_ci
140953a5a1b3Sopenharmony_ci/* Called from IO thread context */
141053a5a1b3Sopenharmony_civoid pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
141153a5a1b3Sopenharmony_ci    pa_memchunk chunk;
141253a5a1b3Sopenharmony_ci    size_t l, d;
141353a5a1b3Sopenharmony_ci
141453a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
141553a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
141653a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
141753a5a1b3Sopenharmony_ci    pa_assert(target);
141853a5a1b3Sopenharmony_ci    pa_assert(target->memblock);
141953a5a1b3Sopenharmony_ci    pa_assert(target->length > 0);
142053a5a1b3Sopenharmony_ci    pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
142153a5a1b3Sopenharmony_ci
142253a5a1b3Sopenharmony_ci    pa_assert(!s->thread_info.rewind_requested);
142353a5a1b3Sopenharmony_ci    pa_assert(s->thread_info.rewind_nbytes == 0);
142453a5a1b3Sopenharmony_ci
142553a5a1b3Sopenharmony_ci    if (s->thread_info.state == PA_SINK_SUSPENDED) {
142653a5a1b3Sopenharmony_ci        pa_silence_memchunk(target, &s->sample_spec);
142753a5a1b3Sopenharmony_ci        return;
142853a5a1b3Sopenharmony_ci    }
142953a5a1b3Sopenharmony_ci
143053a5a1b3Sopenharmony_ci    pa_sink_ref(s);
143153a5a1b3Sopenharmony_ci
143253a5a1b3Sopenharmony_ci    l = target->length;
143353a5a1b3Sopenharmony_ci    d = 0;
143453a5a1b3Sopenharmony_ci    while (l > 0) {
143553a5a1b3Sopenharmony_ci        chunk = *target;
143653a5a1b3Sopenharmony_ci        chunk.index += d;
143753a5a1b3Sopenharmony_ci        chunk.length -= d;
143853a5a1b3Sopenharmony_ci
143953a5a1b3Sopenharmony_ci        pa_sink_render_into(s, &chunk);
144053a5a1b3Sopenharmony_ci
144153a5a1b3Sopenharmony_ci        d += chunk.length;
144253a5a1b3Sopenharmony_ci        l -= chunk.length;
144353a5a1b3Sopenharmony_ci    }
144453a5a1b3Sopenharmony_ci
144553a5a1b3Sopenharmony_ci    pa_sink_unref(s);
144653a5a1b3Sopenharmony_ci}
144753a5a1b3Sopenharmony_ci
144853a5a1b3Sopenharmony_ci/* Called from IO thread context */
144953a5a1b3Sopenharmony_civoid pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
145053a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
145153a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
145253a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
145353a5a1b3Sopenharmony_ci    pa_assert(length > 0);
145453a5a1b3Sopenharmony_ci    pa_assert(pa_frame_aligned(length, &s->sample_spec));
145553a5a1b3Sopenharmony_ci    pa_assert(result);
145653a5a1b3Sopenharmony_ci
145753a5a1b3Sopenharmony_ci    pa_assert(!s->thread_info.rewind_requested);
145853a5a1b3Sopenharmony_ci    pa_assert(s->thread_info.rewind_nbytes == 0);
145953a5a1b3Sopenharmony_ci
146053a5a1b3Sopenharmony_ci    pa_sink_ref(s);
146153a5a1b3Sopenharmony_ci
146253a5a1b3Sopenharmony_ci    pa_sink_render(s, length, result);
146353a5a1b3Sopenharmony_ci
146453a5a1b3Sopenharmony_ci    if (result->length < length) {
146553a5a1b3Sopenharmony_ci        pa_memchunk chunk;
146653a5a1b3Sopenharmony_ci
146753a5a1b3Sopenharmony_ci        pa_memchunk_make_writable(result, length);
146853a5a1b3Sopenharmony_ci
146953a5a1b3Sopenharmony_ci        chunk.memblock = result->memblock;
147053a5a1b3Sopenharmony_ci        chunk.index = result->index + result->length;
147153a5a1b3Sopenharmony_ci        chunk.length = length - result->length;
147253a5a1b3Sopenharmony_ci
147353a5a1b3Sopenharmony_ci        pa_sink_render_into_full(s, &chunk);
147453a5a1b3Sopenharmony_ci
147553a5a1b3Sopenharmony_ci        result->length = length;
147653a5a1b3Sopenharmony_ci    }
147753a5a1b3Sopenharmony_ci
147853a5a1b3Sopenharmony_ci    pa_sink_unref(s);
147953a5a1b3Sopenharmony_ci}
148053a5a1b3Sopenharmony_ci
148153a5a1b3Sopenharmony_ci/* Called from main thread */
148253a5a1b3Sopenharmony_civoid pa_sink_reconfigure(pa_sink *s, pa_sample_spec *spec, bool passthrough) {
148353a5a1b3Sopenharmony_ci    pa_sample_spec desired_spec;
148453a5a1b3Sopenharmony_ci    uint32_t default_rate = s->default_sample_rate;
148553a5a1b3Sopenharmony_ci    uint32_t alternate_rate = s->alternate_sample_rate;
148653a5a1b3Sopenharmony_ci    uint32_t idx;
148753a5a1b3Sopenharmony_ci    pa_sink_input *i;
148853a5a1b3Sopenharmony_ci    bool default_rate_is_usable = false;
148953a5a1b3Sopenharmony_ci    bool alternate_rate_is_usable = false;
149053a5a1b3Sopenharmony_ci    bool avoid_resampling = s->avoid_resampling;
149153a5a1b3Sopenharmony_ci
149253a5a1b3Sopenharmony_ci    if (pa_sample_spec_equal(spec, &s->sample_spec))
149353a5a1b3Sopenharmony_ci        return;
149453a5a1b3Sopenharmony_ci
149553a5a1b3Sopenharmony_ci    if (!s->reconfigure)
149653a5a1b3Sopenharmony_ci        return;
149753a5a1b3Sopenharmony_ci
149853a5a1b3Sopenharmony_ci    if (PA_UNLIKELY(default_rate == alternate_rate && !passthrough && !avoid_resampling)) {
149953a5a1b3Sopenharmony_ci        pa_log_debug("Default and alternate sample rates are the same, so there is no point in switching.");
150053a5a1b3Sopenharmony_ci        return;
150153a5a1b3Sopenharmony_ci    }
150253a5a1b3Sopenharmony_ci
150353a5a1b3Sopenharmony_ci    if (PA_SINK_IS_RUNNING(s->state)) {
150453a5a1b3Sopenharmony_ci        pa_log_info("Cannot update sample spec, SINK_IS_RUNNING, will keep using %s and %u Hz",
150553a5a1b3Sopenharmony_ci                    pa_sample_format_to_string(s->sample_spec.format), s->sample_spec.rate);
150653a5a1b3Sopenharmony_ci        return;
150753a5a1b3Sopenharmony_ci    }
150853a5a1b3Sopenharmony_ci
150953a5a1b3Sopenharmony_ci    if (s->monitor_source) {
151053a5a1b3Sopenharmony_ci        if (PA_SOURCE_IS_RUNNING(s->monitor_source->state) == true) {
151153a5a1b3Sopenharmony_ci            pa_log_info("Cannot update sample spec, monitor source is RUNNING");
151253a5a1b3Sopenharmony_ci            return;
151353a5a1b3Sopenharmony_ci        }
151453a5a1b3Sopenharmony_ci    }
151553a5a1b3Sopenharmony_ci
151653a5a1b3Sopenharmony_ci    if (PA_UNLIKELY(!pa_sample_spec_valid(spec)))
151753a5a1b3Sopenharmony_ci        return;
151853a5a1b3Sopenharmony_ci
151953a5a1b3Sopenharmony_ci    desired_spec = s->sample_spec;
152053a5a1b3Sopenharmony_ci
152153a5a1b3Sopenharmony_ci    if (passthrough) {
152253a5a1b3Sopenharmony_ci        /* We have to try to use the sink input format and rate */
152353a5a1b3Sopenharmony_ci        desired_spec.format = spec->format;
152453a5a1b3Sopenharmony_ci        desired_spec.rate = spec->rate;
152553a5a1b3Sopenharmony_ci
152653a5a1b3Sopenharmony_ci    } else if (avoid_resampling) {
152753a5a1b3Sopenharmony_ci        /* We just try to set the sink input's sample rate if it's not too low */
152853a5a1b3Sopenharmony_ci        if (spec->rate >= default_rate || spec->rate >= alternate_rate)
152953a5a1b3Sopenharmony_ci            desired_spec.rate = spec->rate;
153053a5a1b3Sopenharmony_ci        desired_spec.format = spec->format;
153153a5a1b3Sopenharmony_ci
153253a5a1b3Sopenharmony_ci    } else if (default_rate == spec->rate || alternate_rate == spec->rate) {
153353a5a1b3Sopenharmony_ci        /* We can directly try to use this rate */
153453a5a1b3Sopenharmony_ci        desired_spec.rate = spec->rate;
153553a5a1b3Sopenharmony_ci
153653a5a1b3Sopenharmony_ci    }
153753a5a1b3Sopenharmony_ci
153853a5a1b3Sopenharmony_ci    if (desired_spec.rate != spec->rate) {
153953a5a1b3Sopenharmony_ci        /* See if we can pick a rate that results in less resampling effort */
154053a5a1b3Sopenharmony_ci        if (default_rate % 11025 == 0 && spec->rate % 11025 == 0)
154153a5a1b3Sopenharmony_ci            default_rate_is_usable = true;
154253a5a1b3Sopenharmony_ci        if (default_rate % 4000 == 0 && spec->rate % 4000 == 0)
154353a5a1b3Sopenharmony_ci            default_rate_is_usable = true;
154453a5a1b3Sopenharmony_ci        if (alternate_rate % 11025 == 0 && spec->rate % 11025 == 0)
154553a5a1b3Sopenharmony_ci            alternate_rate_is_usable = true;
154653a5a1b3Sopenharmony_ci        if (alternate_rate % 4000 == 0 && spec->rate % 4000 == 0)
154753a5a1b3Sopenharmony_ci            alternate_rate_is_usable = true;
154853a5a1b3Sopenharmony_ci
154953a5a1b3Sopenharmony_ci        if (alternate_rate_is_usable && !default_rate_is_usable)
155053a5a1b3Sopenharmony_ci            desired_spec.rate = alternate_rate;
155153a5a1b3Sopenharmony_ci        else
155253a5a1b3Sopenharmony_ci            desired_spec.rate = default_rate;
155353a5a1b3Sopenharmony_ci    }
155453a5a1b3Sopenharmony_ci
155553a5a1b3Sopenharmony_ci    if (pa_sample_spec_equal(&desired_spec, &s->sample_spec) && passthrough == pa_sink_is_passthrough(s))
155653a5a1b3Sopenharmony_ci        return;
155753a5a1b3Sopenharmony_ci
155853a5a1b3Sopenharmony_ci    if (!passthrough && pa_sink_used_by(s) > 0)
155953a5a1b3Sopenharmony_ci        return;
156053a5a1b3Sopenharmony_ci
156153a5a1b3Sopenharmony_ci    pa_log_debug("Suspending sink %s due to changing format, desired format = %s rate = %u",
156253a5a1b3Sopenharmony_ci                 s->name, pa_sample_format_to_string(desired_spec.format), desired_spec.rate);
156353a5a1b3Sopenharmony_ci    pa_sink_suspend(s, true, PA_SUSPEND_INTERNAL);
156453a5a1b3Sopenharmony_ci
156553a5a1b3Sopenharmony_ci    s->reconfigure(s, &desired_spec, passthrough);
156653a5a1b3Sopenharmony_ci
156753a5a1b3Sopenharmony_ci    /* update monitor source as well */
156853a5a1b3Sopenharmony_ci    if (s->monitor_source && !passthrough)
156953a5a1b3Sopenharmony_ci        pa_source_reconfigure(s->monitor_source, &s->sample_spec, false);
157053a5a1b3Sopenharmony_ci    pa_log_info("Reconfigured successfully");
157153a5a1b3Sopenharmony_ci
157253a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(i, s->inputs, idx) {
157353a5a1b3Sopenharmony_ci        if (i->state == PA_SINK_INPUT_CORKED)
157453a5a1b3Sopenharmony_ci            pa_sink_input_update_resampler(i, true);
157553a5a1b3Sopenharmony_ci    }
157653a5a1b3Sopenharmony_ci
157753a5a1b3Sopenharmony_ci    pa_sink_suspend(s, false, PA_SUSPEND_INTERNAL);
157853a5a1b3Sopenharmony_ci}
157953a5a1b3Sopenharmony_ci
158053a5a1b3Sopenharmony_ci/* Called from main thread */
158153a5a1b3Sopenharmony_cisize_t pa_sink_get_last_rewind(pa_sink *s) {
158253a5a1b3Sopenharmony_ci    size_t rewind_bytes;
158353a5a1b3Sopenharmony_ci
158453a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
158553a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
158653a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
158753a5a1b3Sopenharmony_ci
158853a5a1b3Sopenharmony_ci    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LAST_REWIND, &rewind_bytes, 0, NULL) == 0);
158953a5a1b3Sopenharmony_ci
159053a5a1b3Sopenharmony_ci    return rewind_bytes;
159153a5a1b3Sopenharmony_ci}
159253a5a1b3Sopenharmony_ci
159353a5a1b3Sopenharmony_ci/* Called from main thread */
159453a5a1b3Sopenharmony_cipa_usec_t pa_sink_get_latency(pa_sink *s) {
159553a5a1b3Sopenharmony_ci    int64_t usec = 0;
159653a5a1b3Sopenharmony_ci
159753a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
159853a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
159953a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
160053a5a1b3Sopenharmony_ci
160153a5a1b3Sopenharmony_ci    /* The returned value is supposed to be in the time domain of the sound card! */
160253a5a1b3Sopenharmony_ci
160353a5a1b3Sopenharmony_ci    if (s->state == PA_SINK_SUSPENDED)
160453a5a1b3Sopenharmony_ci        return 0;
160553a5a1b3Sopenharmony_ci
160653a5a1b3Sopenharmony_ci    if (!(s->flags & PA_SINK_LATENCY))
160753a5a1b3Sopenharmony_ci        return 0;
160853a5a1b3Sopenharmony_ci
160953a5a1b3Sopenharmony_ci    if (s->asyncmsgq == NULL) {
161053a5a1b3Sopenharmony_ci        pa_log_error("pa_asyncmsgq is NULL");
161153a5a1b3Sopenharmony_ci        return 0;
161253a5a1b3Sopenharmony_ci    }
161353a5a1b3Sopenharmony_ci    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
161453a5a1b3Sopenharmony_ci
161553a5a1b3Sopenharmony_ci    /* the return value is unsigned, so check that the offset can be added to usec without
161653a5a1b3Sopenharmony_ci     * underflowing. */
161753a5a1b3Sopenharmony_ci    if (-s->port_latency_offset <= usec)
161853a5a1b3Sopenharmony_ci        usec += s->port_latency_offset;
161953a5a1b3Sopenharmony_ci    else
162053a5a1b3Sopenharmony_ci        usec = 0;
162153a5a1b3Sopenharmony_ci
162253a5a1b3Sopenharmony_ci    return (pa_usec_t)usec;
162353a5a1b3Sopenharmony_ci}
162453a5a1b3Sopenharmony_ci
162553a5a1b3Sopenharmony_ci/* Called from IO thread */
162653a5a1b3Sopenharmony_ciint64_t pa_sink_get_latency_within_thread(pa_sink *s, bool allow_negative) {
162753a5a1b3Sopenharmony_ci    int64_t usec = 0;
162853a5a1b3Sopenharmony_ci    pa_msgobject *o;
162953a5a1b3Sopenharmony_ci
163053a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
163153a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
163253a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
163353a5a1b3Sopenharmony_ci
163453a5a1b3Sopenharmony_ci    /* The returned value is supposed to be in the time domain of the sound card! */
163553a5a1b3Sopenharmony_ci
163653a5a1b3Sopenharmony_ci    if (s->thread_info.state == PA_SINK_SUSPENDED)
163753a5a1b3Sopenharmony_ci        return 0;
163853a5a1b3Sopenharmony_ci
163953a5a1b3Sopenharmony_ci    if (!(s->flags & PA_SINK_LATENCY))
164053a5a1b3Sopenharmony_ci        return 0;
164153a5a1b3Sopenharmony_ci
164253a5a1b3Sopenharmony_ci    o = PA_MSGOBJECT(s);
164353a5a1b3Sopenharmony_ci
164453a5a1b3Sopenharmony_ci    /* FIXME: We probably should make this a proper vtable callback instead of going through process_msg() */
164553a5a1b3Sopenharmony_ci
164653a5a1b3Sopenharmony_ci    o->process_msg(o, PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL);
164753a5a1b3Sopenharmony_ci
164853a5a1b3Sopenharmony_ci    /* If allow_negative is false, the call should only return positive values, */
164953a5a1b3Sopenharmony_ci    usec += s->thread_info.port_latency_offset;
165053a5a1b3Sopenharmony_ci    if (!allow_negative && usec < 0)
165153a5a1b3Sopenharmony_ci        usec = 0;
165253a5a1b3Sopenharmony_ci
165353a5a1b3Sopenharmony_ci    return usec;
165453a5a1b3Sopenharmony_ci}
165553a5a1b3Sopenharmony_ci
165653a5a1b3Sopenharmony_ci/* Called from the main thread (and also from the IO thread while the main
165753a5a1b3Sopenharmony_ci * thread is waiting).
165853a5a1b3Sopenharmony_ci *
165953a5a1b3Sopenharmony_ci * When a sink uses volume sharing, it never has the PA_SINK_FLAT_VOLUME flag
166053a5a1b3Sopenharmony_ci * set. Instead, flat volume mode is detected by checking whether the root sink
166153a5a1b3Sopenharmony_ci * has the flag set. */
166253a5a1b3Sopenharmony_cibool pa_sink_flat_volume_enabled(pa_sink *s) {
166353a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
166453a5a1b3Sopenharmony_ci
166553a5a1b3Sopenharmony_ci    s = pa_sink_get_master(s);
166653a5a1b3Sopenharmony_ci
166753a5a1b3Sopenharmony_ci    if (PA_LIKELY(s))
166853a5a1b3Sopenharmony_ci        return (s->flags & PA_SINK_FLAT_VOLUME);
166953a5a1b3Sopenharmony_ci    else
167053a5a1b3Sopenharmony_ci        return false;
167153a5a1b3Sopenharmony_ci}
167253a5a1b3Sopenharmony_ci
167353a5a1b3Sopenharmony_ci/* Check if the sink has a virtual sink attached.
167453a5a1b3Sopenharmony_ci * Called from the IO thread. */
167553a5a1b3Sopenharmony_cibool pa_sink_has_filter_attached(pa_sink *s) {
167653a5a1b3Sopenharmony_ci    bool vsink_attached = false;
167753a5a1b3Sopenharmony_ci    void *state = NULL;
167853a5a1b3Sopenharmony_ci    pa_sink_input *i;
167953a5a1b3Sopenharmony_ci
168053a5a1b3Sopenharmony_ci    pa_assert(s);
168153a5a1b3Sopenharmony_ci
168253a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
168353a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
168453a5a1b3Sopenharmony_ci            if (!i->origin_sink)
168553a5a1b3Sopenharmony_ci                continue;
168653a5a1b3Sopenharmony_ci
168753a5a1b3Sopenharmony_ci            vsink_attached = true;
168853a5a1b3Sopenharmony_ci            break;
168953a5a1b3Sopenharmony_ci        }
169053a5a1b3Sopenharmony_ci    }
169153a5a1b3Sopenharmony_ci    return vsink_attached;
169253a5a1b3Sopenharmony_ci}
169353a5a1b3Sopenharmony_ci
169453a5a1b3Sopenharmony_ci/* Called from the main thread (and also from the IO thread while the main
169553a5a1b3Sopenharmony_ci * thread is waiting). */
169653a5a1b3Sopenharmony_cipa_sink *pa_sink_get_master(pa_sink *s) {
169753a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
169853a5a1b3Sopenharmony_ci
169953a5a1b3Sopenharmony_ci    while (s && (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
170053a5a1b3Sopenharmony_ci        if (PA_UNLIKELY(!s->input_to_master))
170153a5a1b3Sopenharmony_ci            return NULL;
170253a5a1b3Sopenharmony_ci
170353a5a1b3Sopenharmony_ci        s = s->input_to_master->sink;
170453a5a1b3Sopenharmony_ci    }
170553a5a1b3Sopenharmony_ci
170653a5a1b3Sopenharmony_ci    return s;
170753a5a1b3Sopenharmony_ci}
170853a5a1b3Sopenharmony_ci
170953a5a1b3Sopenharmony_ci/* Called from main context */
171053a5a1b3Sopenharmony_cibool pa_sink_is_filter(pa_sink *s) {
171153a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
171253a5a1b3Sopenharmony_ci
171353a5a1b3Sopenharmony_ci    return (s->input_to_master != NULL);
171453a5a1b3Sopenharmony_ci}
171553a5a1b3Sopenharmony_ci
171653a5a1b3Sopenharmony_ci/* Called from main context */
171753a5a1b3Sopenharmony_cibool pa_sink_is_passthrough(pa_sink *s) {
171853a5a1b3Sopenharmony_ci    pa_sink_input *alt_i;
171953a5a1b3Sopenharmony_ci    uint32_t idx;
172053a5a1b3Sopenharmony_ci
172153a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
172253a5a1b3Sopenharmony_ci
172353a5a1b3Sopenharmony_ci    /* one and only one PASSTHROUGH input can possibly be connected */
172453a5a1b3Sopenharmony_ci    if (pa_idxset_size(s->inputs) == 1) {
172553a5a1b3Sopenharmony_ci        alt_i = pa_idxset_first(s->inputs, &idx);
172653a5a1b3Sopenharmony_ci
172753a5a1b3Sopenharmony_ci        if (pa_sink_input_is_passthrough(alt_i))
172853a5a1b3Sopenharmony_ci            return true;
172953a5a1b3Sopenharmony_ci    }
173053a5a1b3Sopenharmony_ci
173153a5a1b3Sopenharmony_ci    return false;
173253a5a1b3Sopenharmony_ci}
173353a5a1b3Sopenharmony_ci
173453a5a1b3Sopenharmony_ci/* Called from main context */
173553a5a1b3Sopenharmony_civoid pa_sink_enter_passthrough(pa_sink *s) {
173653a5a1b3Sopenharmony_ci    pa_cvolume volume;
173753a5a1b3Sopenharmony_ci
173853a5a1b3Sopenharmony_ci    /* The sink implementation is reconfigured for passthrough in
173953a5a1b3Sopenharmony_ci     * pa_sink_reconfigure(). This function sets the PA core objects to
174053a5a1b3Sopenharmony_ci     * passthrough mode. */
174153a5a1b3Sopenharmony_ci
174253a5a1b3Sopenharmony_ci    /* disable the monitor in passthrough mode */
174353a5a1b3Sopenharmony_ci    if (s->monitor_source) {
174453a5a1b3Sopenharmony_ci        pa_log_debug("Suspending monitor source %s, because the sink is entering the passthrough mode.", s->monitor_source->name);
174553a5a1b3Sopenharmony_ci        pa_source_suspend(s->monitor_source, true, PA_SUSPEND_PASSTHROUGH);
174653a5a1b3Sopenharmony_ci    }
174753a5a1b3Sopenharmony_ci
174853a5a1b3Sopenharmony_ci    /* set the volume to NORM */
174953a5a1b3Sopenharmony_ci    s->saved_volume = *pa_sink_get_volume(s, true);
175053a5a1b3Sopenharmony_ci    s->saved_save_volume = s->save_volume;
175153a5a1b3Sopenharmony_ci
175253a5a1b3Sopenharmony_ci    pa_cvolume_set(&volume, s->sample_spec.channels, PA_MIN(s->base_volume, PA_VOLUME_NORM));
175353a5a1b3Sopenharmony_ci    pa_sink_set_volume(s, &volume, true, false);
175453a5a1b3Sopenharmony_ci
175553a5a1b3Sopenharmony_ci    pa_log_debug("Suspending/Restarting sink %s to enter passthrough mode", s->name);
175653a5a1b3Sopenharmony_ci}
175753a5a1b3Sopenharmony_ci
175853a5a1b3Sopenharmony_ci/* Called from main context */
175953a5a1b3Sopenharmony_civoid pa_sink_leave_passthrough(pa_sink *s) {
176053a5a1b3Sopenharmony_ci    /* Unsuspend monitor */
176153a5a1b3Sopenharmony_ci    if (s->monitor_source) {
176253a5a1b3Sopenharmony_ci        pa_log_debug("Resuming monitor source %s, because the sink is leaving the passthrough mode.", s->monitor_source->name);
176353a5a1b3Sopenharmony_ci        pa_source_suspend(s->monitor_source, false, PA_SUSPEND_PASSTHROUGH);
176453a5a1b3Sopenharmony_ci    }
176553a5a1b3Sopenharmony_ci
176653a5a1b3Sopenharmony_ci    /* Restore sink volume to what it was before we entered passthrough mode */
176753a5a1b3Sopenharmony_ci    pa_sink_set_volume(s, &s->saved_volume, true, s->saved_save_volume);
176853a5a1b3Sopenharmony_ci
176953a5a1b3Sopenharmony_ci    pa_cvolume_init(&s->saved_volume);
177053a5a1b3Sopenharmony_ci    s->saved_save_volume = false;
177153a5a1b3Sopenharmony_ci
177253a5a1b3Sopenharmony_ci}
177353a5a1b3Sopenharmony_ci
177453a5a1b3Sopenharmony_ci/* Called from main context. */
177553a5a1b3Sopenharmony_cistatic void compute_reference_ratio(pa_sink_input *i) {
177653a5a1b3Sopenharmony_ci    unsigned c = 0;
177753a5a1b3Sopenharmony_ci    pa_cvolume remapped;
177853a5a1b3Sopenharmony_ci    pa_cvolume ratio;
177953a5a1b3Sopenharmony_ci
178053a5a1b3Sopenharmony_ci    pa_assert(i);
178153a5a1b3Sopenharmony_ci    pa_assert(pa_sink_flat_volume_enabled(i->sink));
178253a5a1b3Sopenharmony_ci
178353a5a1b3Sopenharmony_ci    /*
178453a5a1b3Sopenharmony_ci     * Calculates the reference ratio from the sink's reference
178553a5a1b3Sopenharmony_ci     * volume. This basically calculates:
178653a5a1b3Sopenharmony_ci     *
178753a5a1b3Sopenharmony_ci     * i->reference_ratio = i->volume / i->sink->reference_volume
178853a5a1b3Sopenharmony_ci     */
178953a5a1b3Sopenharmony_ci
179053a5a1b3Sopenharmony_ci    remapped = i->sink->reference_volume;
179153a5a1b3Sopenharmony_ci    pa_cvolume_remap(&remapped, &i->sink->channel_map, &i->channel_map);
179253a5a1b3Sopenharmony_ci
179353a5a1b3Sopenharmony_ci    ratio = i->reference_ratio;
179453a5a1b3Sopenharmony_ci
179553a5a1b3Sopenharmony_ci    for (c = 0; c < i->sample_spec.channels; c++) {
179653a5a1b3Sopenharmony_ci
179753a5a1b3Sopenharmony_ci        /* We don't update when the sink volume is 0 anyway */
179853a5a1b3Sopenharmony_ci        if (remapped.values[c] <= PA_VOLUME_MUTED)
179953a5a1b3Sopenharmony_ci            continue;
180053a5a1b3Sopenharmony_ci
180153a5a1b3Sopenharmony_ci        /* Don't update the reference ratio unless necessary */
180253a5a1b3Sopenharmony_ci        if (pa_sw_volume_multiply(
180353a5a1b3Sopenharmony_ci                    ratio.values[c],
180453a5a1b3Sopenharmony_ci                    remapped.values[c]) == i->volume.values[c])
180553a5a1b3Sopenharmony_ci            continue;
180653a5a1b3Sopenharmony_ci
180753a5a1b3Sopenharmony_ci        ratio.values[c] = pa_sw_volume_divide(
180853a5a1b3Sopenharmony_ci                i->volume.values[c],
180953a5a1b3Sopenharmony_ci                remapped.values[c]);
181053a5a1b3Sopenharmony_ci    }
181153a5a1b3Sopenharmony_ci
181253a5a1b3Sopenharmony_ci    pa_sink_input_set_reference_ratio(i, &ratio);
181353a5a1b3Sopenharmony_ci}
181453a5a1b3Sopenharmony_ci
181553a5a1b3Sopenharmony_ci/* Called from main context. Only called for the root sink in volume sharing
181653a5a1b3Sopenharmony_ci * cases, except for internal recursive calls. */
181753a5a1b3Sopenharmony_cistatic void compute_reference_ratios(pa_sink *s) {
181853a5a1b3Sopenharmony_ci    uint32_t idx;
181953a5a1b3Sopenharmony_ci    pa_sink_input *i;
182053a5a1b3Sopenharmony_ci
182153a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
182253a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
182353a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
182453a5a1b3Sopenharmony_ci    pa_assert(pa_sink_flat_volume_enabled(s));
182553a5a1b3Sopenharmony_ci
182653a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(i, s->inputs, idx) {
182753a5a1b3Sopenharmony_ci        compute_reference_ratio(i);
182853a5a1b3Sopenharmony_ci
182953a5a1b3Sopenharmony_ci        if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)
183053a5a1b3Sopenharmony_ci                && PA_SINK_IS_LINKED(i->origin_sink->state))
183153a5a1b3Sopenharmony_ci            compute_reference_ratios(i->origin_sink);
183253a5a1b3Sopenharmony_ci    }
183353a5a1b3Sopenharmony_ci}
183453a5a1b3Sopenharmony_ci
183553a5a1b3Sopenharmony_ci/* Called from main context. Only called for the root sink in volume sharing
183653a5a1b3Sopenharmony_ci * cases, except for internal recursive calls. */
183753a5a1b3Sopenharmony_cistatic void compute_real_ratios(pa_sink *s) {
183853a5a1b3Sopenharmony_ci    pa_sink_input *i;
183953a5a1b3Sopenharmony_ci    uint32_t idx;
184053a5a1b3Sopenharmony_ci
184153a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
184253a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
184353a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
184453a5a1b3Sopenharmony_ci    pa_assert(pa_sink_flat_volume_enabled(s));
184553a5a1b3Sopenharmony_ci
184653a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(i, s->inputs, idx) {
184753a5a1b3Sopenharmony_ci        unsigned c;
184853a5a1b3Sopenharmony_ci        pa_cvolume remapped;
184953a5a1b3Sopenharmony_ci
185053a5a1b3Sopenharmony_ci        if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
185153a5a1b3Sopenharmony_ci            /* The origin sink uses volume sharing, so this input's real ratio
185253a5a1b3Sopenharmony_ci             * is handled as a special case - the real ratio must be 0 dB, and
185353a5a1b3Sopenharmony_ci             * as a result i->soft_volume must equal i->volume_factor. */
185453a5a1b3Sopenharmony_ci            pa_cvolume_reset(&i->real_ratio, i->real_ratio.channels);
185553a5a1b3Sopenharmony_ci            i->soft_volume = i->volume_factor;
185653a5a1b3Sopenharmony_ci
185753a5a1b3Sopenharmony_ci            if (PA_SINK_IS_LINKED(i->origin_sink->state))
185853a5a1b3Sopenharmony_ci                compute_real_ratios(i->origin_sink);
185953a5a1b3Sopenharmony_ci
186053a5a1b3Sopenharmony_ci            continue;
186153a5a1b3Sopenharmony_ci        }
186253a5a1b3Sopenharmony_ci
186353a5a1b3Sopenharmony_ci        /*
186453a5a1b3Sopenharmony_ci         * This basically calculates:
186553a5a1b3Sopenharmony_ci         *
186653a5a1b3Sopenharmony_ci         * i->real_ratio := i->volume / s->real_volume
186753a5a1b3Sopenharmony_ci         * i->soft_volume := i->real_ratio * i->volume_factor
186853a5a1b3Sopenharmony_ci         */
186953a5a1b3Sopenharmony_ci
187053a5a1b3Sopenharmony_ci        remapped = s->real_volume;
187153a5a1b3Sopenharmony_ci        pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
187253a5a1b3Sopenharmony_ci
187353a5a1b3Sopenharmony_ci        i->real_ratio.channels = i->sample_spec.channels;
187453a5a1b3Sopenharmony_ci        i->soft_volume.channels = i->sample_spec.channels;
187553a5a1b3Sopenharmony_ci
187653a5a1b3Sopenharmony_ci        for (c = 0; c < i->sample_spec.channels; c++) {
187753a5a1b3Sopenharmony_ci
187853a5a1b3Sopenharmony_ci            if (remapped.values[c] <= PA_VOLUME_MUTED) {
187953a5a1b3Sopenharmony_ci                /* We leave i->real_ratio untouched */
188053a5a1b3Sopenharmony_ci                i->soft_volume.values[c] = PA_VOLUME_MUTED;
188153a5a1b3Sopenharmony_ci                continue;
188253a5a1b3Sopenharmony_ci            }
188353a5a1b3Sopenharmony_ci
188453a5a1b3Sopenharmony_ci            /* Don't lose accuracy unless necessary */
188553a5a1b3Sopenharmony_ci            if (pa_sw_volume_multiply(
188653a5a1b3Sopenharmony_ci                        i->real_ratio.values[c],
188753a5a1b3Sopenharmony_ci                        remapped.values[c]) != i->volume.values[c])
188853a5a1b3Sopenharmony_ci
188953a5a1b3Sopenharmony_ci                i->real_ratio.values[c] = pa_sw_volume_divide(
189053a5a1b3Sopenharmony_ci                        i->volume.values[c],
189153a5a1b3Sopenharmony_ci                        remapped.values[c]);
189253a5a1b3Sopenharmony_ci
189353a5a1b3Sopenharmony_ci            i->soft_volume.values[c] = pa_sw_volume_multiply(
189453a5a1b3Sopenharmony_ci                    i->real_ratio.values[c],
189553a5a1b3Sopenharmony_ci                    i->volume_factor.values[c]);
189653a5a1b3Sopenharmony_ci        }
189753a5a1b3Sopenharmony_ci
189853a5a1b3Sopenharmony_ci        /* We don't copy the soft_volume to the thread_info data
189953a5a1b3Sopenharmony_ci         * here. That must be done by the caller */
190053a5a1b3Sopenharmony_ci    }
190153a5a1b3Sopenharmony_ci}
190253a5a1b3Sopenharmony_ci
190353a5a1b3Sopenharmony_cistatic pa_cvolume *cvolume_remap_minimal_impact(
190453a5a1b3Sopenharmony_ci        pa_cvolume *v,
190553a5a1b3Sopenharmony_ci        const pa_cvolume *template,
190653a5a1b3Sopenharmony_ci        const pa_channel_map *from,
190753a5a1b3Sopenharmony_ci        const pa_channel_map *to) {
190853a5a1b3Sopenharmony_ci
190953a5a1b3Sopenharmony_ci    pa_cvolume t;
191053a5a1b3Sopenharmony_ci
191153a5a1b3Sopenharmony_ci    pa_assert(v);
191253a5a1b3Sopenharmony_ci    pa_assert(template);
191353a5a1b3Sopenharmony_ci    pa_assert(from);
191453a5a1b3Sopenharmony_ci    pa_assert(to);
191553a5a1b3Sopenharmony_ci    pa_assert(pa_cvolume_compatible_with_channel_map(v, from));
191653a5a1b3Sopenharmony_ci    pa_assert(pa_cvolume_compatible_with_channel_map(template, to));
191753a5a1b3Sopenharmony_ci
191853a5a1b3Sopenharmony_ci    /* Much like pa_cvolume_remap(), but tries to minimize impact when
191953a5a1b3Sopenharmony_ci     * mapping from sink input to sink volumes:
192053a5a1b3Sopenharmony_ci     *
192153a5a1b3Sopenharmony_ci     * If template is a possible remapping from v it is used instead
192253a5a1b3Sopenharmony_ci     * of remapping anew.
192353a5a1b3Sopenharmony_ci     *
192453a5a1b3Sopenharmony_ci     * If the channel maps don't match we set an all-channel volume on
192553a5a1b3Sopenharmony_ci     * the sink to ensure that changing a volume on one stream has no
192653a5a1b3Sopenharmony_ci     * effect that cannot be compensated for in another stream that
192753a5a1b3Sopenharmony_ci     * does not have the same channel map as the sink. */
192853a5a1b3Sopenharmony_ci
192953a5a1b3Sopenharmony_ci    if (pa_channel_map_equal(from, to))
193053a5a1b3Sopenharmony_ci        return v;
193153a5a1b3Sopenharmony_ci
193253a5a1b3Sopenharmony_ci    t = *template;
193353a5a1b3Sopenharmony_ci    if (pa_cvolume_equal(pa_cvolume_remap(&t, to, from), v)) {
193453a5a1b3Sopenharmony_ci        *v = *template;
193553a5a1b3Sopenharmony_ci        return v;
193653a5a1b3Sopenharmony_ci    }
193753a5a1b3Sopenharmony_ci
193853a5a1b3Sopenharmony_ci    pa_cvolume_set(v, to->channels, pa_cvolume_max(v));
193953a5a1b3Sopenharmony_ci    return v;
194053a5a1b3Sopenharmony_ci}
194153a5a1b3Sopenharmony_ci
194253a5a1b3Sopenharmony_ci/* Called from main thread. Only called for the root sink in volume sharing
194353a5a1b3Sopenharmony_ci * cases, except for internal recursive calls. */
194453a5a1b3Sopenharmony_cistatic void get_maximum_input_volume(pa_sink *s, pa_cvolume *max_volume, const pa_channel_map *channel_map) {
194553a5a1b3Sopenharmony_ci    pa_sink_input *i;
194653a5a1b3Sopenharmony_ci    uint32_t idx;
194753a5a1b3Sopenharmony_ci
194853a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
194953a5a1b3Sopenharmony_ci    pa_assert(max_volume);
195053a5a1b3Sopenharmony_ci    pa_assert(channel_map);
195153a5a1b3Sopenharmony_ci    pa_assert(pa_sink_flat_volume_enabled(s));
195253a5a1b3Sopenharmony_ci
195353a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(i, s->inputs, idx) {
195453a5a1b3Sopenharmony_ci        pa_cvolume remapped;
195553a5a1b3Sopenharmony_ci
195653a5a1b3Sopenharmony_ci        if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
195753a5a1b3Sopenharmony_ci            if (PA_SINK_IS_LINKED(i->origin_sink->state))
195853a5a1b3Sopenharmony_ci                get_maximum_input_volume(i->origin_sink, max_volume, channel_map);
195953a5a1b3Sopenharmony_ci
196053a5a1b3Sopenharmony_ci            /* Ignore this input. The origin sink uses volume sharing, so this
196153a5a1b3Sopenharmony_ci             * input's volume will be set to be equal to the root sink's real
196253a5a1b3Sopenharmony_ci             * volume. Obviously this input's current volume must not then
196353a5a1b3Sopenharmony_ci             * affect what the root sink's real volume will be. */
196453a5a1b3Sopenharmony_ci            continue;
196553a5a1b3Sopenharmony_ci        }
196653a5a1b3Sopenharmony_ci
196753a5a1b3Sopenharmony_ci        remapped = i->volume;
196853a5a1b3Sopenharmony_ci        cvolume_remap_minimal_impact(&remapped, max_volume, &i->channel_map, channel_map);
196953a5a1b3Sopenharmony_ci        pa_cvolume_merge(max_volume, max_volume, &remapped);
197053a5a1b3Sopenharmony_ci    }
197153a5a1b3Sopenharmony_ci}
197253a5a1b3Sopenharmony_ci
197353a5a1b3Sopenharmony_ci/* Called from main thread. Only called for the root sink in volume sharing
197453a5a1b3Sopenharmony_ci * cases, except for internal recursive calls. */
197553a5a1b3Sopenharmony_cistatic bool has_inputs(pa_sink *s) {
197653a5a1b3Sopenharmony_ci    pa_sink_input *i;
197753a5a1b3Sopenharmony_ci    uint32_t idx;
197853a5a1b3Sopenharmony_ci
197953a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
198053a5a1b3Sopenharmony_ci
198153a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(i, s->inputs, idx) {
198253a5a1b3Sopenharmony_ci        if (!i->origin_sink || !(i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || has_inputs(i->origin_sink))
198353a5a1b3Sopenharmony_ci            return true;
198453a5a1b3Sopenharmony_ci    }
198553a5a1b3Sopenharmony_ci
198653a5a1b3Sopenharmony_ci    return false;
198753a5a1b3Sopenharmony_ci}
198853a5a1b3Sopenharmony_ci
198953a5a1b3Sopenharmony_ci/* Called from main thread. Only called for the root sink in volume sharing
199053a5a1b3Sopenharmony_ci * cases, except for internal recursive calls. */
199153a5a1b3Sopenharmony_cistatic void update_real_volume(pa_sink *s, const pa_cvolume *new_volume, pa_channel_map *channel_map) {
199253a5a1b3Sopenharmony_ci    pa_sink_input *i;
199353a5a1b3Sopenharmony_ci    uint32_t idx;
199453a5a1b3Sopenharmony_ci
199553a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
199653a5a1b3Sopenharmony_ci    pa_assert(new_volume);
199753a5a1b3Sopenharmony_ci    pa_assert(channel_map);
199853a5a1b3Sopenharmony_ci
199953a5a1b3Sopenharmony_ci    s->real_volume = *new_volume;
200053a5a1b3Sopenharmony_ci    pa_cvolume_remap(&s->real_volume, channel_map, &s->channel_map);
200153a5a1b3Sopenharmony_ci
200253a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(i, s->inputs, idx) {
200353a5a1b3Sopenharmony_ci        if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
200453a5a1b3Sopenharmony_ci            if (pa_sink_flat_volume_enabled(s)) {
200553a5a1b3Sopenharmony_ci                pa_cvolume new_input_volume;
200653a5a1b3Sopenharmony_ci
200753a5a1b3Sopenharmony_ci                /* Follow the root sink's real volume. */
200853a5a1b3Sopenharmony_ci                new_input_volume = *new_volume;
200953a5a1b3Sopenharmony_ci                pa_cvolume_remap(&new_input_volume, channel_map, &i->channel_map);
201053a5a1b3Sopenharmony_ci                pa_sink_input_set_volume_direct(i, &new_input_volume);
201153a5a1b3Sopenharmony_ci                compute_reference_ratio(i);
201253a5a1b3Sopenharmony_ci            }
201353a5a1b3Sopenharmony_ci
201453a5a1b3Sopenharmony_ci            if (PA_SINK_IS_LINKED(i->origin_sink->state))
201553a5a1b3Sopenharmony_ci                update_real_volume(i->origin_sink, new_volume, channel_map);
201653a5a1b3Sopenharmony_ci        }
201753a5a1b3Sopenharmony_ci    }
201853a5a1b3Sopenharmony_ci}
201953a5a1b3Sopenharmony_ci
202053a5a1b3Sopenharmony_ci/* Called from main thread. Only called for the root sink in shared volume
202153a5a1b3Sopenharmony_ci * cases. */
202253a5a1b3Sopenharmony_cistatic void compute_real_volume(pa_sink *s) {
202353a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
202453a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
202553a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
202653a5a1b3Sopenharmony_ci    pa_assert(pa_sink_flat_volume_enabled(s));
202753a5a1b3Sopenharmony_ci    pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
202853a5a1b3Sopenharmony_ci
202953a5a1b3Sopenharmony_ci    /* This determines the maximum volume of all streams and sets
203053a5a1b3Sopenharmony_ci     * s->real_volume accordingly. */
203153a5a1b3Sopenharmony_ci
203253a5a1b3Sopenharmony_ci    if (!has_inputs(s)) {
203353a5a1b3Sopenharmony_ci        /* In the special case that we have no sink inputs we leave the
203453a5a1b3Sopenharmony_ci         * volume unmodified. */
203553a5a1b3Sopenharmony_ci        update_real_volume(s, &s->reference_volume, &s->channel_map);
203653a5a1b3Sopenharmony_ci        return;
203753a5a1b3Sopenharmony_ci    }
203853a5a1b3Sopenharmony_ci
203953a5a1b3Sopenharmony_ci    pa_cvolume_mute(&s->real_volume, s->channel_map.channels);
204053a5a1b3Sopenharmony_ci
204153a5a1b3Sopenharmony_ci    /* First let's determine the new maximum volume of all inputs
204253a5a1b3Sopenharmony_ci     * connected to this sink */
204353a5a1b3Sopenharmony_ci    get_maximum_input_volume(s, &s->real_volume, &s->channel_map);
204453a5a1b3Sopenharmony_ci    update_real_volume(s, &s->real_volume, &s->channel_map);
204553a5a1b3Sopenharmony_ci
204653a5a1b3Sopenharmony_ci    /* Then, let's update the real ratios/soft volumes of all inputs
204753a5a1b3Sopenharmony_ci     * connected to this sink */
204853a5a1b3Sopenharmony_ci    compute_real_ratios(s);
204953a5a1b3Sopenharmony_ci}
205053a5a1b3Sopenharmony_ci
205153a5a1b3Sopenharmony_ci/* Called from main thread. Only called for the root sink in shared volume
205253a5a1b3Sopenharmony_ci * cases, except for internal recursive calls. */
205353a5a1b3Sopenharmony_cistatic void propagate_reference_volume(pa_sink *s) {
205453a5a1b3Sopenharmony_ci    pa_sink_input *i;
205553a5a1b3Sopenharmony_ci    uint32_t idx;
205653a5a1b3Sopenharmony_ci
205753a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
205853a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
205953a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
206053a5a1b3Sopenharmony_ci    pa_assert(pa_sink_flat_volume_enabled(s));
206153a5a1b3Sopenharmony_ci
206253a5a1b3Sopenharmony_ci    /* This is called whenever the sink volume changes that is not
206353a5a1b3Sopenharmony_ci     * caused by a sink input volume change. We need to fix up the
206453a5a1b3Sopenharmony_ci     * sink input volumes accordingly */
206553a5a1b3Sopenharmony_ci
206653a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(i, s->inputs, idx) {
206753a5a1b3Sopenharmony_ci        pa_cvolume new_volume;
206853a5a1b3Sopenharmony_ci
206953a5a1b3Sopenharmony_ci        if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
207053a5a1b3Sopenharmony_ci            if (PA_SINK_IS_LINKED(i->origin_sink->state))
207153a5a1b3Sopenharmony_ci                propagate_reference_volume(i->origin_sink);
207253a5a1b3Sopenharmony_ci
207353a5a1b3Sopenharmony_ci            /* Since the origin sink uses volume sharing, this input's volume
207453a5a1b3Sopenharmony_ci             * needs to be updated to match the root sink's real volume, but
207553a5a1b3Sopenharmony_ci             * that will be done later in update_real_volume(). */
207653a5a1b3Sopenharmony_ci            continue;
207753a5a1b3Sopenharmony_ci        }
207853a5a1b3Sopenharmony_ci
207953a5a1b3Sopenharmony_ci        /* This basically calculates:
208053a5a1b3Sopenharmony_ci         *
208153a5a1b3Sopenharmony_ci         * i->volume := s->reference_volume * i->reference_ratio  */
208253a5a1b3Sopenharmony_ci
208353a5a1b3Sopenharmony_ci        new_volume = s->reference_volume;
208453a5a1b3Sopenharmony_ci        pa_cvolume_remap(&new_volume, &s->channel_map, &i->channel_map);
208553a5a1b3Sopenharmony_ci        pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio);
208653a5a1b3Sopenharmony_ci        pa_sink_input_set_volume_direct(i, &new_volume);
208753a5a1b3Sopenharmony_ci    }
208853a5a1b3Sopenharmony_ci}
208953a5a1b3Sopenharmony_ci
209053a5a1b3Sopenharmony_ci/* Called from main thread. Only called for the root sink in volume sharing
209153a5a1b3Sopenharmony_ci * cases, except for internal recursive calls. The return value indicates
209253a5a1b3Sopenharmony_ci * whether any reference volume actually changed. */
209353a5a1b3Sopenharmony_cistatic bool update_reference_volume(pa_sink *s, const pa_cvolume *v, const pa_channel_map *channel_map, bool save) {
209453a5a1b3Sopenharmony_ci    pa_cvolume volume;
209553a5a1b3Sopenharmony_ci    bool reference_volume_changed;
209653a5a1b3Sopenharmony_ci    pa_sink_input *i;
209753a5a1b3Sopenharmony_ci    uint32_t idx;
209853a5a1b3Sopenharmony_ci
209953a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
210053a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
210153a5a1b3Sopenharmony_ci    pa_assert(v);
210253a5a1b3Sopenharmony_ci    pa_assert(channel_map);
210353a5a1b3Sopenharmony_ci    pa_assert(pa_cvolume_valid(v));
210453a5a1b3Sopenharmony_ci
210553a5a1b3Sopenharmony_ci    volume = *v;
210653a5a1b3Sopenharmony_ci    pa_cvolume_remap(&volume, channel_map, &s->channel_map);
210753a5a1b3Sopenharmony_ci
210853a5a1b3Sopenharmony_ci    reference_volume_changed = !pa_cvolume_equal(&volume, &s->reference_volume);
210953a5a1b3Sopenharmony_ci    pa_sink_set_reference_volume_direct(s, &volume);
211053a5a1b3Sopenharmony_ci
211153a5a1b3Sopenharmony_ci    s->save_volume = (!reference_volume_changed && s->save_volume) || save;
211253a5a1b3Sopenharmony_ci
211353a5a1b3Sopenharmony_ci    if (!reference_volume_changed && !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
211453a5a1b3Sopenharmony_ci        /* If the root sink's volume doesn't change, then there can't be any
211553a5a1b3Sopenharmony_ci         * changes in the other sinks in the sink tree either.
211653a5a1b3Sopenharmony_ci         *
211753a5a1b3Sopenharmony_ci         * It's probably theoretically possible that even if the root sink's
211853a5a1b3Sopenharmony_ci         * volume changes slightly, some filter sink doesn't change its volume
211953a5a1b3Sopenharmony_ci         * due to rounding errors. If that happens, we still want to propagate
212053a5a1b3Sopenharmony_ci         * the changed root sink volume to the sinks connected to the
212153a5a1b3Sopenharmony_ci         * intermediate sink that didn't change its volume. This theoretical
212253a5a1b3Sopenharmony_ci         * possibility is the reason why we have that !(s->flags &
212353a5a1b3Sopenharmony_ci         * PA_SINK_SHARE_VOLUME_WITH_MASTER) condition. Probably nobody would
212453a5a1b3Sopenharmony_ci         * notice even if we returned here false always if
212553a5a1b3Sopenharmony_ci         * reference_volume_changed is false. */
212653a5a1b3Sopenharmony_ci        return false;
212753a5a1b3Sopenharmony_ci
212853a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(i, s->inputs, idx) {
212953a5a1b3Sopenharmony_ci        if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)
213053a5a1b3Sopenharmony_ci                && PA_SINK_IS_LINKED(i->origin_sink->state))
213153a5a1b3Sopenharmony_ci            update_reference_volume(i->origin_sink, v, channel_map, false);
213253a5a1b3Sopenharmony_ci    }
213353a5a1b3Sopenharmony_ci
213453a5a1b3Sopenharmony_ci    return true;
213553a5a1b3Sopenharmony_ci}
213653a5a1b3Sopenharmony_ci
213753a5a1b3Sopenharmony_ci/* Called from main thread */
213853a5a1b3Sopenharmony_civoid pa_sink_set_volume(
213953a5a1b3Sopenharmony_ci        pa_sink *s,
214053a5a1b3Sopenharmony_ci        const pa_cvolume *volume,
214153a5a1b3Sopenharmony_ci        bool send_msg,
214253a5a1b3Sopenharmony_ci        bool save) {
214353a5a1b3Sopenharmony_ci
214453a5a1b3Sopenharmony_ci    pa_cvolume new_reference_volume;
214553a5a1b3Sopenharmony_ci    pa_sink *root_sink;
214653a5a1b3Sopenharmony_ci
214753a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
214853a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
214953a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
215053a5a1b3Sopenharmony_ci    pa_assert(!volume || pa_cvolume_valid(volume));
215153a5a1b3Sopenharmony_ci    pa_assert(volume || pa_sink_flat_volume_enabled(s));
215253a5a1b3Sopenharmony_ci    pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
215353a5a1b3Sopenharmony_ci
215453a5a1b3Sopenharmony_ci    /* make sure we don't change the volume when a PASSTHROUGH input is connected ...
215553a5a1b3Sopenharmony_ci     * ... *except* if we're being invoked to reset the volume to ensure 0 dB gain */
215653a5a1b3Sopenharmony_ci    if (pa_sink_is_passthrough(s) && (!volume || !pa_cvolume_is_norm(volume))) {
215753a5a1b3Sopenharmony_ci        pa_log_warn("Cannot change volume, Sink is connected to PASSTHROUGH input");
215853a5a1b3Sopenharmony_ci        return;
215953a5a1b3Sopenharmony_ci    }
216053a5a1b3Sopenharmony_ci
216153a5a1b3Sopenharmony_ci    /* In case of volume sharing, the volume is set for the root sink first,
216253a5a1b3Sopenharmony_ci     * from which it's then propagated to the sharing sinks. */
216353a5a1b3Sopenharmony_ci    root_sink = pa_sink_get_master(s);
216453a5a1b3Sopenharmony_ci
216553a5a1b3Sopenharmony_ci    if (PA_UNLIKELY(!root_sink))
216653a5a1b3Sopenharmony_ci        return;
216753a5a1b3Sopenharmony_ci
216853a5a1b3Sopenharmony_ci    /* As a special exception we accept mono volumes on all sinks --
216953a5a1b3Sopenharmony_ci     * even on those with more complex channel maps */
217053a5a1b3Sopenharmony_ci
217153a5a1b3Sopenharmony_ci    if (volume) {
217253a5a1b3Sopenharmony_ci        if (pa_cvolume_compatible(volume, &s->sample_spec))
217353a5a1b3Sopenharmony_ci            new_reference_volume = *volume;
217453a5a1b3Sopenharmony_ci        else {
217553a5a1b3Sopenharmony_ci            new_reference_volume = s->reference_volume;
217653a5a1b3Sopenharmony_ci            pa_cvolume_scale(&new_reference_volume, pa_cvolume_max(volume));
217753a5a1b3Sopenharmony_ci        }
217853a5a1b3Sopenharmony_ci
217953a5a1b3Sopenharmony_ci        pa_cvolume_remap(&new_reference_volume, &s->channel_map, &root_sink->channel_map);
218053a5a1b3Sopenharmony_ci
218153a5a1b3Sopenharmony_ci        if (update_reference_volume(root_sink, &new_reference_volume, &root_sink->channel_map, save)) {
218253a5a1b3Sopenharmony_ci            if (pa_sink_flat_volume_enabled(root_sink)) {
218353a5a1b3Sopenharmony_ci                /* OK, propagate this volume change back to the inputs */
218453a5a1b3Sopenharmony_ci                propagate_reference_volume(root_sink);
218553a5a1b3Sopenharmony_ci
218653a5a1b3Sopenharmony_ci                /* And now recalculate the real volume */
218753a5a1b3Sopenharmony_ci                compute_real_volume(root_sink);
218853a5a1b3Sopenharmony_ci            } else
218953a5a1b3Sopenharmony_ci                update_real_volume(root_sink, &root_sink->reference_volume, &root_sink->channel_map);
219053a5a1b3Sopenharmony_ci        }
219153a5a1b3Sopenharmony_ci
219253a5a1b3Sopenharmony_ci    } else {
219353a5a1b3Sopenharmony_ci        /* If volume is NULL we synchronize the sink's real and
219453a5a1b3Sopenharmony_ci         * reference volumes with the stream volumes. */
219553a5a1b3Sopenharmony_ci
219653a5a1b3Sopenharmony_ci        pa_assert(pa_sink_flat_volume_enabled(root_sink));
219753a5a1b3Sopenharmony_ci
219853a5a1b3Sopenharmony_ci        /* Ok, let's determine the new real volume */
219953a5a1b3Sopenharmony_ci        compute_real_volume(root_sink);
220053a5a1b3Sopenharmony_ci
220153a5a1b3Sopenharmony_ci        /* Let's 'push' the reference volume if necessary */
220253a5a1b3Sopenharmony_ci        pa_cvolume_merge(&new_reference_volume, &s->reference_volume, &root_sink->real_volume);
220353a5a1b3Sopenharmony_ci        /* If the sink and its root don't have the same number of channels, we need to remap */
220453a5a1b3Sopenharmony_ci        if (s != root_sink && !pa_channel_map_equal(&s->channel_map, &root_sink->channel_map))
220553a5a1b3Sopenharmony_ci            pa_cvolume_remap(&new_reference_volume, &s->channel_map, &root_sink->channel_map);
220653a5a1b3Sopenharmony_ci        update_reference_volume(root_sink, &new_reference_volume, &root_sink->channel_map, save);
220753a5a1b3Sopenharmony_ci
220853a5a1b3Sopenharmony_ci        /* Now that the reference volume is updated, we can update the streams'
220953a5a1b3Sopenharmony_ci         * reference ratios. */
221053a5a1b3Sopenharmony_ci        compute_reference_ratios(root_sink);
221153a5a1b3Sopenharmony_ci    }
221253a5a1b3Sopenharmony_ci
221353a5a1b3Sopenharmony_ci    if (root_sink->set_volume) {
221453a5a1b3Sopenharmony_ci        /* If we have a function set_volume(), then we do not apply a
221553a5a1b3Sopenharmony_ci         * soft volume by default. However, set_volume() is free to
221653a5a1b3Sopenharmony_ci         * apply one to root_sink->soft_volume */
221753a5a1b3Sopenharmony_ci
221853a5a1b3Sopenharmony_ci        pa_cvolume_reset(&root_sink->soft_volume, root_sink->sample_spec.channels);
221953a5a1b3Sopenharmony_ci        if (!(root_sink->flags & PA_SINK_DEFERRED_VOLUME))
222053a5a1b3Sopenharmony_ci            root_sink->set_volume(root_sink);
222153a5a1b3Sopenharmony_ci
222253a5a1b3Sopenharmony_ci    } else
222353a5a1b3Sopenharmony_ci        /* If we have no function set_volume(), then the soft volume
222453a5a1b3Sopenharmony_ci         * becomes the real volume */
222553a5a1b3Sopenharmony_ci        root_sink->soft_volume = root_sink->real_volume;
222653a5a1b3Sopenharmony_ci
222753a5a1b3Sopenharmony_ci    /* This tells the sink that soft volume and/or real volume changed */
222853a5a1b3Sopenharmony_ci    if (send_msg)
222953a5a1b3Sopenharmony_ci        pa_assert_se(pa_asyncmsgq_send(root_sink->asyncmsgq, PA_MSGOBJECT(root_sink), PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL) == 0);
223053a5a1b3Sopenharmony_ci}
223153a5a1b3Sopenharmony_ci
223253a5a1b3Sopenharmony_ci/* Called from the io thread if sync volume is used, otherwise from the main thread.
223353a5a1b3Sopenharmony_ci * Only to be called by sink implementor */
223453a5a1b3Sopenharmony_civoid pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
223553a5a1b3Sopenharmony_ci
223653a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
223753a5a1b3Sopenharmony_ci    pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
223853a5a1b3Sopenharmony_ci
223953a5a1b3Sopenharmony_ci    if (s->flags & PA_SINK_DEFERRED_VOLUME)
224053a5a1b3Sopenharmony_ci        pa_sink_assert_io_context(s);
224153a5a1b3Sopenharmony_ci    else
224253a5a1b3Sopenharmony_ci        pa_assert_ctl_context();
224353a5a1b3Sopenharmony_ci
224453a5a1b3Sopenharmony_ci    if (!volume)
224553a5a1b3Sopenharmony_ci        pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
224653a5a1b3Sopenharmony_ci    else
224753a5a1b3Sopenharmony_ci        s->soft_volume = *volume;
224853a5a1b3Sopenharmony_ci
224953a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->state) && !(s->flags & PA_SINK_DEFERRED_VOLUME))
225053a5a1b3Sopenharmony_ci        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
225153a5a1b3Sopenharmony_ci    else
225253a5a1b3Sopenharmony_ci        s->thread_info.soft_volume = s->soft_volume;
225353a5a1b3Sopenharmony_ci}
225453a5a1b3Sopenharmony_ci
225553a5a1b3Sopenharmony_ci/* Called from the main thread. Only called for the root sink in volume sharing
225653a5a1b3Sopenharmony_ci * cases, except for internal recursive calls. */
225753a5a1b3Sopenharmony_cistatic void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume) {
225853a5a1b3Sopenharmony_ci    pa_sink_input *i;
225953a5a1b3Sopenharmony_ci    uint32_t idx;
226053a5a1b3Sopenharmony_ci
226153a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
226253a5a1b3Sopenharmony_ci    pa_assert(old_real_volume);
226353a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
226453a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
226553a5a1b3Sopenharmony_ci
226653a5a1b3Sopenharmony_ci    /* This is called when the hardware's real volume changes due to
226753a5a1b3Sopenharmony_ci     * some external event. We copy the real volume into our
226853a5a1b3Sopenharmony_ci     * reference volume and then rebuild the stream volumes based on
226953a5a1b3Sopenharmony_ci     * i->real_ratio which should stay fixed. */
227053a5a1b3Sopenharmony_ci
227153a5a1b3Sopenharmony_ci    if (!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
227253a5a1b3Sopenharmony_ci        if (pa_cvolume_equal(old_real_volume, &s->real_volume))
227353a5a1b3Sopenharmony_ci            return;
227453a5a1b3Sopenharmony_ci
227553a5a1b3Sopenharmony_ci        /* 1. Make the real volume the reference volume */
227653a5a1b3Sopenharmony_ci        update_reference_volume(s, &s->real_volume, &s->channel_map, true);
227753a5a1b3Sopenharmony_ci    }
227853a5a1b3Sopenharmony_ci
227953a5a1b3Sopenharmony_ci    if (pa_sink_flat_volume_enabled(s)) {
228053a5a1b3Sopenharmony_ci
228153a5a1b3Sopenharmony_ci        PA_IDXSET_FOREACH(i, s->inputs, idx) {
228253a5a1b3Sopenharmony_ci            pa_cvolume new_volume;
228353a5a1b3Sopenharmony_ci
228453a5a1b3Sopenharmony_ci            /* 2. Since the sink's reference and real volumes are equal
228553a5a1b3Sopenharmony_ci             * now our ratios should be too. */
228653a5a1b3Sopenharmony_ci            pa_sink_input_set_reference_ratio(i, &i->real_ratio);
228753a5a1b3Sopenharmony_ci
228853a5a1b3Sopenharmony_ci            /* 3. Recalculate the new stream reference volume based on the
228953a5a1b3Sopenharmony_ci             * reference ratio and the sink's reference volume.
229053a5a1b3Sopenharmony_ci             *
229153a5a1b3Sopenharmony_ci             * This basically calculates:
229253a5a1b3Sopenharmony_ci             *
229353a5a1b3Sopenharmony_ci             * i->volume = s->reference_volume * i->reference_ratio
229453a5a1b3Sopenharmony_ci             *
229553a5a1b3Sopenharmony_ci             * This is identical to propagate_reference_volume() */
229653a5a1b3Sopenharmony_ci            new_volume = s->reference_volume;
229753a5a1b3Sopenharmony_ci            pa_cvolume_remap(&new_volume, &s->channel_map, &i->channel_map);
229853a5a1b3Sopenharmony_ci            pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio);
229953a5a1b3Sopenharmony_ci            pa_sink_input_set_volume_direct(i, &new_volume);
230053a5a1b3Sopenharmony_ci
230153a5a1b3Sopenharmony_ci            if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)
230253a5a1b3Sopenharmony_ci                    && PA_SINK_IS_LINKED(i->origin_sink->state))
230353a5a1b3Sopenharmony_ci                propagate_real_volume(i->origin_sink, old_real_volume);
230453a5a1b3Sopenharmony_ci        }
230553a5a1b3Sopenharmony_ci    }
230653a5a1b3Sopenharmony_ci
230753a5a1b3Sopenharmony_ci    /* Something got changed in the hardware. It probably makes sense
230853a5a1b3Sopenharmony_ci     * to save changed hw settings given that hw volume changes not
230953a5a1b3Sopenharmony_ci     * triggered by PA are almost certainly done by the user. */
231053a5a1b3Sopenharmony_ci    if (!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
231153a5a1b3Sopenharmony_ci        s->save_volume = true;
231253a5a1b3Sopenharmony_ci}
231353a5a1b3Sopenharmony_ci
231453a5a1b3Sopenharmony_ci/* Called from io thread */
231553a5a1b3Sopenharmony_civoid pa_sink_update_volume_and_mute(pa_sink *s) {
231653a5a1b3Sopenharmony_ci    pa_assert(s);
231753a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
231853a5a1b3Sopenharmony_ci
231953a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE, NULL, 0, NULL, NULL);
232053a5a1b3Sopenharmony_ci}
232153a5a1b3Sopenharmony_ci
232253a5a1b3Sopenharmony_ci/* Called from main thread */
232353a5a1b3Sopenharmony_ciconst pa_cvolume *pa_sink_get_volume(pa_sink *s, bool force_refresh) {
232453a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
232553a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
232653a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
232753a5a1b3Sopenharmony_ci
232853a5a1b3Sopenharmony_ci    if (s->refresh_volume || force_refresh) {
232953a5a1b3Sopenharmony_ci        struct pa_cvolume old_real_volume;
233053a5a1b3Sopenharmony_ci
233153a5a1b3Sopenharmony_ci        pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
233253a5a1b3Sopenharmony_ci
233353a5a1b3Sopenharmony_ci        old_real_volume = s->real_volume;
233453a5a1b3Sopenharmony_ci
233553a5a1b3Sopenharmony_ci        if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_volume)
233653a5a1b3Sopenharmony_ci            s->get_volume(s);
233753a5a1b3Sopenharmony_ci
233853a5a1b3Sopenharmony_ci        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
233953a5a1b3Sopenharmony_ci
234053a5a1b3Sopenharmony_ci        update_real_volume(s, &s->real_volume, &s->channel_map);
234153a5a1b3Sopenharmony_ci        propagate_real_volume(s, &old_real_volume);
234253a5a1b3Sopenharmony_ci    }
234353a5a1b3Sopenharmony_ci
234453a5a1b3Sopenharmony_ci    return &s->reference_volume;
234553a5a1b3Sopenharmony_ci}
234653a5a1b3Sopenharmony_ci
234753a5a1b3Sopenharmony_ci/* Called from main thread. In volume sharing cases, only the root sink may
234853a5a1b3Sopenharmony_ci * call this. */
234953a5a1b3Sopenharmony_civoid pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_real_volume) {
235053a5a1b3Sopenharmony_ci    pa_cvolume old_real_volume;
235153a5a1b3Sopenharmony_ci
235253a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
235353a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
235453a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
235553a5a1b3Sopenharmony_ci    pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
235653a5a1b3Sopenharmony_ci
235753a5a1b3Sopenharmony_ci    /* The sink implementor may call this if the volume changed to make sure everyone is notified */
235853a5a1b3Sopenharmony_ci
235953a5a1b3Sopenharmony_ci    old_real_volume = s->real_volume;
236053a5a1b3Sopenharmony_ci    update_real_volume(s, new_real_volume, &s->channel_map);
236153a5a1b3Sopenharmony_ci    propagate_real_volume(s, &old_real_volume);
236253a5a1b3Sopenharmony_ci}
236353a5a1b3Sopenharmony_ci
236453a5a1b3Sopenharmony_ci/* Called from main thread */
236553a5a1b3Sopenharmony_civoid pa_sink_set_mute(pa_sink *s, bool mute, bool save) {
236653a5a1b3Sopenharmony_ci    bool old_muted;
236753a5a1b3Sopenharmony_ci
236853a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
236953a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
237053a5a1b3Sopenharmony_ci
237153a5a1b3Sopenharmony_ci    old_muted = s->muted;
237253a5a1b3Sopenharmony_ci
237353a5a1b3Sopenharmony_ci    if (mute == old_muted) {
237453a5a1b3Sopenharmony_ci        s->save_muted |= save;
237553a5a1b3Sopenharmony_ci        return;
237653a5a1b3Sopenharmony_ci    }
237753a5a1b3Sopenharmony_ci
237853a5a1b3Sopenharmony_ci    s->muted = mute;
237953a5a1b3Sopenharmony_ci    s->save_muted = save;
238053a5a1b3Sopenharmony_ci
238153a5a1b3Sopenharmony_ci    if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute) {
238253a5a1b3Sopenharmony_ci        s->set_mute_in_progress = true;
238353a5a1b3Sopenharmony_ci        s->set_mute(s);
238453a5a1b3Sopenharmony_ci        s->set_mute_in_progress = false;
238553a5a1b3Sopenharmony_ci    }
238653a5a1b3Sopenharmony_ci
238753a5a1b3Sopenharmony_ci    if (!PA_SINK_IS_LINKED(s->state))
238853a5a1b3Sopenharmony_ci        return;
238953a5a1b3Sopenharmony_ci
239053a5a1b3Sopenharmony_ci    pa_log_debug("The mute of sink %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute));
239153a5a1b3Sopenharmony_ci    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
239253a5a1b3Sopenharmony_ci    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
239353a5a1b3Sopenharmony_ci    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_MUTE_CHANGED], s);
239453a5a1b3Sopenharmony_ci}
239553a5a1b3Sopenharmony_ci
239653a5a1b3Sopenharmony_ci/* Called from main thread */
239753a5a1b3Sopenharmony_cibool pa_sink_get_mute(pa_sink *s, bool force_refresh) {
239853a5a1b3Sopenharmony_ci
239953a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
240053a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
240153a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
240253a5a1b3Sopenharmony_ci
240353a5a1b3Sopenharmony_ci    if ((s->refresh_muted || force_refresh) && s->get_mute) {
240453a5a1b3Sopenharmony_ci        bool mute;
240553a5a1b3Sopenharmony_ci
240653a5a1b3Sopenharmony_ci        if (s->flags & PA_SINK_DEFERRED_VOLUME) {
240753a5a1b3Sopenharmony_ci            if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &mute, 0, NULL) >= 0)
240853a5a1b3Sopenharmony_ci                pa_sink_mute_changed(s, mute);
240953a5a1b3Sopenharmony_ci        } else {
241053a5a1b3Sopenharmony_ci            if (s->get_mute(s, &mute) >= 0)
241153a5a1b3Sopenharmony_ci                pa_sink_mute_changed(s, mute);
241253a5a1b3Sopenharmony_ci        }
241353a5a1b3Sopenharmony_ci    }
241453a5a1b3Sopenharmony_ci
241553a5a1b3Sopenharmony_ci    return s->muted;
241653a5a1b3Sopenharmony_ci}
241753a5a1b3Sopenharmony_ci
241853a5a1b3Sopenharmony_ci/* Called from main thread */
241953a5a1b3Sopenharmony_civoid pa_sink_mute_changed(pa_sink *s, bool new_muted) {
242053a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
242153a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
242253a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
242353a5a1b3Sopenharmony_ci
242453a5a1b3Sopenharmony_ci    if (s->set_mute_in_progress)
242553a5a1b3Sopenharmony_ci        return;
242653a5a1b3Sopenharmony_ci
242753a5a1b3Sopenharmony_ci    /* pa_sink_set_mute() does this same check, so this may appear redundant,
242853a5a1b3Sopenharmony_ci     * but we must have this here also, because the save parameter of
242953a5a1b3Sopenharmony_ci     * pa_sink_set_mute() would otherwise have unintended side effects (saving
243053a5a1b3Sopenharmony_ci     * the mute state when it shouldn't be saved). */
243153a5a1b3Sopenharmony_ci    if (new_muted == s->muted)
243253a5a1b3Sopenharmony_ci        return;
243353a5a1b3Sopenharmony_ci
243453a5a1b3Sopenharmony_ci    pa_sink_set_mute(s, new_muted, true);
243553a5a1b3Sopenharmony_ci}
243653a5a1b3Sopenharmony_ci
243753a5a1b3Sopenharmony_ci/* Called from main thread */
243853a5a1b3Sopenharmony_cibool pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
243953a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
244053a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
244153a5a1b3Sopenharmony_ci
244253a5a1b3Sopenharmony_ci    if (p)
244353a5a1b3Sopenharmony_ci        pa_proplist_update(s->proplist, mode, p);
244453a5a1b3Sopenharmony_ci
244553a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->state)) {
244653a5a1b3Sopenharmony_ci        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
244753a5a1b3Sopenharmony_ci        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
244853a5a1b3Sopenharmony_ci    }
244953a5a1b3Sopenharmony_ci
245053a5a1b3Sopenharmony_ci    return true;
245153a5a1b3Sopenharmony_ci}
245253a5a1b3Sopenharmony_ci
245353a5a1b3Sopenharmony_ci/* Called from main thread */
245453a5a1b3Sopenharmony_ci/* FIXME -- this should be dropped and be merged into pa_sink_update_proplist() */
245553a5a1b3Sopenharmony_civoid pa_sink_set_description(pa_sink *s, const char *description) {
245653a5a1b3Sopenharmony_ci    const char *old;
245753a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
245853a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
245953a5a1b3Sopenharmony_ci
246053a5a1b3Sopenharmony_ci    if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
246153a5a1b3Sopenharmony_ci        return;
246253a5a1b3Sopenharmony_ci
246353a5a1b3Sopenharmony_ci    old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
246453a5a1b3Sopenharmony_ci
246553a5a1b3Sopenharmony_ci    if (old && description && pa_streq(old, description))
246653a5a1b3Sopenharmony_ci        return;
246753a5a1b3Sopenharmony_ci
246853a5a1b3Sopenharmony_ci    if (description)
246953a5a1b3Sopenharmony_ci        pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
247053a5a1b3Sopenharmony_ci    else
247153a5a1b3Sopenharmony_ci        pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
247253a5a1b3Sopenharmony_ci
247353a5a1b3Sopenharmony_ci    if (s->monitor_source) {
247453a5a1b3Sopenharmony_ci        char *n;
247553a5a1b3Sopenharmony_ci
247653a5a1b3Sopenharmony_ci        n = pa_sprintf_malloc("Monitor Source of %s", description ? description : s->name);
247753a5a1b3Sopenharmony_ci        pa_source_set_description(s->monitor_source, n);
247853a5a1b3Sopenharmony_ci        pa_xfree(n);
247953a5a1b3Sopenharmony_ci    }
248053a5a1b3Sopenharmony_ci
248153a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->state)) {
248253a5a1b3Sopenharmony_ci        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
248353a5a1b3Sopenharmony_ci        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
248453a5a1b3Sopenharmony_ci    }
248553a5a1b3Sopenharmony_ci}
248653a5a1b3Sopenharmony_ci
248753a5a1b3Sopenharmony_ci/* Called from main thread */
248853a5a1b3Sopenharmony_ciunsigned pa_sink_linked_by(pa_sink *s) {
248953a5a1b3Sopenharmony_ci    unsigned ret;
249053a5a1b3Sopenharmony_ci
249153a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
249253a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
249353a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
249453a5a1b3Sopenharmony_ci
249553a5a1b3Sopenharmony_ci    ret = pa_idxset_size(s->inputs);
249653a5a1b3Sopenharmony_ci
249753a5a1b3Sopenharmony_ci    /* We add in the number of streams connected to us here. Please
249853a5a1b3Sopenharmony_ci     * note the asymmetry to pa_sink_used_by()! */
249953a5a1b3Sopenharmony_ci
250053a5a1b3Sopenharmony_ci    if (s->monitor_source)
250153a5a1b3Sopenharmony_ci        ret += pa_source_linked_by(s->monitor_source);
250253a5a1b3Sopenharmony_ci
250353a5a1b3Sopenharmony_ci    return ret;
250453a5a1b3Sopenharmony_ci}
250553a5a1b3Sopenharmony_ci
250653a5a1b3Sopenharmony_ci/* Called from main thread */
250753a5a1b3Sopenharmony_ciunsigned pa_sink_used_by(pa_sink *s) {
250853a5a1b3Sopenharmony_ci    unsigned ret;
250953a5a1b3Sopenharmony_ci
251053a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
251153a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
251253a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
251353a5a1b3Sopenharmony_ci
251453a5a1b3Sopenharmony_ci    ret = pa_idxset_size(s->inputs);
251553a5a1b3Sopenharmony_ci    pa_assert(ret >= s->n_corked);
251653a5a1b3Sopenharmony_ci
251753a5a1b3Sopenharmony_ci    /* Streams connected to our monitor source do not matter for
251853a5a1b3Sopenharmony_ci     * pa_sink_used_by()!.*/
251953a5a1b3Sopenharmony_ci
252053a5a1b3Sopenharmony_ci    return ret - s->n_corked;
252153a5a1b3Sopenharmony_ci}
252253a5a1b3Sopenharmony_ci
252353a5a1b3Sopenharmony_ci/* Called from main thread */
252453a5a1b3Sopenharmony_ciunsigned pa_sink_check_suspend(pa_sink *s, pa_sink_input *ignore_input, pa_source_output *ignore_output) {
252553a5a1b3Sopenharmony_ci    unsigned ret;
252653a5a1b3Sopenharmony_ci    pa_sink_input *i;
252753a5a1b3Sopenharmony_ci    uint32_t idx;
252853a5a1b3Sopenharmony_ci
252953a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
253053a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
253153a5a1b3Sopenharmony_ci
253253a5a1b3Sopenharmony_ci    if (!PA_SINK_IS_LINKED(s->state))
253353a5a1b3Sopenharmony_ci        return 0;
253453a5a1b3Sopenharmony_ci
253553a5a1b3Sopenharmony_ci    ret = 0;
253653a5a1b3Sopenharmony_ci
253753a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(i, s->inputs, idx) {
253853a5a1b3Sopenharmony_ci        if (i == ignore_input)
253953a5a1b3Sopenharmony_ci            continue;
254053a5a1b3Sopenharmony_ci
254153a5a1b3Sopenharmony_ci        /* We do not assert here. It is perfectly valid for a sink input to
254253a5a1b3Sopenharmony_ci         * be in the INIT state (i.e. created, marked done but not yet put)
254353a5a1b3Sopenharmony_ci         * and we should not care if it's unlinked as it won't contribute
254453a5a1b3Sopenharmony_ci         * towards our busy status.
254553a5a1b3Sopenharmony_ci         */
254653a5a1b3Sopenharmony_ci        if (!PA_SINK_INPUT_IS_LINKED(i->state))
254753a5a1b3Sopenharmony_ci            continue;
254853a5a1b3Sopenharmony_ci
254953a5a1b3Sopenharmony_ci        if (i->state == PA_SINK_INPUT_CORKED)
255053a5a1b3Sopenharmony_ci            continue;
255153a5a1b3Sopenharmony_ci
255253a5a1b3Sopenharmony_ci        if (i->flags & PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND)
255353a5a1b3Sopenharmony_ci            continue;
255453a5a1b3Sopenharmony_ci
255553a5a1b3Sopenharmony_ci        ret ++;
255653a5a1b3Sopenharmony_ci    }
255753a5a1b3Sopenharmony_ci
255853a5a1b3Sopenharmony_ci    if (s->monitor_source)
255953a5a1b3Sopenharmony_ci        ret += pa_source_check_suspend(s->monitor_source, ignore_output);
256053a5a1b3Sopenharmony_ci
256153a5a1b3Sopenharmony_ci    return ret;
256253a5a1b3Sopenharmony_ci}
256353a5a1b3Sopenharmony_ci
256453a5a1b3Sopenharmony_ciconst char *pa_sink_state_to_string(pa_sink_state_t state) {
256553a5a1b3Sopenharmony_ci    switch (state) {
256653a5a1b3Sopenharmony_ci        case PA_SINK_INIT:          return "INIT";
256753a5a1b3Sopenharmony_ci        case PA_SINK_IDLE:          return "IDLE";
256853a5a1b3Sopenharmony_ci        case PA_SINK_RUNNING:       return "RUNNING";
256953a5a1b3Sopenharmony_ci        case PA_SINK_SUSPENDED:     return "SUSPENDED";
257053a5a1b3Sopenharmony_ci        case PA_SINK_UNLINKED:      return "UNLINKED";
257153a5a1b3Sopenharmony_ci        case PA_SINK_INVALID_STATE: return "INVALID_STATE";
257253a5a1b3Sopenharmony_ci    }
257353a5a1b3Sopenharmony_ci
257453a5a1b3Sopenharmony_ci    pa_assert_not_reached();
257553a5a1b3Sopenharmony_ci}
257653a5a1b3Sopenharmony_ci
257753a5a1b3Sopenharmony_ci/* Called from the IO thread */
257853a5a1b3Sopenharmony_cistatic void sync_input_volumes_within_thread(pa_sink *s) {
257953a5a1b3Sopenharmony_ci    pa_sink_input *i;
258053a5a1b3Sopenharmony_ci    void *state = NULL;
258153a5a1b3Sopenharmony_ci
258253a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
258353a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
258453a5a1b3Sopenharmony_ci
258553a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
258653a5a1b3Sopenharmony_ci        if (pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume))
258753a5a1b3Sopenharmony_ci            continue;
258853a5a1b3Sopenharmony_ci
258953a5a1b3Sopenharmony_ci        i->thread_info.soft_volume = i->soft_volume;
259053a5a1b3Sopenharmony_ci        pa_sink_input_request_rewind(i, 0, true, false, false);
259153a5a1b3Sopenharmony_ci    }
259253a5a1b3Sopenharmony_ci}
259353a5a1b3Sopenharmony_ci
259453a5a1b3Sopenharmony_ci/* Called from the IO thread. Only called for the root sink in volume sharing
259553a5a1b3Sopenharmony_ci * cases, except for internal recursive calls. */
259653a5a1b3Sopenharmony_cistatic void set_shared_volume_within_thread(pa_sink *s) {
259753a5a1b3Sopenharmony_ci    pa_sink_input *i = NULL;
259853a5a1b3Sopenharmony_ci    void *state = NULL;
259953a5a1b3Sopenharmony_ci
260053a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
260153a5a1b3Sopenharmony_ci
260253a5a1b3Sopenharmony_ci    PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME_SYNCED, NULL, 0, NULL);
260353a5a1b3Sopenharmony_ci
260453a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
260553a5a1b3Sopenharmony_ci        if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
260653a5a1b3Sopenharmony_ci            set_shared_volume_within_thread(i->origin_sink);
260753a5a1b3Sopenharmony_ci    }
260853a5a1b3Sopenharmony_ci}
260953a5a1b3Sopenharmony_ci
261053a5a1b3Sopenharmony_ci/* Called from IO thread. Gets max_rewind limit from sink inputs.
261153a5a1b3Sopenharmony_ci * This function is used to communicate the max_rewind value of a
261253a5a1b3Sopenharmony_ci * virtual sink to the master sink. The get_max_rewind_limit()
261353a5a1b3Sopenharmony_ci * callback is implemented by sink inputs connecting a virtual
261453a5a1b3Sopenharmony_ci * sink to its master. */
261553a5a1b3Sopenharmony_cistatic size_t get_max_rewind_limit(pa_sink *s, size_t requested_limit) {
261653a5a1b3Sopenharmony_ci    pa_sink_input *i;
261753a5a1b3Sopenharmony_ci    void *state = NULL;
261853a5a1b3Sopenharmony_ci    size_t rewind_limit;
261953a5a1b3Sopenharmony_ci
262053a5a1b3Sopenharmony_ci    pa_assert(s);
262153a5a1b3Sopenharmony_ci
262253a5a1b3Sopenharmony_ci    /* Get rewind limit in sink sample spec from sink inputs */
262353a5a1b3Sopenharmony_ci    rewind_limit = (size_t)(-1);
262453a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
262553a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
262653a5a1b3Sopenharmony_ci
262753a5a1b3Sopenharmony_ci            if (i->get_max_rewind_limit) {
262853a5a1b3Sopenharmony_ci                size_t limit;
262953a5a1b3Sopenharmony_ci
263053a5a1b3Sopenharmony_ci                limit = i->get_max_rewind_limit(i);
263153a5a1b3Sopenharmony_ci                if (rewind_limit == (size_t)(-1) || rewind_limit > limit)
263253a5a1b3Sopenharmony_ci                    rewind_limit = limit;
263353a5a1b3Sopenharmony_ci            }
263453a5a1b3Sopenharmony_ci        }
263553a5a1b3Sopenharmony_ci    }
263653a5a1b3Sopenharmony_ci
263753a5a1b3Sopenharmony_ci    /* Set max_rewind */
263853a5a1b3Sopenharmony_ci    if (rewind_limit != (size_t)(-1))
263953a5a1b3Sopenharmony_ci        requested_limit = PA_MIN(rewind_limit, requested_limit);
264053a5a1b3Sopenharmony_ci
264153a5a1b3Sopenharmony_ci    return requested_limit;
264253a5a1b3Sopenharmony_ci}
264353a5a1b3Sopenharmony_ci
264453a5a1b3Sopenharmony_ci/* Called from IO thread, except when it is not */
264553a5a1b3Sopenharmony_ciint pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
264653a5a1b3Sopenharmony_ci    pa_sink *s = PA_SINK(o);
264753a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
264853a5a1b3Sopenharmony_ci
264953a5a1b3Sopenharmony_ci    switch ((pa_sink_message_t) code) {
265053a5a1b3Sopenharmony_ci
265153a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_ADD_INPUT: {
265253a5a1b3Sopenharmony_ci            pa_sink_input *i = PA_SINK_INPUT(userdata);
265353a5a1b3Sopenharmony_ci
265453a5a1b3Sopenharmony_ci            /* If you change anything here, make sure to change the
265553a5a1b3Sopenharmony_ci             * sink input handling a few lines down at
265653a5a1b3Sopenharmony_ci             * PA_SINK_MESSAGE_FINISH_MOVE, too. */
265753a5a1b3Sopenharmony_ci
265853a5a1b3Sopenharmony_ci            pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
265953a5a1b3Sopenharmony_ci
266053a5a1b3Sopenharmony_ci            /* Since the caller sleeps in pa_sink_input_put(), we can
266153a5a1b3Sopenharmony_ci             * safely access data outside of thread_info even though
266253a5a1b3Sopenharmony_ci             * it is mutable */
266353a5a1b3Sopenharmony_ci
266453a5a1b3Sopenharmony_ci            if ((i->thread_info.sync_prev = i->sync_prev)) {
266553a5a1b3Sopenharmony_ci                pa_assert(i->sink == i->thread_info.sync_prev->sink);
266653a5a1b3Sopenharmony_ci                pa_assert(i->sync_prev->sync_next == i);
266753a5a1b3Sopenharmony_ci                i->thread_info.sync_prev->thread_info.sync_next = i;
266853a5a1b3Sopenharmony_ci            }
266953a5a1b3Sopenharmony_ci
267053a5a1b3Sopenharmony_ci            if ((i->thread_info.sync_next = i->sync_next)) {
267153a5a1b3Sopenharmony_ci                pa_assert(i->sink == i->thread_info.sync_next->sink);
267253a5a1b3Sopenharmony_ci                pa_assert(i->sync_next->sync_prev == i);
267353a5a1b3Sopenharmony_ci                i->thread_info.sync_next->thread_info.sync_prev = i;
267453a5a1b3Sopenharmony_ci            }
267553a5a1b3Sopenharmony_ci
267653a5a1b3Sopenharmony_ci            pa_sink_input_attach(i);
267753a5a1b3Sopenharmony_ci
267853a5a1b3Sopenharmony_ci            pa_sink_input_set_state_within_thread(i, i->state);
267953a5a1b3Sopenharmony_ci
268053a5a1b3Sopenharmony_ci            /* The requested latency of the sink input needs to be fixed up and
268153a5a1b3Sopenharmony_ci             * then configured on the sink. If this causes the sink latency to
268253a5a1b3Sopenharmony_ci             * go down, the sink implementor is responsible for doing a rewind
268353a5a1b3Sopenharmony_ci             * in the update_requested_latency() callback to ensure that the
268453a5a1b3Sopenharmony_ci             * sink buffer doesn't contain more data than what the new latency
268553a5a1b3Sopenharmony_ci             * allows.
268653a5a1b3Sopenharmony_ci             *
268753a5a1b3Sopenharmony_ci             * XXX: Does it really make sense to push this responsibility to
268853a5a1b3Sopenharmony_ci             * the sink implementors? Wouldn't it be better to do it once in
268953a5a1b3Sopenharmony_ci             * the core than many times in the modules? */
269053a5a1b3Sopenharmony_ci
269153a5a1b3Sopenharmony_ci            if (i->thread_info.requested_sink_latency != (pa_usec_t) -1)
269253a5a1b3Sopenharmony_ci                pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
269353a5a1b3Sopenharmony_ci
269453a5a1b3Sopenharmony_ci            pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
269553a5a1b3Sopenharmony_ci            pa_sink_input_update_max_request(i, s->thread_info.max_request);
269653a5a1b3Sopenharmony_ci
269753a5a1b3Sopenharmony_ci            /* We don't rewind here automatically. This is left to the
269853a5a1b3Sopenharmony_ci             * sink input implementor because some sink inputs need a
269953a5a1b3Sopenharmony_ci             * slow start, i.e. need some time to buffer client
270053a5a1b3Sopenharmony_ci             * samples before beginning streaming.
270153a5a1b3Sopenharmony_ci             *
270253a5a1b3Sopenharmony_ci             * XXX: Does it really make sense to push this functionality to
270353a5a1b3Sopenharmony_ci             * the sink implementors? Wouldn't it be better to do it once in
270453a5a1b3Sopenharmony_ci             * the core than many times in the modules? */
270553a5a1b3Sopenharmony_ci
270653a5a1b3Sopenharmony_ci            /* In flat volume mode we need to update the volume as
270753a5a1b3Sopenharmony_ci             * well */
270853a5a1b3Sopenharmony_ci            return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
270953a5a1b3Sopenharmony_ci        }
271053a5a1b3Sopenharmony_ci
271153a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_REMOVE_INPUT: {
271253a5a1b3Sopenharmony_ci            pa_sink_input *i = PA_SINK_INPUT(userdata);
271353a5a1b3Sopenharmony_ci
271453a5a1b3Sopenharmony_ci            /* If you change anything here, make sure to change the
271553a5a1b3Sopenharmony_ci             * sink input handling a few lines down at
271653a5a1b3Sopenharmony_ci             * PA_SINK_MESSAGE_START_MOVE, too. */
271753a5a1b3Sopenharmony_ci
271853a5a1b3Sopenharmony_ci            pa_sink_input_detach(i);
271953a5a1b3Sopenharmony_ci
272053a5a1b3Sopenharmony_ci            pa_sink_input_set_state_within_thread(i, i->state);
272153a5a1b3Sopenharmony_ci
272253a5a1b3Sopenharmony_ci            /* Since the caller sleeps in pa_sink_input_unlink(),
272353a5a1b3Sopenharmony_ci             * we can safely access data outside of thread_info even
272453a5a1b3Sopenharmony_ci             * though it is mutable */
272553a5a1b3Sopenharmony_ci
272653a5a1b3Sopenharmony_ci            pa_assert(!i->sync_prev);
272753a5a1b3Sopenharmony_ci            pa_assert(!i->sync_next);
272853a5a1b3Sopenharmony_ci
272953a5a1b3Sopenharmony_ci            if (i->thread_info.sync_prev) {
273053a5a1b3Sopenharmony_ci                i->thread_info.sync_prev->thread_info.sync_next = i->thread_info.sync_prev->sync_next;
273153a5a1b3Sopenharmony_ci                i->thread_info.sync_prev = NULL;
273253a5a1b3Sopenharmony_ci            }
273353a5a1b3Sopenharmony_ci
273453a5a1b3Sopenharmony_ci            if (i->thread_info.sync_next) {
273553a5a1b3Sopenharmony_ci                i->thread_info.sync_next->thread_info.sync_prev = i->thread_info.sync_next->sync_prev;
273653a5a1b3Sopenharmony_ci                i->thread_info.sync_next = NULL;
273753a5a1b3Sopenharmony_ci            }
273853a5a1b3Sopenharmony_ci
273953a5a1b3Sopenharmony_ci            pa_hashmap_remove_and_free(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index));
274053a5a1b3Sopenharmony_ci            pa_sink_request_rewind(s, (size_t) -1);
274153a5a1b3Sopenharmony_ci            pa_sink_invalidate_requested_latency(s, true);
274253a5a1b3Sopenharmony_ci
274353a5a1b3Sopenharmony_ci            /* In flat volume mode we need to update the volume as
274453a5a1b3Sopenharmony_ci             * well */
274553a5a1b3Sopenharmony_ci            return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
274653a5a1b3Sopenharmony_ci        }
274753a5a1b3Sopenharmony_ci
274853a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_START_MOVE: {
274953a5a1b3Sopenharmony_ci            pa_sink_input *i = PA_SINK_INPUT(userdata);
275053a5a1b3Sopenharmony_ci
275153a5a1b3Sopenharmony_ci            /* We don't support moving synchronized streams. */
275253a5a1b3Sopenharmony_ci            pa_assert(!i->sync_prev);
275353a5a1b3Sopenharmony_ci            pa_assert(!i->sync_next);
275453a5a1b3Sopenharmony_ci            pa_assert(!i->thread_info.sync_next);
275553a5a1b3Sopenharmony_ci            pa_assert(!i->thread_info.sync_prev);
275653a5a1b3Sopenharmony_ci
275753a5a1b3Sopenharmony_ci            if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
275853a5a1b3Sopenharmony_ci
275953a5a1b3Sopenharmony_ci                /* The old sink probably has some audio from this
276053a5a1b3Sopenharmony_ci                 * stream in its buffer. We want to "take it back" as
276153a5a1b3Sopenharmony_ci                 * much as possible and play it to the new sink. We
276253a5a1b3Sopenharmony_ci                 * don't know at this point how much the old sink can
276353a5a1b3Sopenharmony_ci                 * rewind, so we just save some values and reconstruct
276453a5a1b3Sopenharmony_ci                 * the render memblockq in finish_move(). */
276553a5a1b3Sopenharmony_ci
276653a5a1b3Sopenharmony_ci                /* Save some current values for restore_render_memblockq() */
276753a5a1b3Sopenharmony_ci                i->thread_info.origin_sink_latency = pa_sink_get_latency_within_thread(s, false);
276853a5a1b3Sopenharmony_ci                i->thread_info.move_start_time = pa_rtclock_now();
276953a5a1b3Sopenharmony_ci                i->thread_info.resampler_delay_frames = 0;
277053a5a1b3Sopenharmony_ci                if (i->thread_info.resampler)
277153a5a1b3Sopenharmony_ci                    /* Round down */
277253a5a1b3Sopenharmony_ci                    i->thread_info.resampler_delay_frames = pa_resampler_get_delay(i->thread_info.resampler, false);
277353a5a1b3Sopenharmony_ci            }
277453a5a1b3Sopenharmony_ci
277553a5a1b3Sopenharmony_ci            pa_sink_input_detach(i);
277653a5a1b3Sopenharmony_ci
277753a5a1b3Sopenharmony_ci            /* Let's remove the sink input ...*/
277853a5a1b3Sopenharmony_ci            pa_hashmap_remove_and_free(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index));
277953a5a1b3Sopenharmony_ci
278053a5a1b3Sopenharmony_ci            /* The rewind must be requested before invalidating the latency, otherwise
278153a5a1b3Sopenharmony_ci             * the max_rewind value of the sink may change before the rewind. */
278253a5a1b3Sopenharmony_ci            pa_log_debug("Requesting rewind due to started move");
278353a5a1b3Sopenharmony_ci            pa_sink_request_rewind(s, (size_t) -1);
278453a5a1b3Sopenharmony_ci
278553a5a1b3Sopenharmony_ci            pa_sink_invalidate_requested_latency(s, true);
278653a5a1b3Sopenharmony_ci
278753a5a1b3Sopenharmony_ci            /* In flat volume mode we need to update the volume as
278853a5a1b3Sopenharmony_ci             * well */
278953a5a1b3Sopenharmony_ci            return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
279053a5a1b3Sopenharmony_ci        }
279153a5a1b3Sopenharmony_ci
279253a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_FINISH_MOVE: {
279353a5a1b3Sopenharmony_ci            pa_sink_input *i = PA_SINK_INPUT(userdata);
279453a5a1b3Sopenharmony_ci
279553a5a1b3Sopenharmony_ci            /* We don't support moving synchronized streams. */
279653a5a1b3Sopenharmony_ci            pa_assert(!i->sync_prev);
279753a5a1b3Sopenharmony_ci            pa_assert(!i->sync_next);
279853a5a1b3Sopenharmony_ci            pa_assert(!i->thread_info.sync_next);
279953a5a1b3Sopenharmony_ci            pa_assert(!i->thread_info.sync_prev);
280053a5a1b3Sopenharmony_ci
280153a5a1b3Sopenharmony_ci            pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
280253a5a1b3Sopenharmony_ci
280353a5a1b3Sopenharmony_ci            pa_sink_input_attach(i);
280453a5a1b3Sopenharmony_ci
280553a5a1b3Sopenharmony_ci            if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
280653a5a1b3Sopenharmony_ci                pa_usec_t usec = 0;
280753a5a1b3Sopenharmony_ci                size_t nbytes, delay_bytes;
280853a5a1b3Sopenharmony_ci
280953a5a1b3Sopenharmony_ci                /* In the ideal case the new sink would start playing
281053a5a1b3Sopenharmony_ci                 * the stream immediately. That requires the sink to
281153a5a1b3Sopenharmony_ci                 * be able to rewind all of its latency, which usually
281253a5a1b3Sopenharmony_ci                 * isn't possible, so there will probably be some gap
281353a5a1b3Sopenharmony_ci                 * before the moved stream becomes audible. We then
281453a5a1b3Sopenharmony_ci                 * have two possibilities: 1) start playing the stream
281553a5a1b3Sopenharmony_ci                 * from where it is now, or 2) drop the unrewindable
281653a5a1b3Sopenharmony_ci                 * latency of the sink from the stream. With option 1
281753a5a1b3Sopenharmony_ci                 * we won't lose any audio but the stream will have a
281853a5a1b3Sopenharmony_ci                 * pause. With option 2 we may lose some audio but the
281953a5a1b3Sopenharmony_ci                 * stream time will be somewhat in sync with the wall
282053a5a1b3Sopenharmony_ci                 * clock. Lennart seems to have chosen option 2 (one
282153a5a1b3Sopenharmony_ci                 * of the reasons might have been that option 1 is
282253a5a1b3Sopenharmony_ci                 * actually much harder to implement), so we drop the
282353a5a1b3Sopenharmony_ci                 * latency of the new sink from the moved stream and
282453a5a1b3Sopenharmony_ci                 * hope that the sink will undo most of that in the
282553a5a1b3Sopenharmony_ci                 * rewind. */
282653a5a1b3Sopenharmony_ci
282753a5a1b3Sopenharmony_ci                /* Get the latency of the sink */
282853a5a1b3Sopenharmony_ci                usec = pa_sink_get_latency_within_thread(s, false);
282953a5a1b3Sopenharmony_ci                nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
283053a5a1b3Sopenharmony_ci
283153a5a1b3Sopenharmony_ci                /* Calculate number of samples that have been played during the move */
283253a5a1b3Sopenharmony_ci                delay_bytes = 0;
283353a5a1b3Sopenharmony_ci                if (i->thread_info.move_start_time > 0) {
283453a5a1b3Sopenharmony_ci                    usec = pa_rtclock_now() - i->thread_info.move_start_time;
283553a5a1b3Sopenharmony_ci                    delay_bytes = pa_usec_to_bytes(usec, &s->sample_spec);
283653a5a1b3Sopenharmony_ci                }
283753a5a1b3Sopenharmony_ci
283853a5a1b3Sopenharmony_ci                /* max_rewind must be updated for the sink input because otherwise
283953a5a1b3Sopenharmony_ci                 * the data in the render memblockq will get lost */
284053a5a1b3Sopenharmony_ci                pa_sink_input_update_max_rewind(i, nbytes);
284153a5a1b3Sopenharmony_ci
284253a5a1b3Sopenharmony_ci                if (nbytes + delay_bytes > 0)
284353a5a1b3Sopenharmony_ci                    pa_sink_input_drop(i, nbytes + delay_bytes);
284453a5a1b3Sopenharmony_ci
284553a5a1b3Sopenharmony_ci                pa_log_debug("Requesting rewind due to finished move");
284653a5a1b3Sopenharmony_ci                pa_sink_request_rewind(s, nbytes);
284753a5a1b3Sopenharmony_ci            }
284853a5a1b3Sopenharmony_ci
284953a5a1b3Sopenharmony_ci            /* Updating the requested sink latency has to be done
285053a5a1b3Sopenharmony_ci             * after the sink rewind request, not before, because
285153a5a1b3Sopenharmony_ci             * otherwise the sink may limit the rewind amount
285253a5a1b3Sopenharmony_ci             * needlessly. */
285353a5a1b3Sopenharmony_ci
285453a5a1b3Sopenharmony_ci            if (i->thread_info.requested_sink_latency != (pa_usec_t) -1)
285553a5a1b3Sopenharmony_ci                pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
285653a5a1b3Sopenharmony_ci
285753a5a1b3Sopenharmony_ci            pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
285853a5a1b3Sopenharmony_ci            pa_sink_input_update_max_request(i, s->thread_info.max_request);
285953a5a1b3Sopenharmony_ci
286053a5a1b3Sopenharmony_ci            /* Reset move variables */
286153a5a1b3Sopenharmony_ci            i->thread_info.move_start_time = 0;
286253a5a1b3Sopenharmony_ci            i->thread_info.resampler_delay_frames = 0;
286353a5a1b3Sopenharmony_ci            i->thread_info.origin_sink_latency = 0;
286453a5a1b3Sopenharmony_ci
286553a5a1b3Sopenharmony_ci            return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
286653a5a1b3Sopenharmony_ci        }
286753a5a1b3Sopenharmony_ci
286853a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_SET_SHARED_VOLUME: {
286953a5a1b3Sopenharmony_ci            pa_sink *root_sink = pa_sink_get_master(s);
287053a5a1b3Sopenharmony_ci
287153a5a1b3Sopenharmony_ci            if (PA_LIKELY(root_sink))
287253a5a1b3Sopenharmony_ci                set_shared_volume_within_thread(root_sink);
287353a5a1b3Sopenharmony_ci
287453a5a1b3Sopenharmony_ci            return 0;
287553a5a1b3Sopenharmony_ci        }
287653a5a1b3Sopenharmony_ci
287753a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_SET_VOLUME_SYNCED:
287853a5a1b3Sopenharmony_ci
287953a5a1b3Sopenharmony_ci            if (s->flags & PA_SINK_DEFERRED_VOLUME) {
288053a5a1b3Sopenharmony_ci                s->set_volume(s);
288153a5a1b3Sopenharmony_ci                pa_sink_volume_change_push(s);
288253a5a1b3Sopenharmony_ci            }
288353a5a1b3Sopenharmony_ci            /* Fall through ... */
288453a5a1b3Sopenharmony_ci
288553a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_SET_VOLUME:
288653a5a1b3Sopenharmony_ci
288753a5a1b3Sopenharmony_ci            if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
288853a5a1b3Sopenharmony_ci                s->thread_info.soft_volume = s->soft_volume;
288953a5a1b3Sopenharmony_ci                pa_sink_request_rewind(s, (size_t) -1);
289053a5a1b3Sopenharmony_ci            }
289153a5a1b3Sopenharmony_ci
289253a5a1b3Sopenharmony_ci            /* Fall through ... */
289353a5a1b3Sopenharmony_ci
289453a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_SYNC_VOLUMES:
289553a5a1b3Sopenharmony_ci            sync_input_volumes_within_thread(s);
289653a5a1b3Sopenharmony_ci            return 0;
289753a5a1b3Sopenharmony_ci
289853a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_GET_VOLUME:
289953a5a1b3Sopenharmony_ci
290053a5a1b3Sopenharmony_ci            if ((s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_volume) {
290153a5a1b3Sopenharmony_ci                s->get_volume(s);
290253a5a1b3Sopenharmony_ci                pa_sink_volume_change_flush(s);
290353a5a1b3Sopenharmony_ci                pa_sw_cvolume_divide(&s->thread_info.current_hw_volume, &s->real_volume, &s->soft_volume);
290453a5a1b3Sopenharmony_ci            }
290553a5a1b3Sopenharmony_ci
290653a5a1b3Sopenharmony_ci            /* In case sink implementor reset SW volume. */
290753a5a1b3Sopenharmony_ci            if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
290853a5a1b3Sopenharmony_ci                s->thread_info.soft_volume = s->soft_volume;
290953a5a1b3Sopenharmony_ci                pa_sink_request_rewind(s, (size_t) -1);
291053a5a1b3Sopenharmony_ci            }
291153a5a1b3Sopenharmony_ci
291253a5a1b3Sopenharmony_ci            return 0;
291353a5a1b3Sopenharmony_ci
291453a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_SET_MUTE:
291553a5a1b3Sopenharmony_ci
291653a5a1b3Sopenharmony_ci            if (s->thread_info.soft_muted != s->muted) {
291753a5a1b3Sopenharmony_ci                s->thread_info.soft_muted = s->muted;
291853a5a1b3Sopenharmony_ci                pa_sink_request_rewind(s, (size_t) -1);
291953a5a1b3Sopenharmony_ci            }
292053a5a1b3Sopenharmony_ci
292153a5a1b3Sopenharmony_ci            if (s->flags & PA_SINK_DEFERRED_VOLUME && s->set_mute)
292253a5a1b3Sopenharmony_ci                s->set_mute(s);
292353a5a1b3Sopenharmony_ci
292453a5a1b3Sopenharmony_ci            return 0;
292553a5a1b3Sopenharmony_ci
292653a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_GET_MUTE:
292753a5a1b3Sopenharmony_ci
292853a5a1b3Sopenharmony_ci            if (s->flags & PA_SINK_DEFERRED_VOLUME && s->get_mute)
292953a5a1b3Sopenharmony_ci                return s->get_mute(s, userdata);
293053a5a1b3Sopenharmony_ci
293153a5a1b3Sopenharmony_ci            return 0;
293253a5a1b3Sopenharmony_ci
293353a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_SET_STATE: {
293453a5a1b3Sopenharmony_ci            struct set_state_data *data = userdata;
293553a5a1b3Sopenharmony_ci            bool suspend_change =
293653a5a1b3Sopenharmony_ci                (s->thread_info.state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(data->state)) ||
293753a5a1b3Sopenharmony_ci                (PA_SINK_IS_OPENED(s->thread_info.state) && data->state == PA_SINK_SUSPENDED);
293853a5a1b3Sopenharmony_ci
293953a5a1b3Sopenharmony_ci            if (s->set_state_in_io_thread) {
294053a5a1b3Sopenharmony_ci                int r;
294153a5a1b3Sopenharmony_ci
294253a5a1b3Sopenharmony_ci                if ((r = s->set_state_in_io_thread(s, data->state, data->suspend_cause)) < 0)
294353a5a1b3Sopenharmony_ci                    return r;
294453a5a1b3Sopenharmony_ci            }
294553a5a1b3Sopenharmony_ci
294653a5a1b3Sopenharmony_ci            s->thread_info.state = data->state;
294753a5a1b3Sopenharmony_ci
294853a5a1b3Sopenharmony_ci            if (s->thread_info.state == PA_SINK_SUSPENDED) {
294953a5a1b3Sopenharmony_ci                s->thread_info.rewind_nbytes = 0;
295053a5a1b3Sopenharmony_ci                s->thread_info.rewind_requested = false;
295153a5a1b3Sopenharmony_ci            }
295253a5a1b3Sopenharmony_ci
295353a5a1b3Sopenharmony_ci            if (suspend_change) {
295453a5a1b3Sopenharmony_ci                pa_sink_input *i;
295553a5a1b3Sopenharmony_ci                void *state = NULL;
295653a5a1b3Sopenharmony_ci
295753a5a1b3Sopenharmony_ci                while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
295853a5a1b3Sopenharmony_ci                    if (i->suspend_within_thread)
295953a5a1b3Sopenharmony_ci                        i->suspend_within_thread(i, s->thread_info.state == PA_SINK_SUSPENDED);
296053a5a1b3Sopenharmony_ci            }
296153a5a1b3Sopenharmony_ci
296253a5a1b3Sopenharmony_ci            return 0;
296353a5a1b3Sopenharmony_ci        }
296453a5a1b3Sopenharmony_ci
296553a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY: {
296653a5a1b3Sopenharmony_ci
296753a5a1b3Sopenharmony_ci            pa_usec_t *usec = userdata;
296853a5a1b3Sopenharmony_ci            *usec = pa_sink_get_requested_latency_within_thread(s);
296953a5a1b3Sopenharmony_ci
297053a5a1b3Sopenharmony_ci            /* Yes, that's right, the IO thread will see -1 when no
297153a5a1b3Sopenharmony_ci             * explicit requested latency is configured, the main
297253a5a1b3Sopenharmony_ci             * thread will see max_latency */
297353a5a1b3Sopenharmony_ci            if (*usec == (pa_usec_t) -1)
297453a5a1b3Sopenharmony_ci                *usec = s->thread_info.max_latency;
297553a5a1b3Sopenharmony_ci
297653a5a1b3Sopenharmony_ci            return 0;
297753a5a1b3Sopenharmony_ci        }
297853a5a1b3Sopenharmony_ci
297953a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_SET_LATENCY_RANGE: {
298053a5a1b3Sopenharmony_ci            pa_usec_t *r = userdata;
298153a5a1b3Sopenharmony_ci
298253a5a1b3Sopenharmony_ci            pa_sink_set_latency_range_within_thread(s, r[0], r[1]);
298353a5a1b3Sopenharmony_ci
298453a5a1b3Sopenharmony_ci            return 0;
298553a5a1b3Sopenharmony_ci        }
298653a5a1b3Sopenharmony_ci
298753a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_GET_LATENCY_RANGE: {
298853a5a1b3Sopenharmony_ci            pa_usec_t *r = userdata;
298953a5a1b3Sopenharmony_ci
299053a5a1b3Sopenharmony_ci            r[0] = s->thread_info.min_latency;
299153a5a1b3Sopenharmony_ci            r[1] = s->thread_info.max_latency;
299253a5a1b3Sopenharmony_ci
299353a5a1b3Sopenharmony_ci            return 0;
299453a5a1b3Sopenharmony_ci        }
299553a5a1b3Sopenharmony_ci
299653a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_GET_FIXED_LATENCY:
299753a5a1b3Sopenharmony_ci
299853a5a1b3Sopenharmony_ci            *((pa_usec_t*) userdata) = s->thread_info.fixed_latency;
299953a5a1b3Sopenharmony_ci            return 0;
300053a5a1b3Sopenharmony_ci
300153a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_SET_FIXED_LATENCY:
300253a5a1b3Sopenharmony_ci
300353a5a1b3Sopenharmony_ci            pa_sink_set_fixed_latency_within_thread(s, (pa_usec_t) offset);
300453a5a1b3Sopenharmony_ci            return 0;
300553a5a1b3Sopenharmony_ci
300653a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_GET_MAX_REWIND:
300753a5a1b3Sopenharmony_ci
300853a5a1b3Sopenharmony_ci            *((size_t*) userdata) = s->thread_info.max_rewind;
300953a5a1b3Sopenharmony_ci            return 0;
301053a5a1b3Sopenharmony_ci
301153a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_GET_LAST_REWIND:
301253a5a1b3Sopenharmony_ci
301353a5a1b3Sopenharmony_ci            *((size_t*) userdata) = s->thread_info.last_rewind_nbytes;
301453a5a1b3Sopenharmony_ci            return 0;
301553a5a1b3Sopenharmony_ci
301653a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_GET_MAX_REQUEST:
301753a5a1b3Sopenharmony_ci
301853a5a1b3Sopenharmony_ci            *((size_t*) userdata) = s->thread_info.max_request;
301953a5a1b3Sopenharmony_ci            return 0;
302053a5a1b3Sopenharmony_ci
302153a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_SET_MAX_REWIND:
302253a5a1b3Sopenharmony_ci
302353a5a1b3Sopenharmony_ci            pa_sink_set_max_rewind_within_thread(s, (size_t) offset);
302453a5a1b3Sopenharmony_ci            return 0;
302553a5a1b3Sopenharmony_ci
302653a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_SET_MAX_REQUEST:
302753a5a1b3Sopenharmony_ci
302853a5a1b3Sopenharmony_ci            pa_sink_set_max_request_within_thread(s, (size_t) offset);
302953a5a1b3Sopenharmony_ci            return 0;
303053a5a1b3Sopenharmony_ci
303153a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE:
303253a5a1b3Sopenharmony_ci            /* This message is sent from IO-thread and handled in main thread. */
303353a5a1b3Sopenharmony_ci            pa_assert_ctl_context();
303453a5a1b3Sopenharmony_ci
303553a5a1b3Sopenharmony_ci            /* Make sure we're not messing with main thread when no longer linked */
303653a5a1b3Sopenharmony_ci            if (!PA_SINK_IS_LINKED(s->state))
303753a5a1b3Sopenharmony_ci                return 0;
303853a5a1b3Sopenharmony_ci
303953a5a1b3Sopenharmony_ci            pa_sink_get_volume(s, true);
304053a5a1b3Sopenharmony_ci            pa_sink_get_mute(s, true);
304153a5a1b3Sopenharmony_ci            return 0;
304253a5a1b3Sopenharmony_ci
304353a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_SET_PORT_LATENCY_OFFSET:
304453a5a1b3Sopenharmony_ci            s->thread_info.port_latency_offset = offset;
304553a5a1b3Sopenharmony_ci            return 0;
304653a5a1b3Sopenharmony_ci
304753a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_GET_LATENCY:
304853a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_MAX:
304953a5a1b3Sopenharmony_ci            ;
305053a5a1b3Sopenharmony_ci    }
305153a5a1b3Sopenharmony_ci
305253a5a1b3Sopenharmony_ci    return -1;
305353a5a1b3Sopenharmony_ci}
305453a5a1b3Sopenharmony_ci
305553a5a1b3Sopenharmony_ci/* Called from main thread */
305653a5a1b3Sopenharmony_ciint pa_sink_suspend_all(pa_core *c, bool suspend, pa_suspend_cause_t cause) {
305753a5a1b3Sopenharmony_ci    pa_sink *sink;
305853a5a1b3Sopenharmony_ci    uint32_t idx;
305953a5a1b3Sopenharmony_ci    int ret = 0;
306053a5a1b3Sopenharmony_ci
306153a5a1b3Sopenharmony_ci    pa_core_assert_ref(c);
306253a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
306353a5a1b3Sopenharmony_ci    pa_assert(cause != 0);
306453a5a1b3Sopenharmony_ci
306553a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(sink, c->sinks, idx) {
306653a5a1b3Sopenharmony_ci        int r;
306753a5a1b3Sopenharmony_ci
306853a5a1b3Sopenharmony_ci        if ((r = pa_sink_suspend(sink, suspend, cause)) < 0)
306953a5a1b3Sopenharmony_ci            ret = r;
307053a5a1b3Sopenharmony_ci    }
307153a5a1b3Sopenharmony_ci
307253a5a1b3Sopenharmony_ci    return ret;
307353a5a1b3Sopenharmony_ci}
307453a5a1b3Sopenharmony_ci
307553a5a1b3Sopenharmony_ci/* Called from IO thread */
307653a5a1b3Sopenharmony_civoid pa_sink_detach_within_thread(pa_sink *s) {
307753a5a1b3Sopenharmony_ci    pa_sink_input *i;
307853a5a1b3Sopenharmony_ci    void *state = NULL;
307953a5a1b3Sopenharmony_ci
308053a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
308153a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
308253a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
308353a5a1b3Sopenharmony_ci
308453a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
308553a5a1b3Sopenharmony_ci        pa_sink_input_detach(i);
308653a5a1b3Sopenharmony_ci
308753a5a1b3Sopenharmony_ci    if (s->monitor_source)
308853a5a1b3Sopenharmony_ci        pa_source_detach_within_thread(s->monitor_source);
308953a5a1b3Sopenharmony_ci}
309053a5a1b3Sopenharmony_ci
309153a5a1b3Sopenharmony_ci/* Called from IO thread */
309253a5a1b3Sopenharmony_civoid pa_sink_attach_within_thread(pa_sink *s) {
309353a5a1b3Sopenharmony_ci    pa_sink_input *i;
309453a5a1b3Sopenharmony_ci    void *state = NULL;
309553a5a1b3Sopenharmony_ci
309653a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
309753a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
309853a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
309953a5a1b3Sopenharmony_ci
310053a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
310153a5a1b3Sopenharmony_ci        pa_sink_input_attach(i);
310253a5a1b3Sopenharmony_ci
310353a5a1b3Sopenharmony_ci    if (s->monitor_source)
310453a5a1b3Sopenharmony_ci        pa_source_attach_within_thread(s->monitor_source);
310553a5a1b3Sopenharmony_ci}
310653a5a1b3Sopenharmony_ci
310753a5a1b3Sopenharmony_ci/* Called from IO thread */
310853a5a1b3Sopenharmony_civoid pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
310953a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
311053a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
311153a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
311253a5a1b3Sopenharmony_ci
311353a5a1b3Sopenharmony_ci    if (nbytes == (size_t) -1)
311453a5a1b3Sopenharmony_ci        nbytes = s->thread_info.max_rewind;
311553a5a1b3Sopenharmony_ci
311653a5a1b3Sopenharmony_ci    nbytes = PA_MIN(nbytes, s->thread_info.max_rewind);
311753a5a1b3Sopenharmony_ci
311853a5a1b3Sopenharmony_ci    if (s->thread_info.rewind_requested &&
311953a5a1b3Sopenharmony_ci        nbytes <= s->thread_info.rewind_nbytes)
312053a5a1b3Sopenharmony_ci        return;
312153a5a1b3Sopenharmony_ci
312253a5a1b3Sopenharmony_ci    s->thread_info.rewind_nbytes = nbytes;
312353a5a1b3Sopenharmony_ci    s->thread_info.rewind_requested = true;
312453a5a1b3Sopenharmony_ci
312553a5a1b3Sopenharmony_ci    if (s->request_rewind)
312653a5a1b3Sopenharmony_ci        s->request_rewind(s);
312753a5a1b3Sopenharmony_ci}
312853a5a1b3Sopenharmony_ci
312953a5a1b3Sopenharmony_ci/* Called from IO thread */
313053a5a1b3Sopenharmony_cipa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
313153a5a1b3Sopenharmony_ci    pa_usec_t result = (pa_usec_t) -1;
313253a5a1b3Sopenharmony_ci    pa_sink_input *i;
313353a5a1b3Sopenharmony_ci    void *state = NULL;
313453a5a1b3Sopenharmony_ci    pa_usec_t monitor_latency;
313553a5a1b3Sopenharmony_ci
313653a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
313753a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
313853a5a1b3Sopenharmony_ci
313953a5a1b3Sopenharmony_ci    if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
314053a5a1b3Sopenharmony_ci        return PA_CLAMP(s->thread_info.fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
314153a5a1b3Sopenharmony_ci
314253a5a1b3Sopenharmony_ci    if (s->thread_info.requested_latency_valid)
314353a5a1b3Sopenharmony_ci        return s->thread_info.requested_latency;
314453a5a1b3Sopenharmony_ci
314553a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
314653a5a1b3Sopenharmony_ci        if (i->thread_info.requested_sink_latency != (pa_usec_t) -1 &&
314753a5a1b3Sopenharmony_ci            (result == (pa_usec_t) -1 || result > i->thread_info.requested_sink_latency))
314853a5a1b3Sopenharmony_ci            result = i->thread_info.requested_sink_latency;
314953a5a1b3Sopenharmony_ci
315053a5a1b3Sopenharmony_ci    monitor_latency = pa_source_get_requested_latency_within_thread(s->monitor_source);
315153a5a1b3Sopenharmony_ci
315253a5a1b3Sopenharmony_ci    if (monitor_latency != (pa_usec_t) -1 &&
315353a5a1b3Sopenharmony_ci        (result == (pa_usec_t) -1 || result > monitor_latency))
315453a5a1b3Sopenharmony_ci        result = monitor_latency;
315553a5a1b3Sopenharmony_ci
315653a5a1b3Sopenharmony_ci    if (result != (pa_usec_t) -1)
315753a5a1b3Sopenharmony_ci        result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
315853a5a1b3Sopenharmony_ci
315953a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
316053a5a1b3Sopenharmony_ci        /* Only cache if properly initialized */
316153a5a1b3Sopenharmony_ci        s->thread_info.requested_latency = result;
316253a5a1b3Sopenharmony_ci        s->thread_info.requested_latency_valid = true;
316353a5a1b3Sopenharmony_ci    }
316453a5a1b3Sopenharmony_ci
316553a5a1b3Sopenharmony_ci    return result;
316653a5a1b3Sopenharmony_ci}
316753a5a1b3Sopenharmony_ci
316853a5a1b3Sopenharmony_ci/* Called from main thread */
316953a5a1b3Sopenharmony_cipa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
317053a5a1b3Sopenharmony_ci    pa_usec_t usec = 0;
317153a5a1b3Sopenharmony_ci
317253a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
317353a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
317453a5a1b3Sopenharmony_ci    pa_assert(PA_SINK_IS_LINKED(s->state));
317553a5a1b3Sopenharmony_ci
317653a5a1b3Sopenharmony_ci    if (s->state == PA_SINK_SUSPENDED)
317753a5a1b3Sopenharmony_ci        return 0;
317853a5a1b3Sopenharmony_ci
317953a5a1b3Sopenharmony_ci    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
318053a5a1b3Sopenharmony_ci
318153a5a1b3Sopenharmony_ci    return usec;
318253a5a1b3Sopenharmony_ci}
318353a5a1b3Sopenharmony_ci
318453a5a1b3Sopenharmony_ci/* Called from IO as well as the main thread -- the latter only before the IO thread started up */
318553a5a1b3Sopenharmony_civoid pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind) {
318653a5a1b3Sopenharmony_ci    pa_sink_input *i;
318753a5a1b3Sopenharmony_ci    void *state = NULL;
318853a5a1b3Sopenharmony_ci
318953a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
319053a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
319153a5a1b3Sopenharmony_ci
319253a5a1b3Sopenharmony_ci    max_rewind = get_max_rewind_limit(s, max_rewind);
319353a5a1b3Sopenharmony_ci
319453a5a1b3Sopenharmony_ci    if (max_rewind == s->thread_info.max_rewind)
319553a5a1b3Sopenharmony_ci        return;
319653a5a1b3Sopenharmony_ci
319753a5a1b3Sopenharmony_ci    s->thread_info.max_rewind = max_rewind;
319853a5a1b3Sopenharmony_ci
319953a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->thread_info.state))
320053a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
320153a5a1b3Sopenharmony_ci            pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
320253a5a1b3Sopenharmony_ci
320353a5a1b3Sopenharmony_ci    if (s->monitor_source)
320453a5a1b3Sopenharmony_ci        pa_source_set_max_rewind_within_thread(s->monitor_source, s->thread_info.max_rewind);
320553a5a1b3Sopenharmony_ci}
320653a5a1b3Sopenharmony_ci
320753a5a1b3Sopenharmony_ci/* Called from main thread */
320853a5a1b3Sopenharmony_civoid pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
320953a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
321053a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
321153a5a1b3Sopenharmony_ci
321253a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->state))
321353a5a1b3Sopenharmony_ci        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REWIND, NULL, max_rewind, NULL) == 0);
321453a5a1b3Sopenharmony_ci    else
321553a5a1b3Sopenharmony_ci        pa_sink_set_max_rewind_within_thread(s, max_rewind);
321653a5a1b3Sopenharmony_ci}
321753a5a1b3Sopenharmony_ci
321853a5a1b3Sopenharmony_ci/* Called from IO as well as the main thread -- the latter only before the IO thread started up */
321953a5a1b3Sopenharmony_civoid pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request) {
322053a5a1b3Sopenharmony_ci    void *state = NULL;
322153a5a1b3Sopenharmony_ci
322253a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
322353a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
322453a5a1b3Sopenharmony_ci
322553a5a1b3Sopenharmony_ci    if (max_request == s->thread_info.max_request)
322653a5a1b3Sopenharmony_ci        return;
322753a5a1b3Sopenharmony_ci
322853a5a1b3Sopenharmony_ci    s->thread_info.max_request = max_request;
322953a5a1b3Sopenharmony_ci
323053a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
323153a5a1b3Sopenharmony_ci        pa_sink_input *i;
323253a5a1b3Sopenharmony_ci
323353a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
323453a5a1b3Sopenharmony_ci            pa_sink_input_update_max_request(i, s->thread_info.max_request);
323553a5a1b3Sopenharmony_ci    }
323653a5a1b3Sopenharmony_ci}
323753a5a1b3Sopenharmony_ci
323853a5a1b3Sopenharmony_ci/* Called from main thread */
323953a5a1b3Sopenharmony_civoid pa_sink_set_max_request(pa_sink *s, size_t max_request) {
324053a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
324153a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
324253a5a1b3Sopenharmony_ci
324353a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->state))
324453a5a1b3Sopenharmony_ci        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REQUEST, NULL, max_request, NULL) == 0);
324553a5a1b3Sopenharmony_ci    else
324653a5a1b3Sopenharmony_ci        pa_sink_set_max_request_within_thread(s, max_request);
324753a5a1b3Sopenharmony_ci}
324853a5a1b3Sopenharmony_ci
324953a5a1b3Sopenharmony_ci/* Called from IO thread */
325053a5a1b3Sopenharmony_civoid pa_sink_invalidate_requested_latency(pa_sink *s, bool dynamic) {
325153a5a1b3Sopenharmony_ci    pa_sink_input *i;
325253a5a1b3Sopenharmony_ci    void *state = NULL;
325353a5a1b3Sopenharmony_ci
325453a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
325553a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
325653a5a1b3Sopenharmony_ci
325753a5a1b3Sopenharmony_ci    if ((s->flags & PA_SINK_DYNAMIC_LATENCY))
325853a5a1b3Sopenharmony_ci        s->thread_info.requested_latency_valid = false;
325953a5a1b3Sopenharmony_ci    else if (dynamic)
326053a5a1b3Sopenharmony_ci        return;
326153a5a1b3Sopenharmony_ci
326253a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
326353a5a1b3Sopenharmony_ci
326453a5a1b3Sopenharmony_ci        if (s->update_requested_latency)
326553a5a1b3Sopenharmony_ci            s->update_requested_latency(s);
326653a5a1b3Sopenharmony_ci
326753a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
326853a5a1b3Sopenharmony_ci            if (i->update_sink_requested_latency)
326953a5a1b3Sopenharmony_ci                i->update_sink_requested_latency(i);
327053a5a1b3Sopenharmony_ci    }
327153a5a1b3Sopenharmony_ci}
327253a5a1b3Sopenharmony_ci
327353a5a1b3Sopenharmony_ci/* Called from main thread */
327453a5a1b3Sopenharmony_civoid pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
327553a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
327653a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
327753a5a1b3Sopenharmony_ci
327853a5a1b3Sopenharmony_ci    /* min_latency == 0:           no limit
327953a5a1b3Sopenharmony_ci     * min_latency anything else:  specified limit
328053a5a1b3Sopenharmony_ci     *
328153a5a1b3Sopenharmony_ci     * Similar for max_latency */
328253a5a1b3Sopenharmony_ci
328353a5a1b3Sopenharmony_ci    if (min_latency < ABSOLUTE_MIN_LATENCY)
328453a5a1b3Sopenharmony_ci        min_latency = ABSOLUTE_MIN_LATENCY;
328553a5a1b3Sopenharmony_ci
328653a5a1b3Sopenharmony_ci    if (max_latency <= 0 ||
328753a5a1b3Sopenharmony_ci        max_latency > ABSOLUTE_MAX_LATENCY)
328853a5a1b3Sopenharmony_ci        max_latency = ABSOLUTE_MAX_LATENCY;
328953a5a1b3Sopenharmony_ci
329053a5a1b3Sopenharmony_ci    pa_assert(min_latency <= max_latency);
329153a5a1b3Sopenharmony_ci
329253a5a1b3Sopenharmony_ci    /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
329353a5a1b3Sopenharmony_ci    pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
329453a5a1b3Sopenharmony_ci               max_latency == ABSOLUTE_MAX_LATENCY) ||
329553a5a1b3Sopenharmony_ci              (s->flags & PA_SINK_DYNAMIC_LATENCY));
329653a5a1b3Sopenharmony_ci
329753a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->state)) {
329853a5a1b3Sopenharmony_ci        pa_usec_t r[2];
329953a5a1b3Sopenharmony_ci
330053a5a1b3Sopenharmony_ci        r[0] = min_latency;
330153a5a1b3Sopenharmony_ci        r[1] = max_latency;
330253a5a1b3Sopenharmony_ci
330353a5a1b3Sopenharmony_ci        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
330453a5a1b3Sopenharmony_ci    } else
330553a5a1b3Sopenharmony_ci        pa_sink_set_latency_range_within_thread(s, min_latency, max_latency);
330653a5a1b3Sopenharmony_ci}
330753a5a1b3Sopenharmony_ci
330853a5a1b3Sopenharmony_ci/* Called from main thread */
330953a5a1b3Sopenharmony_civoid pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
331053a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
331153a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
331253a5a1b3Sopenharmony_ci    pa_assert(min_latency);
331353a5a1b3Sopenharmony_ci    pa_assert(max_latency);
331453a5a1b3Sopenharmony_ci
331553a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->state)) {
331653a5a1b3Sopenharmony_ci        pa_usec_t r[2] = { 0, 0 };
331753a5a1b3Sopenharmony_ci
331853a5a1b3Sopenharmony_ci        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
331953a5a1b3Sopenharmony_ci
332053a5a1b3Sopenharmony_ci        *min_latency = r[0];
332153a5a1b3Sopenharmony_ci        *max_latency = r[1];
332253a5a1b3Sopenharmony_ci    } else {
332353a5a1b3Sopenharmony_ci        *min_latency = s->thread_info.min_latency;
332453a5a1b3Sopenharmony_ci        *max_latency = s->thread_info.max_latency;
332553a5a1b3Sopenharmony_ci    }
332653a5a1b3Sopenharmony_ci}
332753a5a1b3Sopenharmony_ci
332853a5a1b3Sopenharmony_ci/* Called from IO thread */
332953a5a1b3Sopenharmony_civoid pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
333053a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
333153a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
333253a5a1b3Sopenharmony_ci
333353a5a1b3Sopenharmony_ci    pa_assert(min_latency >= ABSOLUTE_MIN_LATENCY);
333453a5a1b3Sopenharmony_ci    pa_assert(max_latency <= ABSOLUTE_MAX_LATENCY);
333553a5a1b3Sopenharmony_ci    pa_assert(min_latency <= max_latency);
333653a5a1b3Sopenharmony_ci
333753a5a1b3Sopenharmony_ci    /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
333853a5a1b3Sopenharmony_ci    pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
333953a5a1b3Sopenharmony_ci               max_latency == ABSOLUTE_MAX_LATENCY) ||
334053a5a1b3Sopenharmony_ci              (s->flags & PA_SINK_DYNAMIC_LATENCY));
334153a5a1b3Sopenharmony_ci
334253a5a1b3Sopenharmony_ci    if (s->thread_info.min_latency == min_latency &&
334353a5a1b3Sopenharmony_ci        s->thread_info.max_latency == max_latency)
334453a5a1b3Sopenharmony_ci        return;
334553a5a1b3Sopenharmony_ci
334653a5a1b3Sopenharmony_ci    s->thread_info.min_latency = min_latency;
334753a5a1b3Sopenharmony_ci    s->thread_info.max_latency = max_latency;
334853a5a1b3Sopenharmony_ci
334953a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
335053a5a1b3Sopenharmony_ci        pa_sink_input *i;
335153a5a1b3Sopenharmony_ci        void *state = NULL;
335253a5a1b3Sopenharmony_ci
335353a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
335453a5a1b3Sopenharmony_ci            if (i->update_sink_latency_range)
335553a5a1b3Sopenharmony_ci                i->update_sink_latency_range(i);
335653a5a1b3Sopenharmony_ci    }
335753a5a1b3Sopenharmony_ci
335853a5a1b3Sopenharmony_ci    pa_sink_invalidate_requested_latency(s, false);
335953a5a1b3Sopenharmony_ci
336053a5a1b3Sopenharmony_ci    pa_source_set_latency_range_within_thread(s->monitor_source, min_latency, max_latency);
336153a5a1b3Sopenharmony_ci}
336253a5a1b3Sopenharmony_ci
336353a5a1b3Sopenharmony_ci/* Called from main thread */
336453a5a1b3Sopenharmony_civoid pa_sink_set_fixed_latency(pa_sink *s, pa_usec_t latency) {
336553a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
336653a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
336753a5a1b3Sopenharmony_ci
336853a5a1b3Sopenharmony_ci    if (s->flags & PA_SINK_DYNAMIC_LATENCY) {
336953a5a1b3Sopenharmony_ci        //pa_assert(latency == 0);
337053a5a1b3Sopenharmony_ci        return;
337153a5a1b3Sopenharmony_ci    }
337253a5a1b3Sopenharmony_ci
337353a5a1b3Sopenharmony_ci    if (latency < ABSOLUTE_MIN_LATENCY)
337453a5a1b3Sopenharmony_ci        latency = ABSOLUTE_MIN_LATENCY;
337553a5a1b3Sopenharmony_ci
337653a5a1b3Sopenharmony_ci    if (latency > ABSOLUTE_MAX_LATENCY)
337753a5a1b3Sopenharmony_ci        latency = ABSOLUTE_MAX_LATENCY;
337853a5a1b3Sopenharmony_ci
337953a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->state))
338053a5a1b3Sopenharmony_ci        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_FIXED_LATENCY, NULL, (int64_t) latency, NULL) == 0);
338153a5a1b3Sopenharmony_ci    else
338253a5a1b3Sopenharmony_ci        s->thread_info.fixed_latency = latency;
338353a5a1b3Sopenharmony_ci
338453a5a1b3Sopenharmony_ci    pa_source_set_fixed_latency(s->monitor_source, latency);
338553a5a1b3Sopenharmony_ci}
338653a5a1b3Sopenharmony_ci
338753a5a1b3Sopenharmony_ci/* Called from main thread */
338853a5a1b3Sopenharmony_cipa_usec_t pa_sink_get_fixed_latency(pa_sink *s) {
338953a5a1b3Sopenharmony_ci    pa_usec_t latency;
339053a5a1b3Sopenharmony_ci
339153a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
339253a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
339353a5a1b3Sopenharmony_ci
339453a5a1b3Sopenharmony_ci    if (s->flags & PA_SINK_DYNAMIC_LATENCY)
339553a5a1b3Sopenharmony_ci        return 0;
339653a5a1b3Sopenharmony_ci
339753a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->state))
339853a5a1b3Sopenharmony_ci        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_FIXED_LATENCY, &latency, 0, NULL) == 0);
339953a5a1b3Sopenharmony_ci    else
340053a5a1b3Sopenharmony_ci        latency = s->thread_info.fixed_latency;
340153a5a1b3Sopenharmony_ci
340253a5a1b3Sopenharmony_ci    return latency;
340353a5a1b3Sopenharmony_ci}
340453a5a1b3Sopenharmony_ci
340553a5a1b3Sopenharmony_ci/* Called from IO thread */
340653a5a1b3Sopenharmony_civoid pa_sink_set_fixed_latency_within_thread(pa_sink *s, pa_usec_t latency) {
340753a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
340853a5a1b3Sopenharmony_ci    pa_sink_assert_io_context(s);
340953a5a1b3Sopenharmony_ci
341053a5a1b3Sopenharmony_ci    if (s->flags & PA_SINK_DYNAMIC_LATENCY) {
341153a5a1b3Sopenharmony_ci        pa_assert(latency == 0);
341253a5a1b3Sopenharmony_ci        s->thread_info.fixed_latency = 0;
341353a5a1b3Sopenharmony_ci
341453a5a1b3Sopenharmony_ci        if (s->monitor_source)
341553a5a1b3Sopenharmony_ci            pa_source_set_fixed_latency_within_thread(s->monitor_source, 0);
341653a5a1b3Sopenharmony_ci
341753a5a1b3Sopenharmony_ci        return;
341853a5a1b3Sopenharmony_ci    }
341953a5a1b3Sopenharmony_ci
342053a5a1b3Sopenharmony_ci    pa_assert(latency >= ABSOLUTE_MIN_LATENCY);
342153a5a1b3Sopenharmony_ci    pa_assert(latency <= ABSOLUTE_MAX_LATENCY);
342253a5a1b3Sopenharmony_ci
342353a5a1b3Sopenharmony_ci    if (s->thread_info.fixed_latency == latency)
342453a5a1b3Sopenharmony_ci        return;
342553a5a1b3Sopenharmony_ci
342653a5a1b3Sopenharmony_ci    s->thread_info.fixed_latency = latency;
342753a5a1b3Sopenharmony_ci
342853a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
342953a5a1b3Sopenharmony_ci        pa_sink_input *i;
343053a5a1b3Sopenharmony_ci        void *state = NULL;
343153a5a1b3Sopenharmony_ci
343253a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
343353a5a1b3Sopenharmony_ci            if (i->update_sink_fixed_latency)
343453a5a1b3Sopenharmony_ci                i->update_sink_fixed_latency(i);
343553a5a1b3Sopenharmony_ci    }
343653a5a1b3Sopenharmony_ci
343753a5a1b3Sopenharmony_ci    pa_sink_invalidate_requested_latency(s, false);
343853a5a1b3Sopenharmony_ci
343953a5a1b3Sopenharmony_ci    pa_source_set_fixed_latency_within_thread(s->monitor_source, latency);
344053a5a1b3Sopenharmony_ci}
344153a5a1b3Sopenharmony_ci
344253a5a1b3Sopenharmony_ci/* Called from main context */
344353a5a1b3Sopenharmony_civoid pa_sink_set_port_latency_offset(pa_sink *s, int64_t offset) {
344453a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
344553a5a1b3Sopenharmony_ci
344653a5a1b3Sopenharmony_ci    s->port_latency_offset = offset;
344753a5a1b3Sopenharmony_ci
344853a5a1b3Sopenharmony_ci    if (PA_SINK_IS_LINKED(s->state))
344953a5a1b3Sopenharmony_ci        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT_LATENCY_OFFSET, NULL, offset, NULL) == 0);
345053a5a1b3Sopenharmony_ci    else
345153a5a1b3Sopenharmony_ci        s->thread_info.port_latency_offset = offset;
345253a5a1b3Sopenharmony_ci
345353a5a1b3Sopenharmony_ci    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PORT_LATENCY_OFFSET_CHANGED], s);
345453a5a1b3Sopenharmony_ci}
345553a5a1b3Sopenharmony_ci
345653a5a1b3Sopenharmony_ci/* Called from main context */
345753a5a1b3Sopenharmony_cisize_t pa_sink_get_max_rewind(pa_sink *s) {
345853a5a1b3Sopenharmony_ci    size_t r;
345953a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
346053a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
346153a5a1b3Sopenharmony_ci
346253a5a1b3Sopenharmony_ci    if (!PA_SINK_IS_LINKED(s->state))
346353a5a1b3Sopenharmony_ci        return s->thread_info.max_rewind;
346453a5a1b3Sopenharmony_ci
346553a5a1b3Sopenharmony_ci    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
346653a5a1b3Sopenharmony_ci
346753a5a1b3Sopenharmony_ci    return r;
346853a5a1b3Sopenharmony_ci}
346953a5a1b3Sopenharmony_ci
347053a5a1b3Sopenharmony_ci/* Called from main context */
347153a5a1b3Sopenharmony_cisize_t pa_sink_get_max_request(pa_sink *s) {
347253a5a1b3Sopenharmony_ci    size_t r;
347353a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
347453a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
347553a5a1b3Sopenharmony_ci
347653a5a1b3Sopenharmony_ci    if (!PA_SINK_IS_LINKED(s->state))
347753a5a1b3Sopenharmony_ci        return s->thread_info.max_request;
347853a5a1b3Sopenharmony_ci
347953a5a1b3Sopenharmony_ci    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REQUEST, &r, 0, NULL) == 0);
348053a5a1b3Sopenharmony_ci
348153a5a1b3Sopenharmony_ci    return r;
348253a5a1b3Sopenharmony_ci}
348353a5a1b3Sopenharmony_ci
348453a5a1b3Sopenharmony_ci/* Called from main context */
348553a5a1b3Sopenharmony_ciint pa_sink_set_port(pa_sink *s, const char *name, bool save) {
348653a5a1b3Sopenharmony_ci    pa_device_port *port;
348753a5a1b3Sopenharmony_ci
348853a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
348953a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
349053a5a1b3Sopenharmony_ci
349153a5a1b3Sopenharmony_ci    if (!s->set_port) {
349253a5a1b3Sopenharmony_ci        pa_log_debug("set_port() operation not implemented for sink %u \"%s\"", s->index, s->name);
349353a5a1b3Sopenharmony_ci        return -PA_ERR_NOTIMPLEMENTED;
349453a5a1b3Sopenharmony_ci    }
349553a5a1b3Sopenharmony_ci
349653a5a1b3Sopenharmony_ci    if (!name)
349753a5a1b3Sopenharmony_ci        return -PA_ERR_NOENTITY;
349853a5a1b3Sopenharmony_ci
349953a5a1b3Sopenharmony_ci    if (!(port = pa_hashmap_get(s->ports, name)))
350053a5a1b3Sopenharmony_ci        return -PA_ERR_NOENTITY;
350153a5a1b3Sopenharmony_ci
350253a5a1b3Sopenharmony_ci    if (s->active_port == port) {
350353a5a1b3Sopenharmony_ci        s->save_port = s->save_port || save;
350453a5a1b3Sopenharmony_ci        return 0;
350553a5a1b3Sopenharmony_ci    }
350653a5a1b3Sopenharmony_ci
350753a5a1b3Sopenharmony_ci    s->port_changing = true;
350853a5a1b3Sopenharmony_ci
350953a5a1b3Sopenharmony_ci    if (s->set_port(s, port) < 0)
351053a5a1b3Sopenharmony_ci        return -PA_ERR_NOENTITY;
351153a5a1b3Sopenharmony_ci
351253a5a1b3Sopenharmony_ci    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
351353a5a1b3Sopenharmony_ci
351453a5a1b3Sopenharmony_ci    pa_log_info("Changed port of sink %u \"%s\" to %s", s->index, s->name, port->name);
351553a5a1b3Sopenharmony_ci
351653a5a1b3Sopenharmony_ci    s->active_port = port;
351753a5a1b3Sopenharmony_ci    s->save_port = save;
351853a5a1b3Sopenharmony_ci
351953a5a1b3Sopenharmony_ci    pa_sink_set_port_latency_offset(s, s->active_port->latency_offset);
352053a5a1b3Sopenharmony_ci
352153a5a1b3Sopenharmony_ci    /* The active port affects the default sink selection. */
352253a5a1b3Sopenharmony_ci    pa_core_update_default_sink(s->core);
352353a5a1b3Sopenharmony_ci
352453a5a1b3Sopenharmony_ci    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], s);
352553a5a1b3Sopenharmony_ci
352653a5a1b3Sopenharmony_ci    s->port_changing = false;
352753a5a1b3Sopenharmony_ci
352853a5a1b3Sopenharmony_ci    return 0;
352953a5a1b3Sopenharmony_ci}
353053a5a1b3Sopenharmony_ci
353153a5a1b3Sopenharmony_cibool pa_device_init_icon(pa_proplist *p, bool is_sink) {
353253a5a1b3Sopenharmony_ci    const char *ff, *c, *t = NULL, *s = "", *profile, *bus;
353353a5a1b3Sopenharmony_ci
353453a5a1b3Sopenharmony_ci    pa_assert(p);
353553a5a1b3Sopenharmony_ci
353653a5a1b3Sopenharmony_ci    if (pa_proplist_contains(p, PA_PROP_DEVICE_ICON_NAME))
353753a5a1b3Sopenharmony_ci        return true;
353853a5a1b3Sopenharmony_ci
353953a5a1b3Sopenharmony_ci    if ((ff = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR))) {
354053a5a1b3Sopenharmony_ci
354153a5a1b3Sopenharmony_ci        if (pa_streq(ff, "microphone"))
354253a5a1b3Sopenharmony_ci            t = "audio-input-microphone";
354353a5a1b3Sopenharmony_ci        else if (pa_streq(ff, "webcam"))
354453a5a1b3Sopenharmony_ci            t = "camera-web";
354553a5a1b3Sopenharmony_ci        else if (pa_streq(ff, "computer"))
354653a5a1b3Sopenharmony_ci            t = "computer";
354753a5a1b3Sopenharmony_ci        else if (pa_streq(ff, "handset"))
354853a5a1b3Sopenharmony_ci            t = "phone";
354953a5a1b3Sopenharmony_ci        else if (pa_streq(ff, "portable"))
355053a5a1b3Sopenharmony_ci            t = "multimedia-player";
355153a5a1b3Sopenharmony_ci        else if (pa_streq(ff, "tv"))
355253a5a1b3Sopenharmony_ci            t = "video-display";
355353a5a1b3Sopenharmony_ci
355453a5a1b3Sopenharmony_ci        /*
355553a5a1b3Sopenharmony_ci         * The following icons are not part of the icon naming spec,
355653a5a1b3Sopenharmony_ci         * because Rodney Dawes sucks as the maintainer of that spec.
355753a5a1b3Sopenharmony_ci         *
355853a5a1b3Sopenharmony_ci         * http://lists.freedesktop.org/archives/xdg/2009-May/010397.html
355953a5a1b3Sopenharmony_ci         */
356053a5a1b3Sopenharmony_ci        else if (pa_streq(ff, "headset"))
356153a5a1b3Sopenharmony_ci            t = "audio-headset";
356253a5a1b3Sopenharmony_ci        else if (pa_streq(ff, "headphone"))
356353a5a1b3Sopenharmony_ci            t = "audio-headphones";
356453a5a1b3Sopenharmony_ci        else if (pa_streq(ff, "speaker"))
356553a5a1b3Sopenharmony_ci            t = "audio-speakers";
356653a5a1b3Sopenharmony_ci        else if (pa_streq(ff, "hands-free"))
356753a5a1b3Sopenharmony_ci            t = "audio-handsfree";
356853a5a1b3Sopenharmony_ci    }
356953a5a1b3Sopenharmony_ci
357053a5a1b3Sopenharmony_ci    if (!t)
357153a5a1b3Sopenharmony_ci        if ((c = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
357253a5a1b3Sopenharmony_ci            if (pa_streq(c, "modem"))
357353a5a1b3Sopenharmony_ci                t = "modem";
357453a5a1b3Sopenharmony_ci
357553a5a1b3Sopenharmony_ci    if (!t) {
357653a5a1b3Sopenharmony_ci        if (is_sink)
357753a5a1b3Sopenharmony_ci            t = "audio-card";
357853a5a1b3Sopenharmony_ci        else
357953a5a1b3Sopenharmony_ci            t = "audio-input-microphone";
358053a5a1b3Sopenharmony_ci    }
358153a5a1b3Sopenharmony_ci
358253a5a1b3Sopenharmony_ci    if ((profile = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
358353a5a1b3Sopenharmony_ci        if (strstr(profile, "analog"))
358453a5a1b3Sopenharmony_ci            s = "-analog";
358553a5a1b3Sopenharmony_ci        else if (strstr(profile, "iec958"))
358653a5a1b3Sopenharmony_ci            s = "-iec958";
358753a5a1b3Sopenharmony_ci        else if (strstr(profile, "hdmi"))
358853a5a1b3Sopenharmony_ci            s = "-hdmi";
358953a5a1b3Sopenharmony_ci    }
359053a5a1b3Sopenharmony_ci
359153a5a1b3Sopenharmony_ci    bus = pa_proplist_gets(p, PA_PROP_DEVICE_BUS);
359253a5a1b3Sopenharmony_ci
359353a5a1b3Sopenharmony_ci    pa_proplist_setf(p, PA_PROP_DEVICE_ICON_NAME, "%s%s%s%s", t, pa_strempty(s), bus ? "-" : "", pa_strempty(bus));
359453a5a1b3Sopenharmony_ci
359553a5a1b3Sopenharmony_ci    return true;
359653a5a1b3Sopenharmony_ci}
359753a5a1b3Sopenharmony_ci
359853a5a1b3Sopenharmony_cibool pa_device_init_description(pa_proplist *p, pa_card *card) {
359953a5a1b3Sopenharmony_ci    const char *s, *d = NULL, *k;
360053a5a1b3Sopenharmony_ci    pa_assert(p);
360153a5a1b3Sopenharmony_ci
360253a5a1b3Sopenharmony_ci    if (pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
360353a5a1b3Sopenharmony_ci        return true;
360453a5a1b3Sopenharmony_ci
360553a5a1b3Sopenharmony_ci    if (card)
360653a5a1b3Sopenharmony_ci        if ((s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION)))
360753a5a1b3Sopenharmony_ci            d = s;
360853a5a1b3Sopenharmony_ci
360953a5a1b3Sopenharmony_ci    if (!d)
361053a5a1b3Sopenharmony_ci        if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
361153a5a1b3Sopenharmony_ci            if (pa_streq(s, "internal"))
361253a5a1b3Sopenharmony_ci                d = _("Built-in Audio");
361353a5a1b3Sopenharmony_ci
361453a5a1b3Sopenharmony_ci    if (!d)
361553a5a1b3Sopenharmony_ci        if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
361653a5a1b3Sopenharmony_ci            if (pa_streq(s, "modem"))
361753a5a1b3Sopenharmony_ci                d = _("Modem");
361853a5a1b3Sopenharmony_ci
361953a5a1b3Sopenharmony_ci    if (!d)
362053a5a1b3Sopenharmony_ci        d = pa_proplist_gets(p, PA_PROP_DEVICE_PRODUCT_NAME);
362153a5a1b3Sopenharmony_ci
362253a5a1b3Sopenharmony_ci    if (!d)
362353a5a1b3Sopenharmony_ci        return false;
362453a5a1b3Sopenharmony_ci
362553a5a1b3Sopenharmony_ci    k = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_DESCRIPTION);
362653a5a1b3Sopenharmony_ci
362753a5a1b3Sopenharmony_ci    if (d && k)
362853a5a1b3Sopenharmony_ci        pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, "%s %s", d, k);
362953a5a1b3Sopenharmony_ci    else if (d)
363053a5a1b3Sopenharmony_ci        pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, d);
363153a5a1b3Sopenharmony_ci
363253a5a1b3Sopenharmony_ci    return true;
363353a5a1b3Sopenharmony_ci}
363453a5a1b3Sopenharmony_ci
363553a5a1b3Sopenharmony_cibool pa_device_init_intended_roles(pa_proplist *p) {
363653a5a1b3Sopenharmony_ci    const char *s;
363753a5a1b3Sopenharmony_ci    pa_assert(p);
363853a5a1b3Sopenharmony_ci
363953a5a1b3Sopenharmony_ci    if (pa_proplist_contains(p, PA_PROP_DEVICE_INTENDED_ROLES))
364053a5a1b3Sopenharmony_ci        return true;
364153a5a1b3Sopenharmony_ci
364253a5a1b3Sopenharmony_ci    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
364353a5a1b3Sopenharmony_ci        if (pa_streq(s, "handset") || pa_streq(s, "hands-free")
364453a5a1b3Sopenharmony_ci            || pa_streq(s, "headset")) {
364553a5a1b3Sopenharmony_ci            pa_proplist_sets(p, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
364653a5a1b3Sopenharmony_ci            return true;
364753a5a1b3Sopenharmony_ci        }
364853a5a1b3Sopenharmony_ci
364953a5a1b3Sopenharmony_ci    return false;
365053a5a1b3Sopenharmony_ci}
365153a5a1b3Sopenharmony_ci
365253a5a1b3Sopenharmony_ciunsigned pa_device_init_priority(pa_proplist *p) {
365353a5a1b3Sopenharmony_ci    const char *s;
365453a5a1b3Sopenharmony_ci    unsigned priority = 0;
365553a5a1b3Sopenharmony_ci
365653a5a1b3Sopenharmony_ci    pa_assert(p);
365753a5a1b3Sopenharmony_ci
365853a5a1b3Sopenharmony_ci    /* JACK sinks and sources get very high priority so that we'll switch the
365953a5a1b3Sopenharmony_ci     * default devices automatically when jackd starts and
366053a5a1b3Sopenharmony_ci     * module-jackdbus-detect creates the jack sink and source. */
366153a5a1b3Sopenharmony_ci    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_API))) {
366253a5a1b3Sopenharmony_ci        if (pa_streq(s, "jack"))
366353a5a1b3Sopenharmony_ci            priority += 10000;
366453a5a1b3Sopenharmony_ci    }
366553a5a1b3Sopenharmony_ci
366653a5a1b3Sopenharmony_ci    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS))) {
366753a5a1b3Sopenharmony_ci
366853a5a1b3Sopenharmony_ci        if (pa_streq(s, "sound"))
366953a5a1b3Sopenharmony_ci            priority += 9000;
367053a5a1b3Sopenharmony_ci        else if (!pa_streq(s, "modem"))
367153a5a1b3Sopenharmony_ci            priority += 1000;
367253a5a1b3Sopenharmony_ci    }
367353a5a1b3Sopenharmony_ci
367453a5a1b3Sopenharmony_ci    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR))) {
367553a5a1b3Sopenharmony_ci
367653a5a1b3Sopenharmony_ci        if (pa_streq(s, "headphone"))
367753a5a1b3Sopenharmony_ci            priority += 900;
367853a5a1b3Sopenharmony_ci        else if (pa_streq(s, "hifi"))
367953a5a1b3Sopenharmony_ci            priority += 600;
368053a5a1b3Sopenharmony_ci        else if (pa_streq(s, "speaker"))
368153a5a1b3Sopenharmony_ci            priority += 500;
368253a5a1b3Sopenharmony_ci        else if (pa_streq(s, "portable"))
368353a5a1b3Sopenharmony_ci            priority += 450;
368453a5a1b3Sopenharmony_ci    }
368553a5a1b3Sopenharmony_ci
368653a5a1b3Sopenharmony_ci    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_BUS))) {
368753a5a1b3Sopenharmony_ci
368853a5a1b3Sopenharmony_ci        if (pa_streq(s, "bluetooth"))
368953a5a1b3Sopenharmony_ci            priority += 50;
369053a5a1b3Sopenharmony_ci        else if (pa_streq(s, "usb"))
369153a5a1b3Sopenharmony_ci            priority += 40;
369253a5a1b3Sopenharmony_ci        else if (pa_streq(s, "pci"))
369353a5a1b3Sopenharmony_ci            priority += 30;
369453a5a1b3Sopenharmony_ci    }
369553a5a1b3Sopenharmony_ci
369653a5a1b3Sopenharmony_ci    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
369753a5a1b3Sopenharmony_ci
369853a5a1b3Sopenharmony_ci        if (pa_startswith(s, "analog-")) {
369953a5a1b3Sopenharmony_ci            priority += 9;
370053a5a1b3Sopenharmony_ci
370153a5a1b3Sopenharmony_ci            /* If an analog device has an intended role of "phone", it probably
370253a5a1b3Sopenharmony_ci             * co-exists with another device that is meant for everything else,
370353a5a1b3Sopenharmony_ci             * and that other device should have higher priority than the phone
370453a5a1b3Sopenharmony_ci             * device. */
370553a5a1b3Sopenharmony_ci            if (pa_str_in_list_spaces(pa_proplist_gets(p, PA_PROP_DEVICE_INTENDED_ROLES), "phone"))
370653a5a1b3Sopenharmony_ci                priority -= 1;
370753a5a1b3Sopenharmony_ci        }
370853a5a1b3Sopenharmony_ci        else if (pa_startswith(s, "iec958-"))
370953a5a1b3Sopenharmony_ci            priority += 7;
371053a5a1b3Sopenharmony_ci    }
371153a5a1b3Sopenharmony_ci
371253a5a1b3Sopenharmony_ci    return priority;
371353a5a1b3Sopenharmony_ci}
371453a5a1b3Sopenharmony_ci
371553a5a1b3Sopenharmony_ciPA_STATIC_FLIST_DECLARE(pa_sink_volume_change, 0, pa_xfree);
371653a5a1b3Sopenharmony_ci
371753a5a1b3Sopenharmony_ci/* Called from the IO thread. */
371853a5a1b3Sopenharmony_cistatic pa_sink_volume_change *pa_sink_volume_change_new(pa_sink *s) {
371953a5a1b3Sopenharmony_ci    pa_sink_volume_change *c;
372053a5a1b3Sopenharmony_ci    if (!(c = pa_flist_pop(PA_STATIC_FLIST_GET(pa_sink_volume_change))))
372153a5a1b3Sopenharmony_ci        c = pa_xnew(pa_sink_volume_change, 1);
372253a5a1b3Sopenharmony_ci
372353a5a1b3Sopenharmony_ci    PA_LLIST_INIT(pa_sink_volume_change, c);
372453a5a1b3Sopenharmony_ci    c->at = 0;
372553a5a1b3Sopenharmony_ci    pa_cvolume_reset(&c->hw_volume, s->sample_spec.channels);
372653a5a1b3Sopenharmony_ci    return c;
372753a5a1b3Sopenharmony_ci}
372853a5a1b3Sopenharmony_ci
372953a5a1b3Sopenharmony_ci/* Called from the IO thread. */
373053a5a1b3Sopenharmony_cistatic void pa_sink_volume_change_free(pa_sink_volume_change *c) {
373153a5a1b3Sopenharmony_ci    pa_assert(c);
373253a5a1b3Sopenharmony_ci    if (pa_flist_push(PA_STATIC_FLIST_GET(pa_sink_volume_change), c) < 0)
373353a5a1b3Sopenharmony_ci        pa_xfree(c);
373453a5a1b3Sopenharmony_ci}
373553a5a1b3Sopenharmony_ci
373653a5a1b3Sopenharmony_ci/* Called from the IO thread. */
373753a5a1b3Sopenharmony_civoid pa_sink_volume_change_push(pa_sink *s) {
373853a5a1b3Sopenharmony_ci    pa_sink_volume_change *c = NULL;
373953a5a1b3Sopenharmony_ci    pa_sink_volume_change *nc = NULL;
374053a5a1b3Sopenharmony_ci    pa_sink_volume_change *pc = NULL;
374153a5a1b3Sopenharmony_ci    uint32_t safety_margin = s->thread_info.volume_change_safety_margin;
374253a5a1b3Sopenharmony_ci
374353a5a1b3Sopenharmony_ci    const char *direction = NULL;
374453a5a1b3Sopenharmony_ci
374553a5a1b3Sopenharmony_ci    pa_assert(s);
374653a5a1b3Sopenharmony_ci    nc = pa_sink_volume_change_new(s);
374753a5a1b3Sopenharmony_ci
374853a5a1b3Sopenharmony_ci    /* NOTE: There is already more different volumes in pa_sink that I can remember.
374953a5a1b3Sopenharmony_ci     *       Adding one more volume for HW would get us rid of this, but I am trying
375053a5a1b3Sopenharmony_ci     *       to survive with the ones we already have. */
375153a5a1b3Sopenharmony_ci    pa_sw_cvolume_divide(&nc->hw_volume, &s->real_volume, &s->soft_volume);
375253a5a1b3Sopenharmony_ci
375353a5a1b3Sopenharmony_ci    if (!s->thread_info.volume_changes && pa_cvolume_equal(&nc->hw_volume, &s->thread_info.current_hw_volume)) {
375453a5a1b3Sopenharmony_ci        pa_log_debug("Volume not changing");
375553a5a1b3Sopenharmony_ci        pa_sink_volume_change_free(nc);
375653a5a1b3Sopenharmony_ci        return;
375753a5a1b3Sopenharmony_ci    }
375853a5a1b3Sopenharmony_ci
375953a5a1b3Sopenharmony_ci    nc->at = pa_sink_get_latency_within_thread(s, false);
376053a5a1b3Sopenharmony_ci    nc->at += pa_rtclock_now() + s->thread_info.volume_change_extra_delay;
376153a5a1b3Sopenharmony_ci
376253a5a1b3Sopenharmony_ci    if (s->thread_info.volume_changes_tail) {
376353a5a1b3Sopenharmony_ci        for (c = s->thread_info.volume_changes_tail; c; c = c->prev) {
376453a5a1b3Sopenharmony_ci            /* If volume is going up let's do it a bit late. If it is going
376553a5a1b3Sopenharmony_ci             * down let's do it a bit early. */
376653a5a1b3Sopenharmony_ci            if (pa_cvolume_avg(&nc->hw_volume) > pa_cvolume_avg(&c->hw_volume)) {
376753a5a1b3Sopenharmony_ci                if (nc->at + safety_margin > c->at) {
376853a5a1b3Sopenharmony_ci                    nc->at += safety_margin;
376953a5a1b3Sopenharmony_ci                    direction = "up";
377053a5a1b3Sopenharmony_ci                    break;
377153a5a1b3Sopenharmony_ci                }
377253a5a1b3Sopenharmony_ci            }
377353a5a1b3Sopenharmony_ci            else if (nc->at - safety_margin > c->at) {
377453a5a1b3Sopenharmony_ci                    nc->at -= safety_margin;
377553a5a1b3Sopenharmony_ci                    direction = "down";
377653a5a1b3Sopenharmony_ci                    break;
377753a5a1b3Sopenharmony_ci            }
377853a5a1b3Sopenharmony_ci        }
377953a5a1b3Sopenharmony_ci    }
378053a5a1b3Sopenharmony_ci
378153a5a1b3Sopenharmony_ci    if (c == NULL) {
378253a5a1b3Sopenharmony_ci        if (pa_cvolume_avg(&nc->hw_volume) > pa_cvolume_avg(&s->thread_info.current_hw_volume)) {
378353a5a1b3Sopenharmony_ci            nc->at += safety_margin;
378453a5a1b3Sopenharmony_ci            direction = "up";
378553a5a1b3Sopenharmony_ci        } else {
378653a5a1b3Sopenharmony_ci            nc->at -= safety_margin;
378753a5a1b3Sopenharmony_ci            direction = "down";
378853a5a1b3Sopenharmony_ci        }
378953a5a1b3Sopenharmony_ci        PA_LLIST_PREPEND(pa_sink_volume_change, s->thread_info.volume_changes, nc);
379053a5a1b3Sopenharmony_ci    }
379153a5a1b3Sopenharmony_ci    else {
379253a5a1b3Sopenharmony_ci        PA_LLIST_INSERT_AFTER(pa_sink_volume_change, s->thread_info.volume_changes, c, nc);
379353a5a1b3Sopenharmony_ci    }
379453a5a1b3Sopenharmony_ci
379553a5a1b3Sopenharmony_ci    pa_log_debug("Volume going %s to %d at %llu", direction, pa_cvolume_avg(&nc->hw_volume), (long long unsigned) nc->at);
379653a5a1b3Sopenharmony_ci
379753a5a1b3Sopenharmony_ci    /* We can ignore volume events that came earlier but should happen later than this. */
379853a5a1b3Sopenharmony_ci    PA_LLIST_FOREACH_SAFE(c, pc, nc->next) {
379953a5a1b3Sopenharmony_ci        pa_log_debug("Volume change to %d at %llu was dropped", pa_cvolume_avg(&c->hw_volume), (long long unsigned) c->at);
380053a5a1b3Sopenharmony_ci        pa_sink_volume_change_free(c);
380153a5a1b3Sopenharmony_ci    }
380253a5a1b3Sopenharmony_ci    nc->next = NULL;
380353a5a1b3Sopenharmony_ci    s->thread_info.volume_changes_tail = nc;
380453a5a1b3Sopenharmony_ci}
380553a5a1b3Sopenharmony_ci
380653a5a1b3Sopenharmony_ci/* Called from the IO thread. */
380753a5a1b3Sopenharmony_cistatic void pa_sink_volume_change_flush(pa_sink *s) {
380853a5a1b3Sopenharmony_ci    pa_sink_volume_change *c = s->thread_info.volume_changes;
380953a5a1b3Sopenharmony_ci    pa_assert(s);
381053a5a1b3Sopenharmony_ci    s->thread_info.volume_changes = NULL;
381153a5a1b3Sopenharmony_ci    s->thread_info.volume_changes_tail = NULL;
381253a5a1b3Sopenharmony_ci    while (c) {
381353a5a1b3Sopenharmony_ci        pa_sink_volume_change *next = c->next;
381453a5a1b3Sopenharmony_ci        pa_sink_volume_change_free(c);
381553a5a1b3Sopenharmony_ci        c = next;
381653a5a1b3Sopenharmony_ci    }
381753a5a1b3Sopenharmony_ci}
381853a5a1b3Sopenharmony_ci
381953a5a1b3Sopenharmony_ci/* Called from the IO thread. */
382053a5a1b3Sopenharmony_cibool pa_sink_volume_change_apply(pa_sink *s, pa_usec_t *usec_to_next) {
382153a5a1b3Sopenharmony_ci    pa_usec_t now;
382253a5a1b3Sopenharmony_ci    bool ret = false;
382353a5a1b3Sopenharmony_ci
382453a5a1b3Sopenharmony_ci    pa_assert(s);
382553a5a1b3Sopenharmony_ci
382653a5a1b3Sopenharmony_ci    if (!s->thread_info.volume_changes || !PA_SINK_IS_LINKED(s->state)) {
382753a5a1b3Sopenharmony_ci        if (usec_to_next)
382853a5a1b3Sopenharmony_ci            *usec_to_next = 0;
382953a5a1b3Sopenharmony_ci        return ret;
383053a5a1b3Sopenharmony_ci    }
383153a5a1b3Sopenharmony_ci
383253a5a1b3Sopenharmony_ci    pa_assert(s->write_volume);
383353a5a1b3Sopenharmony_ci
383453a5a1b3Sopenharmony_ci    now = pa_rtclock_now();
383553a5a1b3Sopenharmony_ci
383653a5a1b3Sopenharmony_ci    while (s->thread_info.volume_changes && now >= s->thread_info.volume_changes->at) {
383753a5a1b3Sopenharmony_ci        pa_sink_volume_change *c = s->thread_info.volume_changes;
383853a5a1b3Sopenharmony_ci        PA_LLIST_REMOVE(pa_sink_volume_change, s->thread_info.volume_changes, c);
383953a5a1b3Sopenharmony_ci        pa_log_debug("Volume change to %d at %llu was written %llu usec late",
384053a5a1b3Sopenharmony_ci                     pa_cvolume_avg(&c->hw_volume), (long long unsigned) c->at, (long long unsigned) (now - c->at));
384153a5a1b3Sopenharmony_ci        ret = true;
384253a5a1b3Sopenharmony_ci        s->thread_info.current_hw_volume = c->hw_volume;
384353a5a1b3Sopenharmony_ci        pa_sink_volume_change_free(c);
384453a5a1b3Sopenharmony_ci    }
384553a5a1b3Sopenharmony_ci
384653a5a1b3Sopenharmony_ci    if (ret)
384753a5a1b3Sopenharmony_ci        s->write_volume(s);
384853a5a1b3Sopenharmony_ci
384953a5a1b3Sopenharmony_ci    if (s->thread_info.volume_changes) {
385053a5a1b3Sopenharmony_ci        if (usec_to_next)
385153a5a1b3Sopenharmony_ci            *usec_to_next = s->thread_info.volume_changes->at - now;
385253a5a1b3Sopenharmony_ci        if (pa_log_ratelimit(PA_LOG_DEBUG))
385353a5a1b3Sopenharmony_ci            pa_log_debug("Next volume change in %lld usec", (long long) (s->thread_info.volume_changes->at - now));
385453a5a1b3Sopenharmony_ci    }
385553a5a1b3Sopenharmony_ci    else {
385653a5a1b3Sopenharmony_ci        if (usec_to_next)
385753a5a1b3Sopenharmony_ci            *usec_to_next = 0;
385853a5a1b3Sopenharmony_ci        s->thread_info.volume_changes_tail = NULL;
385953a5a1b3Sopenharmony_ci    }
386053a5a1b3Sopenharmony_ci    return ret;
386153a5a1b3Sopenharmony_ci}
386253a5a1b3Sopenharmony_ci
386353a5a1b3Sopenharmony_ci/* Called from the IO thread. */
386453a5a1b3Sopenharmony_cistatic void pa_sink_volume_change_rewind(pa_sink *s, size_t nbytes) {
386553a5a1b3Sopenharmony_ci    /* All the queued volume events later than current latency are shifted to happen earlier. */
386653a5a1b3Sopenharmony_ci    pa_sink_volume_change *c;
386753a5a1b3Sopenharmony_ci    pa_volume_t prev_vol = pa_cvolume_avg(&s->thread_info.current_hw_volume);
386853a5a1b3Sopenharmony_ci    pa_usec_t rewound = pa_bytes_to_usec(nbytes, &s->sample_spec);
386953a5a1b3Sopenharmony_ci    pa_usec_t limit = pa_sink_get_latency_within_thread(s, false);
387053a5a1b3Sopenharmony_ci
387153a5a1b3Sopenharmony_ci    pa_log_debug("latency = %lld", (long long) limit);
387253a5a1b3Sopenharmony_ci    limit += pa_rtclock_now() + s->thread_info.volume_change_extra_delay;
387353a5a1b3Sopenharmony_ci
387453a5a1b3Sopenharmony_ci    PA_LLIST_FOREACH(c, s->thread_info.volume_changes) {
387553a5a1b3Sopenharmony_ci        pa_usec_t modified_limit = limit;
387653a5a1b3Sopenharmony_ci        if (prev_vol > pa_cvolume_avg(&c->hw_volume))
387753a5a1b3Sopenharmony_ci            modified_limit -= s->thread_info.volume_change_safety_margin;
387853a5a1b3Sopenharmony_ci        else
387953a5a1b3Sopenharmony_ci            modified_limit += s->thread_info.volume_change_safety_margin;
388053a5a1b3Sopenharmony_ci        if (c->at > modified_limit) {
388153a5a1b3Sopenharmony_ci            c->at -= rewound;
388253a5a1b3Sopenharmony_ci            if (c->at < modified_limit)
388353a5a1b3Sopenharmony_ci                c->at = modified_limit;
388453a5a1b3Sopenharmony_ci        }
388553a5a1b3Sopenharmony_ci        prev_vol = pa_cvolume_avg(&c->hw_volume);
388653a5a1b3Sopenharmony_ci    }
388753a5a1b3Sopenharmony_ci    pa_sink_volume_change_apply(s, NULL);
388853a5a1b3Sopenharmony_ci}
388953a5a1b3Sopenharmony_ci
389053a5a1b3Sopenharmony_ci/* Called from the main thread */
389153a5a1b3Sopenharmony_ci/* Gets the list of formats supported by the sink. The members and idxset must
389253a5a1b3Sopenharmony_ci * be freed by the caller. */
389353a5a1b3Sopenharmony_cipa_idxset* pa_sink_get_formats(pa_sink *s) {
389453a5a1b3Sopenharmony_ci    pa_idxset *ret;
389553a5a1b3Sopenharmony_ci
389653a5a1b3Sopenharmony_ci    pa_assert(s);
389753a5a1b3Sopenharmony_ci
389853a5a1b3Sopenharmony_ci    if (s->get_formats) {
389953a5a1b3Sopenharmony_ci        /* Sink supports format query, all is good */
390053a5a1b3Sopenharmony_ci        ret = s->get_formats(s);
390153a5a1b3Sopenharmony_ci    } else {
390253a5a1b3Sopenharmony_ci        /* Sink doesn't support format query, so assume it does PCM */
390353a5a1b3Sopenharmony_ci        pa_format_info *f = pa_format_info_new();
390453a5a1b3Sopenharmony_ci        f->encoding = PA_ENCODING_PCM;
390553a5a1b3Sopenharmony_ci
390653a5a1b3Sopenharmony_ci        ret = pa_idxset_new(NULL, NULL);
390753a5a1b3Sopenharmony_ci        pa_idxset_put(ret, f, NULL);
390853a5a1b3Sopenharmony_ci    }
390953a5a1b3Sopenharmony_ci
391053a5a1b3Sopenharmony_ci    return ret;
391153a5a1b3Sopenharmony_ci}
391253a5a1b3Sopenharmony_ci
391353a5a1b3Sopenharmony_ci/* Called from the main thread */
391453a5a1b3Sopenharmony_ci/* Allows an external source to set what formats a sink supports if the sink
391553a5a1b3Sopenharmony_ci * permits this. The function makes a copy of the formats on success. */
391653a5a1b3Sopenharmony_cibool pa_sink_set_formats(pa_sink *s, pa_idxset *formats) {
391753a5a1b3Sopenharmony_ci    pa_assert(s);
391853a5a1b3Sopenharmony_ci    pa_assert(formats);
391953a5a1b3Sopenharmony_ci
392053a5a1b3Sopenharmony_ci    if (s->set_formats)
392153a5a1b3Sopenharmony_ci        /* Sink supports setting formats -- let's give it a shot */
392253a5a1b3Sopenharmony_ci        return s->set_formats(s, formats);
392353a5a1b3Sopenharmony_ci    else
392453a5a1b3Sopenharmony_ci        /* Sink doesn't support setting this -- bail out */
392553a5a1b3Sopenharmony_ci        return false;
392653a5a1b3Sopenharmony_ci}
392753a5a1b3Sopenharmony_ci
392853a5a1b3Sopenharmony_ci/* Called from the main thread */
392953a5a1b3Sopenharmony_ci/* Checks if the sink can accept this format */
393053a5a1b3Sopenharmony_cibool pa_sink_check_format(pa_sink *s, pa_format_info *f) {
393153a5a1b3Sopenharmony_ci    pa_idxset *formats = NULL;
393253a5a1b3Sopenharmony_ci    bool ret = false;
393353a5a1b3Sopenharmony_ci
393453a5a1b3Sopenharmony_ci    pa_assert(s);
393553a5a1b3Sopenharmony_ci    pa_assert(f);
393653a5a1b3Sopenharmony_ci
393753a5a1b3Sopenharmony_ci    formats = pa_sink_get_formats(s);
393853a5a1b3Sopenharmony_ci
393953a5a1b3Sopenharmony_ci    if (formats) {
394053a5a1b3Sopenharmony_ci        pa_format_info *finfo_device;
394153a5a1b3Sopenharmony_ci        uint32_t i;
394253a5a1b3Sopenharmony_ci
394353a5a1b3Sopenharmony_ci        PA_IDXSET_FOREACH(finfo_device, formats, i) {
394453a5a1b3Sopenharmony_ci            if (pa_format_info_is_compatible(finfo_device, f)) {
394553a5a1b3Sopenharmony_ci                ret = true;
394653a5a1b3Sopenharmony_ci                break;
394753a5a1b3Sopenharmony_ci            }
394853a5a1b3Sopenharmony_ci        }
394953a5a1b3Sopenharmony_ci
395053a5a1b3Sopenharmony_ci        pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
395153a5a1b3Sopenharmony_ci    }
395253a5a1b3Sopenharmony_ci
395353a5a1b3Sopenharmony_ci    return ret;
395453a5a1b3Sopenharmony_ci}
395553a5a1b3Sopenharmony_ci
395653a5a1b3Sopenharmony_ci/* Called from the main thread */
395753a5a1b3Sopenharmony_ci/* Calculates the intersection between formats supported by the sink and
395853a5a1b3Sopenharmony_ci * in_formats, and returns these, in the order of the sink's formats. */
395953a5a1b3Sopenharmony_cipa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats) {
396053a5a1b3Sopenharmony_ci    pa_idxset *out_formats = pa_idxset_new(NULL, NULL), *sink_formats = NULL;
396153a5a1b3Sopenharmony_ci    pa_format_info *f_sink, *f_in;
396253a5a1b3Sopenharmony_ci    uint32_t i, j;
396353a5a1b3Sopenharmony_ci
396453a5a1b3Sopenharmony_ci    pa_assert(s);
396553a5a1b3Sopenharmony_ci
396653a5a1b3Sopenharmony_ci    if (!in_formats || pa_idxset_isempty(in_formats))
396753a5a1b3Sopenharmony_ci        goto done;
396853a5a1b3Sopenharmony_ci
396953a5a1b3Sopenharmony_ci    sink_formats = pa_sink_get_formats(s);
397053a5a1b3Sopenharmony_ci
397153a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(f_sink, sink_formats, i) {
397253a5a1b3Sopenharmony_ci        PA_IDXSET_FOREACH(f_in, in_formats, j) {
397353a5a1b3Sopenharmony_ci            if (pa_format_info_is_compatible(f_sink, f_in))
397453a5a1b3Sopenharmony_ci                pa_idxset_put(out_formats, pa_format_info_copy(f_in), NULL);
397553a5a1b3Sopenharmony_ci        }
397653a5a1b3Sopenharmony_ci    }
397753a5a1b3Sopenharmony_ci
397853a5a1b3Sopenharmony_cidone:
397953a5a1b3Sopenharmony_ci    if (sink_formats)
398053a5a1b3Sopenharmony_ci        pa_idxset_free(sink_formats, (pa_free_cb_t) pa_format_info_free);
398153a5a1b3Sopenharmony_ci
398253a5a1b3Sopenharmony_ci    return out_formats;
398353a5a1b3Sopenharmony_ci}
398453a5a1b3Sopenharmony_ci
398553a5a1b3Sopenharmony_ci/* Called from the main thread */
398653a5a1b3Sopenharmony_civoid pa_sink_set_sample_format(pa_sink *s, pa_sample_format_t format) {
398753a5a1b3Sopenharmony_ci    pa_sample_format_t old_format;
398853a5a1b3Sopenharmony_ci
398953a5a1b3Sopenharmony_ci    pa_assert(s);
399053a5a1b3Sopenharmony_ci    pa_assert(pa_sample_format_valid(format));
399153a5a1b3Sopenharmony_ci
399253a5a1b3Sopenharmony_ci    old_format = s->sample_spec.format;
399353a5a1b3Sopenharmony_ci    if (old_format == format)
399453a5a1b3Sopenharmony_ci        return;
399553a5a1b3Sopenharmony_ci
399653a5a1b3Sopenharmony_ci    pa_log_info("%s: format: %s -> %s",
399753a5a1b3Sopenharmony_ci                s->name, pa_sample_format_to_string(old_format), pa_sample_format_to_string(format));
399853a5a1b3Sopenharmony_ci
399953a5a1b3Sopenharmony_ci    s->sample_spec.format = format;
400053a5a1b3Sopenharmony_ci
400153a5a1b3Sopenharmony_ci    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
400253a5a1b3Sopenharmony_ci}
400353a5a1b3Sopenharmony_ci
400453a5a1b3Sopenharmony_ci/* Called from the main thread */
400553a5a1b3Sopenharmony_civoid pa_sink_set_sample_rate(pa_sink *s, uint32_t rate) {
400653a5a1b3Sopenharmony_ci    uint32_t old_rate;
400753a5a1b3Sopenharmony_ci
400853a5a1b3Sopenharmony_ci    pa_assert(s);
400953a5a1b3Sopenharmony_ci    pa_assert(pa_sample_rate_valid(rate));
401053a5a1b3Sopenharmony_ci
401153a5a1b3Sopenharmony_ci    old_rate = s->sample_spec.rate;
401253a5a1b3Sopenharmony_ci    if (old_rate == rate)
401353a5a1b3Sopenharmony_ci        return;
401453a5a1b3Sopenharmony_ci
401553a5a1b3Sopenharmony_ci    pa_log_info("%s: rate: %u -> %u", s->name, old_rate, rate);
401653a5a1b3Sopenharmony_ci
401753a5a1b3Sopenharmony_ci    s->sample_spec.rate = rate;
401853a5a1b3Sopenharmony_ci
401953a5a1b3Sopenharmony_ci    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
402053a5a1b3Sopenharmony_ci}
402153a5a1b3Sopenharmony_ci
402253a5a1b3Sopenharmony_ci/* Called from the main thread. */
402353a5a1b3Sopenharmony_civoid pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) {
402453a5a1b3Sopenharmony_ci    pa_cvolume old_volume;
402553a5a1b3Sopenharmony_ci    char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
402653a5a1b3Sopenharmony_ci    char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
402753a5a1b3Sopenharmony_ci
402853a5a1b3Sopenharmony_ci    pa_assert(s);
402953a5a1b3Sopenharmony_ci    pa_assert(volume);
403053a5a1b3Sopenharmony_ci
403153a5a1b3Sopenharmony_ci    old_volume = s->reference_volume;
403253a5a1b3Sopenharmony_ci
403353a5a1b3Sopenharmony_ci    if (pa_cvolume_equal(volume, &old_volume))
403453a5a1b3Sopenharmony_ci        return;
403553a5a1b3Sopenharmony_ci
403653a5a1b3Sopenharmony_ci    s->reference_volume = *volume;
403753a5a1b3Sopenharmony_ci    pa_log_debug("The reference volume of sink %s changed from %s to %s.", s->name,
403853a5a1b3Sopenharmony_ci                 pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &s->channel_map,
403953a5a1b3Sopenharmony_ci                                            s->flags & PA_SINK_DECIBEL_VOLUME),
404053a5a1b3Sopenharmony_ci                 pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &s->channel_map,
404153a5a1b3Sopenharmony_ci                                            s->flags & PA_SINK_DECIBEL_VOLUME));
404253a5a1b3Sopenharmony_ci
404353a5a1b3Sopenharmony_ci    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
404453a5a1b3Sopenharmony_ci    pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED], s);
404553a5a1b3Sopenharmony_ci}
404653a5a1b3Sopenharmony_ci
404753a5a1b3Sopenharmony_civoid pa_sink_move_streams_to_default_sink(pa_core *core, pa_sink *old_sink, bool default_sink_changed) {
404853a5a1b3Sopenharmony_ci    pa_sink_input *i;
404953a5a1b3Sopenharmony_ci    uint32_t idx;
405053a5a1b3Sopenharmony_ci
405153a5a1b3Sopenharmony_ci    pa_assert(core);
405253a5a1b3Sopenharmony_ci    pa_assert(old_sink);
405353a5a1b3Sopenharmony_ci
405453a5a1b3Sopenharmony_ci    if (core->state == PA_CORE_SHUTDOWN)
405553a5a1b3Sopenharmony_ci        return;
405653a5a1b3Sopenharmony_ci
405753a5a1b3Sopenharmony_ci    if (core->default_sink == NULL || core->default_sink->unlink_requested)
405853a5a1b3Sopenharmony_ci        return;
405953a5a1b3Sopenharmony_ci
406053a5a1b3Sopenharmony_ci    if (old_sink == core->default_sink)
406153a5a1b3Sopenharmony_ci        return;
406253a5a1b3Sopenharmony_ci
406353a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(i, old_sink->inputs, idx) {
406453a5a1b3Sopenharmony_ci        if (!PA_SINK_INPUT_IS_LINKED(i->state))
406553a5a1b3Sopenharmony_ci            continue;
406653a5a1b3Sopenharmony_ci
406753a5a1b3Sopenharmony_ci        if (!i->sink)
406853a5a1b3Sopenharmony_ci            continue;
406953a5a1b3Sopenharmony_ci
407053a5a1b3Sopenharmony_ci        /* Don't move sink-inputs which connect filter sinks to their target sinks */
407153a5a1b3Sopenharmony_ci        if (i->origin_sink)
407253a5a1b3Sopenharmony_ci            continue;
407353a5a1b3Sopenharmony_ci
407453a5a1b3Sopenharmony_ci        /* If default_sink_changed is false, the old sink became unavailable, so all streams must be moved. */
407553a5a1b3Sopenharmony_ci        if (pa_safe_streq(old_sink->name, i->preferred_sink) && default_sink_changed)
407653a5a1b3Sopenharmony_ci            continue;
407753a5a1b3Sopenharmony_ci
407853a5a1b3Sopenharmony_ci        if (!pa_sink_input_may_move_to(i, core->default_sink))
407953a5a1b3Sopenharmony_ci            continue;
408053a5a1b3Sopenharmony_ci
408153a5a1b3Sopenharmony_ci        if (default_sink_changed)
408253a5a1b3Sopenharmony_ci            pa_log_info("The sink input %u \"%s\" is moving to %s due to change of the default sink.",
408353a5a1b3Sopenharmony_ci                        i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), core->default_sink->name);
408453a5a1b3Sopenharmony_ci        else
408553a5a1b3Sopenharmony_ci            pa_log_info("The sink input %u \"%s\" is moving to %s, because the old sink became unavailable.",
408653a5a1b3Sopenharmony_ci                        i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), core->default_sink->name);
408753a5a1b3Sopenharmony_ci
408853a5a1b3Sopenharmony_ci        pa_sink_input_move_to(i, core->default_sink, false);
408953a5a1b3Sopenharmony_ci    }
409053a5a1b3Sopenharmony_ci}
4091