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