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 : :
30 : : #include <pulse/format.h>
31 : : #include <pulse/utf8.h>
32 : : #include <pulse/xmalloc.h>
33 : : #include <pulse/timeval.h>
34 : : #include <pulse/util.h>
35 : : #include <pulse/rtclock.h>
36 : : #include <pulse/internal.h>
37 : :
38 : : #include <pulsecore/core-util.h>
39 : : #include <pulsecore/source-output.h>
40 : : #include <pulsecore/namereg.h>
41 : : #include <pulsecore/core-subscribe.h>
42 : : #include <pulsecore/log.h>
43 : : #include <pulsecore/sample-util.h>
44 : : #include <pulsecore/flist.h>
45 : :
46 : : #include "source.h"
47 : :
48 : : #define ABSOLUTE_MIN_LATENCY (500)
49 : : #define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
50 : : #define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
51 : :
52 [ # # ]: 0 : PA_DEFINE_PUBLIC_CLASS(pa_source, pa_msgobject);
53 : :
54 : : struct pa_source_volume_change {
55 : : pa_usec_t at;
56 : : pa_cvolume hw_volume;
57 : :
58 : : PA_LLIST_FIELDS(pa_source_volume_change);
59 : : };
60 : :
61 : : struct source_message_set_port {
62 : : pa_device_port *port;
63 : : int ret;
64 : : };
65 : :
66 : : static void source_free(pa_object *o);
67 : :
68 : : static void pa_source_volume_change_push(pa_source *s);
69 : : static void pa_source_volume_change_flush(pa_source *s);
70 : :
71 : 0 : pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data) {
72 [ # # ]: 0 : pa_assert(data);
73 : :
74 : : pa_zero(*data);
75 : 0 : data->proplist = pa_proplist_new();
76 : 0 : data->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
77 : :
78 : 0 : return data;
79 : : }
80 : :
81 : 0 : void pa_source_new_data_set_name(pa_source_new_data *data, const char *name) {
82 [ # # ]: 0 : pa_assert(data);
83 : :
84 : 0 : pa_xfree(data->name);
85 : 0 : data->name = pa_xstrdup(name);
86 : 0 : }
87 : :
88 : 0 : void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec) {
89 [ # # ]: 0 : pa_assert(data);
90 : :
91 [ # # ]: 0 : if ((data->sample_spec_is_set = !!spec))
92 : 0 : data->sample_spec = *spec;
93 : 0 : }
94 : :
95 : 0 : void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map) {
96 [ # # ]: 0 : pa_assert(data);
97 : :
98 [ # # ]: 0 : if ((data->channel_map_is_set = !!map))
99 : 0 : data->channel_map = *map;
100 : 0 : }
101 : :
102 : 0 : void pa_source_new_data_set_alternate_sample_rate(pa_source_new_data *data, const uint32_t alternate_sample_rate) {
103 [ # # ]: 0 : pa_assert(data);
104 : :
105 : 0 : data->alternate_sample_rate_is_set = TRUE;
106 : 0 : data->alternate_sample_rate = alternate_sample_rate;
107 : 0 : }
108 : :
109 : 0 : void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume) {
110 [ # # ]: 0 : pa_assert(data);
111 : :
112 [ # # ]: 0 : if ((data->volume_is_set = !!volume))
113 : 0 : data->volume = *volume;
114 : 0 : }
115 : :
116 : 0 : void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute) {
117 [ # # ]: 0 : pa_assert(data);
118 : :
119 : 0 : data->muted_is_set = TRUE;
120 : 0 : data->muted = !!mute;
121 : 0 : }
122 : :
123 : 0 : void pa_source_new_data_set_port(pa_source_new_data *data, const char *port) {
124 [ # # ]: 0 : pa_assert(data);
125 : :
126 : 0 : pa_xfree(data->active_port);
127 : 0 : data->active_port = pa_xstrdup(port);
128 : 0 : }
129 : :
130 : 0 : void pa_source_new_data_done(pa_source_new_data *data) {
131 [ # # ]: 0 : pa_assert(data);
132 : :
133 : 0 : pa_proplist_free(data->proplist);
134 : :
135 [ # # ]: 0 : if (data->ports)
136 : 0 : pa_device_port_hashmap_free(data->ports);
137 : :
138 : 0 : pa_xfree(data->name);
139 : 0 : pa_xfree(data->active_port);
140 : 0 : }
141 : :
142 : : /* Called from main context */
143 : 0 : static void reset_callbacks(pa_source *s) {
144 [ # # ]: 0 : pa_assert(s);
145 : :
146 : 0 : s->set_state = NULL;
147 : 0 : s->get_volume = NULL;
148 : 0 : s->set_volume = NULL;
149 : 0 : s->write_volume = NULL;
150 : 0 : s->get_mute = NULL;
151 : 0 : s->set_mute = NULL;
152 : 0 : s->update_requested_latency = NULL;
153 : 0 : s->set_port = NULL;
154 : 0 : s->get_formats = NULL;
155 : 0 : s->update_rate = NULL;
156 : 0 : }
157 : :
158 : : /* Called from main context */
159 : 0 : pa_source* pa_source_new(
160 : : pa_core *core,
161 : : pa_source_new_data *data,
162 : : pa_source_flags_t flags) {
163 : :
164 : : pa_source *s;
165 : : const char *name;
166 : : char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
167 : : char *pt;
168 : :
169 [ # # ]: 0 : pa_assert(core);
170 [ # # ]: 0 : pa_assert(data);
171 [ # # ]: 0 : pa_assert(data->name);
172 [ # # ]: 0 : pa_assert_ctl_context();
173 : :
174 : 0 : s = pa_msgobject_new(pa_source);
175 : :
176 [ # # ]: 0 : if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SOURCE, s, data->namereg_fail))) {
177 : 0 : pa_log_debug("Failed to register name %s.", data->name);
178 : 0 : pa_xfree(s);
179 : 0 : return NULL;
180 : : }
181 : :
182 : 0 : pa_source_new_data_set_name(data, name);
183 : :
184 [ # # ]: 0 : if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_NEW], data) < 0) {
185 : 0 : pa_xfree(s);
186 : 0 : pa_namereg_unregister(core, name);
187 : 0 : return NULL;
188 : : }
189 : :
190 : : /* FIXME, need to free s here on failure */
191 : :
192 [ # # ][ # # ]: 0 : pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
193 [ # # ][ # # ]: 0 : pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
[ # # ][ # # ]
194 : :
195 [ # # ][ # # ]: 0 : pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
196 : :
197 [ # # ]: 0 : if (!data->channel_map_is_set)
198 [ # # ]: 0 : pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
199 : :
200 [ # # ]: 0 : pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
201 [ # # ]: 0 : pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
202 : :
203 : : /* FIXME: There should probably be a general function for checking whether
204 : : * the source volume is allowed to be set, like there is for source outputs. */
205 [ # # ][ # # ]: 0 : pa_assert(!data->volume_is_set || !(flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
206 : :
207 [ # # ]: 0 : if (!data->volume_is_set) {
208 : 0 : pa_cvolume_reset(&data->volume, data->sample_spec.channels);
209 : 0 : data->save_volume = FALSE;
210 : : }
211 : :
212 [ # # ]: 0 : pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
213 [ # # ]: 0 : pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));
214 : :
215 [ # # ]: 0 : if (!data->muted_is_set)
216 : 0 : data->muted = FALSE;
217 : :
218 [ # # ]: 0 : if (data->card)
219 : 0 : pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist);
220 : :
221 : 0 : pa_device_init_description(data->proplist);
222 : 0 : pa_device_init_icon(data->proplist, FALSE);
223 : 0 : pa_device_init_intended_roles(data->proplist);
224 : :
225 [ # # ]: 0 : if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) {
226 : 0 : pa_xfree(s);
227 : 0 : pa_namereg_unregister(core, name);
228 : 0 : return NULL;
229 : : }
230 : :
231 : 0 : s->parent.parent.free = source_free;
232 : 0 : s->parent.process_msg = pa_source_process_msg;
233 : :
234 : 0 : s->core = core;
235 : 0 : s->state = PA_SOURCE_INIT;
236 : 0 : s->flags = flags;
237 : 0 : s->priority = 0;
238 : 0 : s->suspend_cause = 0;
239 : 0 : pa_source_set_mixer_dirty(s, FALSE);
240 : 0 : s->name = pa_xstrdup(name);
241 : 0 : s->proplist = pa_proplist_copy(data->proplist);
242 : 0 : s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
243 : 0 : s->module = data->module;
244 : 0 : s->card = data->card;
245 : :
246 : 0 : s->priority = pa_device_init_priority(s->proplist);
247 : :
248 : 0 : s->sample_spec = data->sample_spec;
249 : 0 : s->channel_map = data->channel_map;
250 : 0 : s->default_sample_rate = s->sample_spec.rate;
251 : :
252 [ # # ]: 0 : if (data->alternate_sample_rate_is_set)
253 : 0 : s->alternate_sample_rate = data->alternate_sample_rate;
254 : : else
255 : 0 : s->alternate_sample_rate = s->core->alternate_sample_rate;
256 : :
257 [ # # ]: 0 : if (s->sample_spec.rate == s->alternate_sample_rate) {
258 : 0 : pa_log_warn("Default and alternate sample rates are the same.");
259 : 0 : s->alternate_sample_rate = 0;
260 : : }
261 : :
262 : 0 : s->outputs = pa_idxset_new(NULL, NULL);
263 : 0 : s->n_corked = 0;
264 : 0 : s->monitor_of = NULL;
265 : 0 : s->output_from_master = NULL;
266 : :
267 : 0 : s->reference_volume = s->real_volume = data->volume;
268 : 0 : pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
269 : 0 : s->base_volume = PA_VOLUME_NORM;
270 : 0 : s->n_volume_steps = PA_VOLUME_NORM+1;
271 : 0 : s->muted = data->muted;
272 : 0 : s->refresh_volume = s->refresh_muted = FALSE;
273 : :
274 : 0 : reset_callbacks(s);
275 : 0 : s->userdata = NULL;
276 : :
277 : 0 : s->asyncmsgq = NULL;
278 : :
279 : : /* As a minor optimization we just steal the list instead of
280 : : * copying it here */
281 : 0 : s->ports = data->ports;
282 : 0 : data->ports = NULL;
283 : :
284 : 0 : s->active_port = NULL;
285 : 0 : s->save_port = FALSE;
286 : :
287 [ # # ]: 0 : if (data->active_port)
288 [ # # ]: 0 : if ((s->active_port = pa_hashmap_get(s->ports, data->active_port)))
289 : 0 : s->save_port = data->save_port;
290 : :
291 [ # # ]: 0 : if (!s->active_port) {
292 : : void *state;
293 : : pa_device_port *p;
294 : :
295 [ # # ]: 0 : PA_HASHMAP_FOREACH(p, s->ports, state)
296 [ # # ][ # # ]: 0 : if (!s->active_port || p->priority > s->active_port->priority)
297 : 0 : s->active_port = p;
298 : : }
299 : :
300 [ # # ]: 0 : if (s->active_port)
301 : 0 : s->latency_offset = s->active_port->latency_offset;
302 : : else
303 : 0 : s->latency_offset = 0;
304 : :
305 : 0 : s->save_volume = data->save_volume;
306 : 0 : s->save_muted = data->save_muted;
307 : :
308 : 0 : pa_silence_memchunk_get(
309 : : &core->silence_cache,
310 : : core->mempool,
311 : : &s->silence,
312 : 0 : &s->sample_spec,
313 : : 0);
314 : :
315 : 0 : s->thread_info.rtpoll = NULL;
316 : 0 : s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
317 : 0 : s->thread_info.soft_volume = s->soft_volume;
318 : 0 : s->thread_info.soft_muted = s->muted;
319 : 0 : s->thread_info.state = s->state;
320 : 0 : s->thread_info.max_rewind = 0;
321 : 0 : s->thread_info.requested_latency_valid = FALSE;
322 : 0 : s->thread_info.requested_latency = 0;
323 : 0 : s->thread_info.min_latency = ABSOLUTE_MIN_LATENCY;
324 : 0 : s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
325 [ # # ]: 0 : s->thread_info.fixed_latency = flags & PA_SOURCE_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
326 : :
327 : 0 : PA_LLIST_HEAD_INIT(pa_source_volume_change, s->thread_info.volume_changes);
328 : 0 : s->thread_info.volume_changes_tail = NULL;
329 : 0 : pa_sw_cvolume_multiply(&s->thread_info.current_hw_volume, &s->soft_volume, &s->real_volume);
330 : 0 : s->thread_info.volume_change_safety_margin = core->deferred_volume_safety_margin_usec;
331 : 0 : s->thread_info.volume_change_extra_delay = core->deferred_volume_extra_delay_usec;
332 : 0 : s->thread_info.latency_offset = s->latency_offset;
333 : :
334 : : /* FIXME: This should probably be moved to pa_source_put() */
335 [ # # ]: 0 : pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
336 : :
337 [ # # ]: 0 : if (s->card)
338 [ # # ]: 0 : pa_assert_se(pa_idxset_put(s->card->sources, s, NULL) >= 0);
339 : :
340 : 0 : pt = pa_proplist_to_string_sep(s->proplist, "\n ");
341 : 0 : pa_log_info("Created source %u \"%s\" with sample spec %s and channel map %s\n %s",
342 : : s->index,
343 : : s->name,
344 : : pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
345 : : pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map),
346 : : pt);
347 : 0 : pa_xfree(pt);
348 : :
349 : 0 : return s;
350 : : }
351 : :
352 : : /* Called from main context */
353 : 0 : static int source_set_state(pa_source *s, pa_source_state_t state) {
354 : : int ret;
355 : : pa_bool_t suspend_change;
356 : : pa_source_state_t original_state;
357 : :
358 [ # # ]: 0 : pa_assert(s);
359 [ # # ]: 0 : pa_assert_ctl_context();
360 : :
361 [ # # ]: 0 : if (s->state == state)
362 : : return 0;
363 : :
364 : 0 : original_state = s->state;
365 : :
366 : 0 : suspend_change =
367 [ # # ][ # # ]: 0 : (original_state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(state)) ||
[ # # ]
368 [ # # ]: 0 : (PA_SOURCE_IS_OPENED(original_state) && state == PA_SOURCE_SUSPENDED);
369 : :
370 [ # # ]: 0 : if (s->set_state)
371 [ # # ]: 0 : if ((ret = s->set_state(s, state)) < 0)
372 : : return ret;
373 : :
374 [ # # ]: 0 : if (s->asyncmsgq)
375 [ # # ]: 0 : if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL)) < 0) {
376 : :
377 [ # # ]: 0 : if (s->set_state)
378 : 0 : s->set_state(s, original_state);
379 : :
380 : : return ret;
381 : : }
382 : :
383 : 0 : s->state = state;
384 : :
385 [ # # ]: 0 : if (state != PA_SOURCE_UNLINKED) { /* if we enter UNLINKED state pa_source_unlink() will fire the appropriate events */
386 : 0 : pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], s);
387 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
388 : : }
389 : :
390 [ # # ]: 0 : if (suspend_change) {
391 : : pa_source_output *o;
392 : : uint32_t idx;
393 : :
394 : : /* We're suspending or resuming, tell everyone about it */
395 : :
396 [ # # ]: 0 : PA_IDXSET_FOREACH(o, s->outputs, idx)
397 [ # # ][ # # ]: 0 : if (s->state == PA_SOURCE_SUSPENDED &&
398 : 0 : (o->flags & PA_SOURCE_OUTPUT_KILL_ON_SUSPEND))
399 : 0 : pa_source_output_kill(o);
400 [ # # ]: 0 : else if (o->suspend)
401 : 0 : o->suspend(o, state == PA_SOURCE_SUSPENDED);
402 : : }
403 : :
404 : : return 0;
405 : : }
406 : :
407 : 0 : void pa_source_set_get_volume_callback(pa_source *s, pa_source_cb_t cb) {
408 [ # # ]: 0 : pa_assert(s);
409 : :
410 : 0 : s->get_volume = cb;
411 : 0 : }
412 : :
413 : 0 : void pa_source_set_set_volume_callback(pa_source *s, pa_source_cb_t cb) {
414 : : pa_source_flags_t flags;
415 : :
416 [ # # ]: 0 : pa_assert(s);
417 [ # # ]: 0 : pa_assert(!s->write_volume || cb);
418 : :
419 : 0 : s->set_volume = cb;
420 : :
421 : : /* Save the current flags so we can tell if they've changed */
422 : 0 : flags = s->flags;
423 : :
424 [ # # ]: 0 : if (cb) {
425 : : /* The source implementor is responsible for setting decibel volume support */
426 : 0 : s->flags |= PA_SOURCE_HW_VOLUME_CTRL;
427 : : } else {
428 : 0 : s->flags &= ~PA_SOURCE_HW_VOLUME_CTRL;
429 : : /* See note below in pa_source_put() about volume sharing and decibel volumes */
430 : 0 : pa_source_enable_decibel_volume(s, !(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
431 : : }
432 : :
433 : : /* If the flags have changed after init, let any clients know via a change event */
434 [ # # ][ # # ]: 0 : if (s->state != PA_SOURCE_INIT && flags != s->flags)
435 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
436 : 0 : }
437 : :
438 : 0 : void pa_source_set_write_volume_callback(pa_source *s, pa_source_cb_t cb) {
439 : : pa_source_flags_t flags;
440 : :
441 [ # # ]: 0 : pa_assert(s);
442 [ # # ][ # # ]: 0 : pa_assert(!cb || s->set_volume);
443 : :
444 : 0 : s->write_volume = cb;
445 : :
446 : : /* Save the current flags so we can tell if they've changed */
447 : 0 : flags = s->flags;
448 : :
449 [ # # ]: 0 : if (cb)
450 : 0 : s->flags |= PA_SOURCE_DEFERRED_VOLUME;
451 : : else
452 : 0 : s->flags &= ~PA_SOURCE_DEFERRED_VOLUME;
453 : :
454 : : /* If the flags have changed after init, let any clients know via a change event */
455 [ # # ][ # # ]: 0 : if (s->state != PA_SOURCE_INIT && flags != s->flags)
456 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
457 : 0 : }
458 : :
459 : 0 : void pa_source_set_get_mute_callback(pa_source *s, pa_source_cb_t cb) {
460 [ # # ]: 0 : pa_assert(s);
461 : :
462 : 0 : s->get_mute = cb;
463 : 0 : }
464 : :
465 : 0 : void pa_source_set_set_mute_callback(pa_source *s, pa_source_cb_t cb) {
466 : : pa_source_flags_t flags;
467 : :
468 [ # # ]: 0 : pa_assert(s);
469 : :
470 : 0 : s->set_mute = cb;
471 : :
472 : : /* Save the current flags so we can tell if they've changed */
473 : 0 : flags = s->flags;
474 : :
475 [ # # ]: 0 : if (cb)
476 : 0 : s->flags |= PA_SOURCE_HW_MUTE_CTRL;
477 : : else
478 : 0 : s->flags &= ~PA_SOURCE_HW_MUTE_CTRL;
479 : :
480 : : /* If the flags have changed after init, let any clients know via a change event */
481 [ # # ][ # # ]: 0 : if (s->state != PA_SOURCE_INIT && flags != s->flags)
482 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
483 : 0 : }
484 : :
485 : 0 : static void enable_flat_volume(pa_source *s, pa_bool_t enable) {
486 : : pa_source_flags_t flags;
487 : :
488 [ # # ]: 0 : pa_assert(s);
489 : :
490 : : /* Always follow the overall user preference here */
491 [ # # ][ # # ]: 0 : enable = enable && s->core->flat_volumes;
492 : :
493 : : /* Save the current flags so we can tell if they've changed */
494 : 0 : flags = s->flags;
495 : :
496 [ # # ]: 0 : if (enable)
497 : 0 : s->flags |= PA_SOURCE_FLAT_VOLUME;
498 : : else
499 : 0 : s->flags &= ~PA_SOURCE_FLAT_VOLUME;
500 : :
501 : : /* If the flags have changed after init, let any clients know via a change event */
502 [ # # ][ # # ]: 0 : if (s->state != PA_SOURCE_INIT && flags != s->flags)
503 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
504 : 0 : }
505 : :
506 : 0 : void pa_source_enable_decibel_volume(pa_source *s, pa_bool_t enable) {
507 : : pa_source_flags_t flags;
508 : :
509 [ # # ]: 0 : pa_assert(s);
510 : :
511 : : /* Save the current flags so we can tell if they've changed */
512 : 0 : flags = s->flags;
513 : :
514 [ # # ]: 0 : if (enable) {
515 : 0 : s->flags |= PA_SOURCE_DECIBEL_VOLUME;
516 : 0 : enable_flat_volume(s, TRUE);
517 : : } else {
518 : 0 : s->flags &= ~PA_SOURCE_DECIBEL_VOLUME;
519 : 0 : enable_flat_volume(s, FALSE);
520 : : }
521 : :
522 : : /* If the flags have changed after init, let any clients know via a change event */
523 [ # # ][ # # ]: 0 : if (s->state != PA_SOURCE_INIT && flags != s->flags)
524 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
525 : 0 : }
526 : :
527 : : /* Called from main context */
528 : 0 : void pa_source_put(pa_source *s) {
529 : 0 : pa_source_assert_ref(s);
530 [ # # ]: 0 : pa_assert_ctl_context();
531 : :
532 [ # # ]: 0 : pa_assert(s->state == PA_SOURCE_INIT);
533 [ # # ][ # # ]: 0 : pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) || s->output_from_master);
534 : :
535 : : /* The following fields must be initialized properly when calling _put() */
536 [ # # ]: 0 : pa_assert(s->asyncmsgq);
537 [ # # ]: 0 : pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
538 : :
539 : : /* Generally, flags should be initialized via pa_source_new(). As a
540 : : * special exception we allow some volume related flags to be set
541 : : * between _new() and _put() by the callback setter functions above.
542 : : *
543 : : * Thus we implement a couple safeguards here which ensure the above
544 : : * setters were used (or at least the implementor made manual changes
545 : : * in a compatible way).
546 : : *
547 : : * Note: All of these flags set here can change over the life time
548 : : * of the source. */
549 [ # # ][ # # ]: 0 : pa_assert(!(s->flags & PA_SOURCE_HW_VOLUME_CTRL) || s->set_volume);
550 [ # # ][ # # ]: 0 : pa_assert(!(s->flags & PA_SOURCE_DEFERRED_VOLUME) || s->write_volume);
551 [ # # ][ # # ]: 0 : pa_assert(!(s->flags & PA_SOURCE_HW_MUTE_CTRL) || s->set_mute);
552 : :
553 : : /* XXX: Currently decibel volume is disabled for all sources that use volume
554 : : * sharing. When the master source supports decibel volume, it would be good
555 : : * to have the flag also in the filter source, but currently we don't do that
556 : : * so that the flags of the filter source never change when it's moved from
557 : : * a master source to another. One solution for this problem would be to
558 : : * remove user-visible volume altogether from filter sources when volume
559 : : * sharing is used, but the current approach was easier to implement... */
560 : : /* We always support decibel volumes in software, otherwise we leave it to
561 : : * the source implementor to set this flag as needed.
562 : : *
563 : : * Note: This flag can also change over the life time of the source. */
564 [ # # ]: 0 : if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL) && !(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
565 : 0 : pa_source_enable_decibel_volume(s, TRUE);
566 : :
567 : : /* If the source implementor support DB volumes by itself, we should always
568 : : * try and enable flat volumes too */
569 [ # # ]: 0 : if ((s->flags & PA_SOURCE_DECIBEL_VOLUME))
570 : 0 : enable_flat_volume(s, TRUE);
571 : :
572 [ # # ]: 0 : if (s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) {
573 : 0 : pa_source *root_source = pa_source_get_master(s);
574 : :
575 [ # # ]: 0 : pa_assert(PA_LIKELY(root_source));
576 : :
577 : 0 : s->reference_volume = root_source->reference_volume;
578 : 0 : pa_cvolume_remap(&s->reference_volume, &root_source->channel_map, &s->channel_map);
579 : :
580 : 0 : s->real_volume = root_source->real_volume;
581 : 0 : pa_cvolume_remap(&s->real_volume, &root_source->channel_map, &s->channel_map);
582 : : } else
583 : : /* We assume that if the sink implementor changed the default
584 : : * volume he did so in real_volume, because that is the usual
585 : : * place where he is supposed to place his changes. */
586 : 0 : s->reference_volume = s->real_volume;
587 : :
588 : 0 : s->thread_info.soft_volume = s->soft_volume;
589 : 0 : s->thread_info.soft_muted = s->muted;
590 : 0 : pa_sw_cvolume_multiply(&s->thread_info.current_hw_volume, &s->soft_volume, &s->real_volume);
591 : :
592 [ # # ][ # # ]: 0 : pa_assert((s->flags & PA_SOURCE_HW_VOLUME_CTRL)
[ # # ][ # # ]
593 : : || (s->base_volume == PA_VOLUME_NORM
594 : : && ((s->flags & PA_SOURCE_DECIBEL_VOLUME || (s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)))));
595 [ # # ][ # # ]: 0 : pa_assert(!(s->flags & PA_SOURCE_DECIBEL_VOLUME) || s->n_volume_steps == PA_VOLUME_NORM+1);
596 [ # # ]: 0 : pa_assert(!(s->flags & PA_SOURCE_DYNAMIC_LATENCY) == (s->thread_info.fixed_latency != 0));
597 : :
598 [ # # ]: 0 : pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
599 : :
600 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
601 : 0 : pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PUT], s);
602 : 0 : }
603 : :
604 : : /* Called from main context */
605 : 0 : void pa_source_unlink(pa_source *s) {
606 : : pa_bool_t linked;
607 : 0 : pa_source_output *o, *j = NULL;
608 : :
609 [ # # ]: 0 : pa_assert(s);
610 [ # # ]: 0 : pa_assert_ctl_context();
611 : :
612 : : /* See pa_sink_unlink() for a couple of comments how this function
613 : : * works. */
614 : :
615 : 0 : linked = PA_SOURCE_IS_LINKED(s->state);
616 : :
617 [ # # ]: 0 : if (linked)
618 : 0 : pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s);
619 : :
620 [ # # ]: 0 : if (s->state != PA_SOURCE_UNLINKED)
621 : 0 : pa_namereg_unregister(s->core, s->name);
622 : 0 : pa_idxset_remove_by_data(s->core->sources, s, NULL);
623 : :
624 [ # # ]: 0 : if (s->card)
625 : 0 : pa_idxset_remove_by_data(s->card->sources, s, NULL);
626 : :
627 [ # # ]: 0 : while ((o = pa_idxset_first(s->outputs, NULL))) {
628 [ # # ]: 0 : pa_assert(o != j);
629 : 0 : pa_source_output_kill(o);
630 : 0 : j = o;
631 : : }
632 : :
633 [ # # ]: 0 : if (linked)
634 : 0 : source_set_state(s, PA_SOURCE_UNLINKED);
635 : : else
636 : 0 : s->state = PA_SOURCE_UNLINKED;
637 : :
638 : 0 : reset_callbacks(s);
639 : :
640 [ # # ]: 0 : if (linked) {
641 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
642 : 0 : pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], s);
643 : : }
644 : 0 : }
645 : :
646 : : /* Called from main context */
647 : 0 : static void source_free(pa_object *o) {
648 : : pa_source_output *so;
649 : 0 : pa_source *s = PA_SOURCE(o);
650 : :
651 [ # # ]: 0 : pa_assert(s);
652 [ # # ]: 0 : pa_assert_ctl_context();
653 [ # # ]: 0 : pa_assert(pa_source_refcnt(s) == 0);
654 : :
655 [ # # ]: 0 : if (PA_SOURCE_IS_LINKED(s->state))
656 : 0 : pa_source_unlink(s);
657 : :
658 : 0 : pa_log_info("Freeing source %u \"%s\"", s->index, s->name);
659 : :
660 : 0 : pa_idxset_free(s->outputs, NULL, NULL);
661 : :
662 [ # # ]: 0 : while ((so = pa_hashmap_steal_first(s->thread_info.outputs)))
663 : 0 : pa_source_output_unref(so);
664 : :
665 : 0 : pa_hashmap_free(s->thread_info.outputs, NULL, NULL);
666 : :
667 [ # # ]: 0 : if (s->silence.memblock)
668 : 0 : pa_memblock_unref(s->silence.memblock);
669 : :
670 : 0 : pa_xfree(s->name);
671 : 0 : pa_xfree(s->driver);
672 : :
673 [ # # ]: 0 : if (s->proplist)
674 : 0 : pa_proplist_free(s->proplist);
675 : :
676 [ # # ]: 0 : if (s->ports)
677 : 0 : pa_device_port_hashmap_free(s->ports);
678 : :
679 : 0 : pa_xfree(s);
680 : 0 : }
681 : :
682 : : /* Called from main context, and not while the IO thread is active, please */
683 : 0 : void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) {
684 : 0 : pa_source_assert_ref(s);
685 [ # # ]: 0 : pa_assert_ctl_context();
686 : :
687 : 0 : s->asyncmsgq = q;
688 : 0 : }
689 : :
690 : : /* Called from main context, and not while the IO thread is active, please */
691 : 0 : void pa_source_update_flags(pa_source *s, pa_source_flags_t mask, pa_source_flags_t value) {
692 : 0 : pa_source_assert_ref(s);
693 [ # # ]: 0 : pa_assert_ctl_context();
694 : :
695 [ # # ]: 0 : if (mask == 0)
696 : 0 : return;
697 : :
698 : : /* For now, allow only a minimal set of flags to be changed. */
699 [ # # ]: 0 : pa_assert((mask & ~(PA_SOURCE_DYNAMIC_LATENCY|PA_SOURCE_LATENCY)) == 0);
700 : :
701 : 0 : s->flags = (s->flags & ~mask) | (value & mask);
702 : : }
703 : :
704 : : /* Called from IO context, or before _put() from main context */
705 : 0 : void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) {
706 : 0 : pa_source_assert_ref(s);
707 [ # # ][ # # ]: 0 : pa_source_assert_io_context(s);
708 : :
709 : 0 : s->thread_info.rtpoll = p;
710 : 0 : }
711 : :
712 : : /* Called from main context */
713 : 0 : int pa_source_update_status(pa_source*s) {
714 : 0 : pa_source_assert_ref(s);
715 [ # # ]: 0 : pa_assert_ctl_context();
716 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
717 : :
718 [ # # ]: 0 : if (s->state == PA_SOURCE_SUSPENDED)
719 : : return 0;
720 : :
721 : 0 : return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
722 : : }
723 : :
724 : : /* Called from any context - must be threadsafe */
725 : 0 : void pa_source_set_mixer_dirty(pa_source *s, pa_bool_t is_dirty)
726 : : {
727 : 0 : pa_atomic_store(&s->mixer_dirty, is_dirty ? 1 : 0);
728 : 0 : }
729 : :
730 : : /* Called from main context */
731 : 0 : int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause) {
732 : 0 : pa_source_assert_ref(s);
733 [ # # ]: 0 : pa_assert_ctl_context();
734 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
735 [ # # ]: 0 : pa_assert(cause != 0);
736 : :
737 [ # # ]: 0 : if (s->monitor_of && cause != PA_SUSPEND_PASSTHROUGH)
738 : : return -PA_ERR_NOTSUPPORTED;
739 : :
740 [ # # ]: 0 : if (suspend)
741 : 0 : s->suspend_cause |= cause;
742 : : else
743 : 0 : s->suspend_cause &= ~cause;
744 : :
745 [ # # ][ # # ]: 0 : if (!(s->suspend_cause & PA_SUSPEND_SESSION) && (pa_atomic_load(&s->mixer_dirty) != 0)) {
746 : : /* This might look racy but isn't: If somebody sets mixer_dirty exactly here,
747 : : it'll be handled just fine. */
748 : 0 : pa_source_set_mixer_dirty(s, FALSE);
749 : 0 : pa_log_debug("Mixer is now accessible. Updating alsa mixer settings.");
750 [ # # ][ # # ]: 0 : if (s->active_port && s->set_port) {
751 [ # # ]: 0 : if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
752 : 0 : struct source_message_set_port msg = { .port = s->active_port, .ret = 0 };
753 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
754 : : }
755 : : else
756 : 0 : s->set_port(s, s->active_port);
757 : : }
758 : : else {
759 [ # # ]: 0 : if (s->set_mute)
760 : 0 : s->set_mute(s);
761 [ # # ]: 0 : if (s->set_volume)
762 : 0 : s->set_volume(s);
763 : : }
764 : : }
765 : :
766 [ # # ]: 0 : if ((pa_source_get_state(s) == PA_SOURCE_SUSPENDED) == !!s->suspend_cause)
767 : : return 0;
768 : :
769 [ # # ]: 0 : pa_log_debug("Suspend cause of source %s is 0x%04x, %s", s->name, s->suspend_cause, s->suspend_cause ? "suspending" : "resuming");
770 : :
771 [ # # ]: 0 : if (s->suspend_cause)
772 : 0 : return source_set_state(s, PA_SOURCE_SUSPENDED);
773 : : else
774 : 0 : return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
775 : : }
776 : :
777 : : /* Called from main context */
778 : 0 : int pa_source_sync_suspend(pa_source *s) {
779 : : pa_sink_state_t state;
780 : :
781 : 0 : pa_source_assert_ref(s);
782 [ # # ]: 0 : pa_assert_ctl_context();
783 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
784 [ # # ]: 0 : pa_assert(s->monitor_of);
785 : :
786 : 0 : state = pa_sink_get_state(s->monitor_of);
787 : :
788 [ # # ]: 0 : if (state == PA_SINK_SUSPENDED)
789 : 0 : return source_set_state(s, PA_SOURCE_SUSPENDED);
790 : :
791 [ # # ]: 0 : pa_assert(PA_SINK_IS_OPENED(state));
792 : :
793 : 0 : return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
794 : : }
795 : :
796 : : /* Called from main context */
797 : 0 : pa_queue *pa_source_move_all_start(pa_source *s, pa_queue *q) {
798 : : pa_source_output *o, *n;
799 : : uint32_t idx;
800 : :
801 : 0 : pa_source_assert_ref(s);
802 [ # # ]: 0 : pa_assert_ctl_context();
803 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
804 : :
805 [ # # ]: 0 : if (!q)
806 : 0 : q = pa_queue_new();
807 : :
808 [ # # ]: 0 : for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = n) {
809 : 0 : n = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx));
810 : :
811 : : pa_source_output_ref(o);
812 : :
813 [ # # ]: 0 : if (pa_source_output_start_move(o) >= 0)
814 : 0 : pa_queue_push(q, o);
815 : : else
816 : 0 : pa_source_output_unref(o);
817 : : }
818 : :
819 : 0 : return q;
820 : : }
821 : :
822 : : /* Called from main context */
823 : 0 : void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save) {
824 : : pa_source_output *o;
825 : :
826 : 0 : pa_source_assert_ref(s);
827 [ # # ]: 0 : pa_assert_ctl_context();
828 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
829 [ # # ]: 0 : pa_assert(q);
830 : :
831 [ # # ]: 0 : while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) {
832 [ # # ]: 0 : if (pa_source_output_finish_move(o, s, save) < 0)
833 : 0 : pa_source_output_fail_move(o);
834 : :
835 : 0 : pa_source_output_unref(o);
836 : : }
837 : :
838 : 0 : pa_queue_free(q, NULL);
839 : 0 : }
840 : :
841 : : /* Called from main context */
842 : 0 : void pa_source_move_all_fail(pa_queue *q) {
843 : : pa_source_output *o;
844 : :
845 [ # # ]: 0 : pa_assert_ctl_context();
846 [ # # ]: 0 : pa_assert(q);
847 : :
848 [ # # ]: 0 : while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) {
849 : 0 : pa_source_output_fail_move(o);
850 : 0 : pa_source_output_unref(o);
851 : : }
852 : :
853 : 0 : pa_queue_free(q, NULL);
854 : 0 : }
855 : :
856 : : /* Called from IO thread context */
857 : 0 : void pa_source_process_rewind(pa_source *s, size_t nbytes) {
858 : : pa_source_output *o;
859 : 0 : void *state = NULL;
860 : :
861 : 0 : pa_source_assert_ref(s);
862 [ # # ][ # # ]: 0 : pa_source_assert_io_context(s);
863 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
864 : :
865 [ # # ]: 0 : if (nbytes <= 0)
866 : : return;
867 : :
868 [ # # ]: 0 : if (s->thread_info.state == PA_SOURCE_SUSPENDED)
869 : : return;
870 : :
871 : 0 : pa_log_debug("Processing rewind...");
872 : :
873 [ # # ]: 0 : PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state) {
874 : 0 : pa_source_output_assert_ref(o);
875 : 0 : pa_source_output_process_rewind(o, nbytes);
876 : : }
877 : : }
878 : :
879 : : /* Called from IO thread context */
880 : 0 : void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
881 : : pa_source_output *o;
882 : 0 : void *state = NULL;
883 : :
884 : 0 : pa_source_assert_ref(s);
885 [ # # ][ # # ]: 0 : pa_source_assert_io_context(s);
886 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
887 [ # # ]: 0 : pa_assert(chunk);
888 : :
889 [ # # ]: 0 : if (s->thread_info.state == PA_SOURCE_SUSPENDED)
890 : 0 : return;
891 : :
892 [ # # ][ # # ]: 0 : if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
893 : 0 : pa_memchunk vchunk = *chunk;
894 : :
895 : 0 : pa_memblock_ref(vchunk.memblock);
896 : 0 : pa_memchunk_make_writable(&vchunk, 0);
897 : :
898 [ # # ][ # # ]: 0 : if (s->thread_info.soft_muted || pa_cvolume_is_muted(&s->thread_info.soft_volume))
899 : 0 : pa_silence_memchunk(&vchunk, &s->sample_spec);
900 : : else
901 : 0 : pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.soft_volume);
902 : :
903 [ # # ]: 0 : while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
904 : 0 : pa_source_output_assert_ref(o);
905 : :
906 [ # # ]: 0 : if (!o->thread_info.direct_on_input)
907 : 0 : pa_source_output_push(o, &vchunk);
908 : : }
909 : :
910 : 0 : pa_memblock_unref(vchunk.memblock);
911 : : } else {
912 : :
913 [ # # ]: 0 : while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
914 : 0 : pa_source_output_assert_ref(o);
915 : :
916 [ # # ]: 0 : if (!o->thread_info.direct_on_input)
917 : 0 : pa_source_output_push(o, chunk);
918 : : }
919 : : }
920 : : }
921 : :
922 : : /* Called from IO thread context */
923 : 0 : void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk) {
924 : 0 : pa_source_assert_ref(s);
925 [ # # ][ # # ]: 0 : pa_source_assert_io_context(s);
926 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
927 : 0 : pa_source_output_assert_ref(o);
928 [ # # ]: 0 : pa_assert(o->thread_info.direct_on_input);
929 [ # # ]: 0 : pa_assert(chunk);
930 : :
931 [ # # ]: 0 : if (s->thread_info.state == PA_SOURCE_SUSPENDED)
932 : 0 : return;
933 : :
934 [ # # ][ # # ]: 0 : if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
935 : 0 : pa_memchunk vchunk = *chunk;
936 : :
937 : 0 : pa_memblock_ref(vchunk.memblock);
938 : 0 : pa_memchunk_make_writable(&vchunk, 0);
939 : :
940 [ # # ][ # # ]: 0 : if (s->thread_info.soft_muted || pa_cvolume_is_muted(&s->thread_info.soft_volume))
941 : 0 : pa_silence_memchunk(&vchunk, &s->sample_spec);
942 : : else
943 : 0 : pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.soft_volume);
944 : :
945 : 0 : pa_source_output_push(o, &vchunk);
946 : :
947 : 0 : pa_memblock_unref(vchunk.memblock);
948 : : } else
949 : 0 : pa_source_output_push(o, chunk);
950 : : }
951 : :
952 : : /* Called from main thread */
953 : 0 : pa_bool_t pa_source_update_rate(pa_source *s, uint32_t rate, pa_bool_t passthrough)
954 : : {
955 [ # # ]: 0 : if (s->update_rate) {
956 : 0 : uint32_t desired_rate = rate;
957 : 0 : uint32_t default_rate = s->default_sample_rate;
958 : 0 : uint32_t alternate_rate = s->alternate_sample_rate;
959 : : uint32_t idx;
960 : : pa_source_output *o;
961 : 0 : pa_bool_t use_alternate = FALSE;
962 : :
963 [ # # ]: 0 : if (PA_UNLIKELY(default_rate == alternate_rate)) {
964 : 0 : pa_log_warn("Default and alternate sample rates are the same.");
965 : 0 : return FALSE;
966 : : }
967 : :
968 [ # # ]: 0 : if (PA_SOURCE_IS_RUNNING(s->state)) {
969 : 0 : pa_log_info("Cannot update rate, SOURCE_IS_RUNNING, will keep using %u Hz",
970 : : s->sample_spec.rate);
971 : 0 : return FALSE;
972 : : }
973 : :
974 [ # # ]: 0 : if (PA_UNLIKELY (desired_rate < 8000 ||
975 : : desired_rate > PA_RATE_MAX))
976 : : return FALSE;
977 : :
978 [ # # ]: 0 : if (!passthrough) {
979 [ # # ][ # # ]: 0 : pa_assert(default_rate % 4000 || default_rate % 11025);
980 [ # # ][ # # ]: 0 : pa_assert(alternate_rate % 4000 || alternate_rate % 11025);
981 : :
982 [ # # ]: 0 : if (default_rate % 4000) {
983 : : /* default is a 11025 multiple */
984 [ # # ][ # # ]: 0 : if ((alternate_rate % 4000 == 0) && (desired_rate % 4000 == 0))
985 : 0 : use_alternate=TRUE;
986 : : } else {
987 : : /* default is 4000 multiple */
988 [ # # ][ # # ]: 0 : if ((alternate_rate % 11025 == 0) && (desired_rate % 11025 == 0))
989 : 0 : use_alternate=TRUE;
990 : : }
991 : :
992 [ # # ]: 0 : if (use_alternate)
993 : : desired_rate = alternate_rate;
994 : : else
995 : 0 : desired_rate = default_rate;
996 : : } else {
997 : : desired_rate = rate; /* use stream sampling rate, discard default/alternate settings */
998 : : }
999 : :
1000 [ # # ][ # # ]: 0 : if (!passthrough && pa_source_used_by(s) > 0)
1001 : : return FALSE;
1002 : :
1003 : 0 : pa_source_suspend(s, TRUE, PA_SUSPEND_IDLE); /* needed before rate update, will be resumed automatically */
1004 : :
1005 [ # # ]: 0 : if (s->update_rate(s, desired_rate) == TRUE) {
1006 : 0 : pa_log_info("Changed sampling rate successfully ");
1007 : :
1008 [ # # ]: 0 : PA_IDXSET_FOREACH(o, s->outputs, idx) {
1009 [ # # ]: 0 : if (o->state == PA_SOURCE_OUTPUT_CORKED)
1010 : 0 : pa_source_output_update_rate(o);
1011 : : }
1012 : : return TRUE;
1013 : : }
1014 : : }
1015 : : return FALSE;
1016 : : }
1017 : :
1018 : : /* Called from main thread */
1019 : 0 : pa_usec_t pa_source_get_latency(pa_source *s) {
1020 : : pa_usec_t usec;
1021 : :
1022 : 0 : pa_source_assert_ref(s);
1023 [ # # ]: 0 : pa_assert_ctl_context();
1024 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
1025 : :
1026 [ # # ]: 0 : if (s->state == PA_SOURCE_SUSPENDED)
1027 : : return 0;
1028 : :
1029 [ # # ]: 0 : if (!(s->flags & PA_SOURCE_LATENCY))
1030 : : return 0;
1031 : :
1032 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
1033 : :
1034 : : /* usec is unsigned, so check that the offset can be added to usec without
1035 : : * underflowing. */
1036 [ # # ]: 0 : if (-s->latency_offset <= (int64_t) usec)
1037 : 0 : usec += s->latency_offset;
1038 : : else
1039 : 0 : usec = 0;
1040 : :
1041 : 0 : return usec;
1042 : : }
1043 : :
1044 : : /* Called from IO thread */
1045 : 0 : pa_usec_t pa_source_get_latency_within_thread(pa_source *s) {
1046 : 0 : pa_usec_t usec = 0;
1047 : : pa_msgobject *o;
1048 : :
1049 : 0 : pa_source_assert_ref(s);
1050 [ # # ][ # # ]: 0 : pa_source_assert_io_context(s);
1051 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
1052 : :
1053 : : /* The returned value is supposed to be in the time domain of the sound card! */
1054 : :
1055 [ # # ]: 0 : if (s->thread_info.state == PA_SOURCE_SUSPENDED)
1056 : : return 0;
1057 : :
1058 [ # # ]: 0 : if (!(s->flags & PA_SOURCE_LATENCY))
1059 : : return 0;
1060 : :
1061 : 0 : o = PA_MSGOBJECT(s);
1062 : :
1063 : : /* FIXME: We probably should make this a proper vtable callback instead of going through process_msg() */
1064 : :
1065 [ # # ]: 0 : if (o->process_msg(o, PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
1066 : : return -1;
1067 : :
1068 : : /* usec is unsigned, so check that the offset can be added to usec without
1069 : : * underflowing. */
1070 [ # # ]: 0 : if (-s->thread_info.latency_offset <= (int64_t) usec)
1071 : 0 : usec += s->thread_info.latency_offset;
1072 : : else
1073 : 0 : usec = 0;
1074 : :
1075 : 0 : return usec;
1076 : : }
1077 : :
1078 : : /* Called from the main thread (and also from the IO thread while the main
1079 : : * thread is waiting).
1080 : : *
1081 : : * When a source uses volume sharing, it never has the PA_SOURCE_FLAT_VOLUME flag
1082 : : * set. Instead, flat volume mode is detected by checking whether the root source
1083 : : * has the flag set. */
1084 : 0 : pa_bool_t pa_source_flat_volume_enabled(pa_source *s) {
1085 : 0 : pa_source_assert_ref(s);
1086 : :
1087 : 0 : s = pa_source_get_master(s);
1088 : :
1089 [ # # ]: 0 : if (PA_LIKELY(s))
1090 : 0 : return (s->flags & PA_SOURCE_FLAT_VOLUME);
1091 : : else
1092 : : return FALSE;
1093 : : }
1094 : :
1095 : : /* Called from the main thread (and also from the IO thread while the main
1096 : : * thread is waiting). */
1097 : 0 : pa_source *pa_source_get_master(pa_source *s) {
1098 : 0 : pa_source_assert_ref(s);
1099 : :
1100 [ # # ][ # # ]: 0 : while (s && (s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
1101 [ # # ]: 0 : if (PA_UNLIKELY(!s->output_from_master))
1102 : : return NULL;
1103 : :
1104 : 0 : s = s->output_from_master->source;
1105 : : }
1106 : :
1107 : : return s;
1108 : : }
1109 : :
1110 : : /* Called from main context */
1111 : 0 : pa_bool_t pa_source_is_passthrough(pa_source *s) {
1112 : :
1113 : 0 : pa_source_assert_ref(s);
1114 : :
1115 : : /* NB Currently only monitor sources support passthrough mode */
1116 [ # # ][ # # ]: 0 : return (s->monitor_of && pa_sink_is_passthrough(s->monitor_of));
1117 : : }
1118 : :
1119 : : /* Called from main context */
1120 : 0 : void pa_source_enter_passthrough(pa_source *s) {
1121 : : pa_cvolume volume;
1122 : :
1123 : : /* set the volume to NORM */
1124 : 0 : s->saved_volume = *pa_source_get_volume(s, TRUE);
1125 : 0 : s->saved_save_volume = s->save_volume;
1126 : :
1127 : 0 : pa_cvolume_set(&volume, s->sample_spec.channels, PA_MIN(s->base_volume, PA_VOLUME_NORM));
1128 : 0 : pa_source_set_volume(s, &volume, TRUE, FALSE);
1129 : 0 : }
1130 : :
1131 : : /* Called from main context */
1132 : 0 : void pa_source_leave_passthrough(pa_source *s) {
1133 : : /* Restore source volume to what it was before we entered passthrough mode */
1134 : 0 : pa_source_set_volume(s, &s->saved_volume, TRUE, s->saved_save_volume);
1135 : :
1136 : 0 : pa_cvolume_init(&s->saved_volume);
1137 : 0 : s->saved_save_volume = FALSE;
1138 : 0 : }
1139 : :
1140 : : /* Called from main context. */
1141 : 0 : static void compute_reference_ratio(pa_source_output *o) {
1142 : 0 : unsigned c = 0;
1143 : : pa_cvolume remapped;
1144 : :
1145 [ # # ]: 0 : pa_assert(o);
1146 [ # # ]: 0 : pa_assert(pa_source_flat_volume_enabled(o->source));
1147 : :
1148 : : /*
1149 : : * Calculates the reference ratio from the source's reference
1150 : : * volume. This basically calculates:
1151 : : *
1152 : : * o->reference_ratio = o->volume / o->source->reference_volume
1153 : : */
1154 : :
1155 : 0 : remapped = o->source->reference_volume;
1156 : 0 : pa_cvolume_remap(&remapped, &o->source->channel_map, &o->channel_map);
1157 : :
1158 : 0 : o->reference_ratio.channels = o->sample_spec.channels;
1159 : :
1160 [ # # ]: 0 : for (c = 0; c < o->sample_spec.channels; c++) {
1161 : :
1162 : : /* We don't update when the source volume is 0 anyway */
1163 [ # # ]: 0 : if (remapped.values[c] <= PA_VOLUME_MUTED)
1164 : 0 : continue;
1165 : :
1166 : : /* Don't update the reference ratio unless necessary */
1167 [ # # ]: 0 : if (pa_sw_volume_multiply(
1168 : : o->reference_ratio.values[c],
1169 : 0 : remapped.values[c]) == o->volume.values[c])
1170 : 0 : continue;
1171 : :
1172 : 0 : o->reference_ratio.values[c] = pa_sw_volume_divide(
1173 : : o->volume.values[c],
1174 : : remapped.values[c]);
1175 : : }
1176 : 0 : }
1177 : :
1178 : : /* Called from main context. Only called for the root source in volume sharing
1179 : : * cases, except for internal recursive calls. */
1180 : 0 : static void compute_reference_ratios(pa_source *s) {
1181 : : uint32_t idx;
1182 : : pa_source_output *o;
1183 : :
1184 : 0 : pa_source_assert_ref(s);
1185 [ # # ]: 0 : pa_assert_ctl_context();
1186 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
1187 [ # # ]: 0 : pa_assert(pa_source_flat_volume_enabled(s));
1188 : :
1189 [ # # ]: 0 : PA_IDXSET_FOREACH(o, s->outputs, idx) {
1190 : 0 : compute_reference_ratio(o);
1191 : :
1192 [ # # ][ # # ]: 0 : if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
1193 : 0 : compute_reference_ratios(o->destination_source);
1194 : : }
1195 : 0 : }
1196 : :
1197 : : /* Called from main context. Only called for the root source in volume sharing
1198 : : * cases, except for internal recursive calls. */
1199 : 0 : static void compute_real_ratios(pa_source *s) {
1200 : : pa_source_output *o;
1201 : : uint32_t idx;
1202 : :
1203 : 0 : pa_source_assert_ref(s);
1204 [ # # ]: 0 : pa_assert_ctl_context();
1205 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
1206 [ # # ]: 0 : pa_assert(pa_source_flat_volume_enabled(s));
1207 : :
1208 [ # # ]: 0 : PA_IDXSET_FOREACH(o, s->outputs, idx) {
1209 : : unsigned c;
1210 : : pa_cvolume remapped;
1211 : :
1212 [ # # ][ # # ]: 0 : if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
1213 : : /* The origin source uses volume sharing, so this input's real ratio
1214 : : * is handled as a special case - the real ratio must be 0 dB, and
1215 : : * as a result i->soft_volume must equal i->volume_factor. */
1216 : 0 : pa_cvolume_reset(&o->real_ratio, o->real_ratio.channels);
1217 : 0 : o->soft_volume = o->volume_factor;
1218 : :
1219 : 0 : compute_real_ratios(o->destination_source);
1220 : :
1221 : 0 : continue;
1222 : : }
1223 : :
1224 : : /*
1225 : : * This basically calculates:
1226 : : *
1227 : : * i->real_ratio := i->volume / s->real_volume
1228 : : * i->soft_volume := i->real_ratio * i->volume_factor
1229 : : */
1230 : :
1231 : 0 : remapped = s->real_volume;
1232 : 0 : pa_cvolume_remap(&remapped, &s->channel_map, &o->channel_map);
1233 : :
1234 : 0 : o->real_ratio.channels = o->sample_spec.channels;
1235 : 0 : o->soft_volume.channels = o->sample_spec.channels;
1236 : :
1237 [ # # ]: 0 : for (c = 0; c < o->sample_spec.channels; c++) {
1238 : :
1239 [ # # ]: 0 : if (remapped.values[c] <= PA_VOLUME_MUTED) {
1240 : : /* We leave o->real_ratio untouched */
1241 : 0 : o->soft_volume.values[c] = PA_VOLUME_MUTED;
1242 : 0 : continue;
1243 : : }
1244 : :
1245 : : /* Don't lose accuracy unless necessary */
1246 [ # # ]: 0 : if (pa_sw_volume_multiply(
1247 : : o->real_ratio.values[c],
1248 : 0 : remapped.values[c]) != o->volume.values[c])
1249 : :
1250 : 0 : o->real_ratio.values[c] = pa_sw_volume_divide(
1251 : : o->volume.values[c],
1252 : : remapped.values[c]);
1253 : :
1254 : 0 : o->soft_volume.values[c] = pa_sw_volume_multiply(
1255 : : o->real_ratio.values[c],
1256 : : o->volume_factor.values[c]);
1257 : : }
1258 : :
1259 : : /* We don't copy the soft_volume to the thread_info data
1260 : : * here. That must be done by the caller */
1261 : : }
1262 : 0 : }
1263 : :
1264 : 0 : static pa_cvolume *cvolume_remap_minimal_impact(
1265 : : pa_cvolume *v,
1266 : : const pa_cvolume *template,
1267 : : const pa_channel_map *from,
1268 : : const pa_channel_map *to) {
1269 : :
1270 : : pa_cvolume t;
1271 : :
1272 [ # # ]: 0 : pa_assert(v);
1273 [ # # ]: 0 : pa_assert(template);
1274 [ # # ]: 0 : pa_assert(from);
1275 [ # # ]: 0 : pa_assert(to);
1276 [ # # ]: 0 : pa_assert(pa_cvolume_compatible_with_channel_map(v, from));
1277 [ # # ]: 0 : pa_assert(pa_cvolume_compatible_with_channel_map(template, to));
1278 : :
1279 : : /* Much like pa_cvolume_remap(), but tries to minimize impact when
1280 : : * mapping from source output to source volumes:
1281 : : *
1282 : : * If template is a possible remapping from v it is used instead
1283 : : * of remapping anew.
1284 : : *
1285 : : * If the channel maps don't match we set an all-channel volume on
1286 : : * the source to ensure that changing a volume on one stream has no
1287 : : * effect that cannot be compensated for in another stream that
1288 : : * does not have the same channel map as the source. */
1289 : :
1290 [ # # ]: 0 : if (pa_channel_map_equal(from, to))
1291 : : return v;
1292 : :
1293 : 0 : t = *template;
1294 [ # # ]: 0 : if (pa_cvolume_equal(pa_cvolume_remap(&t, to, from), v)) {
1295 : 0 : *v = *template;
1296 : 0 : return v;
1297 : : }
1298 : :
1299 : 0 : pa_cvolume_set(v, to->channels, pa_cvolume_max(v));
1300 : 0 : return v;
1301 : : }
1302 : :
1303 : : /* Called from main thread. Only called for the root source in volume sharing
1304 : : * cases, except for internal recursive calls. */
1305 : 0 : static void get_maximum_output_volume(pa_source *s, pa_cvolume *max_volume, const pa_channel_map *channel_map) {
1306 : : pa_source_output *o;
1307 : : uint32_t idx;
1308 : :
1309 : 0 : pa_source_assert_ref(s);
1310 [ # # ]: 0 : pa_assert(max_volume);
1311 [ # # ]: 0 : pa_assert(channel_map);
1312 [ # # ]: 0 : pa_assert(pa_source_flat_volume_enabled(s));
1313 : :
1314 [ # # ]: 0 : PA_IDXSET_FOREACH(o, s->outputs, idx) {
1315 : : pa_cvolume remapped;
1316 : :
1317 [ # # ][ # # ]: 0 : if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
1318 : 0 : get_maximum_output_volume(o->destination_source, max_volume, channel_map);
1319 : :
1320 : : /* Ignore this output. The origin source uses volume sharing, so this
1321 : : * output's volume will be set to be equal to the root source's real
1322 : : * volume. Obviously this output's current volume must not then
1323 : : * affect what the root source's real volume will be. */
1324 : 0 : continue;
1325 : : }
1326 : :
1327 : 0 : remapped = o->volume;
1328 : 0 : cvolume_remap_minimal_impact(&remapped, max_volume, &o->channel_map, channel_map);
1329 : 0 : pa_cvolume_merge(max_volume, max_volume, &remapped);
1330 : : }
1331 : 0 : }
1332 : :
1333 : : /* Called from main thread. Only called for the root source in volume sharing
1334 : : * cases, except for internal recursive calls. */
1335 : 0 : static pa_bool_t has_outputs(pa_source *s) {
1336 : : pa_source_output *o;
1337 : : uint32_t idx;
1338 : :
1339 : 0 : pa_source_assert_ref(s);
1340 : :
1341 [ # # ]: 0 : PA_IDXSET_FOREACH(o, s->outputs, idx) {
1342 [ # # ][ # # ]: 0 : if (!o->destination_source || !(o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) || has_outputs(o->destination_source))
[ # # ]
1343 : : return TRUE;
1344 : : }
1345 : :
1346 : : return FALSE;
1347 : : }
1348 : :
1349 : : /* Called from main thread. Only called for the root source in volume sharing
1350 : : * cases, except for internal recursive calls. */
1351 : 0 : static void update_real_volume(pa_source *s, const pa_cvolume *new_volume, pa_channel_map *channel_map) {
1352 : : pa_source_output *o;
1353 : : uint32_t idx;
1354 : :
1355 : 0 : pa_source_assert_ref(s);
1356 [ # # ]: 0 : pa_assert(new_volume);
1357 [ # # ]: 0 : pa_assert(channel_map);
1358 : :
1359 : 0 : s->real_volume = *new_volume;
1360 : 0 : pa_cvolume_remap(&s->real_volume, channel_map, &s->channel_map);
1361 : :
1362 [ # # ]: 0 : PA_IDXSET_FOREACH(o, s->outputs, idx) {
1363 [ # # ][ # # ]: 0 : if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
1364 [ # # ]: 0 : if (pa_source_flat_volume_enabled(s)) {
1365 : 0 : pa_cvolume old_volume = o->volume;
1366 : :
1367 : : /* Follow the root source's real volume. */
1368 : 0 : o->volume = *new_volume;
1369 : 0 : pa_cvolume_remap(&o->volume, channel_map, &o->channel_map);
1370 : 0 : compute_reference_ratio(o);
1371 : :
1372 : : /* The volume changed, let's tell people so */
1373 [ # # ]: 0 : if (!pa_cvolume_equal(&old_volume, &o->volume)) {
1374 [ # # ]: 0 : if (o->volume_changed)
1375 : 0 : o->volume_changed(o);
1376 : :
1377 : 0 : pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
1378 : : }
1379 : : }
1380 : :
1381 : 0 : update_real_volume(o->destination_source, new_volume, channel_map);
1382 : : }
1383 : : }
1384 : 0 : }
1385 : :
1386 : : /* Called from main thread. Only called for the root source in shared volume
1387 : : * cases. */
1388 : 0 : static void compute_real_volume(pa_source *s) {
1389 : 0 : pa_source_assert_ref(s);
1390 [ # # ]: 0 : pa_assert_ctl_context();
1391 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
1392 [ # # ]: 0 : pa_assert(pa_source_flat_volume_enabled(s));
1393 [ # # ]: 0 : pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
1394 : :
1395 : : /* This determines the maximum volume of all streams and sets
1396 : : * s->real_volume accordingly. */
1397 : :
1398 [ # # ]: 0 : if (!has_outputs(s)) {
1399 : : /* In the special case that we have no source outputs we leave the
1400 : : * volume unmodified. */
1401 : 0 : update_real_volume(s, &s->reference_volume, &s->channel_map);
1402 : 0 : return;
1403 : : }
1404 : :
1405 : 0 : pa_cvolume_mute(&s->real_volume, s->channel_map.channels);
1406 : :
1407 : : /* First let's determine the new maximum volume of all outputs
1408 : : * connected to this source */
1409 : 0 : get_maximum_output_volume(s, &s->real_volume, &s->channel_map);
1410 : 0 : update_real_volume(s, &s->real_volume, &s->channel_map);
1411 : :
1412 : : /* Then, let's update the real ratios/soft volumes of all outputs
1413 : : * connected to this source */
1414 : 0 : compute_real_ratios(s);
1415 : : }
1416 : :
1417 : : /* Called from main thread. Only called for the root source in shared volume
1418 : : * cases, except for internal recursive calls. */
1419 : 0 : static void propagate_reference_volume(pa_source *s) {
1420 : : pa_source_output *o;
1421 : : uint32_t idx;
1422 : :
1423 : 0 : pa_source_assert_ref(s);
1424 [ # # ]: 0 : pa_assert_ctl_context();
1425 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
1426 [ # # ]: 0 : pa_assert(pa_source_flat_volume_enabled(s));
1427 : :
1428 : : /* This is called whenever the source volume changes that is not
1429 : : * caused by a source output volume change. We need to fix up the
1430 : : * source output volumes accordingly */
1431 : :
1432 [ # # ]: 0 : PA_IDXSET_FOREACH(o, s->outputs, idx) {
1433 : : pa_cvolume old_volume;
1434 : :
1435 [ # # ][ # # ]: 0 : if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
1436 : 0 : propagate_reference_volume(o->destination_source);
1437 : :
1438 : : /* Since the origin source uses volume sharing, this output's volume
1439 : : * needs to be updated to match the root source's real volume, but
1440 : : * that will be done later in update_shared_real_volume(). */
1441 : 0 : continue;
1442 : : }
1443 : :
1444 : 0 : old_volume = o->volume;
1445 : :
1446 : : /* This basically calculates:
1447 : : *
1448 : : * o->volume := o->reference_volume * o->reference_ratio */
1449 : :
1450 : 0 : o->volume = s->reference_volume;
1451 : 0 : pa_cvolume_remap(&o->volume, &s->channel_map, &o->channel_map);
1452 : 0 : pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio);
1453 : :
1454 : : /* The volume changed, let's tell people so */
1455 [ # # ]: 0 : if (!pa_cvolume_equal(&old_volume, &o->volume)) {
1456 : :
1457 [ # # ]: 0 : if (o->volume_changed)
1458 : 0 : o->volume_changed(o);
1459 : :
1460 : 0 : pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
1461 : : }
1462 : : }
1463 : 0 : }
1464 : :
1465 : : /* Called from main thread. Only called for the root source in volume sharing
1466 : : * cases, except for internal recursive calls. The return value indicates
1467 : : * whether any reference volume actually changed. */
1468 : 0 : static pa_bool_t update_reference_volume(pa_source *s, const pa_cvolume *v, const pa_channel_map *channel_map, pa_bool_t save) {
1469 : : pa_cvolume volume;
1470 : : pa_bool_t reference_volume_changed;
1471 : : pa_source_output *o;
1472 : : uint32_t idx;
1473 : :
1474 : 0 : pa_source_assert_ref(s);
1475 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
1476 [ # # ]: 0 : pa_assert(v);
1477 [ # # ]: 0 : pa_assert(channel_map);
1478 [ # # ]: 0 : pa_assert(pa_cvolume_valid(v));
1479 : :
1480 : 0 : volume = *v;
1481 : 0 : pa_cvolume_remap(&volume, channel_map, &s->channel_map);
1482 : :
1483 : 0 : reference_volume_changed = !pa_cvolume_equal(&volume, &s->reference_volume);
1484 : 0 : s->reference_volume = volume;
1485 : :
1486 [ # # ][ # # ]: 0 : s->save_volume = (!reference_volume_changed && s->save_volume) || save;
[ # # ]
1487 : :
1488 [ # # ]: 0 : if (reference_volume_changed)
1489 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1490 [ # # ]: 0 : else if (!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
1491 : : /* If the root source's volume doesn't change, then there can't be any
1492 : : * changes in the other source in the source tree either.
1493 : : *
1494 : : * It's probably theoretically possible that even if the root source's
1495 : : * volume changes slightly, some filter source doesn't change its volume
1496 : : * due to rounding errors. If that happens, we still want to propagate
1497 : : * the changed root source volume to the sources connected to the
1498 : : * intermediate source that didn't change its volume. This theoretical
1499 : : * possibility is the reason why we have that !(s->flags &
1500 : : * PA_SOURCE_SHARE_VOLUME_WITH_MASTER) condition. Probably nobody would
1501 : : * notice even if we returned here FALSE always if
1502 : : * reference_volume_changed is FALSE. */
1503 : : return FALSE;
1504 : :
1505 [ # # ]: 0 : PA_IDXSET_FOREACH(o, s->outputs, idx) {
1506 [ # # ][ # # ]: 0 : if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
1507 : 0 : update_reference_volume(o->destination_source, v, channel_map, FALSE);
1508 : : }
1509 : :
1510 : : return TRUE;
1511 : : }
1512 : :
1513 : : /* Called from main thread */
1514 : 0 : void pa_source_set_volume(
1515 : : pa_source *s,
1516 : : const pa_cvolume *volume,
1517 : : pa_bool_t send_msg,
1518 : : pa_bool_t save) {
1519 : :
1520 : : pa_cvolume new_reference_volume;
1521 : : pa_source *root_source;
1522 : :
1523 : 0 : pa_source_assert_ref(s);
1524 [ # # ]: 0 : pa_assert_ctl_context();
1525 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
1526 [ # # ][ # # ]: 0 : pa_assert(!volume || pa_cvolume_valid(volume));
1527 [ # # ][ # # ]: 0 : pa_assert(volume || pa_source_flat_volume_enabled(s));
1528 [ # # ][ # # ]: 0 : pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
[ # # ][ # # ]
1529 : :
1530 : : /* make sure we don't change the volume in PASSTHROUGH mode ...
1531 : : * ... *except* if we're being invoked to reset the volume to ensure 0 dB gain */
1532 [ # # ][ # # ]: 0 : if (pa_source_is_passthrough(s) && (!volume || !pa_cvolume_is_norm(volume))) {
[ # # ]
1533 : 0 : pa_log_warn("Cannot change volume, source is monitor of a PASSTHROUGH sink");
1534 : 0 : return;
1535 : : }
1536 : :
1537 : : /* In case of volume sharing, the volume is set for the root source first,
1538 : : * from which it's then propagated to the sharing sources. */
1539 : 0 : root_source = pa_source_get_master(s);
1540 : :
1541 [ # # ]: 0 : if (PA_UNLIKELY(!root_source))
1542 : : return;
1543 : :
1544 : : /* As a special exception we accept mono volumes on all sources --
1545 : : * even on those with more complex channel maps */
1546 : :
1547 [ # # ]: 0 : if (volume) {
1548 [ # # ]: 0 : if (pa_cvolume_compatible(volume, &s->sample_spec))
1549 : 0 : new_reference_volume = *volume;
1550 : : else {
1551 : 0 : new_reference_volume = s->reference_volume;
1552 : 0 : pa_cvolume_scale(&new_reference_volume, pa_cvolume_max(volume));
1553 : : }
1554 : :
1555 : 0 : pa_cvolume_remap(&new_reference_volume, &s->channel_map, &root_source->channel_map);
1556 : :
1557 [ # # ]: 0 : if (update_reference_volume(root_source, &new_reference_volume, &root_source->channel_map, save)) {
1558 [ # # ]: 0 : if (pa_source_flat_volume_enabled(root_source)) {
1559 : : /* OK, propagate this volume change back to the outputs */
1560 : 0 : propagate_reference_volume(root_source);
1561 : :
1562 : : /* And now recalculate the real volume */
1563 : 0 : compute_real_volume(root_source);
1564 : : } else
1565 : 0 : update_real_volume(root_source, &root_source->reference_volume, &root_source->channel_map);
1566 : : }
1567 : :
1568 : : } else {
1569 : : /* If volume is NULL we synchronize the source's real and
1570 : : * reference volumes with the stream volumes. */
1571 : :
1572 [ # # ]: 0 : pa_assert(pa_source_flat_volume_enabled(root_source));
1573 : :
1574 : : /* Ok, let's determine the new real volume */
1575 : 0 : compute_real_volume(root_source);
1576 : :
1577 : : /* Let's 'push' the reference volume if necessary */
1578 : 0 : pa_cvolume_merge(&new_reference_volume, &s->reference_volume, &root_source->real_volume);
1579 : : /* If the source and it's root don't have the same number of channels, we need to remap */
1580 [ # # ][ # # ]: 0 : if (s != root_source && !pa_channel_map_equal(&s->channel_map, &root_source->channel_map))
1581 : 0 : pa_cvolume_remap(&new_reference_volume, &s->channel_map, &root_source->channel_map);
1582 : 0 : update_reference_volume(root_source, &new_reference_volume, &root_source->channel_map, save);
1583 : :
1584 : : /* Now that the reference volume is updated, we can update the streams'
1585 : : * reference ratios. */
1586 : 0 : compute_reference_ratios(root_source);
1587 : : }
1588 : :
1589 [ # # ]: 0 : if (root_source->set_volume) {
1590 : : /* If we have a function set_volume(), then we do not apply a
1591 : : * soft volume by default. However, set_volume() is free to
1592 : : * apply one to root_source->soft_volume */
1593 : :
1594 : 0 : pa_cvolume_reset(&root_source->soft_volume, root_source->sample_spec.channels);
1595 [ # # ]: 0 : if (!(root_source->flags & PA_SOURCE_DEFERRED_VOLUME))
1596 : 0 : root_source->set_volume(root_source);
1597 : :
1598 : : } else
1599 : : /* If we have no function set_volume(), then the soft volume
1600 : : * becomes the real volume */
1601 : 0 : root_source->soft_volume = root_source->real_volume;
1602 : :
1603 : : /* This tells the source that soft volume and/or real volume changed */
1604 [ # # ]: 0 : if (send_msg)
1605 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(root_source->asyncmsgq, PA_MSGOBJECT(root_source), PA_SOURCE_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL) == 0);
1606 : : }
1607 : :
1608 : : /* Called from the io thread if sync volume is used, otherwise from the main thread.
1609 : : * Only to be called by source implementor */
1610 : 0 : void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume) {
1611 : :
1612 : 0 : pa_source_assert_ref(s);
1613 [ # # ]: 0 : pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
1614 : :
1615 [ # # ]: 0 : if (s->flags & PA_SOURCE_DEFERRED_VOLUME)
1616 [ # # ][ # # ]: 0 : pa_source_assert_io_context(s);
1617 : : else
1618 [ # # ]: 0 : pa_assert_ctl_context();
1619 : :
1620 [ # # ]: 0 : if (!volume)
1621 : 0 : pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
1622 : : else
1623 : 0 : s->soft_volume = *volume;
1624 : :
1625 [ # # ][ # # ]: 0 : if (PA_SOURCE_IS_LINKED(s->state) && !(s->flags & PA_SOURCE_DEFERRED_VOLUME))
1626 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
1627 : : else
1628 : 0 : s->thread_info.soft_volume = s->soft_volume;
1629 : 0 : }
1630 : :
1631 : : /* Called from the main thread. Only called for the root source in volume sharing
1632 : : * cases, except for internal recursive calls. */
1633 : 0 : static void propagate_real_volume(pa_source *s, const pa_cvolume *old_real_volume) {
1634 : : pa_source_output *o;
1635 : : uint32_t idx;
1636 : :
1637 : 0 : pa_source_assert_ref(s);
1638 [ # # ]: 0 : pa_assert(old_real_volume);
1639 [ # # ]: 0 : pa_assert_ctl_context();
1640 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
1641 : :
1642 : : /* This is called when the hardware's real volume changes due to
1643 : : * some external event. We copy the real volume into our
1644 : : * reference volume and then rebuild the stream volumes based on
1645 : : * i->real_ratio which should stay fixed. */
1646 : :
1647 [ # # ]: 0 : if (!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
1648 [ # # ]: 0 : if (pa_cvolume_equal(old_real_volume, &s->real_volume))
1649 : 0 : return;
1650 : :
1651 : : /* 1. Make the real volume the reference volume */
1652 : 0 : update_reference_volume(s, &s->real_volume, &s->channel_map, TRUE);
1653 : : }
1654 : :
1655 [ # # ]: 0 : if (pa_source_flat_volume_enabled(s)) {
1656 : :
1657 [ # # ]: 0 : PA_IDXSET_FOREACH(o, s->outputs, idx) {
1658 : 0 : pa_cvolume old_volume = o->volume;
1659 : :
1660 : : /* 2. Since the source's reference and real volumes are equal
1661 : : * now our ratios should be too. */
1662 : 0 : o->reference_ratio = o->real_ratio;
1663 : :
1664 : : /* 3. Recalculate the new stream reference volume based on the
1665 : : * reference ratio and the sink's reference volume.
1666 : : *
1667 : : * This basically calculates:
1668 : : *
1669 : : * o->volume = s->reference_volume * o->reference_ratio
1670 : : *
1671 : : * This is identical to propagate_reference_volume() */
1672 : 0 : o->volume = s->reference_volume;
1673 : 0 : pa_cvolume_remap(&o->volume, &s->channel_map, &o->channel_map);
1674 : 0 : pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio);
1675 : :
1676 : : /* Notify if something changed */
1677 [ # # ]: 0 : if (!pa_cvolume_equal(&old_volume, &o->volume)) {
1678 : :
1679 [ # # ]: 0 : if (o->volume_changed)
1680 : 0 : o->volume_changed(o);
1681 : :
1682 : 0 : pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
1683 : : }
1684 : :
1685 [ # # ][ # # ]: 0 : if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
1686 : 0 : propagate_real_volume(o->destination_source, old_real_volume);
1687 : : }
1688 : : }
1689 : :
1690 : : /* Something got changed in the hardware. It probably makes sense
1691 : : * to save changed hw settings given that hw volume changes not
1692 : : * triggered by PA are almost certainly done by the user. */
1693 [ # # ]: 0 : if (!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
1694 : 0 : s->save_volume = TRUE;
1695 : : }
1696 : :
1697 : : /* Called from io thread */
1698 : 0 : void pa_source_update_volume_and_mute(pa_source *s) {
1699 [ # # ]: 0 : pa_assert(s);
1700 [ # # ][ # # ]: 0 : pa_source_assert_io_context(s);
1701 : :
1702 : 0 : pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_UPDATE_VOLUME_AND_MUTE, NULL, 0, NULL, NULL);
1703 : 0 : }
1704 : :
1705 : : /* Called from main thread */
1706 : 0 : const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) {
1707 : 0 : pa_source_assert_ref(s);
1708 [ # # ]: 0 : pa_assert_ctl_context();
1709 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
1710 : :
1711 [ # # ][ # # ]: 0 : if (s->refresh_volume || force_refresh) {
1712 : : struct pa_cvolume old_real_volume;
1713 : :
1714 [ # # ]: 0 : pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
1715 : :
1716 : 0 : old_real_volume = s->real_volume;
1717 : :
1718 [ # # ][ # # ]: 0 : if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->get_volume)
1719 : 0 : s->get_volume(s);
1720 : :
1721 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
1722 : :
1723 : 0 : update_real_volume(s, &s->real_volume, &s->channel_map);
1724 : 0 : propagate_real_volume(s, &old_real_volume);
1725 : : }
1726 : :
1727 : 0 : return &s->reference_volume;
1728 : : }
1729 : :
1730 : : /* Called from main thread. In volume sharing cases, only the root source may
1731 : : * call this. */
1732 : 0 : void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_real_volume) {
1733 : : pa_cvolume old_real_volume;
1734 : :
1735 : 0 : pa_source_assert_ref(s);
1736 [ # # ]: 0 : pa_assert_ctl_context();
1737 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
1738 [ # # ]: 0 : pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
1739 : :
1740 : : /* The source implementor may call this if the volume changed to make sure everyone is notified */
1741 : :
1742 : 0 : old_real_volume = s->real_volume;
1743 : 0 : update_real_volume(s, new_real_volume, &s->channel_map);
1744 : 0 : propagate_real_volume(s, &old_real_volume);
1745 : 0 : }
1746 : :
1747 : : /* Called from main thread */
1748 : 0 : void pa_source_set_mute(pa_source *s, pa_bool_t mute, pa_bool_t save) {
1749 : : pa_bool_t old_muted;
1750 : :
1751 : 0 : pa_source_assert_ref(s);
1752 [ # # ]: 0 : pa_assert_ctl_context();
1753 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
1754 : :
1755 : 0 : old_muted = s->muted;
1756 : 0 : s->muted = mute;
1757 [ # # ][ # # ]: 0 : s->save_muted = (old_muted == s->muted && s->save_muted) || save;
[ # # ]
1758 : :
1759 [ # # ][ # # ]: 0 : if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->set_mute)
1760 : 0 : s->set_mute(s);
1761 : :
1762 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
1763 : :
1764 [ # # ]: 0 : if (old_muted != s->muted)
1765 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1766 : 0 : }
1767 : :
1768 : : /* Called from main thread */
1769 : 0 : pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
1770 : :
1771 : 0 : pa_source_assert_ref(s);
1772 [ # # ]: 0 : pa_assert_ctl_context();
1773 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
1774 : :
1775 [ # # ][ # # ]: 0 : if (s->refresh_muted || force_refresh) {
1776 : 0 : pa_bool_t old_muted = s->muted;
1777 : :
1778 [ # # ][ # # ]: 0 : if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->get_mute)
1779 : 0 : s->get_mute(s);
1780 : :
1781 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
1782 : :
1783 [ # # ]: 0 : if (old_muted != s->muted) {
1784 : 0 : s->save_muted = TRUE;
1785 : :
1786 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1787 : :
1788 : : /* Make sure the soft mute status stays in sync */
1789 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
1790 : : }
1791 : : }
1792 : :
1793 : 0 : return s->muted;
1794 : : }
1795 : :
1796 : : /* Called from main thread */
1797 : 0 : void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted) {
1798 : 0 : pa_source_assert_ref(s);
1799 [ # # ]: 0 : pa_assert_ctl_context();
1800 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
1801 : :
1802 : : /* The source implementor may call this if the mute state changed to make sure everyone is notified */
1803 : :
1804 [ # # ]: 0 : if (s->muted == new_muted)
1805 : 0 : return;
1806 : :
1807 : 0 : s->muted = new_muted;
1808 : 0 : s->save_muted = TRUE;
1809 : :
1810 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1811 : : }
1812 : :
1813 : : /* Called from main thread */
1814 : 0 : pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p) {
1815 : 0 : pa_source_assert_ref(s);
1816 [ # # ]: 0 : pa_assert_ctl_context();
1817 : :
1818 [ # # ]: 0 : if (p)
1819 : 0 : pa_proplist_update(s->proplist, mode, p);
1820 : :
1821 [ # # ]: 0 : if (PA_SOURCE_IS_LINKED(s->state)) {
1822 : 0 : pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s);
1823 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1824 : : }
1825 : :
1826 : 0 : return TRUE;
1827 : : }
1828 : :
1829 : : /* Called from main thread */
1830 : : /* FIXME -- this should be dropped and be merged into pa_source_update_proplist() */
1831 : 0 : void pa_source_set_description(pa_source *s, const char *description) {
1832 : : const char *old;
1833 : 0 : pa_source_assert_ref(s);
1834 [ # # ]: 0 : pa_assert_ctl_context();
1835 : :
1836 [ # # ][ # # ]: 0 : if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
1837 : : return;
1838 : :
1839 : 0 : old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
1840 : :
1841 [ # # ][ # # ]: 0 : if (old && description && pa_streq(old, description))
1842 : : return;
1843 : :
1844 [ # # ]: 0 : if (description)
1845 : 0 : pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
1846 : : else
1847 : 0 : pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
1848 : :
1849 [ # # ]: 0 : if (PA_SOURCE_IS_LINKED(s->state)) {
1850 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1851 : 0 : pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s);
1852 : : }
1853 : : }
1854 : :
1855 : : /* Called from main thread */
1856 : 0 : unsigned pa_source_linked_by(pa_source *s) {
1857 : 0 : pa_source_assert_ref(s);
1858 [ # # ]: 0 : pa_assert_ctl_context();
1859 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
1860 : :
1861 : 0 : return pa_idxset_size(s->outputs);
1862 : : }
1863 : :
1864 : : /* Called from main thread */
1865 : 0 : unsigned pa_source_used_by(pa_source *s) {
1866 : : unsigned ret;
1867 : :
1868 : 0 : pa_source_assert_ref(s);
1869 [ # # ]: 0 : pa_assert_ctl_context();
1870 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
1871 : :
1872 : 0 : ret = pa_idxset_size(s->outputs);
1873 [ # # ]: 0 : pa_assert(ret >= s->n_corked);
1874 : :
1875 : 0 : return ret - s->n_corked;
1876 : : }
1877 : :
1878 : : /* Called from main thread */
1879 : 0 : unsigned pa_source_check_suspend(pa_source *s) {
1880 : : unsigned ret;
1881 : : pa_source_output *o;
1882 : : uint32_t idx;
1883 : :
1884 : 0 : pa_source_assert_ref(s);
1885 [ # # ]: 0 : pa_assert_ctl_context();
1886 : :
1887 [ # # ]: 0 : if (!PA_SOURCE_IS_LINKED(s->state))
1888 : : return 0;
1889 : :
1890 : 0 : ret = 0;
1891 : :
1892 [ # # ]: 0 : PA_IDXSET_FOREACH(o, s->outputs, idx) {
1893 : : pa_source_output_state_t st;
1894 : :
1895 : 0 : st = pa_source_output_get_state(o);
1896 : :
1897 : : /* We do not assert here. It is perfectly valid for a source output to
1898 : : * be in the INIT state (i.e. created, marked done but not yet put)
1899 : : * and we should not care if it's unlinked as it won't contribute
1900 : : * towards our busy status.
1901 : : */
1902 [ # # ]: 0 : if (!PA_SOURCE_OUTPUT_IS_LINKED(st))
1903 : 0 : continue;
1904 : :
1905 [ # # ]: 0 : if (st == PA_SOURCE_OUTPUT_CORKED)
1906 : 0 : continue;
1907 : :
1908 [ # # ]: 0 : if (o->flags & PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND)
1909 : 0 : continue;
1910 : :
1911 : 0 : ret ++;
1912 : : }
1913 : :
1914 : : return ret;
1915 : : }
1916 : :
1917 : : /* Called from the IO thread */
1918 : 0 : static void sync_output_volumes_within_thread(pa_source *s) {
1919 : : pa_source_output *o;
1920 : 0 : void *state = NULL;
1921 : :
1922 : 0 : pa_source_assert_ref(s);
1923 [ # # ][ # # ]: 0 : pa_source_assert_io_context(s);
1924 : :
1925 [ # # ]: 0 : PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state) {
1926 [ # # ]: 0 : if (pa_cvolume_equal(&o->thread_info.soft_volume, &o->soft_volume))
1927 : 0 : continue;
1928 : :
1929 : 0 : o->thread_info.soft_volume = o->soft_volume;
1930 : : //pa_source_output_request_rewind(o, 0, TRUE, FALSE, FALSE);
1931 : : }
1932 : 0 : }
1933 : :
1934 : : /* Called from the IO thread. Only called for the root source in volume sharing
1935 : : * cases, except for internal recursive calls. */
1936 : 0 : static void set_shared_volume_within_thread(pa_source *s) {
1937 : : pa_source_output *o;
1938 : 0 : void *state = NULL;
1939 : :
1940 : 0 : pa_source_assert_ref(s);
1941 : :
1942 : 0 : PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME_SYNCED, NULL, 0, NULL);
1943 : :
1944 [ # # ]: 0 : PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state) {
1945 [ # # ][ # # ]: 0 : if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
1946 : 0 : set_shared_volume_within_thread(o->destination_source);
1947 : : }
1948 : 0 : }
1949 : :
1950 : : /* Called from IO thread, except when it is not */
1951 : 0 : int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
1952 : 0 : pa_source *s = PA_SOURCE(object);
1953 : 0 : pa_source_assert_ref(s);
1954 : :
1955 [ # # # # : 0 : switch ((pa_source_message_t) code) {
# # # # #
# # # # #
# # # # #
# # # #
# ]
1956 : :
1957 : : case PA_SOURCE_MESSAGE_ADD_OUTPUT: {
1958 : 0 : pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
1959 : :
1960 : 0 : pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index), pa_source_output_ref(o));
1961 : :
1962 [ # # ]: 0 : if (o->direct_on_input) {
1963 : 0 : o->thread_info.direct_on_input = o->direct_on_input;
1964 : 0 : pa_hashmap_put(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index), o);
1965 : : }
1966 : :
1967 [ # # ]: 0 : pa_assert(!o->thread_info.attached);
1968 : 0 : o->thread_info.attached = TRUE;
1969 : :
1970 [ # # ]: 0 : if (o->attach)
1971 : 0 : o->attach(o);
1972 : :
1973 : 0 : pa_source_output_set_state_within_thread(o, o->state);
1974 : :
1975 [ # # ]: 0 : if (o->thread_info.requested_source_latency != (pa_usec_t) -1)
1976 : 0 : pa_source_output_set_requested_latency_within_thread(o, o->thread_info.requested_source_latency);
1977 : :
1978 : 0 : pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);
1979 : :
1980 : : /* We don't just invalidate the requested latency here,
1981 : : * because if we are in a move we might need to fix up the
1982 : : * requested latency. */
1983 : 0 : pa_source_output_set_requested_latency_within_thread(o, o->thread_info.requested_source_latency);
1984 : :
1985 : : /* In flat volume mode we need to update the volume as
1986 : : * well */
1987 : 0 : return object->process_msg(object, PA_SOURCE_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
1988 : : }
1989 : :
1990 : : case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: {
1991 : 0 : pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
1992 : :
1993 : 0 : pa_source_output_set_state_within_thread(o, o->state);
1994 : :
1995 [ # # ]: 0 : if (o->detach)
1996 : 0 : o->detach(o);
1997 : :
1998 [ # # ]: 0 : pa_assert(o->thread_info.attached);
1999 : 0 : o->thread_info.attached = FALSE;
2000 : :
2001 [ # # ]: 0 : if (o->thread_info.direct_on_input) {
2002 : 0 : pa_hashmap_remove(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index));
2003 : 0 : o->thread_info.direct_on_input = NULL;
2004 : : }
2005 : :
2006 [ # # ]: 0 : if (pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index)))
2007 : 0 : pa_source_output_unref(o);
2008 : :
2009 : 0 : pa_source_invalidate_requested_latency(s, TRUE);
2010 : :
2011 : : /* In flat volume mode we need to update the volume as
2012 : : * well */
2013 : 0 : return object->process_msg(object, PA_SOURCE_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
2014 : : }
2015 : :
2016 : : case PA_SOURCE_MESSAGE_SET_SHARED_VOLUME: {
2017 : 0 : pa_source *root_source = pa_source_get_master(s);
2018 : :
2019 [ # # ]: 0 : if (PA_LIKELY(root_source))
2020 : 0 : set_shared_volume_within_thread(root_source);
2021 : :
2022 : : return 0;
2023 : : }
2024 : :
2025 : : case PA_SOURCE_MESSAGE_SET_VOLUME_SYNCED:
2026 : :
2027 [ # # ]: 0 : if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
2028 : 0 : s->set_volume(s);
2029 : 0 : pa_source_volume_change_push(s);
2030 : : }
2031 : : /* Fall through ... */
2032 : :
2033 : : case PA_SOURCE_MESSAGE_SET_VOLUME:
2034 : :
2035 [ # # ]: 0 : if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
2036 : 0 : s->thread_info.soft_volume = s->soft_volume;
2037 : : }
2038 : :
2039 : : /* Fall through ... */
2040 : :
2041 : : case PA_SOURCE_MESSAGE_SYNC_VOLUMES:
2042 : 0 : sync_output_volumes_within_thread(s);
2043 : 0 : return 0;
2044 : :
2045 : : case PA_SOURCE_MESSAGE_GET_VOLUME:
2046 : :
2047 [ # # ][ # # ]: 0 : if ((s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->get_volume) {
2048 : 0 : s->get_volume(s);
2049 : : pa_source_volume_change_flush(s);
2050 : 0 : pa_sw_cvolume_divide(&s->thread_info.current_hw_volume, &s->real_volume, &s->soft_volume);
2051 : : }
2052 : :
2053 : : /* In case source implementor reset SW volume. */
2054 [ # # ]: 0 : if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
2055 : 0 : s->thread_info.soft_volume = s->soft_volume;
2056 : : }
2057 : :
2058 : : return 0;
2059 : :
2060 : : case PA_SOURCE_MESSAGE_SET_MUTE:
2061 : :
2062 [ # # ]: 0 : if (s->thread_info.soft_muted != s->muted) {
2063 : 0 : s->thread_info.soft_muted = s->muted;
2064 : : }
2065 : :
2066 [ # # ][ # # ]: 0 : if (s->flags & PA_SOURCE_DEFERRED_VOLUME && s->set_mute)
2067 : 0 : s->set_mute(s);
2068 : :
2069 : : return 0;
2070 : :
2071 : : case PA_SOURCE_MESSAGE_GET_MUTE:
2072 : :
2073 [ # # ][ # # ]: 0 : if (s->flags & PA_SOURCE_DEFERRED_VOLUME && s->get_mute)
2074 : 0 : s->get_mute(s);
2075 : :
2076 : : return 0;
2077 : :
2078 : : case PA_SOURCE_MESSAGE_SET_STATE: {
2079 : :
2080 : 0 : pa_bool_t suspend_change =
2081 [ # # ][ # # ]: 0 : (s->thread_info.state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
[ # # ]
2082 [ # # ]: 0 : (PA_SOURCE_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SOURCE_SUSPENDED);
2083 : :
2084 : 0 : s->thread_info.state = PA_PTR_TO_UINT(userdata);
2085 : :
2086 [ # # ]: 0 : if (suspend_change) {
2087 : : pa_source_output *o;
2088 : 0 : void *state = NULL;
2089 : :
2090 [ # # ]: 0 : while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
2091 [ # # ]: 0 : if (o->suspend_within_thread)
2092 : 0 : o->suspend_within_thread(o, s->thread_info.state == PA_SOURCE_SUSPENDED);
2093 : : }
2094 : :
2095 : : return 0;
2096 : : }
2097 : :
2098 : : case PA_SOURCE_MESSAGE_DETACH:
2099 : :
2100 : : /* Detach all streams */
2101 : 0 : pa_source_detach_within_thread(s);
2102 : 0 : return 0;
2103 : :
2104 : : case PA_SOURCE_MESSAGE_ATTACH:
2105 : :
2106 : : /* Reattach all streams */
2107 : 0 : pa_source_attach_within_thread(s);
2108 : 0 : return 0;
2109 : :
2110 : : case PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY: {
2111 : :
2112 : 0 : pa_usec_t *usec = userdata;
2113 : 0 : *usec = pa_source_get_requested_latency_within_thread(s);
2114 : :
2115 : : /* Yes, that's right, the IO thread will see -1 when no
2116 : : * explicit requested latency is configured, the main
2117 : : * thread will see max_latency */
2118 [ # # ]: 0 : if (*usec == (pa_usec_t) -1)
2119 : 0 : *usec = s->thread_info.max_latency;
2120 : :
2121 : : return 0;
2122 : : }
2123 : :
2124 : : case PA_SOURCE_MESSAGE_SET_LATENCY_RANGE: {
2125 : 0 : pa_usec_t *r = userdata;
2126 : :
2127 : 0 : pa_source_set_latency_range_within_thread(s, r[0], r[1]);
2128 : :
2129 : 0 : return 0;
2130 : : }
2131 : :
2132 : : case PA_SOURCE_MESSAGE_GET_LATENCY_RANGE: {
2133 : 0 : pa_usec_t *r = userdata;
2134 : :
2135 : 0 : r[0] = s->thread_info.min_latency;
2136 : 0 : r[1] = s->thread_info.max_latency;
2137 : :
2138 : 0 : return 0;
2139 : : }
2140 : :
2141 : : case PA_SOURCE_MESSAGE_GET_FIXED_LATENCY:
2142 : :
2143 : 0 : *((pa_usec_t*) userdata) = s->thread_info.fixed_latency;
2144 : 0 : return 0;
2145 : :
2146 : : case PA_SOURCE_MESSAGE_SET_FIXED_LATENCY:
2147 : :
2148 : 0 : pa_source_set_fixed_latency_within_thread(s, (pa_usec_t) offset);
2149 : 0 : return 0;
2150 : :
2151 : : case PA_SOURCE_MESSAGE_GET_MAX_REWIND:
2152 : :
2153 : 0 : *((size_t*) userdata) = s->thread_info.max_rewind;
2154 : 0 : return 0;
2155 : :
2156 : : case PA_SOURCE_MESSAGE_SET_MAX_REWIND:
2157 : :
2158 : 0 : pa_source_set_max_rewind_within_thread(s, (size_t) offset);
2159 : 0 : return 0;
2160 : :
2161 : : case PA_SOURCE_MESSAGE_GET_LATENCY:
2162 : :
2163 [ # # ]: 0 : if (s->monitor_of) {
2164 : 0 : *((pa_usec_t*) userdata) = 0;
2165 : 0 : return 0;
2166 : : }
2167 : :
2168 : : /* Implementors need to overwrite this implementation! */
2169 : : return -1;
2170 : :
2171 : : case PA_SOURCE_MESSAGE_SET_PORT:
2172 : :
2173 [ # # ]: 0 : pa_assert(userdata);
2174 [ # # ]: 0 : if (s->set_port) {
2175 : 0 : struct source_message_set_port *msg_data = userdata;
2176 : 0 : msg_data->ret = s->set_port(s, msg_data->port);
2177 : : }
2178 : : return 0;
2179 : :
2180 : : case PA_SOURCE_MESSAGE_UPDATE_VOLUME_AND_MUTE:
2181 : : /* This message is sent from IO-thread and handled in main thread. */
2182 [ # # ]: 0 : pa_assert_ctl_context();
2183 : :
2184 : : /* Make sure we're not messing with main thread when no longer linked */
2185 [ # # ]: 0 : if (!PA_SOURCE_IS_LINKED(s->state))
2186 : : return 0;
2187 : :
2188 : 0 : pa_source_get_volume(s, TRUE);
2189 : 0 : pa_source_get_mute(s, TRUE);
2190 : 0 : return 0;
2191 : :
2192 : : case PA_SOURCE_MESSAGE_SET_LATENCY_OFFSET:
2193 : 0 : s->thread_info.latency_offset = offset;
2194 : 0 : return 0;
2195 : :
2196 : : case PA_SOURCE_MESSAGE_MAX:
2197 : : ;
2198 : : }
2199 : :
2200 : : return -1;
2201 : : }
2202 : :
2203 : : /* Called from main thread */
2204 : 0 : int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause) {
2205 : : pa_source *source;
2206 : : uint32_t idx;
2207 : 0 : int ret = 0;
2208 : :
2209 : : pa_core_assert_ref(c);
2210 [ # # ]: 0 : pa_assert_ctl_context();
2211 [ # # ]: 0 : pa_assert(cause != 0);
2212 : :
2213 [ # # ]: 0 : for (source = PA_SOURCE(pa_idxset_first(c->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(c->sources, &idx))) {
2214 : : int r;
2215 : :
2216 [ # # ]: 0 : if (source->monitor_of)
2217 : 0 : continue;
2218 : :
2219 [ # # ]: 0 : if ((r = pa_source_suspend(source, suspend, cause)) < 0)
2220 : 0 : ret = r;
2221 : : }
2222 : :
2223 : 0 : return ret;
2224 : : }
2225 : :
2226 : : /* Called from main thread */
2227 : 0 : void pa_source_detach(pa_source *s) {
2228 : 0 : pa_source_assert_ref(s);
2229 [ # # ]: 0 : pa_assert_ctl_context();
2230 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
2231 : :
2232 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL) == 0);
2233 : 0 : }
2234 : :
2235 : : /* Called from main thread */
2236 : 0 : void pa_source_attach(pa_source *s) {
2237 : 0 : pa_source_assert_ref(s);
2238 [ # # ]: 0 : pa_assert_ctl_context();
2239 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
2240 : :
2241 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
2242 : 0 : }
2243 : :
2244 : : /* Called from IO thread */
2245 : 0 : void pa_source_detach_within_thread(pa_source *s) {
2246 : : pa_source_output *o;
2247 : 0 : void *state = NULL;
2248 : :
2249 : 0 : pa_source_assert_ref(s);
2250 [ # # ][ # # ]: 0 : pa_source_assert_io_context(s);
2251 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
2252 : :
2253 [ # # ]: 0 : PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
2254 [ # # ]: 0 : if (o->detach)
2255 : 0 : o->detach(o);
2256 : 0 : }
2257 : :
2258 : : /* Called from IO thread */
2259 : 0 : void pa_source_attach_within_thread(pa_source *s) {
2260 : : pa_source_output *o;
2261 : 0 : void *state = NULL;
2262 : :
2263 : 0 : pa_source_assert_ref(s);
2264 [ # # ][ # # ]: 0 : pa_source_assert_io_context(s);
2265 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
2266 : :
2267 [ # # ]: 0 : PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
2268 [ # # ]: 0 : if (o->attach)
2269 : 0 : o->attach(o);
2270 : 0 : }
2271 : :
2272 : : /* Called from IO thread */
2273 : 0 : pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
2274 : 0 : pa_usec_t result = (pa_usec_t) -1;
2275 : : pa_source_output *o;
2276 : 0 : void *state = NULL;
2277 : :
2278 : 0 : pa_source_assert_ref(s);
2279 [ # # ][ # # ]: 0 : pa_source_assert_io_context(s);
2280 : :
2281 [ # # ]: 0 : if (!(s->flags & PA_SOURCE_DYNAMIC_LATENCY))
2282 [ # # ]: 0 : return PA_CLAMP(s->thread_info.fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
2283 : :
2284 [ # # ]: 0 : if (s->thread_info.requested_latency_valid)
2285 : 0 : return s->thread_info.requested_latency;
2286 : :
2287 [ # # ]: 0 : PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
2288 [ # # ][ # # ]: 0 : if (o->thread_info.requested_source_latency != (pa_usec_t) -1 &&
2289 [ # # ]: 0 : (result == (pa_usec_t) -1 || result > o->thread_info.requested_source_latency))
2290 : 0 : result = o->thread_info.requested_source_latency;
2291 : :
2292 [ # # ]: 0 : if (result != (pa_usec_t) -1)
2293 [ # # ]: 0 : result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
2294 : :
2295 [ # # ]: 0 : if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
2296 : : /* Only cache this if we are fully set up */
2297 : 0 : s->thread_info.requested_latency = result;
2298 : 0 : s->thread_info.requested_latency_valid = TRUE;
2299 : : }
2300 : :
2301 : : return result;
2302 : : }
2303 : :
2304 : : /* Called from main thread */
2305 : 0 : pa_usec_t pa_source_get_requested_latency(pa_source *s) {
2306 : 0 : pa_usec_t usec = 0;
2307 : :
2308 : 0 : pa_source_assert_ref(s);
2309 [ # # ]: 0 : pa_assert_ctl_context();
2310 [ # # ]: 0 : pa_assert(PA_SOURCE_IS_LINKED(s->state));
2311 : :
2312 [ # # ]: 0 : if (s->state == PA_SOURCE_SUSPENDED)
2313 : : return 0;
2314 : :
2315 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
2316 : :
2317 : 0 : return usec;
2318 : : }
2319 : :
2320 : : /* Called from IO thread */
2321 : 0 : void pa_source_set_max_rewind_within_thread(pa_source *s, size_t max_rewind) {
2322 : : pa_source_output *o;
2323 : 0 : void *state = NULL;
2324 : :
2325 : 0 : pa_source_assert_ref(s);
2326 [ # # ][ # # ]: 0 : pa_source_assert_io_context(s);
2327 : :
2328 [ # # ]: 0 : if (max_rewind == s->thread_info.max_rewind)
2329 : 0 : return;
2330 : :
2331 : 0 : s->thread_info.max_rewind = max_rewind;
2332 : :
2333 [ # # ]: 0 : if (PA_SOURCE_IS_LINKED(s->thread_info.state))
2334 [ # # ]: 0 : PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
2335 : 0 : pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);
2336 : : }
2337 : :
2338 : : /* Called from main thread */
2339 : 0 : void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
2340 : 0 : pa_source_assert_ref(s);
2341 [ # # ]: 0 : pa_assert_ctl_context();
2342 : :
2343 [ # # ]: 0 : if (PA_SOURCE_IS_LINKED(s->state))
2344 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MAX_REWIND, NULL, max_rewind, NULL) == 0);
2345 : : else
2346 : 0 : pa_source_set_max_rewind_within_thread(s, max_rewind);
2347 : 0 : }
2348 : :
2349 : : /* Called from IO thread */
2350 : 0 : void pa_source_invalidate_requested_latency(pa_source *s, pa_bool_t dynamic) {
2351 : : pa_source_output *o;
2352 : 0 : void *state = NULL;
2353 : :
2354 : 0 : pa_source_assert_ref(s);
2355 [ # # ][ # # ]: 0 : pa_source_assert_io_context(s);
2356 : :
2357 [ # # ]: 0 : if ((s->flags & PA_SOURCE_DYNAMIC_LATENCY))
2358 : 0 : s->thread_info.requested_latency_valid = FALSE;
2359 [ # # ]: 0 : else if (dynamic)
2360 : 0 : return;
2361 : :
2362 [ # # ]: 0 : if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
2363 : :
2364 [ # # ]: 0 : if (s->update_requested_latency)
2365 : 0 : s->update_requested_latency(s);
2366 : :
2367 [ # # ]: 0 : while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
2368 [ # # ]: 0 : if (o->update_source_requested_latency)
2369 : 0 : o->update_source_requested_latency(o);
2370 : : }
2371 : :
2372 [ # # ]: 0 : if (s->monitor_of)
2373 : 0 : pa_sink_invalidate_requested_latency(s->monitor_of, dynamic);
2374 : : }
2375 : :
2376 : : /* Called from main thread */
2377 : 0 : void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
2378 : 0 : pa_source_assert_ref(s);
2379 [ # # ]: 0 : pa_assert_ctl_context();
2380 : :
2381 : : /* min_latency == 0: no limit
2382 : : * min_latency anything else: specified limit
2383 : : *
2384 : : * Similar for max_latency */
2385 : :
2386 [ # # ]: 0 : if (min_latency < ABSOLUTE_MIN_LATENCY)
2387 : 0 : min_latency = ABSOLUTE_MIN_LATENCY;
2388 : :
2389 [ # # ]: 0 : if (max_latency <= 0 ||
2390 : : max_latency > ABSOLUTE_MAX_LATENCY)
2391 : 0 : max_latency = ABSOLUTE_MAX_LATENCY;
2392 : :
2393 [ # # ]: 0 : pa_assert(min_latency <= max_latency);
2394 : :
2395 : : /* Hmm, let's see if someone forgot to set PA_SOURCE_DYNAMIC_LATENCY here... */
2396 [ # # ][ # # ]: 0 : pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
2397 : : max_latency == ABSOLUTE_MAX_LATENCY) ||
2398 : : (s->flags & PA_SOURCE_DYNAMIC_LATENCY));
2399 : :
2400 [ # # ]: 0 : if (PA_SOURCE_IS_LINKED(s->state)) {
2401 : : pa_usec_t r[2];
2402 : :
2403 : 0 : r[0] = min_latency;
2404 : 0 : r[1] = max_latency;
2405 : :
2406 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
2407 : : } else
2408 : 0 : pa_source_set_latency_range_within_thread(s, min_latency, max_latency);
2409 : 0 : }
2410 : :
2411 : : /* Called from main thread */
2412 : 0 : void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
2413 : 0 : pa_source_assert_ref(s);
2414 [ # # ]: 0 : pa_assert_ctl_context();
2415 [ # # ]: 0 : pa_assert(min_latency);
2416 [ # # ]: 0 : pa_assert(max_latency);
2417 : :
2418 [ # # ]: 0 : if (PA_SOURCE_IS_LINKED(s->state)) {
2419 : 0 : pa_usec_t r[2] = { 0, 0 };
2420 : :
2421 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
2422 : :
2423 : 0 : *min_latency = r[0];
2424 : 0 : *max_latency = r[1];
2425 : : } else {
2426 : 0 : *min_latency = s->thread_info.min_latency;
2427 : 0 : *max_latency = s->thread_info.max_latency;
2428 : : }
2429 : 0 : }
2430 : :
2431 : : /* Called from IO thread, and from main thread before pa_source_put() is called */
2432 : 0 : void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
2433 : 0 : pa_source_assert_ref(s);
2434 [ # # ][ # # ]: 0 : pa_source_assert_io_context(s);
2435 : :
2436 [ # # ]: 0 : pa_assert(min_latency >= ABSOLUTE_MIN_LATENCY);
2437 [ # # ]: 0 : pa_assert(max_latency <= ABSOLUTE_MAX_LATENCY);
2438 [ # # ]: 0 : pa_assert(min_latency <= max_latency);
2439 : :
2440 : : /* Hmm, let's see if someone forgot to set PA_SOURCE_DYNAMIC_LATENCY here... */
2441 [ # # ][ # # ]: 0 : pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
[ # # ][ # # ]
2442 : : max_latency == ABSOLUTE_MAX_LATENCY) ||
2443 : : (s->flags & PA_SOURCE_DYNAMIC_LATENCY) ||
2444 : : s->monitor_of);
2445 : :
2446 [ # # ][ # # ]: 0 : if (s->thread_info.min_latency == min_latency &&
2447 : 0 : s->thread_info.max_latency == max_latency)
2448 : 0 : return;
2449 : :
2450 : 0 : s->thread_info.min_latency = min_latency;
2451 : 0 : s->thread_info.max_latency = max_latency;
2452 : :
2453 [ # # ]: 0 : if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
2454 : : pa_source_output *o;
2455 : 0 : void *state = NULL;
2456 : :
2457 [ # # ]: 0 : PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
2458 [ # # ]: 0 : if (o->update_source_latency_range)
2459 : 0 : o->update_source_latency_range(o);
2460 : : }
2461 : :
2462 : 0 : pa_source_invalidate_requested_latency(s, FALSE);
2463 : : }
2464 : :
2465 : : /* Called from main thread, before the source is put */
2466 : 0 : void pa_source_set_fixed_latency(pa_source *s, pa_usec_t latency) {
2467 : 0 : pa_source_assert_ref(s);
2468 [ # # ]: 0 : pa_assert_ctl_context();
2469 : :
2470 [ # # ]: 0 : if (s->flags & PA_SOURCE_DYNAMIC_LATENCY) {
2471 [ # # ]: 0 : pa_assert(latency == 0);
2472 : 0 : return;
2473 : : }
2474 : :
2475 [ # # ]: 0 : if (latency < ABSOLUTE_MIN_LATENCY)
2476 : 0 : latency = ABSOLUTE_MIN_LATENCY;
2477 : :
2478 [ # # ]: 0 : if (latency > ABSOLUTE_MAX_LATENCY)
2479 : 0 : latency = ABSOLUTE_MAX_LATENCY;
2480 : :
2481 [ # # ]: 0 : if (PA_SOURCE_IS_LINKED(s->state))
2482 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_FIXED_LATENCY, NULL, (int64_t) latency, NULL) == 0);
2483 : : else
2484 : 0 : s->thread_info.fixed_latency = latency;
2485 : : }
2486 : :
2487 : : /* Called from main thread */
2488 : 0 : pa_usec_t pa_source_get_fixed_latency(pa_source *s) {
2489 : : pa_usec_t latency;
2490 : :
2491 : 0 : pa_source_assert_ref(s);
2492 [ # # ]: 0 : pa_assert_ctl_context();
2493 : :
2494 [ # # ]: 0 : if (s->flags & PA_SOURCE_DYNAMIC_LATENCY)
2495 : : return 0;
2496 : :
2497 [ # # ]: 0 : if (PA_SOURCE_IS_LINKED(s->state))
2498 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_FIXED_LATENCY, &latency, 0, NULL) == 0);
2499 : : else
2500 : 0 : latency = s->thread_info.fixed_latency;
2501 : :
2502 : 0 : return latency;
2503 : : }
2504 : :
2505 : : /* Called from IO thread */
2506 : 0 : void pa_source_set_fixed_latency_within_thread(pa_source *s, pa_usec_t latency) {
2507 : 0 : pa_source_assert_ref(s);
2508 [ # # ][ # # ]: 0 : pa_source_assert_io_context(s);
2509 : :
2510 [ # # ]: 0 : if (s->flags & PA_SOURCE_DYNAMIC_LATENCY) {
2511 [ # # ]: 0 : pa_assert(latency == 0);
2512 : : return;
2513 : : }
2514 : :
2515 [ # # ]: 0 : pa_assert(latency >= ABSOLUTE_MIN_LATENCY);
2516 [ # # ]: 0 : pa_assert(latency <= ABSOLUTE_MAX_LATENCY);
2517 : :
2518 [ # # ]: 0 : if (s->thread_info.fixed_latency == latency)
2519 : : return;
2520 : :
2521 : 0 : s->thread_info.fixed_latency = latency;
2522 : :
2523 [ # # ]: 0 : if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
2524 : : pa_source_output *o;
2525 : 0 : void *state = NULL;
2526 : :
2527 [ # # ]: 0 : PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
2528 [ # # ]: 0 : if (o->update_source_fixed_latency)
2529 : 0 : o->update_source_fixed_latency(o);
2530 : : }
2531 : :
2532 : 0 : pa_source_invalidate_requested_latency(s, FALSE);
2533 : : }
2534 : :
2535 : : /* Called from main thread */
2536 : 0 : void pa_source_set_latency_offset(pa_source *s, int64_t offset) {
2537 : 0 : pa_source_assert_ref(s);
2538 : :
2539 : 0 : s->latency_offset = offset;
2540 : :
2541 [ # # ]: 0 : if (PA_SOURCE_IS_LINKED(s->state))
2542 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_LATENCY_OFFSET, NULL, offset, NULL) == 0);
2543 : : else
2544 : 0 : s->thread_info.latency_offset = offset;
2545 : 0 : }
2546 : :
2547 : : /* Called from main thread */
2548 : 0 : size_t pa_source_get_max_rewind(pa_source *s) {
2549 : : size_t r;
2550 [ # # ]: 0 : pa_assert_ctl_context();
2551 : 0 : pa_source_assert_ref(s);
2552 : :
2553 [ # # ]: 0 : if (!PA_SOURCE_IS_LINKED(s->state))
2554 : 0 : return s->thread_info.max_rewind;
2555 : :
2556 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
2557 : :
2558 : 0 : return r;
2559 : : }
2560 : :
2561 : : /* Called from main context */
2562 : 0 : int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save) {
2563 : : pa_device_port *port;
2564 : : int ret;
2565 : :
2566 : 0 : pa_source_assert_ref(s);
2567 [ # # ]: 0 : pa_assert_ctl_context();
2568 : :
2569 [ # # ]: 0 : if (!s->set_port) {
2570 : 0 : pa_log_debug("set_port() operation not implemented for source %u \"%s\"", s->index, s->name);
2571 : 0 : return -PA_ERR_NOTIMPLEMENTED;
2572 : : }
2573 : :
2574 [ # # ]: 0 : if (!name)
2575 : : return -PA_ERR_NOENTITY;
2576 : :
2577 [ # # ]: 0 : if (!(port = pa_hashmap_get(s->ports, name)))
2578 : : return -PA_ERR_NOENTITY;
2579 : :
2580 [ # # ]: 0 : if (s->active_port == port) {
2581 [ # # ][ # # ]: 0 : s->save_port = s->save_port || save;
2582 : 0 : return 0;
2583 : : }
2584 : :
2585 [ # # ]: 0 : if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
2586 : 0 : struct source_message_set_port msg = { .port = port, .ret = 0 };
2587 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
2588 : 0 : ret = msg.ret;
2589 : : }
2590 : : else
2591 : 0 : ret = s->set_port(s, port);
2592 : :
2593 [ # # ]: 0 : if (ret < 0)
2594 : : return -PA_ERR_NOENTITY;
2595 : :
2596 : 0 : pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
2597 : :
2598 : 0 : pa_log_info("Changed port of source %u \"%s\" to %s", s->index, s->name, port->name);
2599 : :
2600 : 0 : s->active_port = port;
2601 : 0 : s->save_port = save;
2602 : :
2603 : 0 : pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED], s);
2604 : :
2605 : 0 : return 0;
2606 : : }
2607 : :
2608 [ - + ][ # # ]: 21 : PA_STATIC_FLIST_DECLARE(pa_source_volume_change, 0, pa_xfree);
2609 : :
2610 : : /* Called from the IO thread. */
2611 : 0 : static pa_source_volume_change *pa_source_volume_change_new(pa_source *s) {
2612 : : pa_source_volume_change *c;
2613 [ # # ]: 0 : if (!(c = pa_flist_pop(PA_STATIC_FLIST_GET(pa_source_volume_change))))
2614 : 0 : c = pa_xnew(pa_source_volume_change, 1);
2615 : :
2616 [ # # ]: 0 : PA_LLIST_INIT(pa_source_volume_change, c);
2617 : 0 : c->at = 0;
2618 : 0 : pa_cvolume_reset(&c->hw_volume, s->sample_spec.channels);
2619 : 0 : return c;
2620 : : }
2621 : :
2622 : : /* Called from the IO thread. */
2623 : 0 : static void pa_source_volume_change_free(pa_source_volume_change *c) {
2624 [ # # ]: 0 : pa_assert(c);
2625 [ # # ]: 0 : if (pa_flist_push(PA_STATIC_FLIST_GET(pa_source_volume_change), c) < 0)
2626 : 0 : pa_xfree(c);
2627 : 0 : }
2628 : :
2629 : : /* Called from the IO thread. */
2630 : 0 : void pa_source_volume_change_push(pa_source *s) {
2631 : 0 : pa_source_volume_change *c = NULL;
2632 : 0 : pa_source_volume_change *nc = NULL;
2633 : 0 : uint32_t safety_margin = s->thread_info.volume_change_safety_margin;
2634 : :
2635 : 0 : const char *direction = NULL;
2636 : :
2637 [ # # ]: 0 : pa_assert(s);
2638 : 0 : nc = pa_source_volume_change_new(s);
2639 : :
2640 : : /* NOTE: There is already more different volumes in pa_source that I can remember.
2641 : : * Adding one more volume for HW would get us rid of this, but I am trying
2642 : : * to survive with the ones we already have. */
2643 : 0 : pa_sw_cvolume_divide(&nc->hw_volume, &s->real_volume, &s->soft_volume);
2644 : :
2645 [ # # ][ # # ]: 0 : if (!s->thread_info.volume_changes && pa_cvolume_equal(&nc->hw_volume, &s->thread_info.current_hw_volume)) {
2646 : 0 : pa_log_debug("Volume not changing");
2647 : 0 : pa_source_volume_change_free(nc);
2648 : 0 : return;
2649 : : }
2650 : :
2651 : 0 : nc->at = pa_source_get_latency_within_thread(s);
2652 : 0 : nc->at += pa_rtclock_now() + s->thread_info.volume_change_extra_delay;
2653 : :
2654 [ # # ]: 0 : if (s->thread_info.volume_changes_tail) {
2655 [ # # ]: 0 : for (c = s->thread_info.volume_changes_tail; c; c = c->prev) {
2656 : : /* If volume is going up let's do it a bit late. If it is going
2657 : : * down let's do it a bit early. */
2658 [ # # ]: 0 : if (pa_cvolume_avg(&nc->hw_volume) > pa_cvolume_avg(&c->hw_volume)) {
2659 [ # # ]: 0 : if (nc->at + safety_margin > c->at) {
2660 : 0 : nc->at += safety_margin;
2661 : 0 : direction = "up";
2662 : 0 : break;
2663 : : }
2664 : : }
2665 [ # # ]: 0 : else if (nc->at - safety_margin > c->at) {
2666 : 0 : nc->at -= safety_margin;
2667 : 0 : direction = "down";
2668 : 0 : break;
2669 : : }
2670 : : }
2671 : : }
2672 : :
2673 [ # # ]: 0 : if (c == NULL) {
2674 [ # # ]: 0 : if (pa_cvolume_avg(&nc->hw_volume) > pa_cvolume_avg(&s->thread_info.current_hw_volume)) {
2675 : 0 : nc->at += safety_margin;
2676 : 0 : direction = "up";
2677 : : } else {
2678 : 0 : nc->at -= safety_margin;
2679 : 0 : direction = "down";
2680 : : }
2681 [ # # ][ # # ]: 0 : PA_LLIST_PREPEND(pa_source_volume_change, s->thread_info.volume_changes, nc);
2682 : : }
2683 : : else {
2684 [ # # ][ # # ]: 0 : PA_LLIST_INSERT_AFTER(pa_source_volume_change, s->thread_info.volume_changes, c, nc);
[ # # ][ # # ]
2685 : : }
2686 : :
2687 : 0 : pa_log_debug("Volume going %s to %d at %llu", direction, pa_cvolume_avg(&nc->hw_volume), (long long unsigned) nc->at);
2688 : :
2689 : : /* We can ignore volume events that came earlier but should happen later than this. */
2690 [ # # ]: 0 : PA_LLIST_FOREACH(c, nc->next) {
2691 : 0 : pa_log_debug("Volume change to %d at %llu was dropped", pa_cvolume_avg(&c->hw_volume), (long long unsigned) c->at);
2692 : 0 : pa_source_volume_change_free(c);
2693 : : }
2694 : 0 : nc->next = NULL;
2695 : 0 : s->thread_info.volume_changes_tail = nc;
2696 : : }
2697 : :
2698 : : /* Called from the IO thread. */
2699 : : static void pa_source_volume_change_flush(pa_source *s) {
2700 : 0 : pa_source_volume_change *c = s->thread_info.volume_changes;
2701 [ # # ]: 0 : pa_assert(s);
2702 : 0 : s->thread_info.volume_changes = NULL;
2703 : 0 : s->thread_info.volume_changes_tail = NULL;
2704 [ # # ]: 0 : while (c) {
2705 : 0 : pa_source_volume_change *next = c->next;
2706 : 0 : pa_source_volume_change_free(c);
2707 : 0 : c = next;
2708 : : }
2709 : : }
2710 : :
2711 : : /* Called from the IO thread. */
2712 : 0 : pa_bool_t pa_source_volume_change_apply(pa_source *s, pa_usec_t *usec_to_next) {
2713 : : pa_usec_t now;
2714 : 0 : pa_bool_t ret = FALSE;
2715 : :
2716 [ # # ]: 0 : pa_assert(s);
2717 : :
2718 [ # # ][ # # ]: 0 : if (!s->thread_info.volume_changes || !PA_SOURCE_IS_LINKED(s->state)) {
2719 [ # # ]: 0 : if (usec_to_next)
2720 : 0 : *usec_to_next = 0;
2721 : : return ret;
2722 : : }
2723 : :
2724 [ # # ]: 0 : pa_assert(s->write_volume);
2725 : :
2726 : 0 : now = pa_rtclock_now();
2727 : :
2728 [ # # ][ # # ]: 0 : while (s->thread_info.volume_changes && now >= s->thread_info.volume_changes->at) {
2729 : 0 : pa_source_volume_change *c = s->thread_info.volume_changes;
2730 [ # # ][ # # ]: 0 : PA_LLIST_REMOVE(pa_source_volume_change, s->thread_info.volume_changes, c);
[ # # ][ # # ]
2731 : 0 : pa_log_debug("Volume change to %d at %llu was written %llu usec late",
2732 : : pa_cvolume_avg(&c->hw_volume), (long long unsigned) c->at, (long long unsigned) (now - c->at));
2733 : 0 : ret = TRUE;
2734 : 0 : s->thread_info.current_hw_volume = c->hw_volume;
2735 : 0 : pa_source_volume_change_free(c);
2736 : : }
2737 : :
2738 [ # # ]: 0 : if (ret)
2739 : 0 : s->write_volume(s);
2740 : :
2741 [ # # ]: 0 : if (s->thread_info.volume_changes) {
2742 [ # # ]: 0 : if (usec_to_next)
2743 : 0 : *usec_to_next = s->thread_info.volume_changes->at - now;
2744 [ # # ]: 0 : if (pa_log_ratelimit(PA_LOG_DEBUG))
2745 : 0 : pa_log_debug("Next volume change in %lld usec", (long long) (s->thread_info.volume_changes->at - now));
2746 : : }
2747 : : else {
2748 [ # # ]: 0 : if (usec_to_next)
2749 : 0 : *usec_to_next = 0;
2750 : 0 : s->thread_info.volume_changes_tail = NULL;
2751 : : }
2752 : : return ret;
2753 : : }
2754 : :
2755 : :
2756 : : /* Called from the main thread */
2757 : : /* Gets the list of formats supported by the source. The members and idxset must
2758 : : * be freed by the caller. */
2759 : 0 : pa_idxset* pa_source_get_formats(pa_source *s) {
2760 : : pa_idxset *ret;
2761 : :
2762 [ # # ]: 0 : pa_assert(s);
2763 : :
2764 [ # # ]: 0 : if (s->get_formats) {
2765 : : /* Source supports format query, all is good */
2766 : 0 : ret = s->get_formats(s);
2767 : : } else {
2768 : : /* Source doesn't support format query, so assume it does PCM */
2769 : 0 : pa_format_info *f = pa_format_info_new();
2770 : 0 : f->encoding = PA_ENCODING_PCM;
2771 : :
2772 : 0 : ret = pa_idxset_new(NULL, NULL);
2773 : 0 : pa_idxset_put(ret, f, NULL);
2774 : : }
2775 : :
2776 : 0 : return ret;
2777 : : }
2778 : :
2779 : : /* Called from the main thread */
2780 : : /* Checks if the source can accept this format */
2781 : 0 : pa_bool_t pa_source_check_format(pa_source *s, pa_format_info *f)
2782 : : {
2783 : 0 : pa_idxset *formats = NULL;
2784 : 0 : pa_bool_t ret = FALSE;
2785 : :
2786 [ # # ]: 0 : pa_assert(s);
2787 [ # # ]: 0 : pa_assert(f);
2788 : :
2789 : 0 : formats = pa_source_get_formats(s);
2790 : :
2791 [ # # ]: 0 : if (formats) {
2792 : : pa_format_info *finfo_device;
2793 : : uint32_t i;
2794 : :
2795 [ # # ]: 0 : PA_IDXSET_FOREACH(finfo_device, formats, i) {
2796 [ # # ]: 0 : if (pa_format_info_is_compatible(finfo_device, f)) {
2797 : : ret = TRUE;
2798 : : break;
2799 : : }
2800 : : }
2801 : :
2802 : 0 : pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
2803 : : }
2804 : :
2805 : 0 : return ret;
2806 : : }
2807 : :
2808 : : /* Called from the main thread */
2809 : : /* Calculates the intersection between formats supported by the source and
2810 : : * in_formats, and returns these, in the order of the source's formats. */
2811 : 0 : pa_idxset* pa_source_check_formats(pa_source *s, pa_idxset *in_formats) {
2812 : 0 : pa_idxset *out_formats = pa_idxset_new(NULL, NULL), *source_formats = NULL;
2813 : : pa_format_info *f_source, *f_in;
2814 : : uint32_t i, j;
2815 : :
2816 [ # # ]: 0 : pa_assert(s);
2817 : :
2818 [ # # ][ # # ]: 0 : if (!in_formats || pa_idxset_isempty(in_formats))
2819 : : goto done;
2820 : :
2821 : 0 : source_formats = pa_source_get_formats(s);
2822 : :
2823 [ # # ]: 0 : PA_IDXSET_FOREACH(f_source, source_formats, i) {
2824 [ # # ]: 0 : PA_IDXSET_FOREACH(f_in, in_formats, j) {
2825 [ # # ]: 0 : if (pa_format_info_is_compatible(f_source, f_in))
2826 : 0 : pa_idxset_put(out_formats, pa_format_info_copy(f_in), NULL);
2827 : : }
2828 : : }
2829 : :
2830 : : done:
2831 [ # # ]: 0 : if (source_formats)
2832 : 0 : pa_idxset_free(source_formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
2833 : :
2834 : 0 : return out_formats;
2835 : : }
|