Branch data Line data Source code
1 : : /***
2 : : This file is part of PulseAudio.
3 : :
4 : : Copyright 2004-2006 Lennart Poettering
5 : : Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6 : :
7 : : PulseAudio is free software; you can redistribute it and/or modify
8 : : it under the terms of the GNU Lesser General Public License as published
9 : : by the Free Software Foundation; either version 2.1 of the License,
10 : : or (at your option) any later version.
11 : :
12 : : PulseAudio is distributed in the hope that it will be useful, but
13 : : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : General Public License for more details.
16 : :
17 : : You should have received a copy of the GNU Lesser General Public License
18 : : along with PulseAudio; if not, write to the Free Software
19 : : Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 : : USA.
21 : : ***/
22 : :
23 : : #ifdef HAVE_CONFIG_H
24 : : #include <config.h>
25 : : #endif
26 : :
27 : : #include <stdio.h>
28 : : #include <stdlib.h>
29 : : #include <string.h>
30 : :
31 : : #include <pulse/introspect.h>
32 : : #include <pulse/format.h>
33 : : #include <pulse/utf8.h>
34 : : #include <pulse/xmalloc.h>
35 : : #include <pulse/timeval.h>
36 : : #include <pulse/util.h>
37 : : #include <pulse/rtclock.h>
38 : : #include <pulse/internal.h>
39 : :
40 : : #include <pulsecore/i18n.h>
41 : : #include <pulsecore/sink-input.h>
42 : : #include <pulsecore/namereg.h>
43 : : #include <pulsecore/core-util.h>
44 : : #include <pulsecore/sample-util.h>
45 : : #include <pulsecore/core-subscribe.h>
46 : : #include <pulsecore/log.h>
47 : : #include <pulsecore/macro.h>
48 : : #include <pulsecore/play-memblockq.h>
49 : : #include <pulsecore/flist.h>
50 : :
51 : : #include "sink.h"
52 : :
53 : : #define MAX_MIX_CHANNELS 32
54 : : #define MIX_BUFFER_LENGTH (PA_PAGE_SIZE)
55 : : #define ABSOLUTE_MIN_LATENCY (500)
56 : : #define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
57 : : #define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
58 : :
59 [ # # ]: 0 : PA_DEFINE_PUBLIC_CLASS(pa_sink, pa_msgobject);
60 : :
61 : : struct pa_sink_volume_change {
62 : : pa_usec_t at;
63 : : pa_cvolume hw_volume;
64 : :
65 : : PA_LLIST_FIELDS(pa_sink_volume_change);
66 : : };
67 : :
68 : : struct sink_message_set_port {
69 : : pa_device_port *port;
70 : : int ret;
71 : : };
72 : :
73 : : static void sink_free(pa_object *s);
74 : :
75 : : static void pa_sink_volume_change_push(pa_sink *s);
76 : : static void pa_sink_volume_change_flush(pa_sink *s);
77 : : static void pa_sink_volume_change_rewind(pa_sink *s, size_t nbytes);
78 : :
79 : 0 : pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) {
80 [ # # ]: 0 : pa_assert(data);
81 : :
82 : : pa_zero(*data);
83 : 0 : data->proplist = pa_proplist_new();
84 : 0 : data->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
85 : :
86 : 0 : return data;
87 : : }
88 : :
89 : 0 : void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name) {
90 [ # # ]: 0 : pa_assert(data);
91 : :
92 : 0 : pa_xfree(data->name);
93 : 0 : data->name = pa_xstrdup(name);
94 : 0 : }
95 : :
96 : 0 : void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec) {
97 [ # # ]: 0 : pa_assert(data);
98 : :
99 [ # # ]: 0 : if ((data->sample_spec_is_set = !!spec))
100 : 0 : data->sample_spec = *spec;
101 : 0 : }
102 : :
103 : 0 : void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map) {
104 [ # # ]: 0 : pa_assert(data);
105 : :
106 [ # # ]: 0 : if ((data->channel_map_is_set = !!map))
107 : 0 : data->channel_map = *map;
108 : 0 : }
109 : :
110 : 0 : void pa_sink_new_data_set_alternate_sample_rate(pa_sink_new_data *data, const uint32_t alternate_sample_rate) {
111 [ # # ]: 0 : pa_assert(data);
112 : :
113 : 0 : data->alternate_sample_rate_is_set = TRUE;
114 : 0 : data->alternate_sample_rate = alternate_sample_rate;
115 : 0 : }
116 : :
117 : 0 : void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume) {
118 [ # # ]: 0 : pa_assert(data);
119 : :
120 [ # # ]: 0 : if ((data->volume_is_set = !!volume))
121 : 0 : data->volume = *volume;
122 : 0 : }
123 : :
124 : 0 : void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute) {
125 [ # # ]: 0 : pa_assert(data);
126 : :
127 : 0 : data->muted_is_set = TRUE;
128 : 0 : data->muted = !!mute;
129 : 0 : }
130 : :
131 : 0 : void pa_sink_new_data_set_port(pa_sink_new_data *data, const char *port) {
132 [ # # ]: 0 : pa_assert(data);
133 : :
134 : 0 : pa_xfree(data->active_port);
135 : 0 : data->active_port = pa_xstrdup(port);
136 : 0 : }
137 : :
138 : 0 : void pa_sink_new_data_done(pa_sink_new_data *data) {
139 [ # # ]: 0 : pa_assert(data);
140 : :
141 : 0 : pa_proplist_free(data->proplist);
142 : :
143 [ # # ]: 0 : if (data->ports)
144 : 0 : pa_device_port_hashmap_free(data->ports);
145 : :
146 : 0 : pa_xfree(data->name);
147 : 0 : pa_xfree(data->active_port);
148 : 0 : }
149 : :
150 : :
151 : : /* Called from main context */
152 : 0 : static void reset_callbacks(pa_sink *s) {
153 [ # # ]: 0 : pa_assert(s);
154 : :
155 : 0 : s->set_state = NULL;
156 : 0 : s->get_volume = NULL;
157 : 0 : s->set_volume = NULL;
158 : 0 : s->write_volume = NULL;
159 : 0 : s->get_mute = NULL;
160 : 0 : s->set_mute = NULL;
161 : 0 : s->request_rewind = NULL;
162 : 0 : s->update_requested_latency = NULL;
163 : 0 : s->set_port = NULL;
164 : 0 : s->get_formats = NULL;
165 : 0 : s->set_formats = NULL;
166 : 0 : s->update_rate = NULL;
167 : 0 : }
168 : :
169 : : /* Called from main context */
170 : 0 : pa_sink* pa_sink_new(
171 : : pa_core *core,
172 : : pa_sink_new_data *data,
173 : : pa_sink_flags_t flags) {
174 : :
175 : : pa_sink *s;
176 : : const char *name;
177 : : char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
178 : : pa_source_new_data source_data;
179 : : const char *dn;
180 : : char *pt;
181 : :
182 [ # # ]: 0 : pa_assert(core);
183 [ # # ]: 0 : pa_assert(data);
184 [ # # ]: 0 : pa_assert(data->name);
185 [ # # ]: 0 : pa_assert_ctl_context();
186 : :
187 : 0 : s = pa_msgobject_new(pa_sink);
188 : :
189 [ # # ]: 0 : if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SINK, s, data->namereg_fail))) {
190 : 0 : pa_log_debug("Failed to register name %s.", data->name);
191 : 0 : pa_xfree(s);
192 : 0 : return NULL;
193 : : }
194 : :
195 : 0 : pa_sink_new_data_set_name(data, name);
196 : :
197 [ # # ]: 0 : if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_NEW], data) < 0) {
198 : 0 : pa_xfree(s);
199 : 0 : pa_namereg_unregister(core, name);
200 : 0 : return NULL;
201 : : }
202 : :
203 : : /* FIXME, need to free s here on failure */
204 : :
205 [ # # ][ # # ]: 0 : pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
206 [ # # ][ # # ]: 0 : pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
[ # # ][ # # ]
207 : :
208 [ # # ][ # # ]: 0 : pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
209 : :
210 [ # # ]: 0 : if (!data->channel_map_is_set)
211 [ # # ]: 0 : pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
212 : :
213 [ # # ]: 0 : pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
214 [ # # ]: 0 : pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
215 : :
216 : : /* FIXME: There should probably be a general function for checking whether
217 : : * the sink volume is allowed to be set, like there is for sink inputs. */
218 [ # # ][ # # ]: 0 : pa_assert(!data->volume_is_set || !(flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
219 : :
220 [ # # ]: 0 : if (!data->volume_is_set) {
221 : 0 : pa_cvolume_reset(&data->volume, data->sample_spec.channels);
222 : 0 : data->save_volume = FALSE;
223 : : }
224 : :
225 [ # # ]: 0 : pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
226 [ # # ]: 0 : pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));
227 : :
228 [ # # ]: 0 : if (!data->muted_is_set)
229 : 0 : data->muted = FALSE;
230 : :
231 [ # # ]: 0 : if (data->card)
232 : 0 : pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist);
233 : :
234 : 0 : pa_device_init_description(data->proplist);
235 : 0 : pa_device_init_icon(data->proplist, TRUE);
236 : 0 : pa_device_init_intended_roles(data->proplist);
237 : :
238 [ # # ]: 0 : if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
239 : 0 : pa_xfree(s);
240 : 0 : pa_namereg_unregister(core, name);
241 : 0 : return NULL;
242 : : }
243 : :
244 : 0 : s->parent.parent.free = sink_free;
245 : 0 : s->parent.process_msg = pa_sink_process_msg;
246 : :
247 : 0 : s->core = core;
248 : 0 : s->state = PA_SINK_INIT;
249 : 0 : s->flags = flags;
250 : 0 : s->priority = 0;
251 : 0 : s->suspend_cause = 0;
252 : 0 : pa_sink_set_mixer_dirty(s, FALSE);
253 : 0 : s->name = pa_xstrdup(name);
254 : 0 : s->proplist = pa_proplist_copy(data->proplist);
255 : 0 : s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
256 : 0 : s->module = data->module;
257 : 0 : s->card = data->card;
258 : :
259 : 0 : s->priority = pa_device_init_priority(s->proplist);
260 : :
261 : 0 : s->sample_spec = data->sample_spec;
262 : 0 : s->channel_map = data->channel_map;
263 : 0 : s->default_sample_rate = s->sample_spec.rate;
264 : :
265 [ # # ]: 0 : if (data->alternate_sample_rate_is_set)
266 : 0 : s->alternate_sample_rate = data->alternate_sample_rate;
267 : : else
268 : 0 : s->alternate_sample_rate = s->core->alternate_sample_rate;
269 : :
270 [ # # ]: 0 : if (s->sample_spec.rate == s->alternate_sample_rate) {
271 : 0 : pa_log_warn("Default and alternate sample rates are the same.");
272 : 0 : s->alternate_sample_rate = 0;
273 : : }
274 : :
275 : 0 : s->inputs = pa_idxset_new(NULL, NULL);
276 : 0 : s->n_corked = 0;
277 : 0 : s->input_to_master = NULL;
278 : :
279 : 0 : s->reference_volume = s->real_volume = data->volume;
280 : 0 : pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
281 : 0 : s->base_volume = PA_VOLUME_NORM;
282 : 0 : s->n_volume_steps = PA_VOLUME_NORM+1;
283 : 0 : s->muted = data->muted;
284 : 0 : s->refresh_volume = s->refresh_muted = FALSE;
285 : :
286 : 0 : reset_callbacks(s);
287 : 0 : s->userdata = NULL;
288 : :
289 : 0 : s->asyncmsgq = NULL;
290 : :
291 : : /* As a minor optimization we just steal the list instead of
292 : : * copying it here */
293 : 0 : s->ports = data->ports;
294 : 0 : data->ports = NULL;
295 : :
296 : 0 : s->active_port = NULL;
297 : 0 : s->save_port = FALSE;
298 : :
299 [ # # ]: 0 : if (data->active_port)
300 [ # # ]: 0 : if ((s->active_port = pa_hashmap_get(s->ports, data->active_port)))
301 : 0 : s->save_port = data->save_port;
302 : :
303 [ # # ]: 0 : if (!s->active_port) {
304 : : void *state;
305 : : pa_device_port *p;
306 : :
307 [ # # ]: 0 : PA_HASHMAP_FOREACH(p, s->ports, state)
308 [ # # ][ # # ]: 0 : if (!s->active_port || p->priority > s->active_port->priority)
309 : 0 : s->active_port = p;
310 : : }
311 : :
312 [ # # ]: 0 : if (s->active_port)
313 : 0 : s->latency_offset = s->active_port->latency_offset;
314 : : else
315 : 0 : s->latency_offset = 0;
316 : :
317 : 0 : s->save_volume = data->save_volume;
318 : 0 : s->save_muted = data->save_muted;
319 : :
320 : 0 : pa_silence_memchunk_get(
321 : : &core->silence_cache,
322 : : core->mempool,
323 : : &s->silence,
324 : 0 : &s->sample_spec,
325 : : 0);
326 : :
327 : 0 : s->thread_info.rtpoll = NULL;
328 : 0 : s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
329 : 0 : s->thread_info.soft_volume = s->soft_volume;
330 : 0 : s->thread_info.soft_muted = s->muted;
331 : 0 : s->thread_info.state = s->state;
332 : 0 : s->thread_info.rewind_nbytes = 0;
333 : 0 : s->thread_info.rewind_requested = FALSE;
334 : 0 : s->thread_info.max_rewind = 0;
335 : 0 : s->thread_info.max_request = 0;
336 : 0 : s->thread_info.requested_latency_valid = FALSE;
337 : 0 : s->thread_info.requested_latency = 0;
338 : 0 : s->thread_info.min_latency = ABSOLUTE_MIN_LATENCY;
339 : 0 : s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
340 [ # # ]: 0 : s->thread_info.fixed_latency = flags & PA_SINK_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
341 : :
342 : 0 : PA_LLIST_HEAD_INIT(pa_sink_volume_change, s->thread_info.volume_changes);
343 : 0 : s->thread_info.volume_changes_tail = NULL;
344 : 0 : pa_sw_cvolume_multiply(&s->thread_info.current_hw_volume, &s->soft_volume, &s->real_volume);
345 : 0 : s->thread_info.volume_change_safety_margin = core->deferred_volume_safety_margin_usec;
346 : 0 : s->thread_info.volume_change_extra_delay = core->deferred_volume_extra_delay_usec;
347 : 0 : s->thread_info.latency_offset = s->latency_offset;
348 : :
349 : : /* FIXME: This should probably be moved to pa_sink_put() */
350 [ # # ]: 0 : pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
351 : :
352 [ # # ]: 0 : if (s->card)
353 [ # # ]: 0 : pa_assert_se(pa_idxset_put(s->card->sinks, s, NULL) >= 0);
354 : :
355 : 0 : pt = pa_proplist_to_string_sep(s->proplist, "\n ");
356 : 0 : pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s\n %s",
357 : : s->index,
358 : : s->name,
359 : : pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
360 : : pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map),
361 : : pt);
362 : 0 : pa_xfree(pt);
363 : :
364 : 0 : pa_source_new_data_init(&source_data);
365 : 0 : pa_source_new_data_set_sample_spec(&source_data, &s->sample_spec);
366 : 0 : pa_source_new_data_set_channel_map(&source_data, &s->channel_map);
367 : 0 : pa_source_new_data_set_alternate_sample_rate(&source_data, s->alternate_sample_rate);
368 : 0 : source_data.name = pa_sprintf_malloc("%s.monitor", name);
369 : 0 : source_data.driver = data->driver;
370 : 0 : source_data.module = data->module;
371 : 0 : source_data.card = data->card;
372 : :
373 : 0 : dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
374 [ # # ]: 0 : pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name);
375 : 0 : pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor");
376 : :
377 : 0 : s->monitor_source = pa_source_new(core, &source_data,
378 : 0 : ((flags & PA_SINK_LATENCY) ? PA_SOURCE_LATENCY : 0) |
379 [ # # ]: 0 : ((flags & PA_SINK_DYNAMIC_LATENCY) ? PA_SOURCE_DYNAMIC_LATENCY : 0));
380 : :
381 : 0 : pa_source_new_data_done(&source_data);
382 : :
383 [ # # ]: 0 : if (!s->monitor_source) {
384 : 0 : pa_sink_unlink(s);
385 : 0 : pa_sink_unref(s);
386 : 0 : return NULL;
387 : : }
388 : :
389 : 0 : s->monitor_source->monitor_of = s;
390 : :
391 : 0 : pa_source_set_latency_range(s->monitor_source, s->thread_info.min_latency, s->thread_info.max_latency);
392 : 0 : pa_source_set_fixed_latency(s->monitor_source, s->thread_info.fixed_latency);
393 : 0 : pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
394 : :
395 : 0 : return s;
396 : : }
397 : :
398 : : /* Called from main context */
399 : 0 : static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
400 : : int ret;
401 : : pa_bool_t suspend_change;
402 : : pa_sink_state_t original_state;
403 : :
404 [ # # ]: 0 : pa_assert(s);
405 [ # # ]: 0 : pa_assert_ctl_context();
406 : :
407 [ # # ]: 0 : if (s->state == state)
408 : : return 0;
409 : :
410 : 0 : original_state = s->state;
411 : :
412 : 0 : suspend_change =
413 [ # # ][ # # ]: 0 : (original_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state)) ||
[ # # ]
414 [ # # ]: 0 : (PA_SINK_IS_OPENED(original_state) && state == PA_SINK_SUSPENDED);
415 : :
416 [ # # ]: 0 : if (s->set_state)
417 [ # # ]: 0 : if ((ret = s->set_state(s, state)) < 0)
418 : : return ret;
419 : :
420 [ # # ]: 0 : if (s->asyncmsgq)
421 [ # # ]: 0 : if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL)) < 0) {
422 : :
423 [ # # ]: 0 : if (s->set_state)
424 : 0 : s->set_state(s, original_state);
425 : :
426 : : return ret;
427 : : }
428 : :
429 : 0 : s->state = state;
430 : :
431 [ # # ]: 0 : if (state != PA_SINK_UNLINKED) { /* if we enter UNLINKED state pa_sink_unlink() will fire the appropriate events */
432 : 0 : pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
433 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
434 : : }
435 : :
436 [ # # ]: 0 : if (suspend_change) {
437 : : pa_sink_input *i;
438 : : uint32_t idx;
439 : :
440 : : /* We're suspending or resuming, tell everyone about it */
441 : :
442 [ # # ]: 0 : PA_IDXSET_FOREACH(i, s->inputs, idx)
443 [ # # ][ # # ]: 0 : if (s->state == PA_SINK_SUSPENDED &&
444 : 0 : (i->flags & PA_SINK_INPUT_KILL_ON_SUSPEND))
445 : 0 : pa_sink_input_kill(i);
446 [ # # ]: 0 : else if (i->suspend)
447 : 0 : i->suspend(i, state == PA_SINK_SUSPENDED);
448 : :
449 [ # # ]: 0 : if (s->monitor_source)
450 : 0 : pa_source_sync_suspend(s->monitor_source);
451 : : }
452 : :
453 : : return 0;
454 : : }
455 : :
456 : 0 : void pa_sink_set_get_volume_callback(pa_sink *s, pa_sink_cb_t cb) {
457 [ # # ]: 0 : pa_assert(s);
458 : :
459 : 0 : s->get_volume = cb;
460 : 0 : }
461 : :
462 : 0 : void pa_sink_set_set_volume_callback(pa_sink *s, pa_sink_cb_t cb) {
463 : : pa_sink_flags_t flags;
464 : :
465 [ # # ]: 0 : pa_assert(s);
466 [ # # ]: 0 : pa_assert(!s->write_volume || cb);
467 : :
468 : 0 : s->set_volume = cb;
469 : :
470 : : /* Save the current flags so we can tell if they've changed */
471 : 0 : flags = s->flags;
472 : :
473 [ # # ]: 0 : if (cb) {
474 : : /* The sink implementor is responsible for setting decibel volume support */
475 : 0 : s->flags |= PA_SINK_HW_VOLUME_CTRL;
476 : : } else {
477 : 0 : s->flags &= ~PA_SINK_HW_VOLUME_CTRL;
478 : : /* See note below in pa_sink_put() about volume sharing and decibel volumes */
479 : 0 : pa_sink_enable_decibel_volume(s, !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
480 : : }
481 : :
482 : : /* If the flags have changed after init, let any clients know via a change event */
483 [ # # ][ # # ]: 0 : if (s->state != PA_SINK_INIT && flags != s->flags)
484 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
485 : 0 : }
486 : :
487 : 0 : void pa_sink_set_write_volume_callback(pa_sink *s, pa_sink_cb_t cb) {
488 : : pa_sink_flags_t flags;
489 : :
490 [ # # ]: 0 : pa_assert(s);
491 [ # # ][ # # ]: 0 : pa_assert(!cb || s->set_volume);
492 : :
493 : 0 : s->write_volume = cb;
494 : :
495 : : /* Save the current flags so we can tell if they've changed */
496 : 0 : flags = s->flags;
497 : :
498 [ # # ]: 0 : if (cb)
499 : 0 : s->flags |= PA_SINK_DEFERRED_VOLUME;
500 : : else
501 : 0 : s->flags &= ~PA_SINK_DEFERRED_VOLUME;
502 : :
503 : : /* If the flags have changed after init, let any clients know via a change event */
504 [ # # ][ # # ]: 0 : if (s->state != PA_SINK_INIT && flags != s->flags)
505 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
506 : 0 : }
507 : :
508 : 0 : void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_cb_t cb) {
509 [ # # ]: 0 : pa_assert(s);
510 : :
511 : 0 : s->get_mute = cb;
512 : 0 : }
513 : :
514 : 0 : void pa_sink_set_set_mute_callback(pa_sink *s, pa_sink_cb_t cb) {
515 : : pa_sink_flags_t flags;
516 : :
517 [ # # ]: 0 : pa_assert(s);
518 : :
519 : 0 : s->set_mute = cb;
520 : :
521 : : /* Save the current flags so we can tell if they've changed */
522 : 0 : flags = s->flags;
523 : :
524 [ # # ]: 0 : if (cb)
525 : 0 : s->flags |= PA_SINK_HW_MUTE_CTRL;
526 : : else
527 : 0 : s->flags &= ~PA_SINK_HW_MUTE_CTRL;
528 : :
529 : : /* If the flags have changed after init, let any clients know via a change event */
530 [ # # ][ # # ]: 0 : if (s->state != PA_SINK_INIT && flags != s->flags)
531 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
532 : 0 : }
533 : :
534 : 0 : static void enable_flat_volume(pa_sink *s, pa_bool_t enable) {
535 : : pa_sink_flags_t flags;
536 : :
537 [ # # ]: 0 : pa_assert(s);
538 : :
539 : : /* Always follow the overall user preference here */
540 [ # # ][ # # ]: 0 : enable = enable && s->core->flat_volumes;
541 : :
542 : : /* Save the current flags so we can tell if they've changed */
543 : 0 : flags = s->flags;
544 : :
545 [ # # ]: 0 : if (enable)
546 : 0 : s->flags |= PA_SINK_FLAT_VOLUME;
547 : : else
548 : 0 : s->flags &= ~PA_SINK_FLAT_VOLUME;
549 : :
550 : : /* If the flags have changed after init, let any clients know via a change event */
551 [ # # ][ # # ]: 0 : if (s->state != PA_SINK_INIT && flags != s->flags)
552 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
553 : 0 : }
554 : :
555 : 0 : void pa_sink_enable_decibel_volume(pa_sink *s, pa_bool_t enable) {
556 : : pa_sink_flags_t flags;
557 : :
558 [ # # ]: 0 : pa_assert(s);
559 : :
560 : : /* Save the current flags so we can tell if they've changed */
561 : 0 : flags = s->flags;
562 : :
563 [ # # ]: 0 : if (enable) {
564 : 0 : s->flags |= PA_SINK_DECIBEL_VOLUME;
565 : 0 : enable_flat_volume(s, TRUE);
566 : : } else {
567 : 0 : s->flags &= ~PA_SINK_DECIBEL_VOLUME;
568 : 0 : enable_flat_volume(s, FALSE);
569 : : }
570 : :
571 : : /* If the flags have changed after init, let any clients know via a change event */
572 [ # # ][ # # ]: 0 : if (s->state != PA_SINK_INIT && flags != s->flags)
573 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
574 : 0 : }
575 : :
576 : : /* Called from main context */
577 : 0 : void pa_sink_put(pa_sink* s) {
578 : 0 : pa_sink_assert_ref(s);
579 [ # # ]: 0 : pa_assert_ctl_context();
580 : :
581 [ # # ]: 0 : pa_assert(s->state == PA_SINK_INIT);
582 [ # # ][ # # ]: 0 : pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || s->input_to_master);
583 : :
584 : : /* The following fields must be initialized properly when calling _put() */
585 [ # # ]: 0 : pa_assert(s->asyncmsgq);
586 [ # # ]: 0 : pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
587 : :
588 : : /* Generally, flags should be initialized via pa_sink_new(). As a
589 : : * special exception we allow some volume related flags to be set
590 : : * between _new() and _put() by the callback setter functions above.
591 : : *
592 : : * Thus we implement a couple safeguards here which ensure the above
593 : : * setters were used (or at least the implementor made manual changes
594 : : * in a compatible way).
595 : : *
596 : : * Note: All of these flags set here can change over the life time
597 : : * of the sink. */
598 [ # # ][ # # ]: 0 : pa_assert(!(s->flags & PA_SINK_HW_VOLUME_CTRL) || s->set_volume);
599 [ # # ][ # # ]: 0 : pa_assert(!(s->flags & PA_SINK_DEFERRED_VOLUME) || s->write_volume);
600 [ # # ][ # # ]: 0 : pa_assert(!(s->flags & PA_SINK_HW_MUTE_CTRL) || s->set_mute);
601 : :
602 : : /* XXX: Currently decibel volume is disabled for all sinks that use volume
603 : : * sharing. When the master sink supports decibel volume, it would be good
604 : : * to have the flag also in the filter sink, but currently we don't do that
605 : : * so that the flags of the filter sink never change when it's moved from
606 : : * a master sink to another. One solution for this problem would be to
607 : : * remove user-visible volume altogether from filter sinks when volume
608 : : * sharing is used, but the current approach was easier to implement... */
609 : : /* We always support decibel volumes in software, otherwise we leave it to
610 : : * the sink implementor to set this flag as needed.
611 : : *
612 : : * Note: This flag can also change over the life time of the sink. */
613 [ # # ]: 0 : if (!(s->flags & PA_SINK_HW_VOLUME_CTRL) && !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
614 : 0 : pa_sink_enable_decibel_volume(s, TRUE);
615 : :
616 : : /* If the sink implementor support DB volumes by itself, we should always
617 : : * try and enable flat volumes too */
618 [ # # ]: 0 : if ((s->flags & PA_SINK_DECIBEL_VOLUME))
619 : 0 : enable_flat_volume(s, TRUE);
620 : :
621 [ # # ]: 0 : if (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) {
622 : 0 : pa_sink *root_sink = pa_sink_get_master(s);
623 : :
624 [ # # ]: 0 : pa_assert(root_sink);
625 : :
626 : 0 : s->reference_volume = root_sink->reference_volume;
627 : 0 : pa_cvolume_remap(&s->reference_volume, &root_sink->channel_map, &s->channel_map);
628 : :
629 : 0 : s->real_volume = root_sink->real_volume;
630 : 0 : pa_cvolume_remap(&s->real_volume, &root_sink->channel_map, &s->channel_map);
631 : : } else
632 : : /* We assume that if the sink implementor changed the default
633 : : * volume he did so in real_volume, because that is the usual
634 : : * place where he is supposed to place his changes. */
635 : 0 : s->reference_volume = s->real_volume;
636 : :
637 : 0 : s->thread_info.soft_volume = s->soft_volume;
638 : 0 : s->thread_info.soft_muted = s->muted;
639 : 0 : pa_sw_cvolume_multiply(&s->thread_info.current_hw_volume, &s->soft_volume, &s->real_volume);
640 : :
641 [ # # ][ # # ]: 0 : pa_assert((s->flags & PA_SINK_HW_VOLUME_CTRL)
[ # # ][ # # ]
642 : : || (s->base_volume == PA_VOLUME_NORM
643 : : && ((s->flags & PA_SINK_DECIBEL_VOLUME || (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)))));
644 [ # # ][ # # ]: 0 : pa_assert(!(s->flags & PA_SINK_DECIBEL_VOLUME) || s->n_volume_steps == PA_VOLUME_NORM+1);
645 [ # # ]: 0 : pa_assert(!(s->flags & PA_SINK_DYNAMIC_LATENCY) == (s->thread_info.fixed_latency != 0));
646 [ # # ]: 0 : pa_assert(!(s->flags & PA_SINK_LATENCY) == !(s->monitor_source->flags & PA_SOURCE_LATENCY));
647 [ # # ]: 0 : pa_assert(!(s->flags & PA_SINK_DYNAMIC_LATENCY) == !(s->monitor_source->flags & PA_SOURCE_DYNAMIC_LATENCY));
648 : :
649 [ # # ]: 0 : pa_assert(s->monitor_source->thread_info.fixed_latency == s->thread_info.fixed_latency);
650 [ # # ]: 0 : pa_assert(s->monitor_source->thread_info.min_latency == s->thread_info.min_latency);
651 [ # # ]: 0 : pa_assert(s->monitor_source->thread_info.max_latency == s->thread_info.max_latency);
652 : :
653 [ # # ]: 0 : pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
654 : :
655 : 0 : pa_source_put(s->monitor_source);
656 : :
657 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
658 : 0 : pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s);
659 : 0 : }
660 : :
661 : : /* Called from main context */
662 : 0 : void pa_sink_unlink(pa_sink* s) {
663 : : pa_bool_t linked;
664 : 0 : pa_sink_input *i, *j = NULL;
665 : :
666 [ # # ]: 0 : pa_assert(s);
667 [ # # ]: 0 : pa_assert_ctl_context();
668 : :
669 : : /* Please note that pa_sink_unlink() does more than simply
670 : : * reversing pa_sink_put(). It also undoes the registrations
671 : : * already done in pa_sink_new()! */
672 : :
673 : : /* All operations here shall be idempotent, i.e. pa_sink_unlink()
674 : : * may be called multiple times on the same sink without bad
675 : : * effects. */
676 : :
677 : 0 : linked = PA_SINK_IS_LINKED(s->state);
678 : :
679 [ # # ]: 0 : if (linked)
680 : 0 : pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s);
681 : :
682 [ # # ]: 0 : if (s->state != PA_SINK_UNLINKED)
683 : 0 : pa_namereg_unregister(s->core, s->name);
684 : 0 : pa_idxset_remove_by_data(s->core->sinks, s, NULL);
685 : :
686 [ # # ]: 0 : if (s->card)
687 : 0 : pa_idxset_remove_by_data(s->card->sinks, s, NULL);
688 : :
689 [ # # ]: 0 : while ((i = pa_idxset_first(s->inputs, NULL))) {
690 [ # # ]: 0 : pa_assert(i != j);
691 : 0 : pa_sink_input_kill(i);
692 : 0 : j = i;
693 : : }
694 : :
695 [ # # ]: 0 : if (linked)
696 : 0 : sink_set_state(s, PA_SINK_UNLINKED);
697 : : else
698 : 0 : s->state = PA_SINK_UNLINKED;
699 : :
700 : 0 : reset_callbacks(s);
701 : :
702 [ # # ]: 0 : if (s->monitor_source)
703 : 0 : pa_source_unlink(s->monitor_source);
704 : :
705 [ # # ]: 0 : if (linked) {
706 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
707 : 0 : pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], s);
708 : : }
709 : 0 : }
710 : :
711 : : /* Called from main context */
712 : 0 : static void sink_free(pa_object *o) {
713 : 0 : pa_sink *s = PA_SINK(o);
714 : : pa_sink_input *i;
715 : :
716 [ # # ]: 0 : pa_assert(s);
717 [ # # ]: 0 : pa_assert_ctl_context();
718 [ # # ]: 0 : pa_assert(pa_sink_refcnt(s) == 0);
719 : :
720 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->state))
721 : 0 : pa_sink_unlink(s);
722 : :
723 : 0 : pa_log_info("Freeing sink %u \"%s\"", s->index, s->name);
724 : :
725 [ # # ]: 0 : if (s->monitor_source) {
726 : 0 : pa_source_unref(s->monitor_source);
727 : 0 : s->monitor_source = NULL;
728 : : }
729 : :
730 : 0 : pa_idxset_free(s->inputs, NULL, NULL);
731 : :
732 [ # # ]: 0 : while ((i = pa_hashmap_steal_first(s->thread_info.inputs)))
733 : 0 : pa_sink_input_unref(i);
734 : :
735 : 0 : pa_hashmap_free(s->thread_info.inputs, NULL, NULL);
736 : :
737 [ # # ]: 0 : if (s->silence.memblock)
738 : 0 : pa_memblock_unref(s->silence.memblock);
739 : :
740 : 0 : pa_xfree(s->name);
741 : 0 : pa_xfree(s->driver);
742 : :
743 [ # # ]: 0 : if (s->proplist)
744 : 0 : pa_proplist_free(s->proplist);
745 : :
746 [ # # ]: 0 : if (s->ports)
747 : 0 : pa_device_port_hashmap_free(s->ports);
748 : :
749 : 0 : pa_xfree(s);
750 : 0 : }
751 : :
752 : : /* Called from main context, and not while the IO thread is active, please */
753 : 0 : void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
754 : 0 : pa_sink_assert_ref(s);
755 [ # # ]: 0 : pa_assert_ctl_context();
756 : :
757 : 0 : s->asyncmsgq = q;
758 : :
759 [ # # ]: 0 : if (s->monitor_source)
760 : 0 : pa_source_set_asyncmsgq(s->monitor_source, q);
761 : 0 : }
762 : :
763 : : /* Called from main context, and not while the IO thread is active, please */
764 : 0 : void pa_sink_update_flags(pa_sink *s, pa_sink_flags_t mask, pa_sink_flags_t value) {
765 : 0 : pa_sink_assert_ref(s);
766 [ # # ]: 0 : pa_assert_ctl_context();
767 : :
768 [ # # ]: 0 : if (mask == 0)
769 : 0 : return;
770 : :
771 : : /* For now, allow only a minimal set of flags to be changed. */
772 [ # # ]: 0 : pa_assert((mask & ~(PA_SINK_DYNAMIC_LATENCY|PA_SINK_LATENCY)) == 0);
773 : :
774 : 0 : s->flags = (s->flags & ~mask) | (value & mask);
775 : :
776 : 0 : pa_source_update_flags(s->monitor_source,
777 : 0 : ((mask & PA_SINK_LATENCY) ? PA_SOURCE_LATENCY : 0) |
778 [ # # ]: 0 : ((mask & PA_SINK_DYNAMIC_LATENCY) ? PA_SOURCE_DYNAMIC_LATENCY : 0),
779 : 0 : ((value & PA_SINK_LATENCY) ? PA_SOURCE_LATENCY : 0) |
780 : : ((value & PA_SINK_DYNAMIC_LATENCY) ? PA_SINK_DYNAMIC_LATENCY : 0));
781 : : }
782 : :
783 : : /* Called from IO context, or before _put() from main context */
784 : 0 : void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
785 : 0 : pa_sink_assert_ref(s);
786 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
787 : :
788 : 0 : s->thread_info.rtpoll = p;
789 : :
790 [ # # ]: 0 : if (s->monitor_source)
791 : 0 : pa_source_set_rtpoll(s->monitor_source, p);
792 : 0 : }
793 : :
794 : : /* Called from main context */
795 : 0 : int pa_sink_update_status(pa_sink*s) {
796 : 0 : pa_sink_assert_ref(s);
797 [ # # ]: 0 : pa_assert_ctl_context();
798 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
799 : :
800 [ # # ]: 0 : if (s->state == PA_SINK_SUSPENDED)
801 : : return 0;
802 : :
803 : 0 : return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
804 : : }
805 : :
806 : : /* Called from any context - must be threadsafe */
807 : 0 : void pa_sink_set_mixer_dirty(pa_sink *s, pa_bool_t is_dirty)
808 : : {
809 : 0 : pa_atomic_store(&s->mixer_dirty, is_dirty ? 1 : 0);
810 : 0 : }
811 : :
812 : : /* Called from main context */
813 : 0 : int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause) {
814 : 0 : pa_sink_assert_ref(s);
815 [ # # ]: 0 : pa_assert_ctl_context();
816 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
817 [ # # ]: 0 : pa_assert(cause != 0);
818 : :
819 [ # # ]: 0 : if (suspend) {
820 : 0 : s->suspend_cause |= cause;
821 : 0 : s->monitor_source->suspend_cause |= cause;
822 : : } else {
823 : 0 : s->suspend_cause &= ~cause;
824 : 0 : s->monitor_source->suspend_cause &= ~cause;
825 : : }
826 : :
827 [ # # ][ # # ]: 0 : if (!(s->suspend_cause & PA_SUSPEND_SESSION) && (pa_atomic_load(&s->mixer_dirty) != 0)) {
828 : : /* This might look racy but isn't: If somebody sets mixer_dirty exactly here,
829 : : it'll be handled just fine. */
830 : 0 : pa_sink_set_mixer_dirty(s, FALSE);
831 : 0 : pa_log_debug("Mixer is now accessible. Updating alsa mixer settings.");
832 [ # # ][ # # ]: 0 : if (s->active_port && s->set_port) {
833 [ # # ]: 0 : if (s->flags & PA_SINK_DEFERRED_VOLUME) {
834 : 0 : struct sink_message_set_port msg = { .port = s->active_port, .ret = 0 };
835 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
836 : : }
837 : : else
838 : 0 : s->set_port(s, s->active_port);
839 : : }
840 : : else {
841 [ # # ]: 0 : if (s->set_mute)
842 : 0 : s->set_mute(s);
843 [ # # ]: 0 : if (s->set_volume)
844 : 0 : s->set_volume(s);
845 : : }
846 : : }
847 : :
848 [ # # ]: 0 : if ((pa_sink_get_state(s) == PA_SINK_SUSPENDED) == !!s->suspend_cause)
849 : : return 0;
850 : :
851 [ # # ]: 0 : pa_log_debug("Suspend cause of sink %s is 0x%04x, %s", s->name, s->suspend_cause, s->suspend_cause ? "suspending" : "resuming");
852 : :
853 [ # # ]: 0 : if (s->suspend_cause)
854 : 0 : return sink_set_state(s, PA_SINK_SUSPENDED);
855 : : else
856 : 0 : return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
857 : : }
858 : :
859 : : /* Called from main context */
860 : 0 : pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q) {
861 : : pa_sink_input *i, *n;
862 : : uint32_t idx;
863 : :
864 : 0 : pa_sink_assert_ref(s);
865 [ # # ]: 0 : pa_assert_ctl_context();
866 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
867 : :
868 [ # # ]: 0 : if (!q)
869 : 0 : q = pa_queue_new();
870 : :
871 [ # # ]: 0 : for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) {
872 : 0 : n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx));
873 : :
874 : 0 : pa_sink_input_ref(i);
875 : :
876 [ # # ]: 0 : if (pa_sink_input_start_move(i) >= 0)
877 : 0 : pa_queue_push(q, i);
878 : : else
879 : 0 : pa_sink_input_unref(i);
880 : : }
881 : :
882 : 0 : return q;
883 : : }
884 : :
885 : : /* Called from main context */
886 : 0 : void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save) {
887 : : pa_sink_input *i;
888 : :
889 : 0 : pa_sink_assert_ref(s);
890 [ # # ]: 0 : pa_assert_ctl_context();
891 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
892 [ # # ]: 0 : pa_assert(q);
893 : :
894 [ # # ]: 0 : while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
895 [ # # ]: 0 : if (pa_sink_input_finish_move(i, s, save) < 0)
896 : 0 : pa_sink_input_fail_move(i);
897 : :
898 : 0 : pa_sink_input_unref(i);
899 : : }
900 : :
901 : 0 : pa_queue_free(q, NULL);
902 : 0 : }
903 : :
904 : : /* Called from main context */
905 : 0 : void pa_sink_move_all_fail(pa_queue *q) {
906 : : pa_sink_input *i;
907 : :
908 [ # # ]: 0 : pa_assert_ctl_context();
909 [ # # ]: 0 : pa_assert(q);
910 : :
911 [ # # ]: 0 : while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
912 : 0 : pa_sink_input_fail_move(i);
913 : 0 : pa_sink_input_unref(i);
914 : : }
915 : :
916 : 0 : pa_queue_free(q, NULL);
917 : 0 : }
918 : :
919 : : /* Called from IO thread context */
920 : 0 : void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
921 : : pa_sink_input *i;
922 : 0 : void *state = NULL;
923 : :
924 : 0 : pa_sink_assert_ref(s);
925 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
926 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
927 : :
928 : : /* If nobody requested this and this is actually no real rewind
929 : : * then we can short cut this. Please note that this means that
930 : : * not all rewind requests triggered upstream will always be
931 : : * translated in actual requests! */
932 [ # # ][ # # ]: 0 : if (!s->thread_info.rewind_requested && nbytes <= 0)
933 : : return;
934 : :
935 : 0 : s->thread_info.rewind_nbytes = 0;
936 : 0 : s->thread_info.rewind_requested = FALSE;
937 : :
938 [ # # ]: 0 : if (s->thread_info.state == PA_SINK_SUSPENDED)
939 : : return;
940 : :
941 [ # # ]: 0 : if (nbytes > 0) {
942 : 0 : pa_log_debug("Processing rewind...");
943 [ # # ]: 0 : if (s->flags & PA_SINK_DEFERRED_VOLUME)
944 : 0 : pa_sink_volume_change_rewind(s, nbytes);
945 : : }
946 : :
947 [ # # ]: 0 : PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
948 : 0 : pa_sink_input_assert_ref(i);
949 : 0 : pa_sink_input_process_rewind(i, nbytes);
950 : : }
951 : :
952 [ # # ]: 0 : if (nbytes > 0) {
953 [ # # ][ # # ]: 0 : if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state))
954 : 0 : pa_source_process_rewind(s->monitor_source, nbytes);
955 : : }
956 : : }
957 : :
958 : : /* Called from IO thread context */
959 : 0 : static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, unsigned maxinfo) {
960 : : pa_sink_input *i;
961 : 0 : unsigned n = 0;
962 : 0 : void *state = NULL;
963 : 0 : size_t mixlength = *length;
964 : :
965 : 0 : pa_sink_assert_ref(s);
966 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
967 [ # # ]: 0 : pa_assert(info);
968 : :
969 [ # # ][ # # ]: 0 : while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) {
970 : 0 : pa_sink_input_assert_ref(i);
971 : :
972 : 0 : pa_sink_input_peek(i, *length, &info->chunk, &info->volume);
973 : :
974 [ # # ][ # # ]: 0 : if (mixlength == 0 || info->chunk.length < mixlength)
975 : 0 : mixlength = info->chunk.length;
976 : :
977 [ # # ]: 0 : if (pa_memblock_is_silence(info->chunk.memblock)) {
978 : 0 : pa_memblock_unref(info->chunk.memblock);
979 : 0 : continue;
980 : : }
981 : :
982 : 0 : info->userdata = pa_sink_input_ref(i);
983 : :
984 [ # # ]: 0 : pa_assert(info->chunk.memblock);
985 [ # # ]: 0 : pa_assert(info->chunk.length > 0);
986 : :
987 : 0 : info++;
988 : 0 : n++;
989 : 0 : maxinfo--;
990 : : }
991 : :
992 [ # # ]: 0 : if (mixlength > 0)
993 : 0 : *length = mixlength;
994 : :
995 : 0 : return n;
996 : : }
997 : :
998 : : /* Called from IO thread context */
999 : 0 : static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *result) {
1000 : : pa_sink_input *i;
1001 : : void *state;
1002 : 0 : unsigned p = 0;
1003 : 0 : unsigned n_unreffed = 0;
1004 : :
1005 : 0 : pa_sink_assert_ref(s);
1006 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
1007 [ # # ]: 0 : pa_assert(result);
1008 [ # # ]: 0 : pa_assert(result->memblock);
1009 [ # # ]: 0 : pa_assert(result->length > 0);
1010 : :
1011 : : /* We optimize for the case where the order of the inputs has not changed */
1012 : :
1013 [ # # ]: 0 : PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
1014 : : unsigned j;
1015 : 0 : pa_mix_info* m = NULL;
1016 : :
1017 : 0 : pa_sink_input_assert_ref(i);
1018 : :
1019 : : /* Let's try to find the matching entry info the pa_mix_info array */
1020 [ # # ]: 0 : for (j = 0; j < n; j ++) {
1021 : :
1022 [ # # ]: 0 : if (info[p].userdata == i) {
1023 : 0 : m = info + p;
1024 : 0 : break;
1025 : : }
1026 : :
1027 : 0 : p++;
1028 [ # # ]: 0 : if (p >= n)
1029 : 0 : p = 0;
1030 : : }
1031 : :
1032 : : /* Drop read data */
1033 : 0 : pa_sink_input_drop(i, result->length);
1034 : :
1035 [ # # ][ # # ]: 0 : if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state)) {
1036 : :
1037 [ # # ]: 0 : if (pa_hashmap_size(i->thread_info.direct_outputs) > 0) {
1038 : 0 : void *ostate = NULL;
1039 : : pa_source_output *o;
1040 : : pa_memchunk c;
1041 : :
1042 [ # # ][ # # ]: 0 : if (m && m->chunk.memblock) {
1043 : 0 : c = m->chunk;
1044 : 0 : pa_memblock_ref(c.memblock);
1045 [ # # ]: 0 : pa_assert(result->length <= c.length);
1046 : 0 : c.length = result->length;
1047 : :
1048 : 0 : pa_memchunk_make_writable(&c, 0);
1049 : 0 : pa_volume_memchunk(&c, &s->sample_spec, &m->volume);
1050 : : } else {
1051 : 0 : c = s->silence;
1052 : 0 : pa_memblock_ref(c.memblock);
1053 [ # # ]: 0 : pa_assert(result->length <= c.length);
1054 : 0 : c.length = result->length;
1055 : : }
1056 : :
1057 [ # # ]: 0 : while ((o = pa_hashmap_iterate(i->thread_info.direct_outputs, &ostate, NULL))) {
1058 : : pa_source_output_assert_ref(o);
1059 [ # # ]: 0 : pa_assert(o->direct_on_input == i);
1060 : 0 : pa_source_post_direct(s->monitor_source, o, &c);
1061 : : }
1062 : :
1063 : 0 : pa_memblock_unref(c.memblock);
1064 : : }
1065 : : }
1066 : :
1067 [ # # ]: 0 : if (m) {
1068 [ # # ]: 0 : if (m->chunk.memblock)
1069 : 0 : pa_memblock_unref(m->chunk.memblock);
1070 : 0 : pa_memchunk_reset(&m->chunk);
1071 : :
1072 : 0 : pa_sink_input_unref(m->userdata);
1073 : 0 : m->userdata = NULL;
1074 : :
1075 : 0 : n_unreffed += 1;
1076 : : }
1077 : : }
1078 : :
1079 : : /* Now drop references to entries that are included in the
1080 : : * pa_mix_info array but don't exist anymore */
1081 : :
1082 [ # # ]: 0 : if (n_unreffed < n) {
1083 [ # # ]: 0 : for (; n > 0; info++, n--) {
1084 [ # # ]: 0 : if (info->userdata)
1085 : 0 : pa_sink_input_unref(info->userdata);
1086 [ # # ]: 0 : if (info->chunk.memblock)
1087 : 0 : pa_memblock_unref(info->chunk.memblock);
1088 : : }
1089 : : }
1090 : :
1091 [ # # ][ # # ]: 0 : if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state))
1092 : 0 : pa_source_post(s->monitor_source, result);
1093 : 0 : }
1094 : :
1095 : : /* Called from IO thread context */
1096 : 0 : void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
1097 : : pa_mix_info info[MAX_MIX_CHANNELS];
1098 : : unsigned n;
1099 : : size_t block_size_max;
1100 : :
1101 : 0 : pa_sink_assert_ref(s);
1102 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
1103 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1104 [ # # ]: 0 : pa_assert(pa_frame_aligned(length, &s->sample_spec));
1105 [ # # ]: 0 : pa_assert(result);
1106 : :
1107 [ # # ]: 0 : pa_assert(!s->thread_info.rewind_requested);
1108 [ # # ]: 0 : pa_assert(s->thread_info.rewind_nbytes == 0);
1109 : :
1110 [ # # ]: 0 : if (s->thread_info.state == PA_SINK_SUSPENDED) {
1111 : 0 : result->memblock = pa_memblock_ref(s->silence.memblock);
1112 : 0 : result->index = s->silence.index;
1113 : 0 : result->length = PA_MIN(s->silence.length, length);
1114 : 0 : return;
1115 : : }
1116 : :
1117 : 0 : pa_sink_ref(s);
1118 : :
1119 [ # # ]: 0 : if (length <= 0)
1120 : 0 : length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
1121 : :
1122 : 0 : block_size_max = pa_mempool_block_size_max(s->core->mempool);
1123 [ # # ]: 0 : if (length > block_size_max)
1124 : 0 : length = pa_frame_align(block_size_max, &s->sample_spec);
1125 : :
1126 [ # # ]: 0 : pa_assert(length > 0);
1127 : :
1128 : 0 : n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS);
1129 : :
1130 [ # # ]: 0 : if (n == 0) {
1131 : :
1132 : 0 : *result = s->silence;
1133 : 0 : pa_memblock_ref(result->memblock);
1134 : :
1135 [ # # ]: 0 : if (result->length > length)
1136 : 0 : result->length = length;
1137 : :
1138 [ # # ]: 0 : } else if (n == 1) {
1139 : : pa_cvolume volume;
1140 : :
1141 : 0 : *result = info[0].chunk;
1142 : 0 : pa_memblock_ref(result->memblock);
1143 : :
1144 [ # # ]: 0 : if (result->length > length)
1145 : 0 : result->length = length;
1146 : :
1147 : 0 : pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
1148 : :
1149 [ # # ][ # # ]: 0 : if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) {
1150 : 0 : pa_memblock_unref(result->memblock);
1151 : 0 : pa_silence_memchunk_get(&s->core->silence_cache,
1152 : 0 : s->core->mempool,
1153 : : result,
1154 : 0 : &s->sample_spec,
1155 : : result->length);
1156 [ # # ]: 0 : } else if (!pa_cvolume_is_norm(&volume)) {
1157 : 0 : pa_memchunk_make_writable(result, 0);
1158 : 0 : pa_volume_memchunk(result, &s->sample_spec, &volume);
1159 : : }
1160 : : } else {
1161 : : void *ptr;
1162 : 0 : result->memblock = pa_memblock_new(s->core->mempool, length);
1163 : :
1164 : 0 : ptr = pa_memblock_acquire(result->memblock);
1165 : 0 : result->length = pa_mix(info, n,
1166 : : ptr, length,
1167 : 0 : &s->sample_spec,
1168 : 0 : &s->thread_info.soft_volume,
1169 : 0 : s->thread_info.soft_muted);
1170 : 0 : pa_memblock_release(result->memblock);
1171 : :
1172 : 0 : result->index = 0;
1173 : : }
1174 : :
1175 : 0 : inputs_drop(s, info, n, result);
1176 : :
1177 : 0 : pa_sink_unref(s);
1178 : : }
1179 : :
1180 : : /* Called from IO thread context */
1181 : 0 : void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
1182 : : pa_mix_info info[MAX_MIX_CHANNELS];
1183 : : unsigned n;
1184 : : size_t length, block_size_max;
1185 : :
1186 : 0 : pa_sink_assert_ref(s);
1187 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
1188 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1189 [ # # ]: 0 : pa_assert(target);
1190 [ # # ]: 0 : pa_assert(target->memblock);
1191 [ # # ]: 0 : pa_assert(target->length > 0);
1192 [ # # ]: 0 : pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
1193 : :
1194 [ # # ]: 0 : pa_assert(!s->thread_info.rewind_requested);
1195 [ # # ]: 0 : pa_assert(s->thread_info.rewind_nbytes == 0);
1196 : :
1197 [ # # ]: 0 : if (s->thread_info.state == PA_SINK_SUSPENDED) {
1198 : 0 : pa_silence_memchunk(target, &s->sample_spec);
1199 : 0 : return;
1200 : : }
1201 : :
1202 : 0 : pa_sink_ref(s);
1203 : :
1204 : 0 : length = target->length;
1205 : 0 : block_size_max = pa_mempool_block_size_max(s->core->mempool);
1206 [ # # ]: 0 : if (length > block_size_max)
1207 : 0 : length = pa_frame_align(block_size_max, &s->sample_spec);
1208 : :
1209 [ # # ]: 0 : pa_assert(length > 0);
1210 : :
1211 : 0 : n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS);
1212 : :
1213 [ # # ]: 0 : if (n == 0) {
1214 [ # # ]: 0 : if (target->length > length)
1215 : 0 : target->length = length;
1216 : :
1217 : 0 : pa_silence_memchunk(target, &s->sample_spec);
1218 [ # # ]: 0 : } else if (n == 1) {
1219 : : pa_cvolume volume;
1220 : :
1221 [ # # ]: 0 : if (target->length > length)
1222 : 0 : target->length = length;
1223 : :
1224 : 0 : pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
1225 : :
1226 [ # # ][ # # ]: 0 : if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
1227 : 0 : pa_silence_memchunk(target, &s->sample_spec);
1228 : : else {
1229 : : pa_memchunk vchunk;
1230 : :
1231 : 0 : vchunk = info[0].chunk;
1232 : 0 : pa_memblock_ref(vchunk.memblock);
1233 : :
1234 [ # # ]: 0 : if (vchunk.length > length)
1235 : 0 : vchunk.length = length;
1236 : :
1237 [ # # ]: 0 : if (!pa_cvolume_is_norm(&volume)) {
1238 : 0 : pa_memchunk_make_writable(&vchunk, 0);
1239 : 0 : pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
1240 : : }
1241 : :
1242 : 0 : pa_memchunk_memcpy(target, &vchunk);
1243 : 0 : pa_memblock_unref(vchunk.memblock);
1244 : : }
1245 : :
1246 : : } else {
1247 : : void *ptr;
1248 : :
1249 : 0 : ptr = pa_memblock_acquire(target->memblock);
1250 : :
1251 : 0 : target->length = pa_mix(info, n,
1252 : 0 : (uint8_t*) ptr + target->index, length,
1253 : 0 : &s->sample_spec,
1254 : 0 : &s->thread_info.soft_volume,
1255 : 0 : s->thread_info.soft_muted);
1256 : :
1257 : 0 : pa_memblock_release(target->memblock);
1258 : : }
1259 : :
1260 : 0 : inputs_drop(s, info, n, target);
1261 : :
1262 : 0 : pa_sink_unref(s);
1263 : : }
1264 : :
1265 : : /* Called from IO thread context */
1266 : 0 : void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
1267 : : pa_memchunk chunk;
1268 : : size_t l, d;
1269 : :
1270 : 0 : pa_sink_assert_ref(s);
1271 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
1272 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1273 [ # # ]: 0 : pa_assert(target);
1274 [ # # ]: 0 : pa_assert(target->memblock);
1275 [ # # ]: 0 : pa_assert(target->length > 0);
1276 [ # # ]: 0 : pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
1277 : :
1278 [ # # ]: 0 : pa_assert(!s->thread_info.rewind_requested);
1279 [ # # ]: 0 : pa_assert(s->thread_info.rewind_nbytes == 0);
1280 : :
1281 [ # # ]: 0 : if (s->thread_info.state == PA_SINK_SUSPENDED) {
1282 : 0 : pa_silence_memchunk(target, &s->sample_spec);
1283 : 0 : return;
1284 : : }
1285 : :
1286 : 0 : pa_sink_ref(s);
1287 : :
1288 : 0 : l = target->length;
1289 : 0 : d = 0;
1290 [ # # ]: 0 : while (l > 0) {
1291 : 0 : chunk = *target;
1292 : 0 : chunk.index += d;
1293 : 0 : chunk.length -= d;
1294 : :
1295 : 0 : pa_sink_render_into(s, &chunk);
1296 : :
1297 : 0 : d += chunk.length;
1298 : 0 : l -= chunk.length;
1299 : : }
1300 : :
1301 : 0 : pa_sink_unref(s);
1302 : : }
1303 : :
1304 : : /* Called from IO thread context */
1305 : 0 : void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
1306 : 0 : pa_sink_assert_ref(s);
1307 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
1308 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1309 [ # # ]: 0 : pa_assert(length > 0);
1310 [ # # ]: 0 : pa_assert(pa_frame_aligned(length, &s->sample_spec));
1311 [ # # ]: 0 : pa_assert(result);
1312 : :
1313 [ # # ]: 0 : pa_assert(!s->thread_info.rewind_requested);
1314 [ # # ]: 0 : pa_assert(s->thread_info.rewind_nbytes == 0);
1315 : :
1316 : 0 : pa_sink_ref(s);
1317 : :
1318 : 0 : pa_sink_render(s, length, result);
1319 : :
1320 [ # # ]: 0 : if (result->length < length) {
1321 : : pa_memchunk chunk;
1322 : :
1323 : 0 : pa_memchunk_make_writable(result, length);
1324 : :
1325 : 0 : chunk.memblock = result->memblock;
1326 : 0 : chunk.index = result->index + result->length;
1327 : 0 : chunk.length = length - result->length;
1328 : :
1329 : 0 : pa_sink_render_into_full(s, &chunk);
1330 : :
1331 : 0 : result->length = length;
1332 : : }
1333 : :
1334 : 0 : pa_sink_unref(s);
1335 : 0 : }
1336 : :
1337 : : /* Called from main thread */
1338 : 0 : pa_bool_t pa_sink_update_rate(pa_sink *s, uint32_t rate, pa_bool_t passthrough)
1339 : : {
1340 [ # # ]: 0 : if (s->update_rate) {
1341 : 0 : uint32_t desired_rate = rate;
1342 : 0 : uint32_t default_rate = s->default_sample_rate;
1343 : 0 : uint32_t alternate_rate = s->alternate_sample_rate;
1344 : : uint32_t idx;
1345 : : pa_sink_input *i;
1346 : 0 : pa_bool_t use_alternate = FALSE;
1347 : :
1348 [ # # ]: 0 : if (PA_UNLIKELY(default_rate == alternate_rate)) {
1349 : 0 : pa_log_warn("Default and alternate sample rates are the same.");
1350 : 0 : return FALSE;
1351 : : }
1352 : :
1353 [ # # ]: 0 : if (PA_SINK_IS_RUNNING(s->state)) {
1354 : 0 : pa_log_info("Cannot update rate, SINK_IS_RUNNING, will keep using %u Hz",
1355 : : s->sample_spec.rate);
1356 : 0 : return FALSE;
1357 : : }
1358 : :
1359 [ # # ]: 0 : if (s->monitor_source) {
1360 [ # # ]: 0 : if (PA_SOURCE_IS_RUNNING(s->monitor_source->state) == TRUE) {
1361 : 0 : pa_log_info("Cannot update rate, monitor source is RUNNING");
1362 : 0 : return FALSE;
1363 : : }
1364 : : }
1365 : :
1366 [ # # ]: 0 : if (PA_UNLIKELY (desired_rate < 8000 ||
1367 : : desired_rate > PA_RATE_MAX))
1368 : : return FALSE;
1369 : :
1370 [ # # ]: 0 : if (!passthrough) {
1371 [ # # ][ # # ]: 0 : pa_assert(default_rate % 4000 || default_rate % 11025);
1372 [ # # ][ # # ]: 0 : pa_assert(alternate_rate % 4000 || alternate_rate % 11025);
1373 : :
1374 [ # # ]: 0 : if (default_rate % 4000) {
1375 : : /* default is a 11025 multiple */
1376 [ # # ][ # # ]: 0 : if ((alternate_rate % 4000 == 0) && (desired_rate % 4000 == 0))
1377 : 0 : use_alternate=TRUE;
1378 : : } else {
1379 : : /* default is 4000 multiple */
1380 [ # # ][ # # ]: 0 : if ((alternate_rate % 11025 == 0) && (desired_rate % 11025 == 0))
1381 : 0 : use_alternate=TRUE;
1382 : : }
1383 : :
1384 [ # # ]: 0 : if (use_alternate)
1385 : : desired_rate = alternate_rate;
1386 : : else
1387 : 0 : desired_rate = default_rate;
1388 : : } else {
1389 : : desired_rate = rate; /* use stream sampling rate, discard default/alternate settings */
1390 : : }
1391 : :
1392 [ # # ][ # # ]: 0 : if (!passthrough && pa_sink_used_by(s) > 0)
1393 : : return FALSE;
1394 : :
1395 : 0 : pa_sink_suspend(s, TRUE, PA_SUSPEND_IDLE); /* needed before rate update, will be resumed automatically */
1396 : :
1397 [ # # ]: 0 : if (s->update_rate(s, desired_rate) == TRUE) {
1398 : : /* update monitor source as well */
1399 [ # # ][ # # ]: 0 : if (s->monitor_source && !passthrough)
1400 : 0 : pa_source_update_rate(s->monitor_source, desired_rate, FALSE);
1401 : 0 : pa_log_info("Changed sampling rate successfully");
1402 : :
1403 [ # # ]: 0 : PA_IDXSET_FOREACH(i, s->inputs, idx) {
1404 [ # # ]: 0 : if (i->state == PA_SINK_INPUT_CORKED)
1405 : 0 : pa_sink_input_update_rate(i);
1406 : : }
1407 : :
1408 : : return TRUE;
1409 : : }
1410 : : }
1411 : : return FALSE;
1412 : : }
1413 : :
1414 : : /* Called from main thread */
1415 : 0 : pa_usec_t pa_sink_get_latency(pa_sink *s) {
1416 : 0 : pa_usec_t usec = 0;
1417 : :
1418 : 0 : pa_sink_assert_ref(s);
1419 [ # # ]: 0 : pa_assert_ctl_context();
1420 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
1421 : :
1422 : : /* The returned value is supposed to be in the time domain of the sound card! */
1423 : :
1424 [ # # ]: 0 : if (s->state == PA_SINK_SUSPENDED)
1425 : : return 0;
1426 : :
1427 [ # # ]: 0 : if (!(s->flags & PA_SINK_LATENCY))
1428 : : return 0;
1429 : :
1430 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
1431 : :
1432 : : /* usec is unsigned, so check that the offset can be added to usec without
1433 : : * underflowing. */
1434 [ # # ]: 0 : if (-s->latency_offset <= (int64_t) usec)
1435 : 0 : usec += s->latency_offset;
1436 : : else
1437 : 0 : usec = 0;
1438 : :
1439 : 0 : return usec;
1440 : : }
1441 : :
1442 : : /* Called from IO thread */
1443 : 0 : pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
1444 : 0 : pa_usec_t usec = 0;
1445 : : pa_msgobject *o;
1446 : :
1447 : 0 : pa_sink_assert_ref(s);
1448 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
1449 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1450 : :
1451 : : /* The returned value is supposed to be in the time domain of the sound card! */
1452 : :
1453 [ # # ]: 0 : if (s->thread_info.state == PA_SINK_SUSPENDED)
1454 : : return 0;
1455 : :
1456 [ # # ]: 0 : if (!(s->flags & PA_SINK_LATENCY))
1457 : : return 0;
1458 : :
1459 : 0 : o = PA_MSGOBJECT(s);
1460 : :
1461 : : /* FIXME: We probably should make this a proper vtable callback instead of going through process_msg() */
1462 : :
1463 [ # # ]: 0 : if (o->process_msg(o, PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
1464 : : return -1;
1465 : :
1466 : : /* usec is unsigned, so check that the offset can be added to usec without
1467 : : * underflowing. */
1468 [ # # ]: 0 : if (-s->thread_info.latency_offset <= (int64_t) usec)
1469 : 0 : usec += s->thread_info.latency_offset;
1470 : : else
1471 : 0 : usec = 0;
1472 : :
1473 : 0 : return usec;
1474 : : }
1475 : :
1476 : : /* Called from the main thread (and also from the IO thread while the main
1477 : : * thread is waiting).
1478 : : *
1479 : : * When a sink uses volume sharing, it never has the PA_SINK_FLAT_VOLUME flag
1480 : : * set. Instead, flat volume mode is detected by checking whether the root sink
1481 : : * has the flag set. */
1482 : 0 : pa_bool_t pa_sink_flat_volume_enabled(pa_sink *s) {
1483 : 0 : pa_sink_assert_ref(s);
1484 : :
1485 : 0 : s = pa_sink_get_master(s);
1486 : :
1487 [ # # ]: 0 : if (PA_LIKELY(s))
1488 : 0 : return (s->flags & PA_SINK_FLAT_VOLUME);
1489 : : else
1490 : : return FALSE;
1491 : : }
1492 : :
1493 : : /* Called from the main thread (and also from the IO thread while the main
1494 : : * thread is waiting). */
1495 : 0 : pa_sink *pa_sink_get_master(pa_sink *s) {
1496 : 0 : pa_sink_assert_ref(s);
1497 : :
1498 [ # # ][ # # ]: 0 : while (s && (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
1499 [ # # ]: 0 : if (PA_UNLIKELY(!s->input_to_master))
1500 : : return NULL;
1501 : :
1502 : 0 : s = s->input_to_master->sink;
1503 : : }
1504 : :
1505 : : return s;
1506 : : }
1507 : :
1508 : : /* Called from main context */
1509 : 0 : pa_bool_t pa_sink_is_passthrough(pa_sink *s) {
1510 : : pa_sink_input *alt_i;
1511 : : uint32_t idx;
1512 : :
1513 : 0 : pa_sink_assert_ref(s);
1514 : :
1515 : : /* one and only one PASSTHROUGH input can possibly be connected */
1516 [ # # ]: 0 : if (pa_idxset_size(s->inputs) == 1) {
1517 : 0 : alt_i = pa_idxset_first(s->inputs, &idx);
1518 : :
1519 [ # # ]: 0 : if (pa_sink_input_is_passthrough(alt_i))
1520 : : return TRUE;
1521 : : }
1522 : :
1523 : 0 : return FALSE;
1524 : : }
1525 : :
1526 : : /* Called from main context */
1527 : 0 : void pa_sink_enter_passthrough(pa_sink *s) {
1528 : : pa_cvolume volume;
1529 : :
1530 : : /* disable the monitor in passthrough mode */
1531 [ # # ]: 0 : if (s->monitor_source)
1532 : 0 : pa_source_suspend(s->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH);
1533 : :
1534 : : /* set the volume to NORM */
1535 : 0 : s->saved_volume = *pa_sink_get_volume(s, TRUE);
1536 : 0 : s->saved_save_volume = s->save_volume;
1537 : :
1538 : 0 : pa_cvolume_set(&volume, s->sample_spec.channels, PA_MIN(s->base_volume, PA_VOLUME_NORM));
1539 : 0 : pa_sink_set_volume(s, &volume, TRUE, FALSE);
1540 : 0 : }
1541 : :
1542 : : /* Called from main context */
1543 : 0 : void pa_sink_leave_passthrough(pa_sink *s) {
1544 : : /* Unsuspend monitor */
1545 [ # # ]: 0 : if (s->monitor_source)
1546 : 0 : pa_source_suspend(s->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH);
1547 : :
1548 : : /* Restore sink volume to what it was before we entered passthrough mode */
1549 : 0 : pa_sink_set_volume(s, &s->saved_volume, TRUE, s->saved_save_volume);
1550 : :
1551 : 0 : pa_cvolume_init(&s->saved_volume);
1552 : 0 : s->saved_save_volume = FALSE;
1553 : 0 : }
1554 : :
1555 : : /* Called from main context. */
1556 : 0 : static void compute_reference_ratio(pa_sink_input *i) {
1557 : 0 : unsigned c = 0;
1558 : : pa_cvolume remapped;
1559 : :
1560 [ # # ]: 0 : pa_assert(i);
1561 [ # # ]: 0 : pa_assert(pa_sink_flat_volume_enabled(i->sink));
1562 : :
1563 : : /*
1564 : : * Calculates the reference ratio from the sink's reference
1565 : : * volume. This basically calculates:
1566 : : *
1567 : : * i->reference_ratio = i->volume / i->sink->reference_volume
1568 : : */
1569 : :
1570 : 0 : remapped = i->sink->reference_volume;
1571 : 0 : pa_cvolume_remap(&remapped, &i->sink->channel_map, &i->channel_map);
1572 : :
1573 : 0 : i->reference_ratio.channels = i->sample_spec.channels;
1574 : :
1575 [ # # ]: 0 : for (c = 0; c < i->sample_spec.channels; c++) {
1576 : :
1577 : : /* We don't update when the sink volume is 0 anyway */
1578 [ # # ]: 0 : if (remapped.values[c] <= PA_VOLUME_MUTED)
1579 : 0 : continue;
1580 : :
1581 : : /* Don't update the reference ratio unless necessary */
1582 [ # # ]: 0 : if (pa_sw_volume_multiply(
1583 : : i->reference_ratio.values[c],
1584 : 0 : remapped.values[c]) == i->volume.values[c])
1585 : 0 : continue;
1586 : :
1587 : 0 : i->reference_ratio.values[c] = pa_sw_volume_divide(
1588 : : i->volume.values[c],
1589 : : remapped.values[c]);
1590 : : }
1591 : 0 : }
1592 : :
1593 : : /* Called from main context. Only called for the root sink in volume sharing
1594 : : * cases, except for internal recursive calls. */
1595 : 0 : static void compute_reference_ratios(pa_sink *s) {
1596 : : uint32_t idx;
1597 : : pa_sink_input *i;
1598 : :
1599 : 0 : pa_sink_assert_ref(s);
1600 [ # # ]: 0 : pa_assert_ctl_context();
1601 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
1602 [ # # ]: 0 : pa_assert(pa_sink_flat_volume_enabled(s));
1603 : :
1604 [ # # ]: 0 : PA_IDXSET_FOREACH(i, s->inputs, idx) {
1605 : 0 : compute_reference_ratio(i);
1606 : :
1607 [ # # ][ # # ]: 0 : if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
1608 : 0 : compute_reference_ratios(i->origin_sink);
1609 : : }
1610 : 0 : }
1611 : :
1612 : : /* Called from main context. Only called for the root sink in volume sharing
1613 : : * cases, except for internal recursive calls. */
1614 : 0 : static void compute_real_ratios(pa_sink *s) {
1615 : : pa_sink_input *i;
1616 : : uint32_t idx;
1617 : :
1618 : 0 : pa_sink_assert_ref(s);
1619 [ # # ]: 0 : pa_assert_ctl_context();
1620 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
1621 [ # # ]: 0 : pa_assert(pa_sink_flat_volume_enabled(s));
1622 : :
1623 [ # # ]: 0 : PA_IDXSET_FOREACH(i, s->inputs, idx) {
1624 : : unsigned c;
1625 : : pa_cvolume remapped;
1626 : :
1627 [ # # ][ # # ]: 0 : if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
1628 : : /* The origin sink uses volume sharing, so this input's real ratio
1629 : : * is handled as a special case - the real ratio must be 0 dB, and
1630 : : * as a result i->soft_volume must equal i->volume_factor. */
1631 : 0 : pa_cvolume_reset(&i->real_ratio, i->real_ratio.channels);
1632 : 0 : i->soft_volume = i->volume_factor;
1633 : :
1634 : 0 : compute_real_ratios(i->origin_sink);
1635 : :
1636 : 0 : continue;
1637 : : }
1638 : :
1639 : : /*
1640 : : * This basically calculates:
1641 : : *
1642 : : * i->real_ratio := i->volume / s->real_volume
1643 : : * i->soft_volume := i->real_ratio * i->volume_factor
1644 : : */
1645 : :
1646 : 0 : remapped = s->real_volume;
1647 : 0 : pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
1648 : :
1649 : 0 : i->real_ratio.channels = i->sample_spec.channels;
1650 : 0 : i->soft_volume.channels = i->sample_spec.channels;
1651 : :
1652 [ # # ]: 0 : for (c = 0; c < i->sample_spec.channels; c++) {
1653 : :
1654 [ # # ]: 0 : if (remapped.values[c] <= PA_VOLUME_MUTED) {
1655 : : /* We leave i->real_ratio untouched */
1656 : 0 : i->soft_volume.values[c] = PA_VOLUME_MUTED;
1657 : 0 : continue;
1658 : : }
1659 : :
1660 : : /* Don't lose accuracy unless necessary */
1661 [ # # ]: 0 : if (pa_sw_volume_multiply(
1662 : : i->real_ratio.values[c],
1663 : 0 : remapped.values[c]) != i->volume.values[c])
1664 : :
1665 : 0 : i->real_ratio.values[c] = pa_sw_volume_divide(
1666 : : i->volume.values[c],
1667 : : remapped.values[c]);
1668 : :
1669 : 0 : i->soft_volume.values[c] = pa_sw_volume_multiply(
1670 : : i->real_ratio.values[c],
1671 : : i->volume_factor.values[c]);
1672 : : }
1673 : :
1674 : : /* We don't copy the soft_volume to the thread_info data
1675 : : * here. That must be done by the caller */
1676 : : }
1677 : 0 : }
1678 : :
1679 : 0 : static pa_cvolume *cvolume_remap_minimal_impact(
1680 : : pa_cvolume *v,
1681 : : const pa_cvolume *template,
1682 : : const pa_channel_map *from,
1683 : : const pa_channel_map *to) {
1684 : :
1685 : : pa_cvolume t;
1686 : :
1687 [ # # ]: 0 : pa_assert(v);
1688 [ # # ]: 0 : pa_assert(template);
1689 [ # # ]: 0 : pa_assert(from);
1690 [ # # ]: 0 : pa_assert(to);
1691 [ # # ]: 0 : pa_assert(pa_cvolume_compatible_with_channel_map(v, from));
1692 [ # # ]: 0 : pa_assert(pa_cvolume_compatible_with_channel_map(template, to));
1693 : :
1694 : : /* Much like pa_cvolume_remap(), but tries to minimize impact when
1695 : : * mapping from sink input to sink volumes:
1696 : : *
1697 : : * If template is a possible remapping from v it is used instead
1698 : : * of remapping anew.
1699 : : *
1700 : : * If the channel maps don't match we set an all-channel volume on
1701 : : * the sink to ensure that changing a volume on one stream has no
1702 : : * effect that cannot be compensated for in another stream that
1703 : : * does not have the same channel map as the sink. */
1704 : :
1705 [ # # ]: 0 : if (pa_channel_map_equal(from, to))
1706 : : return v;
1707 : :
1708 : 0 : t = *template;
1709 [ # # ]: 0 : if (pa_cvolume_equal(pa_cvolume_remap(&t, to, from), v)) {
1710 : 0 : *v = *template;
1711 : 0 : return v;
1712 : : }
1713 : :
1714 : 0 : pa_cvolume_set(v, to->channels, pa_cvolume_max(v));
1715 : 0 : return v;
1716 : : }
1717 : :
1718 : : /* Called from main thread. Only called for the root sink in volume sharing
1719 : : * cases, except for internal recursive calls. */
1720 : 0 : static void get_maximum_input_volume(pa_sink *s, pa_cvolume *max_volume, const pa_channel_map *channel_map) {
1721 : : pa_sink_input *i;
1722 : : uint32_t idx;
1723 : :
1724 : 0 : pa_sink_assert_ref(s);
1725 [ # # ]: 0 : pa_assert(max_volume);
1726 [ # # ]: 0 : pa_assert(channel_map);
1727 [ # # ]: 0 : pa_assert(pa_sink_flat_volume_enabled(s));
1728 : :
1729 [ # # ]: 0 : PA_IDXSET_FOREACH(i, s->inputs, idx) {
1730 : : pa_cvolume remapped;
1731 : :
1732 [ # # ][ # # ]: 0 : if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
1733 : 0 : get_maximum_input_volume(i->origin_sink, max_volume, channel_map);
1734 : :
1735 : : /* Ignore this input. The origin sink uses volume sharing, so this
1736 : : * input's volume will be set to be equal to the root sink's real
1737 : : * volume. Obviously this input's current volume must not then
1738 : : * affect what the root sink's real volume will be. */
1739 : 0 : continue;
1740 : : }
1741 : :
1742 : 0 : remapped = i->volume;
1743 : 0 : cvolume_remap_minimal_impact(&remapped, max_volume, &i->channel_map, channel_map);
1744 : 0 : pa_cvolume_merge(max_volume, max_volume, &remapped);
1745 : : }
1746 : 0 : }
1747 : :
1748 : : /* Called from main thread. Only called for the root sink in volume sharing
1749 : : * cases, except for internal recursive calls. */
1750 : 0 : static pa_bool_t has_inputs(pa_sink *s) {
1751 : : pa_sink_input *i;
1752 : : uint32_t idx;
1753 : :
1754 : 0 : pa_sink_assert_ref(s);
1755 : :
1756 [ # # ]: 0 : PA_IDXSET_FOREACH(i, s->inputs, idx) {
1757 [ # # ][ # # ]: 0 : if (!i->origin_sink || !(i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || has_inputs(i->origin_sink))
[ # # ]
1758 : : return TRUE;
1759 : : }
1760 : :
1761 : : return FALSE;
1762 : : }
1763 : :
1764 : : /* Called from main thread. Only called for the root sink in volume sharing
1765 : : * cases, except for internal recursive calls. */
1766 : 0 : static void update_real_volume(pa_sink *s, const pa_cvolume *new_volume, pa_channel_map *channel_map) {
1767 : : pa_sink_input *i;
1768 : : uint32_t idx;
1769 : :
1770 : 0 : pa_sink_assert_ref(s);
1771 [ # # ]: 0 : pa_assert(new_volume);
1772 [ # # ]: 0 : pa_assert(channel_map);
1773 : :
1774 : 0 : s->real_volume = *new_volume;
1775 : 0 : pa_cvolume_remap(&s->real_volume, channel_map, &s->channel_map);
1776 : :
1777 [ # # ]: 0 : PA_IDXSET_FOREACH(i, s->inputs, idx) {
1778 [ # # ][ # # ]: 0 : if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
1779 [ # # ]: 0 : if (pa_sink_flat_volume_enabled(s)) {
1780 : 0 : pa_cvolume old_volume = i->volume;
1781 : :
1782 : : /* Follow the root sink's real volume. */
1783 : 0 : i->volume = *new_volume;
1784 : 0 : pa_cvolume_remap(&i->volume, channel_map, &i->channel_map);
1785 : 0 : compute_reference_ratio(i);
1786 : :
1787 : : /* The volume changed, let's tell people so */
1788 [ # # ]: 0 : if (!pa_cvolume_equal(&old_volume, &i->volume)) {
1789 [ # # ]: 0 : if (i->volume_changed)
1790 : 0 : i->volume_changed(i);
1791 : :
1792 : 0 : pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
1793 : : }
1794 : : }
1795 : :
1796 : 0 : update_real_volume(i->origin_sink, new_volume, channel_map);
1797 : : }
1798 : : }
1799 : 0 : }
1800 : :
1801 : : /* Called from main thread. Only called for the root sink in shared volume
1802 : : * cases. */
1803 : 0 : static void compute_real_volume(pa_sink *s) {
1804 : 0 : pa_sink_assert_ref(s);
1805 [ # # ]: 0 : pa_assert_ctl_context();
1806 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
1807 [ # # ]: 0 : pa_assert(pa_sink_flat_volume_enabled(s));
1808 [ # # ]: 0 : pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
1809 : :
1810 : : /* This determines the maximum volume of all streams and sets
1811 : : * s->real_volume accordingly. */
1812 : :
1813 [ # # ]: 0 : if (!has_inputs(s)) {
1814 : : /* In the special case that we have no sink inputs we leave the
1815 : : * volume unmodified. */
1816 : 0 : update_real_volume(s, &s->reference_volume, &s->channel_map);
1817 : 0 : return;
1818 : : }
1819 : :
1820 : 0 : pa_cvolume_mute(&s->real_volume, s->channel_map.channels);
1821 : :
1822 : : /* First let's determine the new maximum volume of all inputs
1823 : : * connected to this sink */
1824 : 0 : get_maximum_input_volume(s, &s->real_volume, &s->channel_map);
1825 : 0 : update_real_volume(s, &s->real_volume, &s->channel_map);
1826 : :
1827 : : /* Then, let's update the real ratios/soft volumes of all inputs
1828 : : * connected to this sink */
1829 : 0 : compute_real_ratios(s);
1830 : : }
1831 : :
1832 : : /* Called from main thread. Only called for the root sink in shared volume
1833 : : * cases, except for internal recursive calls. */
1834 : 0 : static void propagate_reference_volume(pa_sink *s) {
1835 : : pa_sink_input *i;
1836 : : uint32_t idx;
1837 : :
1838 : 0 : pa_sink_assert_ref(s);
1839 [ # # ]: 0 : pa_assert_ctl_context();
1840 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
1841 [ # # ]: 0 : pa_assert(pa_sink_flat_volume_enabled(s));
1842 : :
1843 : : /* This is called whenever the sink volume changes that is not
1844 : : * caused by a sink input volume change. We need to fix up the
1845 : : * sink input volumes accordingly */
1846 : :
1847 [ # # ]: 0 : PA_IDXSET_FOREACH(i, s->inputs, idx) {
1848 : : pa_cvolume old_volume;
1849 : :
1850 [ # # ][ # # ]: 0 : if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
1851 : 0 : propagate_reference_volume(i->origin_sink);
1852 : :
1853 : : /* Since the origin sink uses volume sharing, this input's volume
1854 : : * needs to be updated to match the root sink's real volume, but
1855 : : * that will be done later in update_shared_real_volume(). */
1856 : 0 : continue;
1857 : : }
1858 : :
1859 : 0 : old_volume = i->volume;
1860 : :
1861 : : /* This basically calculates:
1862 : : *
1863 : : * i->volume := s->reference_volume * i->reference_ratio */
1864 : :
1865 : 0 : i->volume = s->reference_volume;
1866 : 0 : pa_cvolume_remap(&i->volume, &s->channel_map, &i->channel_map);
1867 : 0 : pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio);
1868 : :
1869 : : /* The volume changed, let's tell people so */
1870 [ # # ]: 0 : if (!pa_cvolume_equal(&old_volume, &i->volume)) {
1871 : :
1872 [ # # ]: 0 : if (i->volume_changed)
1873 : 0 : i->volume_changed(i);
1874 : :
1875 : 0 : pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
1876 : : }
1877 : : }
1878 : 0 : }
1879 : :
1880 : : /* Called from main thread. Only called for the root sink in volume sharing
1881 : : * cases, except for internal recursive calls. The return value indicates
1882 : : * whether any reference volume actually changed. */
1883 : 0 : static pa_bool_t update_reference_volume(pa_sink *s, const pa_cvolume *v, const pa_channel_map *channel_map, pa_bool_t save) {
1884 : : pa_cvolume volume;
1885 : : pa_bool_t reference_volume_changed;
1886 : : pa_sink_input *i;
1887 : : uint32_t idx;
1888 : :
1889 : 0 : pa_sink_assert_ref(s);
1890 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
1891 [ # # ]: 0 : pa_assert(v);
1892 [ # # ]: 0 : pa_assert(channel_map);
1893 [ # # ]: 0 : pa_assert(pa_cvolume_valid(v));
1894 : :
1895 : 0 : volume = *v;
1896 : 0 : pa_cvolume_remap(&volume, channel_map, &s->channel_map);
1897 : :
1898 : 0 : reference_volume_changed = !pa_cvolume_equal(&volume, &s->reference_volume);
1899 : 0 : s->reference_volume = volume;
1900 : :
1901 [ # # ][ # # ]: 0 : s->save_volume = (!reference_volume_changed && s->save_volume) || save;
[ # # ]
1902 : :
1903 [ # # ]: 0 : if (reference_volume_changed)
1904 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1905 [ # # ]: 0 : else if (!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
1906 : : /* If the root sink's volume doesn't change, then there can't be any
1907 : : * changes in the other sinks in the sink tree either.
1908 : : *
1909 : : * It's probably theoretically possible that even if the root sink's
1910 : : * volume changes slightly, some filter sink doesn't change its volume
1911 : : * due to rounding errors. If that happens, we still want to propagate
1912 : : * the changed root sink volume to the sinks connected to the
1913 : : * intermediate sink that didn't change its volume. This theoretical
1914 : : * possibility is the reason why we have that !(s->flags &
1915 : : * PA_SINK_SHARE_VOLUME_WITH_MASTER) condition. Probably nobody would
1916 : : * notice even if we returned here FALSE always if
1917 : : * reference_volume_changed is FALSE. */
1918 : : return FALSE;
1919 : :
1920 [ # # ]: 0 : PA_IDXSET_FOREACH(i, s->inputs, idx) {
1921 [ # # ][ # # ]: 0 : if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
1922 : 0 : update_reference_volume(i->origin_sink, v, channel_map, FALSE);
1923 : : }
1924 : :
1925 : : return TRUE;
1926 : : }
1927 : :
1928 : : /* Called from main thread */
1929 : 0 : void pa_sink_set_volume(
1930 : : pa_sink *s,
1931 : : const pa_cvolume *volume,
1932 : : pa_bool_t send_msg,
1933 : : pa_bool_t save) {
1934 : :
1935 : : pa_cvolume new_reference_volume;
1936 : : pa_sink *root_sink;
1937 : :
1938 : 0 : pa_sink_assert_ref(s);
1939 [ # # ]: 0 : pa_assert_ctl_context();
1940 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
1941 [ # # ][ # # ]: 0 : pa_assert(!volume || pa_cvolume_valid(volume));
1942 [ # # ][ # # ]: 0 : pa_assert(volume || pa_sink_flat_volume_enabled(s));
1943 [ # # ][ # # ]: 0 : pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
[ # # ][ # # ]
1944 : :
1945 : : /* make sure we don't change the volume when a PASSTHROUGH input is connected ...
1946 : : * ... *except* if we're being invoked to reset the volume to ensure 0 dB gain */
1947 [ # # ][ # # ]: 0 : if (pa_sink_is_passthrough(s) && (!volume || !pa_cvolume_is_norm(volume))) {
[ # # ]
1948 : 0 : pa_log_warn("Cannot change volume, Sink is connected to PASSTHROUGH input");
1949 : 0 : return;
1950 : : }
1951 : :
1952 : : /* In case of volume sharing, the volume is set for the root sink first,
1953 : : * from which it's then propagated to the sharing sinks. */
1954 : 0 : root_sink = pa_sink_get_master(s);
1955 : :
1956 [ # # ]: 0 : if (PA_UNLIKELY(!root_sink))
1957 : : return;
1958 : :
1959 : : /* As a special exception we accept mono volumes on all sinks --
1960 : : * even on those with more complex channel maps */
1961 : :
1962 [ # # ]: 0 : if (volume) {
1963 [ # # ]: 0 : if (pa_cvolume_compatible(volume, &s->sample_spec))
1964 : 0 : new_reference_volume = *volume;
1965 : : else {
1966 : 0 : new_reference_volume = s->reference_volume;
1967 : 0 : pa_cvolume_scale(&new_reference_volume, pa_cvolume_max(volume));
1968 : : }
1969 : :
1970 : 0 : pa_cvolume_remap(&new_reference_volume, &s->channel_map, &root_sink->channel_map);
1971 : :
1972 [ # # ]: 0 : if (update_reference_volume(root_sink, &new_reference_volume, &root_sink->channel_map, save)) {
1973 [ # # ]: 0 : if (pa_sink_flat_volume_enabled(root_sink)) {
1974 : : /* OK, propagate this volume change back to the inputs */
1975 : 0 : propagate_reference_volume(root_sink);
1976 : :
1977 : : /* And now recalculate the real volume */
1978 : 0 : compute_real_volume(root_sink);
1979 : : } else
1980 : 0 : update_real_volume(root_sink, &root_sink->reference_volume, &root_sink->channel_map);
1981 : : }
1982 : :
1983 : : } else {
1984 : : /* If volume is NULL we synchronize the sink's real and
1985 : : * reference volumes with the stream volumes. */
1986 : :
1987 [ # # ]: 0 : pa_assert(pa_sink_flat_volume_enabled(root_sink));
1988 : :
1989 : : /* Ok, let's determine the new real volume */
1990 : 0 : compute_real_volume(root_sink);
1991 : :
1992 : : /* Let's 'push' the reference volume if necessary */
1993 : 0 : pa_cvolume_merge(&new_reference_volume, &s->reference_volume, &root_sink->real_volume);
1994 : : /* If the sink and it's root don't have the same number of channels, we need to remap */
1995 [ # # ][ # # ]: 0 : if (s != root_sink && !pa_channel_map_equal(&s->channel_map, &root_sink->channel_map))
1996 : 0 : pa_cvolume_remap(&new_reference_volume, &s->channel_map, &root_sink->channel_map);
1997 : 0 : update_reference_volume(root_sink, &new_reference_volume, &root_sink->channel_map, save);
1998 : :
1999 : : /* Now that the reference volume is updated, we can update the streams'
2000 : : * reference ratios. */
2001 : 0 : compute_reference_ratios(root_sink);
2002 : : }
2003 : :
2004 [ # # ]: 0 : if (root_sink->set_volume) {
2005 : : /* If we have a function set_volume(), then we do not apply a
2006 : : * soft volume by default. However, set_volume() is free to
2007 : : * apply one to root_sink->soft_volume */
2008 : :
2009 : 0 : pa_cvolume_reset(&root_sink->soft_volume, root_sink->sample_spec.channels);
2010 [ # # ]: 0 : if (!(root_sink->flags & PA_SINK_DEFERRED_VOLUME))
2011 : 0 : root_sink->set_volume(root_sink);
2012 : :
2013 : : } else
2014 : : /* If we have no function set_volume(), then the soft volume
2015 : : * becomes the real volume */
2016 : 0 : root_sink->soft_volume = root_sink->real_volume;
2017 : :
2018 : : /* This tells the sink that soft volume and/or real volume changed */
2019 [ # # ]: 0 : if (send_msg)
2020 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(root_sink->asyncmsgq, PA_MSGOBJECT(root_sink), PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL) == 0);
2021 : : }
2022 : :
2023 : : /* Called from the io thread if sync volume is used, otherwise from the main thread.
2024 : : * Only to be called by sink implementor */
2025 : 0 : void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
2026 : :
2027 : 0 : pa_sink_assert_ref(s);
2028 [ # # ]: 0 : pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
2029 : :
2030 [ # # ]: 0 : if (s->flags & PA_SINK_DEFERRED_VOLUME)
2031 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
2032 : : else
2033 [ # # ]: 0 : pa_assert_ctl_context();
2034 : :
2035 [ # # ]: 0 : if (!volume)
2036 : 0 : pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
2037 : : else
2038 : 0 : s->soft_volume = *volume;
2039 : :
2040 [ # # ][ # # ]: 0 : if (PA_SINK_IS_LINKED(s->state) && !(s->flags & PA_SINK_DEFERRED_VOLUME))
2041 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
2042 : : else
2043 : 0 : s->thread_info.soft_volume = s->soft_volume;
2044 : 0 : }
2045 : :
2046 : : /* Called from the main thread. Only called for the root sink in volume sharing
2047 : : * cases, except for internal recursive calls. */
2048 : 0 : static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume) {
2049 : : pa_sink_input *i;
2050 : : uint32_t idx;
2051 : :
2052 : 0 : pa_sink_assert_ref(s);
2053 [ # # ]: 0 : pa_assert(old_real_volume);
2054 [ # # ]: 0 : pa_assert_ctl_context();
2055 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
2056 : :
2057 : : /* This is called when the hardware's real volume changes due to
2058 : : * some external event. We copy the real volume into our
2059 : : * reference volume and then rebuild the stream volumes based on
2060 : : * i->real_ratio which should stay fixed. */
2061 : :
2062 [ # # ]: 0 : if (!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
2063 [ # # ]: 0 : if (pa_cvolume_equal(old_real_volume, &s->real_volume))
2064 : 0 : return;
2065 : :
2066 : : /* 1. Make the real volume the reference volume */
2067 : 0 : update_reference_volume(s, &s->real_volume, &s->channel_map, TRUE);
2068 : : }
2069 : :
2070 [ # # ]: 0 : if (pa_sink_flat_volume_enabled(s)) {
2071 : :
2072 [ # # ]: 0 : PA_IDXSET_FOREACH(i, s->inputs, idx) {
2073 : 0 : pa_cvolume old_volume = i->volume;
2074 : :
2075 : : /* 2. Since the sink's reference and real volumes are equal
2076 : : * now our ratios should be too. */
2077 : 0 : i->reference_ratio = i->real_ratio;
2078 : :
2079 : : /* 3. Recalculate the new stream reference volume based on the
2080 : : * reference ratio and the sink's reference volume.
2081 : : *
2082 : : * This basically calculates:
2083 : : *
2084 : : * i->volume = s->reference_volume * i->reference_ratio
2085 : : *
2086 : : * This is identical to propagate_reference_volume() */
2087 : 0 : i->volume = s->reference_volume;
2088 : 0 : pa_cvolume_remap(&i->volume, &s->channel_map, &i->channel_map);
2089 : 0 : pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio);
2090 : :
2091 : : /* Notify if something changed */
2092 [ # # ]: 0 : if (!pa_cvolume_equal(&old_volume, &i->volume)) {
2093 : :
2094 [ # # ]: 0 : if (i->volume_changed)
2095 : 0 : i->volume_changed(i);
2096 : :
2097 : 0 : pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
2098 : : }
2099 : :
2100 [ # # ][ # # ]: 0 : if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
2101 : 0 : propagate_real_volume(i->origin_sink, old_real_volume);
2102 : : }
2103 : : }
2104 : :
2105 : : /* Something got changed in the hardware. It probably makes sense
2106 : : * to save changed hw settings given that hw volume changes not
2107 : : * triggered by PA are almost certainly done by the user. */
2108 [ # # ]: 0 : if (!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
2109 : 0 : s->save_volume = TRUE;
2110 : : }
2111 : :
2112 : : /* Called from io thread */
2113 : 0 : void pa_sink_update_volume_and_mute(pa_sink *s) {
2114 [ # # ]: 0 : pa_assert(s);
2115 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
2116 : :
2117 : 0 : pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE, NULL, 0, NULL, NULL);
2118 : 0 : }
2119 : :
2120 : : /* Called from main thread */
2121 : 0 : const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
2122 : 0 : pa_sink_assert_ref(s);
2123 [ # # ]: 0 : pa_assert_ctl_context();
2124 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
2125 : :
2126 [ # # ][ # # ]: 0 : if (s->refresh_volume || force_refresh) {
2127 : : struct pa_cvolume old_real_volume;
2128 : :
2129 [ # # ]: 0 : pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
2130 : :
2131 : 0 : old_real_volume = s->real_volume;
2132 : :
2133 [ # # ][ # # ]: 0 : if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_volume)
2134 : 0 : s->get_volume(s);
2135 : :
2136 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
2137 : :
2138 : 0 : update_real_volume(s, &s->real_volume, &s->channel_map);
2139 : 0 : propagate_real_volume(s, &old_real_volume);
2140 : : }
2141 : :
2142 : 0 : return &s->reference_volume;
2143 : : }
2144 : :
2145 : : /* Called from main thread. In volume sharing cases, only the root sink may
2146 : : * call this. */
2147 : 0 : void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_real_volume) {
2148 : : pa_cvolume old_real_volume;
2149 : :
2150 : 0 : pa_sink_assert_ref(s);
2151 [ # # ]: 0 : pa_assert_ctl_context();
2152 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
2153 [ # # ]: 0 : pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
2154 : :
2155 : : /* The sink implementor may call this if the volume changed to make sure everyone is notified */
2156 : :
2157 : 0 : old_real_volume = s->real_volume;
2158 : 0 : update_real_volume(s, new_real_volume, &s->channel_map);
2159 : 0 : propagate_real_volume(s, &old_real_volume);
2160 : 0 : }
2161 : :
2162 : : /* Called from main thread */
2163 : 0 : void pa_sink_set_mute(pa_sink *s, pa_bool_t mute, pa_bool_t save) {
2164 : : pa_bool_t old_muted;
2165 : :
2166 : 0 : pa_sink_assert_ref(s);
2167 [ # # ]: 0 : pa_assert_ctl_context();
2168 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
2169 : :
2170 : 0 : old_muted = s->muted;
2171 : 0 : s->muted = mute;
2172 [ # # ][ # # ]: 0 : s->save_muted = (old_muted == s->muted && s->save_muted) || save;
[ # # ]
2173 : :
2174 [ # # ][ # # ]: 0 : if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute)
2175 : 0 : s->set_mute(s);
2176 : :
2177 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
2178 : :
2179 [ # # ]: 0 : if (old_muted != s->muted)
2180 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
2181 : 0 : }
2182 : :
2183 : : /* Called from main thread */
2184 : 0 : pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
2185 : :
2186 : 0 : pa_sink_assert_ref(s);
2187 [ # # ]: 0 : pa_assert_ctl_context();
2188 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
2189 : :
2190 [ # # ][ # # ]: 0 : if (s->refresh_muted || force_refresh) {
2191 : 0 : pa_bool_t old_muted = s->muted;
2192 : :
2193 [ # # ][ # # ]: 0 : if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_mute)
2194 : 0 : s->get_mute(s);
2195 : :
2196 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
2197 : :
2198 [ # # ]: 0 : if (old_muted != s->muted) {
2199 : 0 : s->save_muted = TRUE;
2200 : :
2201 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
2202 : :
2203 : : /* Make sure the soft mute status stays in sync */
2204 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
2205 : : }
2206 : : }
2207 : :
2208 : 0 : return s->muted;
2209 : : }
2210 : :
2211 : : /* Called from main thread */
2212 : 0 : void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted) {
2213 : 0 : pa_sink_assert_ref(s);
2214 [ # # ]: 0 : pa_assert_ctl_context();
2215 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
2216 : :
2217 : : /* The sink implementor may call this if the volume changed to make sure everyone is notified */
2218 : :
2219 [ # # ]: 0 : if (s->muted == new_muted)
2220 : 0 : return;
2221 : :
2222 : 0 : s->muted = new_muted;
2223 : 0 : s->save_muted = TRUE;
2224 : :
2225 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
2226 : : }
2227 : :
2228 : : /* Called from main thread */
2229 : 0 : pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
2230 : 0 : pa_sink_assert_ref(s);
2231 [ # # ]: 0 : pa_assert_ctl_context();
2232 : :
2233 [ # # ]: 0 : if (p)
2234 : 0 : pa_proplist_update(s->proplist, mode, p);
2235 : :
2236 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->state)) {
2237 : 0 : pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
2238 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
2239 : : }
2240 : :
2241 : 0 : return TRUE;
2242 : : }
2243 : :
2244 : : /* Called from main thread */
2245 : : /* FIXME -- this should be dropped and be merged into pa_sink_update_proplist() */
2246 : 0 : void pa_sink_set_description(pa_sink *s, const char *description) {
2247 : : const char *old;
2248 : 0 : pa_sink_assert_ref(s);
2249 [ # # ]: 0 : pa_assert_ctl_context();
2250 : :
2251 [ # # ][ # # ]: 0 : if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
2252 : : return;
2253 : :
2254 : 0 : old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
2255 : :
2256 [ # # ][ # # ]: 0 : if (old && description && pa_streq(old, description))
2257 : : return;
2258 : :
2259 [ # # ]: 0 : if (description)
2260 : 0 : pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
2261 : : else
2262 : 0 : pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
2263 : :
2264 [ # # ]: 0 : if (s->monitor_source) {
2265 : : char *n;
2266 : :
2267 [ # # ]: 0 : n = pa_sprintf_malloc("Monitor Source of %s", description ? description : s->name);
2268 : 0 : pa_source_set_description(s->monitor_source, n);
2269 : 0 : pa_xfree(n);
2270 : : }
2271 : :
2272 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->state)) {
2273 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
2274 : 0 : pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
2275 : : }
2276 : : }
2277 : :
2278 : : /* Called from main thread */
2279 : 0 : unsigned pa_sink_linked_by(pa_sink *s) {
2280 : : unsigned ret;
2281 : :
2282 : 0 : pa_sink_assert_ref(s);
2283 [ # # ]: 0 : pa_assert_ctl_context();
2284 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
2285 : :
2286 : 0 : ret = pa_idxset_size(s->inputs);
2287 : :
2288 : : /* We add in the number of streams connected to us here. Please
2289 : : * note the asymmetry to pa_sink_used_by()! */
2290 : :
2291 [ # # ]: 0 : if (s->monitor_source)
2292 : 0 : ret += pa_source_linked_by(s->monitor_source);
2293 : :
2294 : 0 : return ret;
2295 : : }
2296 : :
2297 : : /* Called from main thread */
2298 : 0 : unsigned pa_sink_used_by(pa_sink *s) {
2299 : : unsigned ret;
2300 : :
2301 : 0 : pa_sink_assert_ref(s);
2302 [ # # ]: 0 : pa_assert_ctl_context();
2303 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
2304 : :
2305 : 0 : ret = pa_idxset_size(s->inputs);
2306 [ # # ]: 0 : pa_assert(ret >= s->n_corked);
2307 : :
2308 : : /* Streams connected to our monitor source do not matter for
2309 : : * pa_sink_used_by()!.*/
2310 : :
2311 : 0 : return ret - s->n_corked;
2312 : : }
2313 : :
2314 : : /* Called from main thread */
2315 : 0 : unsigned pa_sink_check_suspend(pa_sink *s) {
2316 : : unsigned ret;
2317 : : pa_sink_input *i;
2318 : : uint32_t idx;
2319 : :
2320 : 0 : pa_sink_assert_ref(s);
2321 [ # # ]: 0 : pa_assert_ctl_context();
2322 : :
2323 [ # # ]: 0 : if (!PA_SINK_IS_LINKED(s->state))
2324 : : return 0;
2325 : :
2326 : 0 : ret = 0;
2327 : :
2328 [ # # ]: 0 : PA_IDXSET_FOREACH(i, s->inputs, idx) {
2329 : : pa_sink_input_state_t st;
2330 : :
2331 : 0 : st = pa_sink_input_get_state(i);
2332 : :
2333 : : /* We do not assert here. It is perfectly valid for a sink input to
2334 : : * be in the INIT state (i.e. created, marked done but not yet put)
2335 : : * and we should not care if it's unlinked as it won't contribute
2336 : : * towards our busy status.
2337 : : */
2338 [ # # ]: 0 : if (!PA_SINK_INPUT_IS_LINKED(st))
2339 : 0 : continue;
2340 : :
2341 [ # # ]: 0 : if (st == PA_SINK_INPUT_CORKED)
2342 : 0 : continue;
2343 : :
2344 [ # # ]: 0 : if (i->flags & PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND)
2345 : 0 : continue;
2346 : :
2347 : 0 : ret ++;
2348 : : }
2349 : :
2350 [ # # ]: 0 : if (s->monitor_source)
2351 : 0 : ret += pa_source_check_suspend(s->monitor_source);
2352 : :
2353 : 0 : return ret;
2354 : : }
2355 : :
2356 : : /* Called from the IO thread */
2357 : 0 : static void sync_input_volumes_within_thread(pa_sink *s) {
2358 : : pa_sink_input *i;
2359 : 0 : void *state = NULL;
2360 : :
2361 : 0 : pa_sink_assert_ref(s);
2362 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
2363 : :
2364 [ # # ]: 0 : PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
2365 [ # # ]: 0 : if (pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume))
2366 : 0 : continue;
2367 : :
2368 : 0 : i->thread_info.soft_volume = i->soft_volume;
2369 : 0 : pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
2370 : : }
2371 : 0 : }
2372 : :
2373 : : /* Called from the IO thread. Only called for the root sink in volume sharing
2374 : : * cases, except for internal recursive calls. */
2375 : 0 : static void set_shared_volume_within_thread(pa_sink *s) {
2376 : 0 : pa_sink_input *i = NULL;
2377 : 0 : void *state = NULL;
2378 : :
2379 : 0 : pa_sink_assert_ref(s);
2380 : :
2381 : 0 : PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME_SYNCED, NULL, 0, NULL);
2382 : :
2383 [ # # ]: 0 : PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
2384 [ # # ][ # # ]: 0 : if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
2385 : 0 : set_shared_volume_within_thread(i->origin_sink);
2386 : : }
2387 : 0 : }
2388 : :
2389 : : /* Called from IO thread, except when it is not */
2390 : 0 : int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
2391 : 0 : pa_sink *s = PA_SINK(o);
2392 : 0 : pa_sink_assert_ref(s);
2393 : :
2394 [ # # # # : 0 : switch ((pa_sink_message_t) code) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
2395 : :
2396 : : case PA_SINK_MESSAGE_ADD_INPUT: {
2397 : 0 : pa_sink_input *i = PA_SINK_INPUT(userdata);
2398 : :
2399 : : /* If you change anything here, make sure to change the
2400 : : * sink input handling a few lines down at
2401 : : * PA_SINK_MESSAGE_FINISH_MOVE, too. */
2402 : :
2403 : 0 : pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
2404 : :
2405 : : /* Since the caller sleeps in pa_sink_input_put(), we can
2406 : : * safely access data outside of thread_info even though
2407 : : * it is mutable */
2408 : :
2409 [ # # ]: 0 : if ((i->thread_info.sync_prev = i->sync_prev)) {
2410 [ # # ]: 0 : pa_assert(i->sink == i->thread_info.sync_prev->sink);
2411 [ # # ]: 0 : pa_assert(i->sync_prev->sync_next == i);
2412 : 0 : i->thread_info.sync_prev->thread_info.sync_next = i;
2413 : : }
2414 : :
2415 [ # # ]: 0 : if ((i->thread_info.sync_next = i->sync_next)) {
2416 [ # # ]: 0 : pa_assert(i->sink == i->thread_info.sync_next->sink);
2417 [ # # ]: 0 : pa_assert(i->sync_next->sync_prev == i);
2418 : 0 : i->thread_info.sync_next->thread_info.sync_prev = i;
2419 : : }
2420 : :
2421 [ # # ]: 0 : pa_assert(!i->thread_info.attached);
2422 : 0 : i->thread_info.attached = TRUE;
2423 : :
2424 [ # # ]: 0 : if (i->attach)
2425 : 0 : i->attach(i);
2426 : :
2427 : 0 : pa_sink_input_set_state_within_thread(i, i->state);
2428 : :
2429 : : /* The requested latency of the sink input needs to be
2430 : : * fixed up and then configured on the sink */
2431 : :
2432 [ # # ]: 0 : if (i->thread_info.requested_sink_latency != (pa_usec_t) -1)
2433 : 0 : pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
2434 : :
2435 : 0 : pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
2436 : 0 : pa_sink_input_update_max_request(i, s->thread_info.max_request);
2437 : :
2438 : : /* We don't rewind here automatically. This is left to the
2439 : : * sink input implementor because some sink inputs need a
2440 : : * slow start, i.e. need some time to buffer client
2441 : : * samples before beginning streaming. */
2442 : :
2443 : : /* FIXME: Actually rewinding should be requested before
2444 : : * updating the sink requested latency, because updating
2445 : : * the requested latency updates also max_rewind of the
2446 : : * sink. Now consider this: a sink has a 10 s buffer and
2447 : : * nobody has requested anything less. Then a new stream
2448 : : * appears while the sink buffer is full. The new stream
2449 : : * requests e.g. 100 ms latency. That request is forwarded
2450 : : * to the sink, so now max_rewind is 100 ms. When a rewind
2451 : : * is requested, the sink will only rewind 100 ms, and the
2452 : : * new stream will have to wait about 10 seconds before it
2453 : : * becomes audible. */
2454 : :
2455 : : /* In flat volume mode we need to update the volume as
2456 : : * well */
2457 : 0 : return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
2458 : : }
2459 : :
2460 : : case PA_SINK_MESSAGE_REMOVE_INPUT: {
2461 : 0 : pa_sink_input *i = PA_SINK_INPUT(userdata);
2462 : :
2463 : : /* If you change anything here, make sure to change the
2464 : : * sink input handling a few lines down at
2465 : : * PA_SINK_MESSAGE_START_MOVE, too. */
2466 : :
2467 [ # # ]: 0 : if (i->detach)
2468 : 0 : i->detach(i);
2469 : :
2470 : 0 : pa_sink_input_set_state_within_thread(i, i->state);
2471 : :
2472 [ # # ]: 0 : pa_assert(i->thread_info.attached);
2473 : 0 : i->thread_info.attached = FALSE;
2474 : :
2475 : : /* Since the caller sleeps in pa_sink_input_unlink(),
2476 : : * we can safely access data outside of thread_info even
2477 : : * though it is mutable */
2478 : :
2479 [ # # ]: 0 : pa_assert(!i->sync_prev);
2480 [ # # ]: 0 : pa_assert(!i->sync_next);
2481 : :
2482 [ # # ]: 0 : if (i->thread_info.sync_prev) {
2483 : 0 : i->thread_info.sync_prev->thread_info.sync_next = i->thread_info.sync_prev->sync_next;
2484 : 0 : i->thread_info.sync_prev = NULL;
2485 : : }
2486 : :
2487 [ # # ]: 0 : if (i->thread_info.sync_next) {
2488 : 0 : i->thread_info.sync_next->thread_info.sync_prev = i->thread_info.sync_next->sync_prev;
2489 : 0 : i->thread_info.sync_next = NULL;
2490 : : }
2491 : :
2492 [ # # ]: 0 : if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
2493 : 0 : pa_sink_input_unref(i);
2494 : :
2495 : 0 : pa_sink_invalidate_requested_latency(s, TRUE);
2496 : 0 : pa_sink_request_rewind(s, (size_t) -1);
2497 : :
2498 : : /* In flat volume mode we need to update the volume as
2499 : : * well */
2500 : 0 : return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
2501 : : }
2502 : :
2503 : : case PA_SINK_MESSAGE_START_MOVE: {
2504 : 0 : pa_sink_input *i = PA_SINK_INPUT(userdata);
2505 : :
2506 : : /* We don't support moving synchronized streams. */
2507 [ # # ]: 0 : pa_assert(!i->sync_prev);
2508 [ # # ]: 0 : pa_assert(!i->sync_next);
2509 [ # # ]: 0 : pa_assert(!i->thread_info.sync_next);
2510 [ # # ]: 0 : pa_assert(!i->thread_info.sync_prev);
2511 : :
2512 [ # # ]: 0 : if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
2513 : 0 : pa_usec_t usec = 0;
2514 : : size_t sink_nbytes, total_nbytes;
2515 : :
2516 : : /* The old sink probably has some audio from this
2517 : : * stream in its buffer. We want to "take it back" as
2518 : : * much as possible and play it to the new sink. We
2519 : : * don't know at this point how much the old sink can
2520 : : * rewind. We have to pick something, and that
2521 : : * something is the full latency of the old sink here.
2522 : : * So we rewind the stream buffer by the sink latency
2523 : : * amount, which may be more than what we should
2524 : : * rewind. This can result in a chunk of audio being
2525 : : * played both to the old sink and the new sink.
2526 : : *
2527 : : * FIXME: Fix this code so that we don't have to make
2528 : : * guesses about how much the sink will actually be
2529 : : * able to rewind. If someone comes up with a solution
2530 : : * for this, something to note is that the part of the
2531 : : * latency that the old sink couldn't rewind should
2532 : : * ideally be compensated after the stream has moved
2533 : : * to the new sink by adding silence. The new sink
2534 : : * most likely can't start playing the moved stream
2535 : : * immediately, and that gap should be removed from
2536 : : * the "compensation silence" (at least at the time of
2537 : : * writing this, the move finish code will actually
2538 : : * already take care of dropping the new sink's
2539 : : * unrewindable latency, so taking into account the
2540 : : * unrewindable latency of the old sink is the only
2541 : : * problem).
2542 : : *
2543 : : * The render_memblockq contents are discarded,
2544 : : * because when the sink changes, the format of the
2545 : : * audio stored in the render_memblockq may change
2546 : : * too, making the stored audio invalid. FIXME:
2547 : : * However, the read and write indices are moved back
2548 : : * the same amount, so if they are not the same now,
2549 : : * they won't be the same after the rewind either. If
2550 : : * the write index of the render_memblockq is ahead of
2551 : : * the read index, then the render_memblockq will feed
2552 : : * the new sink some silence first, which it shouldn't
2553 : : * do. The write index should be flushed to be the
2554 : : * same as the read index. */
2555 : :
2556 : : /* Get the latency of the sink */
2557 : 0 : usec = pa_sink_get_latency_within_thread(s);
2558 : 0 : sink_nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
2559 : 0 : total_nbytes = sink_nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq);
2560 : :
2561 [ # # ]: 0 : if (total_nbytes > 0) {
2562 [ # # ]: 0 : i->thread_info.rewrite_nbytes = i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, total_nbytes) : total_nbytes;
2563 : 0 : i->thread_info.rewrite_flush = TRUE;
2564 : 0 : pa_sink_input_process_rewind(i, sink_nbytes);
2565 : : }
2566 : : }
2567 : :
2568 [ # # ]: 0 : if (i->detach)
2569 : 0 : i->detach(i);
2570 : :
2571 [ # # ]: 0 : pa_assert(i->thread_info.attached);
2572 : 0 : i->thread_info.attached = FALSE;
2573 : :
2574 : : /* Let's remove the sink input ...*/
2575 [ # # ]: 0 : if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
2576 : 0 : pa_sink_input_unref(i);
2577 : :
2578 : 0 : pa_sink_invalidate_requested_latency(s, TRUE);
2579 : :
2580 : 0 : pa_log_debug("Requesting rewind due to started move");
2581 : 0 : pa_sink_request_rewind(s, (size_t) -1);
2582 : :
2583 : : /* In flat volume mode we need to update the volume as
2584 : : * well */
2585 : 0 : return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
2586 : : }
2587 : :
2588 : : case PA_SINK_MESSAGE_FINISH_MOVE: {
2589 : 0 : pa_sink_input *i = PA_SINK_INPUT(userdata);
2590 : :
2591 : : /* We don't support moving synchronized streams. */
2592 [ # # ]: 0 : pa_assert(!i->sync_prev);
2593 [ # # ]: 0 : pa_assert(!i->sync_next);
2594 [ # # ]: 0 : pa_assert(!i->thread_info.sync_next);
2595 [ # # ]: 0 : pa_assert(!i->thread_info.sync_prev);
2596 : :
2597 : 0 : pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
2598 : :
2599 [ # # ]: 0 : pa_assert(!i->thread_info.attached);
2600 : 0 : i->thread_info.attached = TRUE;
2601 : :
2602 [ # # ]: 0 : if (i->attach)
2603 : 0 : i->attach(i);
2604 : :
2605 [ # # ]: 0 : if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
2606 : 0 : pa_usec_t usec = 0;
2607 : : size_t nbytes;
2608 : :
2609 : : /* In the ideal case the new sink would start playing
2610 : : * the stream immediately. That requires the sink to
2611 : : * be able to rewind all of its latency, which usually
2612 : : * isn't possible, so there will probably be some gap
2613 : : * before the moved stream becomes audible. We then
2614 : : * have two possibilities: 1) start playing the stream
2615 : : * from where it is now, or 2) drop the unrewindable
2616 : : * latency of the sink from the stream. With option 1
2617 : : * we won't lose any audio but the stream will have a
2618 : : * pause. With option 2 we may lose some audio but the
2619 : : * stream time will be somewhat in sync with the wall
2620 : : * clock. Lennart seems to have chosen option 2 (one
2621 : : * of the reasons might have been that option 1 is
2622 : : * actually much harder to implement), so we drop the
2623 : : * latency of the new sink from the moved stream and
2624 : : * hope that the sink will undo most of that in the
2625 : : * rewind. */
2626 : :
2627 : : /* Get the latency of the sink */
2628 : 0 : usec = pa_sink_get_latency_within_thread(s);
2629 : 0 : nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
2630 : :
2631 [ # # ]: 0 : if (nbytes > 0)
2632 : 0 : pa_sink_input_drop(i, nbytes);
2633 : :
2634 : 0 : pa_log_debug("Requesting rewind due to finished move");
2635 : 0 : pa_sink_request_rewind(s, nbytes);
2636 : : }
2637 : :
2638 : : /* Updating the requested sink latency has to be done
2639 : : * after the sink rewind request, not before, because
2640 : : * otherwise the sink may limit the rewind amount
2641 : : * needlessly. */
2642 : :
2643 [ # # ]: 0 : if (i->thread_info.requested_sink_latency != (pa_usec_t) -1)
2644 : 0 : pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
2645 : :
2646 : 0 : pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
2647 : 0 : pa_sink_input_update_max_request(i, s->thread_info.max_request);
2648 : :
2649 : 0 : return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
2650 : : }
2651 : :
2652 : : case PA_SINK_MESSAGE_SET_SHARED_VOLUME: {
2653 : 0 : pa_sink *root_sink = pa_sink_get_master(s);
2654 : :
2655 [ # # ]: 0 : if (PA_LIKELY(root_sink))
2656 : 0 : set_shared_volume_within_thread(root_sink);
2657 : :
2658 : : return 0;
2659 : : }
2660 : :
2661 : : case PA_SINK_MESSAGE_SET_VOLUME_SYNCED:
2662 : :
2663 [ # # ]: 0 : if (s->flags & PA_SINK_DEFERRED_VOLUME) {
2664 : 0 : s->set_volume(s);
2665 : 0 : pa_sink_volume_change_push(s);
2666 : : }
2667 : : /* Fall through ... */
2668 : :
2669 : : case PA_SINK_MESSAGE_SET_VOLUME:
2670 : :
2671 [ # # ]: 0 : if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
2672 : 0 : s->thread_info.soft_volume = s->soft_volume;
2673 : 0 : pa_sink_request_rewind(s, (size_t) -1);
2674 : : }
2675 : :
2676 : : /* Fall through ... */
2677 : :
2678 : : case PA_SINK_MESSAGE_SYNC_VOLUMES:
2679 : 0 : sync_input_volumes_within_thread(s);
2680 : 0 : return 0;
2681 : :
2682 : : case PA_SINK_MESSAGE_GET_VOLUME:
2683 : :
2684 [ # # ][ # # ]: 0 : if ((s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_volume) {
2685 : 0 : s->get_volume(s);
2686 : : pa_sink_volume_change_flush(s);
2687 : 0 : pa_sw_cvolume_divide(&s->thread_info.current_hw_volume, &s->real_volume, &s->soft_volume);
2688 : : }
2689 : :
2690 : : /* In case sink implementor reset SW volume. */
2691 [ # # ]: 0 : if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
2692 : 0 : s->thread_info.soft_volume = s->soft_volume;
2693 : 0 : pa_sink_request_rewind(s, (size_t) -1);
2694 : : }
2695 : :
2696 : : return 0;
2697 : :
2698 : : case PA_SINK_MESSAGE_SET_MUTE:
2699 : :
2700 [ # # ]: 0 : if (s->thread_info.soft_muted != s->muted) {
2701 : 0 : s->thread_info.soft_muted = s->muted;
2702 : 0 : pa_sink_request_rewind(s, (size_t) -1);
2703 : : }
2704 : :
2705 [ # # ][ # # ]: 0 : if (s->flags & PA_SINK_DEFERRED_VOLUME && s->set_mute)
2706 : 0 : s->set_mute(s);
2707 : :
2708 : : return 0;
2709 : :
2710 : : case PA_SINK_MESSAGE_GET_MUTE:
2711 : :
2712 [ # # ][ # # ]: 0 : if (s->flags & PA_SINK_DEFERRED_VOLUME && s->get_mute)
2713 : 0 : s->get_mute(s);
2714 : :
2715 : : return 0;
2716 : :
2717 : : case PA_SINK_MESSAGE_SET_STATE: {
2718 : :
2719 : 0 : pa_bool_t suspend_change =
2720 [ # # ][ # # ]: 0 : (s->thread_info.state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
[ # # ]
2721 [ # # ]: 0 : (PA_SINK_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SINK_SUSPENDED);
2722 : :
2723 : 0 : s->thread_info.state = PA_PTR_TO_UINT(userdata);
2724 : :
2725 [ # # ]: 0 : if (s->thread_info.state == PA_SINK_SUSPENDED) {
2726 : 0 : s->thread_info.rewind_nbytes = 0;
2727 : 0 : s->thread_info.rewind_requested = FALSE;
2728 : : }
2729 : :
2730 [ # # ]: 0 : if (suspend_change) {
2731 : : pa_sink_input *i;
2732 : 0 : void *state = NULL;
2733 : :
2734 [ # # ]: 0 : while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
2735 [ # # ]: 0 : if (i->suspend_within_thread)
2736 : 0 : i->suspend_within_thread(i, s->thread_info.state == PA_SINK_SUSPENDED);
2737 : : }
2738 : :
2739 : : return 0;
2740 : : }
2741 : :
2742 : : case PA_SINK_MESSAGE_DETACH:
2743 : :
2744 : : /* Detach all streams */
2745 : 0 : pa_sink_detach_within_thread(s);
2746 : 0 : return 0;
2747 : :
2748 : : case PA_SINK_MESSAGE_ATTACH:
2749 : :
2750 : : /* Reattach all streams */
2751 : 0 : pa_sink_attach_within_thread(s);
2752 : 0 : return 0;
2753 : :
2754 : : case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY: {
2755 : :
2756 : 0 : pa_usec_t *usec = userdata;
2757 : 0 : *usec = pa_sink_get_requested_latency_within_thread(s);
2758 : :
2759 : : /* Yes, that's right, the IO thread will see -1 when no
2760 : : * explicit requested latency is configured, the main
2761 : : * thread will see max_latency */
2762 [ # # ]: 0 : if (*usec == (pa_usec_t) -1)
2763 : 0 : *usec = s->thread_info.max_latency;
2764 : :
2765 : : return 0;
2766 : : }
2767 : :
2768 : : case PA_SINK_MESSAGE_SET_LATENCY_RANGE: {
2769 : 0 : pa_usec_t *r = userdata;
2770 : :
2771 : 0 : pa_sink_set_latency_range_within_thread(s, r[0], r[1]);
2772 : :
2773 : 0 : return 0;
2774 : : }
2775 : :
2776 : : case PA_SINK_MESSAGE_GET_LATENCY_RANGE: {
2777 : 0 : pa_usec_t *r = userdata;
2778 : :
2779 : 0 : r[0] = s->thread_info.min_latency;
2780 : 0 : r[1] = s->thread_info.max_latency;
2781 : :
2782 : 0 : return 0;
2783 : : }
2784 : :
2785 : : case PA_SINK_MESSAGE_GET_FIXED_LATENCY:
2786 : :
2787 : 0 : *((pa_usec_t*) userdata) = s->thread_info.fixed_latency;
2788 : 0 : return 0;
2789 : :
2790 : : case PA_SINK_MESSAGE_SET_FIXED_LATENCY:
2791 : :
2792 : 0 : pa_sink_set_fixed_latency_within_thread(s, (pa_usec_t) offset);
2793 : 0 : return 0;
2794 : :
2795 : : case PA_SINK_MESSAGE_GET_MAX_REWIND:
2796 : :
2797 : 0 : *((size_t*) userdata) = s->thread_info.max_rewind;
2798 : 0 : return 0;
2799 : :
2800 : : case PA_SINK_MESSAGE_GET_MAX_REQUEST:
2801 : :
2802 : 0 : *((size_t*) userdata) = s->thread_info.max_request;
2803 : 0 : return 0;
2804 : :
2805 : : case PA_SINK_MESSAGE_SET_MAX_REWIND:
2806 : :
2807 : 0 : pa_sink_set_max_rewind_within_thread(s, (size_t) offset);
2808 : 0 : return 0;
2809 : :
2810 : : case PA_SINK_MESSAGE_SET_MAX_REQUEST:
2811 : :
2812 : 0 : pa_sink_set_max_request_within_thread(s, (size_t) offset);
2813 : 0 : return 0;
2814 : :
2815 : : case PA_SINK_MESSAGE_SET_PORT:
2816 : :
2817 [ # # ]: 0 : pa_assert(userdata);
2818 [ # # ]: 0 : if (s->set_port) {
2819 : 0 : struct sink_message_set_port *msg_data = userdata;
2820 : 0 : msg_data->ret = s->set_port(s, msg_data->port);
2821 : : }
2822 : : return 0;
2823 : :
2824 : : case PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE:
2825 : : /* This message is sent from IO-thread and handled in main thread. */
2826 [ # # ]: 0 : pa_assert_ctl_context();
2827 : :
2828 : : /* Make sure we're not messing with main thread when no longer linked */
2829 [ # # ]: 0 : if (!PA_SINK_IS_LINKED(s->state))
2830 : : return 0;
2831 : :
2832 : 0 : pa_sink_get_volume(s, TRUE);
2833 : 0 : pa_sink_get_mute(s, TRUE);
2834 : 0 : return 0;
2835 : :
2836 : : case PA_SINK_MESSAGE_SET_LATENCY_OFFSET:
2837 : 0 : s->thread_info.latency_offset = offset;
2838 : 0 : return 0;
2839 : :
2840 : : case PA_SINK_MESSAGE_GET_LATENCY:
2841 : : case PA_SINK_MESSAGE_MAX:
2842 : : ;
2843 : : }
2844 : :
2845 : : return -1;
2846 : : }
2847 : :
2848 : : /* Called from main thread */
2849 : 0 : int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause) {
2850 : : pa_sink *sink;
2851 : : uint32_t idx;
2852 : 0 : int ret = 0;
2853 : :
2854 : : pa_core_assert_ref(c);
2855 [ # # ]: 0 : pa_assert_ctl_context();
2856 [ # # ]: 0 : pa_assert(cause != 0);
2857 : :
2858 [ # # ]: 0 : PA_IDXSET_FOREACH(sink, c->sinks, idx) {
2859 : : int r;
2860 : :
2861 [ # # ]: 0 : if ((r = pa_sink_suspend(sink, suspend, cause)) < 0)
2862 : 0 : ret = r;
2863 : : }
2864 : :
2865 : 0 : return ret;
2866 : : }
2867 : :
2868 : : /* Called from main thread */
2869 : 0 : void pa_sink_detach(pa_sink *s) {
2870 : 0 : pa_sink_assert_ref(s);
2871 [ # # ]: 0 : pa_assert_ctl_context();
2872 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
2873 : :
2874 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL) == 0);
2875 : 0 : }
2876 : :
2877 : : /* Called from main thread */
2878 : 0 : void pa_sink_attach(pa_sink *s) {
2879 : 0 : pa_sink_assert_ref(s);
2880 [ # # ]: 0 : pa_assert_ctl_context();
2881 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
2882 : :
2883 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
2884 : 0 : }
2885 : :
2886 : : /* Called from IO thread */
2887 : 0 : void pa_sink_detach_within_thread(pa_sink *s) {
2888 : : pa_sink_input *i;
2889 : 0 : void *state = NULL;
2890 : :
2891 : 0 : pa_sink_assert_ref(s);
2892 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
2893 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
2894 : :
2895 [ # # ]: 0 : PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
2896 [ # # ]: 0 : if (i->detach)
2897 : 0 : i->detach(i);
2898 : :
2899 [ # # ]: 0 : if (s->monitor_source)
2900 : 0 : pa_source_detach_within_thread(s->monitor_source);
2901 : 0 : }
2902 : :
2903 : : /* Called from IO thread */
2904 : 0 : void pa_sink_attach_within_thread(pa_sink *s) {
2905 : : pa_sink_input *i;
2906 : 0 : void *state = NULL;
2907 : :
2908 : 0 : pa_sink_assert_ref(s);
2909 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
2910 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
2911 : :
2912 [ # # ]: 0 : PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
2913 [ # # ]: 0 : if (i->attach)
2914 : 0 : i->attach(i);
2915 : :
2916 [ # # ]: 0 : if (s->monitor_source)
2917 : 0 : pa_source_attach_within_thread(s->monitor_source);
2918 : 0 : }
2919 : :
2920 : : /* Called from IO thread */
2921 : 0 : void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
2922 : 0 : pa_sink_assert_ref(s);
2923 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
2924 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
2925 : :
2926 [ # # ]: 0 : if (s->thread_info.state == PA_SINK_SUSPENDED)
2927 : : return;
2928 : :
2929 [ # # ]: 0 : if (nbytes == (size_t) -1)
2930 : 0 : nbytes = s->thread_info.max_rewind;
2931 : :
2932 : 0 : nbytes = PA_MIN(nbytes, s->thread_info.max_rewind);
2933 : :
2934 [ # # ][ # # ]: 0 : if (s->thread_info.rewind_requested &&
2935 : 0 : nbytes <= s->thread_info.rewind_nbytes)
2936 : : return;
2937 : :
2938 : 0 : s->thread_info.rewind_nbytes = nbytes;
2939 : 0 : s->thread_info.rewind_requested = TRUE;
2940 : :
2941 [ # # ]: 0 : if (s->request_rewind)
2942 : 0 : s->request_rewind(s);
2943 : : }
2944 : :
2945 : : /* Called from IO thread */
2946 : 0 : pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
2947 : 0 : pa_usec_t result = (pa_usec_t) -1;
2948 : : pa_sink_input *i;
2949 : 0 : void *state = NULL;
2950 : : pa_usec_t monitor_latency;
2951 : :
2952 : 0 : pa_sink_assert_ref(s);
2953 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
2954 : :
2955 [ # # ]: 0 : if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
2956 [ # # ]: 0 : return PA_CLAMP(s->thread_info.fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
2957 : :
2958 [ # # ]: 0 : if (s->thread_info.requested_latency_valid)
2959 : 0 : return s->thread_info.requested_latency;
2960 : :
2961 [ # # ]: 0 : PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
2962 [ # # ][ # # ]: 0 : if (i->thread_info.requested_sink_latency != (pa_usec_t) -1 &&
2963 [ # # ]: 0 : (result == (pa_usec_t) -1 || result > i->thread_info.requested_sink_latency))
2964 : 0 : result = i->thread_info.requested_sink_latency;
2965 : :
2966 : 0 : monitor_latency = pa_source_get_requested_latency_within_thread(s->monitor_source);
2967 : :
2968 [ # # ][ # # ]: 0 : if (monitor_latency != (pa_usec_t) -1 &&
2969 : 0 : (result == (pa_usec_t) -1 || result > monitor_latency))
2970 : 0 : result = monitor_latency;
2971 : :
2972 [ # # ]: 0 : if (result != (pa_usec_t) -1)
2973 [ # # ]: 0 : result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
2974 : :
2975 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->thread_info.state)) {
2976 : : /* Only cache if properly initialized */
2977 : 0 : s->thread_info.requested_latency = result;
2978 : 0 : s->thread_info.requested_latency_valid = TRUE;
2979 : : }
2980 : :
2981 : : return result;
2982 : : }
2983 : :
2984 : : /* Called from main thread */
2985 : 0 : pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
2986 : 0 : pa_usec_t usec = 0;
2987 : :
2988 : 0 : pa_sink_assert_ref(s);
2989 [ # # ]: 0 : pa_assert_ctl_context();
2990 [ # # ]: 0 : pa_assert(PA_SINK_IS_LINKED(s->state));
2991 : :
2992 [ # # ]: 0 : if (s->state == PA_SINK_SUSPENDED)
2993 : : return 0;
2994 : :
2995 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
2996 : :
2997 : 0 : return usec;
2998 : : }
2999 : :
3000 : : /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
3001 : 0 : void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind) {
3002 : : pa_sink_input *i;
3003 : 0 : void *state = NULL;
3004 : :
3005 : 0 : pa_sink_assert_ref(s);
3006 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
3007 : :
3008 [ # # ]: 0 : if (max_rewind == s->thread_info.max_rewind)
3009 : 0 : return;
3010 : :
3011 : 0 : s->thread_info.max_rewind = max_rewind;
3012 : :
3013 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->thread_info.state))
3014 [ # # ]: 0 : PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
3015 : 0 : pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
3016 : :
3017 [ # # ]: 0 : if (s->monitor_source)
3018 : 0 : pa_source_set_max_rewind_within_thread(s->monitor_source, s->thread_info.max_rewind);
3019 : : }
3020 : :
3021 : : /* Called from main thread */
3022 : 0 : void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
3023 : 0 : pa_sink_assert_ref(s);
3024 [ # # ]: 0 : pa_assert_ctl_context();
3025 : :
3026 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->state))
3027 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REWIND, NULL, max_rewind, NULL) == 0);
3028 : : else
3029 : 0 : pa_sink_set_max_rewind_within_thread(s, max_rewind);
3030 : 0 : }
3031 : :
3032 : : /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
3033 : 0 : void pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request) {
3034 : 0 : void *state = NULL;
3035 : :
3036 : 0 : pa_sink_assert_ref(s);
3037 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
3038 : :
3039 [ # # ]: 0 : if (max_request == s->thread_info.max_request)
3040 : 0 : return;
3041 : :
3042 : 0 : s->thread_info.max_request = max_request;
3043 : :
3044 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->thread_info.state)) {
3045 : : pa_sink_input *i;
3046 : :
3047 [ # # ]: 0 : PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
3048 : 0 : pa_sink_input_update_max_request(i, s->thread_info.max_request);
3049 : : }
3050 : : }
3051 : :
3052 : : /* Called from main thread */
3053 : 0 : void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
3054 : 0 : pa_sink_assert_ref(s);
3055 [ # # ]: 0 : pa_assert_ctl_context();
3056 : :
3057 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->state))
3058 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REQUEST, NULL, max_request, NULL) == 0);
3059 : : else
3060 : 0 : pa_sink_set_max_request_within_thread(s, max_request);
3061 : 0 : }
3062 : :
3063 : : /* Called from IO thread */
3064 : 0 : void pa_sink_invalidate_requested_latency(pa_sink *s, pa_bool_t dynamic) {
3065 : : pa_sink_input *i;
3066 : 0 : void *state = NULL;
3067 : :
3068 : 0 : pa_sink_assert_ref(s);
3069 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
3070 : :
3071 [ # # ]: 0 : if ((s->flags & PA_SINK_DYNAMIC_LATENCY))
3072 : 0 : s->thread_info.requested_latency_valid = FALSE;
3073 [ # # ]: 0 : else if (dynamic)
3074 : 0 : return;
3075 : :
3076 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->thread_info.state)) {
3077 : :
3078 [ # # ]: 0 : if (s->update_requested_latency)
3079 : 0 : s->update_requested_latency(s);
3080 : :
3081 [ # # ]: 0 : PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
3082 [ # # ]: 0 : if (i->update_sink_requested_latency)
3083 : 0 : i->update_sink_requested_latency(i);
3084 : : }
3085 : : }
3086 : :
3087 : : /* Called from main thread */
3088 : 0 : void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
3089 : 0 : pa_sink_assert_ref(s);
3090 [ # # ]: 0 : pa_assert_ctl_context();
3091 : :
3092 : : /* min_latency == 0: no limit
3093 : : * min_latency anything else: specified limit
3094 : : *
3095 : : * Similar for max_latency */
3096 : :
3097 [ # # ]: 0 : if (min_latency < ABSOLUTE_MIN_LATENCY)
3098 : 0 : min_latency = ABSOLUTE_MIN_LATENCY;
3099 : :
3100 [ # # ]: 0 : if (max_latency <= 0 ||
3101 : : max_latency > ABSOLUTE_MAX_LATENCY)
3102 : 0 : max_latency = ABSOLUTE_MAX_LATENCY;
3103 : :
3104 [ # # ]: 0 : pa_assert(min_latency <= max_latency);
3105 : :
3106 : : /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
3107 [ # # ][ # # ]: 0 : pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
3108 : : max_latency == ABSOLUTE_MAX_LATENCY) ||
3109 : : (s->flags & PA_SINK_DYNAMIC_LATENCY));
3110 : :
3111 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->state)) {
3112 : : pa_usec_t r[2];
3113 : :
3114 : 0 : r[0] = min_latency;
3115 : 0 : r[1] = max_latency;
3116 : :
3117 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
3118 : : } else
3119 : 0 : pa_sink_set_latency_range_within_thread(s, min_latency, max_latency);
3120 : 0 : }
3121 : :
3122 : : /* Called from main thread */
3123 : 0 : void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
3124 : 0 : pa_sink_assert_ref(s);
3125 [ # # ]: 0 : pa_assert_ctl_context();
3126 [ # # ]: 0 : pa_assert(min_latency);
3127 [ # # ]: 0 : pa_assert(max_latency);
3128 : :
3129 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->state)) {
3130 : 0 : pa_usec_t r[2] = { 0, 0 };
3131 : :
3132 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
3133 : :
3134 : 0 : *min_latency = r[0];
3135 : 0 : *max_latency = r[1];
3136 : : } else {
3137 : 0 : *min_latency = s->thread_info.min_latency;
3138 : 0 : *max_latency = s->thread_info.max_latency;
3139 : : }
3140 : 0 : }
3141 : :
3142 : : /* Called from IO thread */
3143 : 0 : void pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
3144 : 0 : pa_sink_assert_ref(s);
3145 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
3146 : :
3147 [ # # ]: 0 : pa_assert(min_latency >= ABSOLUTE_MIN_LATENCY);
3148 [ # # ]: 0 : pa_assert(max_latency <= ABSOLUTE_MAX_LATENCY);
3149 [ # # ]: 0 : pa_assert(min_latency <= max_latency);
3150 : :
3151 : : /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
3152 [ # # ][ # # ]: 0 : pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
3153 : : max_latency == ABSOLUTE_MAX_LATENCY) ||
3154 : : (s->flags & PA_SINK_DYNAMIC_LATENCY));
3155 : :
3156 [ # # ][ # # ]: 0 : if (s->thread_info.min_latency == min_latency &&
3157 : 0 : s->thread_info.max_latency == max_latency)
3158 : 0 : return;
3159 : :
3160 : 0 : s->thread_info.min_latency = min_latency;
3161 : 0 : s->thread_info.max_latency = max_latency;
3162 : :
3163 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->thread_info.state)) {
3164 : : pa_sink_input *i;
3165 : 0 : void *state = NULL;
3166 : :
3167 [ # # ]: 0 : PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
3168 [ # # ]: 0 : if (i->update_sink_latency_range)
3169 : 0 : i->update_sink_latency_range(i);
3170 : : }
3171 : :
3172 : 0 : pa_sink_invalidate_requested_latency(s, FALSE);
3173 : :
3174 : 0 : pa_source_set_latency_range_within_thread(s->monitor_source, min_latency, max_latency);
3175 : : }
3176 : :
3177 : : /* Called from main thread */
3178 : 0 : void pa_sink_set_fixed_latency(pa_sink *s, pa_usec_t latency) {
3179 : 0 : pa_sink_assert_ref(s);
3180 [ # # ]: 0 : pa_assert_ctl_context();
3181 : :
3182 [ # # ]: 0 : if (s->flags & PA_SINK_DYNAMIC_LATENCY) {
3183 [ # # ]: 0 : pa_assert(latency == 0);
3184 : 0 : return;
3185 : : }
3186 : :
3187 [ # # ]: 0 : if (latency < ABSOLUTE_MIN_LATENCY)
3188 : 0 : latency = ABSOLUTE_MIN_LATENCY;
3189 : :
3190 [ # # ]: 0 : if (latency > ABSOLUTE_MAX_LATENCY)
3191 : 0 : latency = ABSOLUTE_MAX_LATENCY;
3192 : :
3193 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->state))
3194 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_FIXED_LATENCY, NULL, (int64_t) latency, NULL) == 0);
3195 : : else
3196 : 0 : s->thread_info.fixed_latency = latency;
3197 : :
3198 : 0 : pa_source_set_fixed_latency(s->monitor_source, latency);
3199 : : }
3200 : :
3201 : : /* Called from main thread */
3202 : 0 : pa_usec_t pa_sink_get_fixed_latency(pa_sink *s) {
3203 : : pa_usec_t latency;
3204 : :
3205 : 0 : pa_sink_assert_ref(s);
3206 [ # # ]: 0 : pa_assert_ctl_context();
3207 : :
3208 [ # # ]: 0 : if (s->flags & PA_SINK_DYNAMIC_LATENCY)
3209 : : return 0;
3210 : :
3211 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->state))
3212 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_FIXED_LATENCY, &latency, 0, NULL) == 0);
3213 : : else
3214 : 0 : latency = s->thread_info.fixed_latency;
3215 : :
3216 : 0 : return latency;
3217 : : }
3218 : :
3219 : : /* Called from IO thread */
3220 : 0 : void pa_sink_set_fixed_latency_within_thread(pa_sink *s, pa_usec_t latency) {
3221 : 0 : pa_sink_assert_ref(s);
3222 [ # # ][ # # ]: 0 : pa_sink_assert_io_context(s);
3223 : :
3224 [ # # ]: 0 : if (s->flags & PA_SINK_DYNAMIC_LATENCY) {
3225 [ # # ]: 0 : pa_assert(latency == 0);
3226 : : return;
3227 : : }
3228 : :
3229 [ # # ]: 0 : pa_assert(latency >= ABSOLUTE_MIN_LATENCY);
3230 [ # # ]: 0 : pa_assert(latency <= ABSOLUTE_MAX_LATENCY);
3231 : :
3232 [ # # ]: 0 : if (s->thread_info.fixed_latency == latency)
3233 : : return;
3234 : :
3235 : 0 : s->thread_info.fixed_latency = latency;
3236 : :
3237 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->thread_info.state)) {
3238 : : pa_sink_input *i;
3239 : 0 : void *state = NULL;
3240 : :
3241 [ # # ]: 0 : PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state)
3242 [ # # ]: 0 : if (i->update_sink_fixed_latency)
3243 : 0 : i->update_sink_fixed_latency(i);
3244 : : }
3245 : :
3246 : 0 : pa_sink_invalidate_requested_latency(s, FALSE);
3247 : :
3248 : 0 : pa_source_set_fixed_latency_within_thread(s->monitor_source, latency);
3249 : : }
3250 : :
3251 : : /* Called from main context */
3252 : 0 : void pa_sink_set_latency_offset(pa_sink *s, int64_t offset) {
3253 : 0 : pa_sink_assert_ref(s);
3254 : :
3255 : 0 : s->latency_offset = offset;
3256 : :
3257 [ # # ]: 0 : if (PA_SINK_IS_LINKED(s->state))
3258 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_OFFSET, NULL, offset, NULL) == 0);
3259 : : else
3260 : 0 : s->thread_info.latency_offset = offset;
3261 : 0 : }
3262 : :
3263 : : /* Called from main context */
3264 : 0 : size_t pa_sink_get_max_rewind(pa_sink *s) {
3265 : : size_t r;
3266 [ # # ]: 0 : pa_assert_ctl_context();
3267 : 0 : pa_sink_assert_ref(s);
3268 : :
3269 [ # # ]: 0 : if (!PA_SINK_IS_LINKED(s->state))
3270 : 0 : return s->thread_info.max_rewind;
3271 : :
3272 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
3273 : :
3274 : 0 : return r;
3275 : : }
3276 : :
3277 : : /* Called from main context */
3278 : 0 : size_t pa_sink_get_max_request(pa_sink *s) {
3279 : : size_t r;
3280 : 0 : pa_sink_assert_ref(s);
3281 [ # # ]: 0 : pa_assert_ctl_context();
3282 : :
3283 [ # # ]: 0 : if (!PA_SINK_IS_LINKED(s->state))
3284 : 0 : return s->thread_info.max_request;
3285 : :
3286 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REQUEST, &r, 0, NULL) == 0);
3287 : :
3288 : 0 : return r;
3289 : : }
3290 : :
3291 : : /* Called from main context */
3292 : 0 : int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save) {
3293 : : pa_device_port *port;
3294 : : int ret;
3295 : :
3296 : 0 : pa_sink_assert_ref(s);
3297 [ # # ]: 0 : pa_assert_ctl_context();
3298 : :
3299 [ # # ]: 0 : if (!s->set_port) {
3300 : 0 : pa_log_debug("set_port() operation not implemented for sink %u \"%s\"", s->index, s->name);
3301 : 0 : return -PA_ERR_NOTIMPLEMENTED;
3302 : : }
3303 : :
3304 [ # # ]: 0 : if (!name)
3305 : : return -PA_ERR_NOENTITY;
3306 : :
3307 [ # # ]: 0 : if (!(port = pa_hashmap_get(s->ports, name)))
3308 : : return -PA_ERR_NOENTITY;
3309 : :
3310 [ # # ]: 0 : if (s->active_port == port) {
3311 [ # # ][ # # ]: 0 : s->save_port = s->save_port || save;
3312 : 0 : return 0;
3313 : : }
3314 : :
3315 [ # # ]: 0 : if (s->flags & PA_SINK_DEFERRED_VOLUME) {
3316 : 0 : struct sink_message_set_port msg = { .port = port, .ret = 0 };
3317 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
3318 : 0 : ret = msg.ret;
3319 : : }
3320 : : else
3321 : 0 : ret = s->set_port(s, port);
3322 : :
3323 [ # # ]: 0 : if (ret < 0)
3324 : : return -PA_ERR_NOENTITY;
3325 : :
3326 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
3327 : :
3328 : 0 : pa_log_info("Changed port of sink %u \"%s\" to %s", s->index, s->name, port->name);
3329 : :
3330 : 0 : s->active_port = port;
3331 : 0 : s->save_port = save;
3332 : :
3333 : 0 : pa_sink_set_latency_offset(s, s->active_port->latency_offset);
3334 : :
3335 : 0 : pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], s);
3336 : :
3337 : 0 : return 0;
3338 : : }
3339 : :
3340 : 0 : pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) {
3341 : 0 : const char *ff, *c, *t = NULL, *s = "", *profile, *bus;
3342 : :
3343 [ # # ]: 0 : pa_assert(p);
3344 : :
3345 [ # # ]: 0 : if (pa_proplist_contains(p, PA_PROP_DEVICE_ICON_NAME))
3346 : : return TRUE;
3347 : :
3348 [ # # ]: 0 : if ((ff = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR))) {
3349 : :
3350 [ # # ]: 0 : if (pa_streq(ff, "microphone"))
3351 : : t = "audio-input-microphone";
3352 [ # # ]: 0 : else if (pa_streq(ff, "webcam"))
3353 : : t = "camera-web";
3354 [ # # ]: 0 : else if (pa_streq(ff, "computer"))
3355 : : t = "computer";
3356 [ # # ]: 0 : else if (pa_streq(ff, "handset"))
3357 : : t = "phone";
3358 [ # # ]: 0 : else if (pa_streq(ff, "portable"))
3359 : : t = "multimedia-player";
3360 [ # # ][ # # ]: 0 : else if (pa_streq(ff, "tv"))
[ # # ]
3361 : : t = "video-display";
3362 : :
3363 : : /*
3364 : : * The following icons are not part of the icon naming spec,
3365 : : * because Rodney Dawes sucks as the maintainer of that spec.
3366 : : *
3367 : : * http://lists.freedesktop.org/archives/xdg/2009-May/010397.html
3368 : : */
3369 [ # # ]: 0 : else if (pa_streq(ff, "headset"))
3370 : : t = "audio-headset";
3371 [ # # ]: 0 : else if (pa_streq(ff, "headphone"))
3372 : : t = "audio-headphones";
3373 [ # # ]: 0 : else if (pa_streq(ff, "speaker"))
3374 : : t = "audio-speakers";
3375 [ # # ]: 0 : else if (pa_streq(ff, "hands-free"))
3376 : 0 : t = "audio-handsfree";
3377 : : }
3378 : :
3379 [ # # ]: 0 : if (!t)
3380 [ # # ]: 0 : if ((c = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
3381 [ # # ]: 0 : if (pa_streq(c, "modem"))
3382 : 0 : t = "modem";
3383 : :
3384 [ # # ]: 0 : if (!t) {
3385 [ # # ]: 0 : if (is_sink)
3386 : : t = "audio-card";
3387 : : else
3388 : 0 : t = "audio-input-microphone";
3389 : : }
3390 : :
3391 [ # # ]: 0 : if ((profile = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
3392 [ # # ]: 0 : if (strstr(profile, "analog"))
3393 : : s = "-analog";
3394 [ # # ]: 0 : else if (strstr(profile, "iec958"))
3395 : : s = "-iec958";
3396 [ # # ]: 0 : else if (strstr(profile, "hdmi"))
3397 : 0 : s = "-hdmi";
3398 : : }
3399 : :
3400 : 0 : bus = pa_proplist_gets(p, PA_PROP_DEVICE_BUS);
3401 : :
3402 [ # # ]: 0 : pa_proplist_setf(p, PA_PROP_DEVICE_ICON_NAME, "%s%s%s%s", t, pa_strempty(s), bus ? "-" : "", pa_strempty(bus));
3403 : :
3404 : 0 : return TRUE;
3405 : : }
3406 : :
3407 : 0 : pa_bool_t pa_device_init_description(pa_proplist *p) {
3408 : 0 : const char *s, *d = NULL, *k;
3409 [ # # ]: 0 : pa_assert(p);
3410 : :
3411 [ # # ]: 0 : if (pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
3412 : : return TRUE;
3413 : :
3414 [ # # ]: 0 : if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
3415 [ # # ]: 0 : if (pa_streq(s, "internal"))
3416 : 0 : d = _("Built-in Audio");
3417 : :
3418 [ # # ]: 0 : if (!d)
3419 [ # # ]: 0 : if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
3420 [ # # ]: 0 : if (pa_streq(s, "modem"))
3421 : 0 : d = _("Modem");
3422 : :
3423 [ # # ]: 0 : if (!d)
3424 : 0 : d = pa_proplist_gets(p, PA_PROP_DEVICE_PRODUCT_NAME);
3425 : :
3426 [ # # ]: 0 : if (!d)
3427 : : return FALSE;
3428 : :
3429 : 0 : k = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_DESCRIPTION);
3430 : :
3431 [ # # ]: 0 : if (d && k)
3432 : 0 : pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, "%s %s", d, k);
3433 [ # # ]: 0 : else if (d)
3434 : 0 : pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, d);
3435 : :
3436 : : return TRUE;
3437 : : }
3438 : :
3439 : 0 : pa_bool_t pa_device_init_intended_roles(pa_proplist *p) {
3440 : : const char *s;
3441 [ # # ]: 0 : pa_assert(p);
3442 : :
3443 [ # # ]: 0 : if (pa_proplist_contains(p, PA_PROP_DEVICE_INTENDED_ROLES))
3444 : : return TRUE;
3445 : :
3446 [ # # ]: 0 : if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
3447 [ # # ][ # # ]: 0 : if (pa_streq(s, "handset") || pa_streq(s, "hands-free")
3448 [ # # ]: 0 : || pa_streq(s, "headset")) {
3449 : 0 : pa_proplist_sets(p, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
3450 : 0 : return TRUE;
3451 : : }
3452 : :
3453 : : return FALSE;
3454 : : }
3455 : :
3456 : 0 : unsigned pa_device_init_priority(pa_proplist *p) {
3457 : : const char *s;
3458 : 0 : unsigned priority = 0;
3459 : :
3460 [ # # ]: 0 : pa_assert(p);
3461 : :
3462 [ # # ]: 0 : if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS))) {
3463 : :
3464 [ # # ]: 0 : if (pa_streq(s, "sound"))
3465 : : priority += 9000;
3466 [ # # ]: 0 : else if (!pa_streq(s, "modem"))
3467 : 0 : priority += 1000;
3468 : : }
3469 : :
3470 [ # # ]: 0 : if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR))) {
3471 : :
3472 [ # # ]: 0 : if (pa_streq(s, "internal"))
3473 : 0 : priority += 900;
3474 [ # # ]: 0 : else if (pa_streq(s, "speaker"))
3475 : 0 : priority += 500;
3476 [ # # ]: 0 : else if (pa_streq(s, "headphone"))
3477 : 0 : priority += 400;
3478 : : }
3479 : :
3480 [ # # ]: 0 : if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_BUS))) {
3481 : :
3482 [ # # ][ # # ]: 0 : if (pa_streq(s, "pci"))
[ # # ][ # # ]
3483 : 0 : priority += 50;
3484 [ # # ][ # # ]: 0 : else if (pa_streq(s, "usb"))
[ # # ][ # # ]
3485 : 0 : priority += 40;
3486 [ # # ]: 0 : else if (pa_streq(s, "bluetooth"))
3487 : 0 : priority += 30;
3488 : : }
3489 : :
3490 [ # # ]: 0 : if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
3491 : :
3492 [ # # ]: 0 : if (pa_startswith(s, "analog-"))
3493 : 0 : priority += 9;
3494 [ # # ]: 0 : else if (pa_startswith(s, "iec958-"))
3495 : 0 : priority += 8;
3496 : : }
3497 : :
3498 : 0 : return priority;
3499 : : }
3500 : :
3501 [ - + ][ # # ]: 21 : PA_STATIC_FLIST_DECLARE(pa_sink_volume_change, 0, pa_xfree);
3502 : :
3503 : : /* Called from the IO thread. */
3504 : 0 : static pa_sink_volume_change *pa_sink_volume_change_new(pa_sink *s) {
3505 : : pa_sink_volume_change *c;
3506 [ # # ]: 0 : if (!(c = pa_flist_pop(PA_STATIC_FLIST_GET(pa_sink_volume_change))))
3507 : 0 : c = pa_xnew(pa_sink_volume_change, 1);
3508 : :
3509 [ # # ]: 0 : PA_LLIST_INIT(pa_sink_volume_change, c);
3510 : 0 : c->at = 0;
3511 : 0 : pa_cvolume_reset(&c->hw_volume, s->sample_spec.channels);
3512 : 0 : return c;
3513 : : }
3514 : :
3515 : : /* Called from the IO thread. */
3516 : 0 : static void pa_sink_volume_change_free(pa_sink_volume_change *c) {
3517 [ # # ]: 0 : pa_assert(c);
3518 [ # # ]: 0 : if (pa_flist_push(PA_STATIC_FLIST_GET(pa_sink_volume_change), c) < 0)
3519 : 0 : pa_xfree(c);
3520 : 0 : }
3521 : :
3522 : : /* Called from the IO thread. */
3523 : 0 : void pa_sink_volume_change_push(pa_sink *s) {
3524 : 0 : pa_sink_volume_change *c = NULL;
3525 : 0 : pa_sink_volume_change *nc = NULL;
3526 : 0 : uint32_t safety_margin = s->thread_info.volume_change_safety_margin;
3527 : :
3528 : 0 : const char *direction = NULL;
3529 : :
3530 [ # # ]: 0 : pa_assert(s);
3531 : 0 : nc = pa_sink_volume_change_new(s);
3532 : :
3533 : : /* NOTE: There is already more different volumes in pa_sink that I can remember.
3534 : : * Adding one more volume for HW would get us rid of this, but I am trying
3535 : : * to survive with the ones we already have. */
3536 : 0 : pa_sw_cvolume_divide(&nc->hw_volume, &s->real_volume, &s->soft_volume);
3537 : :
3538 [ # # ][ # # ]: 0 : if (!s->thread_info.volume_changes && pa_cvolume_equal(&nc->hw_volume, &s->thread_info.current_hw_volume)) {
3539 : 0 : pa_log_debug("Volume not changing");
3540 : 0 : pa_sink_volume_change_free(nc);
3541 : 0 : return;
3542 : : }
3543 : :
3544 : 0 : nc->at = pa_sink_get_latency_within_thread(s);
3545 : 0 : nc->at += pa_rtclock_now() + s->thread_info.volume_change_extra_delay;
3546 : :
3547 [ # # ]: 0 : if (s->thread_info.volume_changes_tail) {
3548 [ # # ]: 0 : for (c = s->thread_info.volume_changes_tail; c; c = c->prev) {
3549 : : /* If volume is going up let's do it a bit late. If it is going
3550 : : * down let's do it a bit early. */
3551 [ # # ]: 0 : if (pa_cvolume_avg(&nc->hw_volume) > pa_cvolume_avg(&c->hw_volume)) {
3552 [ # # ]: 0 : if (nc->at + safety_margin > c->at) {
3553 : 0 : nc->at += safety_margin;
3554 : 0 : direction = "up";
3555 : 0 : break;
3556 : : }
3557 : : }
3558 [ # # ]: 0 : else if (nc->at - safety_margin > c->at) {
3559 : 0 : nc->at -= safety_margin;
3560 : 0 : direction = "down";
3561 : 0 : break;
3562 : : }
3563 : : }
3564 : : }
3565 : :
3566 [ # # ]: 0 : if (c == NULL) {
3567 [ # # ]: 0 : if (pa_cvolume_avg(&nc->hw_volume) > pa_cvolume_avg(&s->thread_info.current_hw_volume)) {
3568 : 0 : nc->at += safety_margin;
3569 : 0 : direction = "up";
3570 : : } else {
3571 : 0 : nc->at -= safety_margin;
3572 : 0 : direction = "down";
3573 : : }
3574 [ # # ][ # # ]: 0 : PA_LLIST_PREPEND(pa_sink_volume_change, s->thread_info.volume_changes, nc);
3575 : : }
3576 : : else {
3577 [ # # ][ # # ]: 0 : PA_LLIST_INSERT_AFTER(pa_sink_volume_change, s->thread_info.volume_changes, c, nc);
[ # # ][ # # ]
3578 : : }
3579 : :
3580 : 0 : pa_log_debug("Volume going %s to %d at %llu", direction, pa_cvolume_avg(&nc->hw_volume), (long long unsigned) nc->at);
3581 : :
3582 : : /* We can ignore volume events that came earlier but should happen later than this. */
3583 [ # # ]: 0 : PA_LLIST_FOREACH(c, nc->next) {
3584 : 0 : pa_log_debug("Volume change to %d at %llu was dropped", pa_cvolume_avg(&c->hw_volume), (long long unsigned) c->at);
3585 : 0 : pa_sink_volume_change_free(c);
3586 : : }
3587 : 0 : nc->next = NULL;
3588 : 0 : s->thread_info.volume_changes_tail = nc;
3589 : : }
3590 : :
3591 : : /* Called from the IO thread. */
3592 : : static void pa_sink_volume_change_flush(pa_sink *s) {
3593 : 0 : pa_sink_volume_change *c = s->thread_info.volume_changes;
3594 [ # # ]: 0 : pa_assert(s);
3595 : 0 : s->thread_info.volume_changes = NULL;
3596 : 0 : s->thread_info.volume_changes_tail = NULL;
3597 [ # # ]: 0 : while (c) {
3598 : 0 : pa_sink_volume_change *next = c->next;
3599 : 0 : pa_sink_volume_change_free(c);
3600 : 0 : c = next;
3601 : : }
3602 : : }
3603 : :
3604 : : /* Called from the IO thread. */
3605 : 0 : pa_bool_t pa_sink_volume_change_apply(pa_sink *s, pa_usec_t *usec_to_next) {
3606 : : pa_usec_t now;
3607 : 0 : pa_bool_t ret = FALSE;
3608 : :
3609 [ # # ]: 0 : pa_assert(s);
3610 : :
3611 [ # # ][ # # ]: 0 : if (!s->thread_info.volume_changes || !PA_SINK_IS_LINKED(s->state)) {
3612 [ # # ]: 0 : if (usec_to_next)
3613 : 0 : *usec_to_next = 0;
3614 : : return ret;
3615 : : }
3616 : :
3617 [ # # ]: 0 : pa_assert(s->write_volume);
3618 : :
3619 : 0 : now = pa_rtclock_now();
3620 : :
3621 [ # # ][ # # ]: 0 : while (s->thread_info.volume_changes && now >= s->thread_info.volume_changes->at) {
3622 : 0 : pa_sink_volume_change *c = s->thread_info.volume_changes;
3623 [ # # ][ # # ]: 0 : PA_LLIST_REMOVE(pa_sink_volume_change, s->thread_info.volume_changes, c);
[ # # ][ # # ]
3624 : 0 : pa_log_debug("Volume change to %d at %llu was written %llu usec late",
3625 : : pa_cvolume_avg(&c->hw_volume), (long long unsigned) c->at, (long long unsigned) (now - c->at));
3626 : 0 : ret = TRUE;
3627 : 0 : s->thread_info.current_hw_volume = c->hw_volume;
3628 : 0 : pa_sink_volume_change_free(c);
3629 : : }
3630 : :
3631 [ # # ]: 0 : if (ret)
3632 : 0 : s->write_volume(s);
3633 : :
3634 [ # # ]: 0 : if (s->thread_info.volume_changes) {
3635 [ # # ]: 0 : if (usec_to_next)
3636 : 0 : *usec_to_next = s->thread_info.volume_changes->at - now;
3637 [ # # ]: 0 : if (pa_log_ratelimit(PA_LOG_DEBUG))
3638 : 0 : pa_log_debug("Next volume change in %lld usec", (long long) (s->thread_info.volume_changes->at - now));
3639 : : }
3640 : : else {
3641 [ # # ]: 0 : if (usec_to_next)
3642 : 0 : *usec_to_next = 0;
3643 : 0 : s->thread_info.volume_changes_tail = NULL;
3644 : : }
3645 : : return ret;
3646 : : }
3647 : :
3648 : : /* Called from the IO thread. */
3649 : 0 : static void pa_sink_volume_change_rewind(pa_sink *s, size_t nbytes) {
3650 : : /* All the queued volume events later than current latency are shifted to happen earlier. */
3651 : : pa_sink_volume_change *c;
3652 : 0 : pa_volume_t prev_vol = pa_cvolume_avg(&s->thread_info.current_hw_volume);
3653 : 0 : pa_usec_t rewound = pa_bytes_to_usec(nbytes, &s->sample_spec);
3654 : 0 : pa_usec_t limit = pa_sink_get_latency_within_thread(s);
3655 : :
3656 : 0 : pa_log_debug("latency = %lld", (long long) limit);
3657 : 0 : limit += pa_rtclock_now() + s->thread_info.volume_change_extra_delay;
3658 : :
3659 [ # # ]: 0 : PA_LLIST_FOREACH(c, s->thread_info.volume_changes) {
3660 : 0 : pa_usec_t modified_limit = limit;
3661 [ # # ]: 0 : if (prev_vol > pa_cvolume_avg(&c->hw_volume))
3662 : 0 : modified_limit -= s->thread_info.volume_change_safety_margin;
3663 : : else
3664 : 0 : modified_limit += s->thread_info.volume_change_safety_margin;
3665 [ # # ]: 0 : if (c->at > modified_limit) {
3666 : 0 : c->at -= rewound;
3667 [ # # ]: 0 : if (c->at < modified_limit)
3668 : 0 : c->at = modified_limit;
3669 : : }
3670 : 0 : prev_vol = pa_cvolume_avg(&c->hw_volume);
3671 : : }
3672 : 0 : pa_sink_volume_change_apply(s, NULL);
3673 : 0 : }
3674 : :
3675 : : /* Called from the main thread */
3676 : : /* Gets the list of formats supported by the sink. The members and idxset must
3677 : : * be freed by the caller. */
3678 : 0 : pa_idxset* pa_sink_get_formats(pa_sink *s) {
3679 : : pa_idxset *ret;
3680 : :
3681 [ # # ]: 0 : pa_assert(s);
3682 : :
3683 [ # # ]: 0 : if (s->get_formats) {
3684 : : /* Sink supports format query, all is good */
3685 : 0 : ret = s->get_formats(s);
3686 : : } else {
3687 : : /* Sink doesn't support format query, so assume it does PCM */
3688 : 0 : pa_format_info *f = pa_format_info_new();
3689 : 0 : f->encoding = PA_ENCODING_PCM;
3690 : :
3691 : 0 : ret = pa_idxset_new(NULL, NULL);
3692 : 0 : pa_idxset_put(ret, f, NULL);
3693 : : }
3694 : :
3695 : 0 : return ret;
3696 : : }
3697 : :
3698 : : /* Called from the main thread */
3699 : : /* Allows an external source to set what formats a sink supports if the sink
3700 : : * permits this. The function makes a copy of the formats on success. */
3701 : 0 : pa_bool_t pa_sink_set_formats(pa_sink *s, pa_idxset *formats) {
3702 [ # # ]: 0 : pa_assert(s);
3703 [ # # ]: 0 : pa_assert(formats);
3704 : :
3705 [ # # ]: 0 : if (s->set_formats)
3706 : : /* Sink supports setting formats -- let's give it a shot */
3707 : 0 : return s->set_formats(s, formats);
3708 : : else
3709 : : /* Sink doesn't support setting this -- bail out */
3710 : : return FALSE;
3711 : : }
3712 : :
3713 : : /* Called from the main thread */
3714 : : /* Checks if the sink can accept this format */
3715 : 0 : pa_bool_t pa_sink_check_format(pa_sink *s, pa_format_info *f)
3716 : : {
3717 : 0 : pa_idxset *formats = NULL;
3718 : 0 : pa_bool_t ret = FALSE;
3719 : :
3720 [ # # ]: 0 : pa_assert(s);
3721 [ # # ]: 0 : pa_assert(f);
3722 : :
3723 : 0 : formats = pa_sink_get_formats(s);
3724 : :
3725 [ # # ]: 0 : if (formats) {
3726 : : pa_format_info *finfo_device;
3727 : : uint32_t i;
3728 : :
3729 [ # # ]: 0 : PA_IDXSET_FOREACH(finfo_device, formats, i) {
3730 [ # # ]: 0 : if (pa_format_info_is_compatible(finfo_device, f)) {
3731 : : ret = TRUE;
3732 : : break;
3733 : : }
3734 : : }
3735 : :
3736 : 0 : pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
3737 : : }
3738 : :
3739 : 0 : return ret;
3740 : : }
3741 : :
3742 : : /* Called from the main thread */
3743 : : /* Calculates the intersection between formats supported by the sink and
3744 : : * in_formats, and returns these, in the order of the sink's formats. */
3745 : 0 : pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats) {
3746 : 0 : pa_idxset *out_formats = pa_idxset_new(NULL, NULL), *sink_formats = NULL;
3747 : : pa_format_info *f_sink, *f_in;
3748 : : uint32_t i, j;
3749 : :
3750 [ # # ]: 0 : pa_assert(s);
3751 : :
3752 [ # # ][ # # ]: 0 : if (!in_formats || pa_idxset_isempty(in_formats))
3753 : : goto done;
3754 : :
3755 : 0 : sink_formats = pa_sink_get_formats(s);
3756 : :
3757 [ # # ]: 0 : PA_IDXSET_FOREACH(f_sink, sink_formats, i) {
3758 [ # # ]: 0 : PA_IDXSET_FOREACH(f_in, in_formats, j) {
3759 [ # # ]: 0 : if (pa_format_info_is_compatible(f_sink, f_in))
3760 : 0 : pa_idxset_put(out_formats, pa_format_info_copy(f_in), NULL);
3761 : : }
3762 : : }
3763 : :
3764 : : done:
3765 [ # # ]: 0 : if (sink_formats)
3766 : 0 : pa_idxset_free(sink_formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
3767 : :
3768 : 0 : return out_formats;
3769 : : }
|