]> code.delx.au - pulseaudio/blob - src/pulsecore/sink.c
core: add a suspend cause flags field
[pulseaudio] / src / pulsecore / sink.c
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 <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30
31 #include <pulse/introspect.h>
32 #include <pulse/utf8.h>
33 #include <pulse/xmalloc.h>
34 #include <pulse/timeval.h>
35 #include <pulse/util.h>
36 #include <pulse/i18n.h>
37
38 #include <pulsecore/sink-input.h>
39 #include <pulsecore/namereg.h>
40 #include <pulsecore/core-util.h>
41 #include <pulsecore/sample-util.h>
42 #include <pulsecore/core-subscribe.h>
43 #include <pulsecore/log.h>
44 #include <pulsecore/macro.h>
45 #include <pulsecore/play-memblockq.h>
46
47 #include "sink.h"
48
49 #define MAX_MIX_CHANNELS 32
50 #define MIX_BUFFER_LENGTH (PA_PAGE_SIZE)
51 #define ABSOLUTE_MIN_LATENCY (500)
52 #define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
53 #define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
54
55 static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
56
57 static void sink_free(pa_object *s);
58
59 pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) {
60 pa_assert(data);
61
62 memset(data, 0, sizeof(*data));
63 data->proplist = pa_proplist_new();
64
65 return data;
66 }
67
68 void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name) {
69 pa_assert(data);
70
71 pa_xfree(data->name);
72 data->name = pa_xstrdup(name);
73 }
74
75 void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec) {
76 pa_assert(data);
77
78 if ((data->sample_spec_is_set = !!spec))
79 data->sample_spec = *spec;
80 }
81
82 void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map) {
83 pa_assert(data);
84
85 if ((data->channel_map_is_set = !!map))
86 data->channel_map = *map;
87 }
88
89 void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume) {
90 pa_assert(data);
91
92 if ((data->volume_is_set = !!volume))
93 data->volume = *volume;
94 }
95
96 void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute) {
97 pa_assert(data);
98
99 data->muted_is_set = TRUE;
100 data->muted = !!mute;
101 }
102
103 void pa_sink_new_data_done(pa_sink_new_data *data) {
104 pa_assert(data);
105
106 pa_xfree(data->name);
107 pa_proplist_free(data->proplist);
108 }
109
110 /* Called from main context */
111 static void reset_callbacks(pa_sink *s) {
112 pa_assert(s);
113
114 s->set_state = NULL;
115 s->get_volume = NULL;
116 s->set_volume = NULL;
117 s->get_mute = NULL;
118 s->set_mute = NULL;
119 s->request_rewind = NULL;
120 s->update_requested_latency = NULL;
121 }
122
123 /* Called from main context */
124 pa_sink* pa_sink_new(
125 pa_core *core,
126 pa_sink_new_data *data,
127 pa_sink_flags_t flags) {
128
129 pa_sink *s;
130 const char *name;
131 char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
132 pa_source_new_data source_data;
133 const char *dn;
134 char *pt;
135
136 pa_assert(core);
137 pa_assert(data);
138 pa_assert(data->name);
139
140 s = pa_msgobject_new(pa_sink);
141
142 if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SINK, s, data->namereg_fail))) {
143 pa_xfree(s);
144 return NULL;
145 }
146
147 pa_sink_new_data_set_name(data, name);
148
149 if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_NEW], data) < 0) {
150 pa_xfree(s);
151 pa_namereg_unregister(core, name);
152 return NULL;
153 }
154
155 pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
156 pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
157
158 pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
159
160 if (!data->channel_map_is_set)
161 pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
162
163 pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
164 pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
165
166 if (!data->volume_is_set)
167 pa_cvolume_reset(&data->volume, data->sample_spec.channels);
168
169 pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
170 pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
171
172 if (!data->muted_is_set)
173 data->muted = FALSE;
174
175 if (data->card)
176 pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist);
177
178 pa_device_init_description(data->proplist);
179 pa_device_init_icon(data->proplist, TRUE);
180
181 if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
182 pa_xfree(s);
183 pa_namereg_unregister(core, name);
184 return NULL;
185 }
186
187 s->parent.parent.free = sink_free;
188 s->parent.process_msg = pa_sink_process_msg;
189
190 s->core = core;
191 s->state = PA_SINK_INIT;
192 s->flags = flags;
193 s->suspend_cause = 0;
194 s->name = pa_xstrdup(name);
195 s->proplist = pa_proplist_copy(data->proplist);
196 s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
197 s->module = data->module;
198 s->card = data->card;
199
200 s->sample_spec = data->sample_spec;
201 s->channel_map = data->channel_map;
202
203 s->inputs = pa_idxset_new(NULL, NULL);
204 s->n_corked = 0;
205
206 s->reference_volume = s->virtual_volume = data->volume;
207 pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
208 s->base_volume = PA_VOLUME_NORM;
209 s->n_volume_steps = PA_VOLUME_NORM+1;
210 s->muted = data->muted;
211 s->refresh_volume = s->refresh_muted = FALSE;
212
213 s->fixed_latency = flags & PA_SINK_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
214
215 reset_callbacks(s);
216 s->userdata = NULL;
217
218 s->asyncmsgq = NULL;
219 s->rtpoll = NULL;
220
221 pa_silence_memchunk_get(
222 &core->silence_cache,
223 core->mempool,
224 &s->silence,
225 &s->sample_spec,
226 0);
227
228 s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
229 s->thread_info.soft_volume = s->soft_volume;
230 s->thread_info.soft_muted = s->muted;
231 s->thread_info.state = s->state;
232 s->thread_info.rewind_nbytes = 0;
233 s->thread_info.rewind_requested = FALSE;
234 s->thread_info.max_rewind = 0;
235 s->thread_info.max_request = 0;
236 s->thread_info.requested_latency_valid = FALSE;
237 s->thread_info.requested_latency = 0;
238 s->thread_info.min_latency = ABSOLUTE_MIN_LATENCY;
239 s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
240
241 pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
242
243 if (s->card)
244 pa_assert_se(pa_idxset_put(s->card->sinks, s, NULL) >= 0);
245
246 pt = pa_proplist_to_string_sep(s->proplist, "\n ");
247 pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s\n %s",
248 s->index,
249 s->name,
250 pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
251 pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map),
252 pt);
253 pa_xfree(pt);
254
255 pa_source_new_data_init(&source_data);
256 pa_source_new_data_set_sample_spec(&source_data, &s->sample_spec);
257 pa_source_new_data_set_channel_map(&source_data, &s->channel_map);
258 source_data.name = pa_sprintf_malloc("%s.monitor", name);
259 source_data.driver = data->driver;
260 source_data.module = data->module;
261 source_data.card = data->card;
262
263 dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
264 pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name);
265 pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor");
266
267 s->monitor_source = pa_source_new(core, &source_data,
268 ((flags & PA_SINK_LATENCY) ? PA_SOURCE_LATENCY : 0) |
269 ((flags & PA_SINK_DYNAMIC_LATENCY) ? PA_SOURCE_DYNAMIC_LATENCY : 0));
270
271 pa_source_new_data_done(&source_data);
272
273 if (!s->monitor_source) {
274 pa_sink_unlink(s);
275 pa_sink_unref(s);
276 return NULL;
277 }
278
279 s->monitor_source->monitor_of = s;
280
281 pa_source_set_latency_range(s->monitor_source, s->thread_info.min_latency, s->thread_info.max_latency);
282 pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
283
284 return s;
285 }
286
287 /* Called from main context */
288 static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
289 int ret;
290 pa_bool_t suspend_change;
291 pa_sink_state_t original_state;
292
293 pa_assert(s);
294
295 if (s->state == state)
296 return 0;
297
298 original_state = s->state;
299
300 suspend_change =
301 (original_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state)) ||
302 (PA_SINK_IS_OPENED(original_state) && state == PA_SINK_SUSPENDED);
303
304 if (s->set_state)
305 if ((ret = s->set_state(s, state)) < 0)
306 return ret;
307
308 if (s->asyncmsgq)
309 if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL)) < 0) {
310
311 if (s->set_state)
312 s->set_state(s, original_state);
313
314 return ret;
315 }
316
317 s->state = state;
318
319 if (state != PA_SINK_UNLINKED) { /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
320 pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
321 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
322 }
323
324 if (suspend_change) {
325 pa_sink_input *i;
326 uint32_t idx;
327
328 /* We're suspending or resuming, tell everyone about it */
329
330 for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)))
331 if (s->state == PA_SINK_SUSPENDED &&
332 (i->flags & PA_SINK_INPUT_FAIL_ON_SUSPEND))
333 pa_sink_input_kill(i);
334 else if (i->suspend)
335 i->suspend(i, state == PA_SINK_SUSPENDED);
336
337 if (s->monitor_source)
338 pa_source_sync_suspend(s->monitor_source);
339 }
340
341 return 0;
342 }
343
344 /* Called from main context */
345 void pa_sink_put(pa_sink* s) {
346 pa_sink_assert_ref(s);
347
348 pa_assert(s->state == PA_SINK_INIT);
349
350 /* The following fields must be initialized properly when calling _put() */
351 pa_assert(s->asyncmsgq);
352 pa_assert(s->rtpoll);
353 pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
354
355 /* Generally, flags should be initialized via pa_sink_new(). As a
356 * special exception we allow volume related flags to be set
357 * between _new() and _put(). */
358
359 if (!(s->flags & PA_SINK_HW_VOLUME_CTRL))
360 s->flags |= PA_SINK_DECIBEL_VOLUME;
361
362 if ((s->flags & PA_SINK_DECIBEL_VOLUME) && s->core->flat_volumes)
363 s->flags |= PA_SINK_FLAT_VOLUME;
364
365 s->thread_info.soft_volume = s->soft_volume;
366 s->thread_info.soft_muted = s->muted;
367
368 pa_assert((s->flags & PA_SINK_HW_VOLUME_CTRL) || (s->base_volume == PA_VOLUME_NORM && s->flags & PA_SINK_DECIBEL_VOLUME));
369 pa_assert(!(s->flags & PA_SINK_DECIBEL_VOLUME) || s->n_volume_steps == PA_VOLUME_NORM+1);
370 pa_assert(!(s->flags & PA_SINK_DYNAMIC_LATENCY) == (s->fixed_latency != 0));
371 pa_assert(!(s->flags & PA_SINK_LATENCY) == !(s->monitor_source->flags & PA_SOURCE_LATENCY));
372 pa_assert(!(s->flags & PA_SINK_DYNAMIC_LATENCY) == !(s->monitor_source->flags & PA_SOURCE_DYNAMIC_LATENCY));
373
374 pa_assert(s->monitor_source->fixed_latency == s->fixed_latency);
375 pa_assert(s->monitor_source->thread_info.min_latency == s->thread_info.min_latency);
376 pa_assert(s->monitor_source->thread_info.max_latency == s->thread_info.max_latency);
377
378 pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
379
380 pa_source_put(s->monitor_source);
381
382 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
383 pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s);
384 }
385
386 /* Called from main context */
387 void pa_sink_unlink(pa_sink* s) {
388 pa_bool_t linked;
389 pa_sink_input *i, *j = NULL;
390
391 pa_assert(s);
392
393 /* Please note that pa_sink_unlink() does more than simply
394 * reversing pa_sink_put(). It also undoes the registrations
395 * already done in pa_sink_new()! */
396
397 /* All operations here shall be idempotent, i.e. pa_sink_unlink()
398 * may be called multiple times on the same sink without bad
399 * effects. */
400
401 linked = PA_SINK_IS_LINKED(s->state);
402
403 if (linked)
404 pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s);
405
406 if (s->state != PA_SINK_UNLINKED)
407 pa_namereg_unregister(s->core, s->name);
408 pa_idxset_remove_by_data(s->core->sinks, s, NULL);
409
410 if (s->card)
411 pa_idxset_remove_by_data(s->card->sinks, s, NULL);
412
413 while ((i = pa_idxset_first(s->inputs, NULL))) {
414 pa_assert(i != j);
415 pa_sink_input_kill(i);
416 j = i;
417 }
418
419 if (linked)
420 sink_set_state(s, PA_SINK_UNLINKED);
421 else
422 s->state = PA_SINK_UNLINKED;
423
424 reset_callbacks(s);
425
426 if (s->monitor_source)
427 pa_source_unlink(s->monitor_source);
428
429 if (linked) {
430 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
431 pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], s);
432 }
433 }
434
435 /* Called from main context */
436 static void sink_free(pa_object *o) {
437 pa_sink *s = PA_SINK(o);
438 pa_sink_input *i;
439
440 pa_assert(s);
441 pa_assert(pa_sink_refcnt(s) == 0);
442
443 if (PA_SINK_IS_LINKED(s->state))
444 pa_sink_unlink(s);
445
446 pa_log_info("Freeing sink %u \"%s\"", s->index, s->name);
447
448 if (s->monitor_source) {
449 pa_source_unref(s->monitor_source);
450 s->monitor_source = NULL;
451 }
452
453 pa_idxset_free(s->inputs, NULL, NULL);
454
455 while ((i = pa_hashmap_steal_first(s->thread_info.inputs)))
456 pa_sink_input_unref(i);
457
458 pa_hashmap_free(s->thread_info.inputs, NULL, NULL);
459
460 if (s->silence.memblock)
461 pa_memblock_unref(s->silence.memblock);
462
463 pa_xfree(s->name);
464 pa_xfree(s->driver);
465
466 if (s->proplist)
467 pa_proplist_free(s->proplist);
468
469 pa_xfree(s);
470 }
471
472 /* Called from main context */
473 void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
474 pa_sink_assert_ref(s);
475
476 s->asyncmsgq = q;
477
478 if (s->monitor_source)
479 pa_source_set_asyncmsgq(s->monitor_source, q);
480 }
481
482 /* Called from main context */
483 void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
484 pa_sink_assert_ref(s);
485
486 s->rtpoll = p;
487 if (s->monitor_source)
488 pa_source_set_rtpoll(s->monitor_source, p);
489 }
490
491 /* Called from main context */
492 int pa_sink_update_status(pa_sink*s) {
493 pa_sink_assert_ref(s);
494 pa_assert(PA_SINK_IS_LINKED(s->state));
495
496 if (s->state == PA_SINK_SUSPENDED)
497 return 0;
498
499 return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
500 }
501
502 /* Called from main context */
503 int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause) {
504 pa_sink_assert_ref(s);
505 pa_assert(PA_SINK_IS_LINKED(s->state));
506 pa_assert(cause != 0);
507
508 if (suspend)
509 s->suspend_cause |= cause;
510 else
511 s->suspend_cause &= ~cause;
512
513 pa_log_debug("Suspend cause of sink %s is 0x%04x, %s", s->name, s->suspend_cause, s->suspend_cause ? "suspending" : "resuming");
514
515 if (s->suspend_cause)
516 return sink_set_state(s, PA_SINK_SUSPENDED);
517 else
518 return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
519 }
520
521 /* Called from main context */
522 pa_queue *pa_sink_move_all_start(pa_sink *s) {
523 pa_queue *q;
524 pa_sink_input *i, *n;
525 uint32_t idx;
526
527 pa_sink_assert_ref(s);
528 pa_assert(PA_SINK_IS_LINKED(s->state));
529
530 q = pa_queue_new();
531
532 for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) {
533 n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx));
534
535 pa_sink_input_ref(i);
536
537 if (pa_sink_input_start_move(i) >= 0)
538 pa_queue_push(q, i);
539 else
540 pa_sink_input_unref(i);
541 }
542
543 return q;
544 }
545
546 /* Called from main context */
547 void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save) {
548 pa_sink_input *i;
549
550 pa_sink_assert_ref(s);
551 pa_assert(PA_SINK_IS_LINKED(s->state));
552 pa_assert(q);
553
554 while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
555 if (pa_sink_input_finish_move(i, s, save) < 0)
556 pa_sink_input_kill(i);
557
558 pa_sink_input_unref(i);
559 }
560
561 pa_queue_free(q, NULL, NULL);
562 }
563
564 /* Called from main context */
565 void pa_sink_move_all_fail(pa_queue *q) {
566 pa_sink_input *i;
567 pa_assert(q);
568
569 while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
570 if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL], i) == PA_HOOK_OK) {
571 pa_sink_input_kill(i);
572 pa_sink_input_unref(i);
573 }
574 }
575
576 pa_queue_free(q, NULL, NULL);
577 }
578
579 /* Called from IO thread context */
580 void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
581 pa_sink_input *i;
582 void *state = NULL;
583 pa_sink_assert_ref(s);
584 pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
585
586 /* If nobody requested this and this is actually no real rewind
587 * then we can short cut this */
588 if (!s->thread_info.rewind_requested && nbytes <= 0)
589 return;
590
591 s->thread_info.rewind_nbytes = 0;
592 s->thread_info.rewind_requested = FALSE;
593
594 if (s->thread_info.state == PA_SINK_SUSPENDED)
595 return;
596
597 if (nbytes > 0)
598 pa_log_debug("Processing rewind...");
599
600 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
601 pa_sink_input_assert_ref(i);
602 pa_sink_input_process_rewind(i, nbytes);
603 }
604
605 if (nbytes > 0)
606 if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state))
607 pa_source_process_rewind(s->monitor_source, nbytes);
608 }
609
610 /* Called from IO thread context */
611 static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, unsigned maxinfo) {
612 pa_sink_input *i;
613 unsigned n = 0;
614 void *state = NULL;
615 size_t mixlength = *length;
616
617 pa_sink_assert_ref(s);
618 pa_assert(info);
619
620 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) {
621 pa_sink_input_assert_ref(i);
622
623 pa_sink_input_peek(i, *length, &info->chunk, &info->volume);
624
625 if (mixlength == 0 || info->chunk.length < mixlength)
626 mixlength = info->chunk.length;
627
628 if (pa_memblock_is_silence(info->chunk.memblock)) {
629 pa_memblock_unref(info->chunk.memblock);
630 continue;
631 }
632
633 info->userdata = pa_sink_input_ref(i);
634
635 pa_assert(info->chunk.memblock);
636 pa_assert(info->chunk.length > 0);
637
638 info++;
639 n++;
640 maxinfo--;
641 }
642
643 if (mixlength > 0)
644 *length = mixlength;
645
646 return n;
647 }
648
649 /* Called from IO thread context */
650 static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *result) {
651 pa_sink_input *i;
652 void *state = NULL;
653 unsigned p = 0;
654 unsigned n_unreffed = 0;
655
656 pa_sink_assert_ref(s);
657 pa_assert(result);
658 pa_assert(result->memblock);
659 pa_assert(result->length > 0);
660
661 /* We optimize for the case where the order of the inputs has not changed */
662
663 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
664 unsigned j;
665 pa_mix_info* m = NULL;
666
667 pa_sink_input_assert_ref(i);
668
669 /* Let's try to find the matching entry info the pa_mix_info array */
670 for (j = 0; j < n; j ++) {
671
672 if (info[p].userdata == i) {
673 m = info + p;
674 break;
675 }
676
677 p++;
678 if (p >= n)
679 p = 0;
680 }
681
682 /* Drop read data */
683 pa_sink_input_drop(i, result->length);
684
685 if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state)) {
686
687 if (pa_hashmap_size(i->thread_info.direct_outputs) > 0) {
688 void *ostate = NULL;
689 pa_source_output *o;
690 pa_memchunk c;
691
692 if (m && m->chunk.memblock) {
693 c = m->chunk;
694 pa_memblock_ref(c.memblock);
695 pa_assert(result->length <= c.length);
696 c.length = result->length;
697
698 pa_memchunk_make_writable(&c, 0);
699 pa_volume_memchunk(&c, &s->sample_spec, &m->volume);
700 } else {
701 c = s->silence;
702 pa_memblock_ref(c.memblock);
703 pa_assert(result->length <= c.length);
704 c.length = result->length;
705 }
706
707 while ((o = pa_hashmap_iterate(i->thread_info.direct_outputs, &ostate, NULL))) {
708 pa_source_output_assert_ref(o);
709 pa_assert(o->direct_on_input == i);
710 pa_source_post_direct(s->monitor_source, o, &c);
711 }
712
713 pa_memblock_unref(c.memblock);
714 }
715 }
716
717 if (m) {
718 if (m->chunk.memblock)
719 pa_memblock_unref(m->chunk.memblock);
720 pa_memchunk_reset(&m->chunk);
721
722 pa_sink_input_unref(m->userdata);
723 m->userdata = NULL;
724
725 n_unreffed += 1;
726 }
727 }
728
729 /* Now drop references to entries that are included in the
730 * pa_mix_info array but don't exist anymore */
731
732 if (n_unreffed < n) {
733 for (; n > 0; info++, n--) {
734 if (info->userdata)
735 pa_sink_input_unref(info->userdata);
736 if (info->chunk.memblock)
737 pa_memblock_unref(info->chunk.memblock);
738 }
739 }
740
741 if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state))
742 pa_source_post(s->monitor_source, result);
743 }
744
745 /* Called from IO thread context */
746 void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
747 pa_mix_info info[MAX_MIX_CHANNELS];
748 unsigned n;
749 size_t block_size_max;
750
751 pa_sink_assert_ref(s);
752 pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
753 pa_assert(pa_frame_aligned(length, &s->sample_spec));
754 pa_assert(result);
755
756 pa_sink_ref(s);
757
758 pa_assert(!s->thread_info.rewind_requested);
759 pa_assert(s->thread_info.rewind_nbytes == 0);
760
761 if (s->thread_info.state == PA_SINK_SUSPENDED) {
762 result->memblock = pa_memblock_ref(s->silence.memblock);
763 result->index = s->silence.index;
764 result->length = PA_MIN(s->silence.length, length);
765 return;
766 }
767
768 if (length <= 0)
769 length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
770
771 block_size_max = pa_mempool_block_size_max(s->core->mempool);
772 if (length > block_size_max)
773 length = pa_frame_align(block_size_max, &s->sample_spec);
774
775 pa_assert(length > 0);
776
777 n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS);
778
779 if (n == 0) {
780
781 *result = s->silence;
782 pa_memblock_ref(result->memblock);
783
784 if (result->length > length)
785 result->length = length;
786
787 } else if (n == 1) {
788 pa_cvolume volume;
789
790 *result = info[0].chunk;
791 pa_memblock_ref(result->memblock);
792
793 if (result->length > length)
794 result->length = length;
795
796 pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
797
798 if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {
799 if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) {
800 pa_memblock_unref(result->memblock);
801 pa_silence_memchunk_get(&s->core->silence_cache,
802 s->core->mempool,
803 result,
804 &s->sample_spec,
805 result->length);
806 } else {
807 pa_memchunk_make_writable(result, 0);
808 pa_volume_memchunk(result, &s->sample_spec, &volume);
809 }
810 }
811 } else {
812 void *ptr;
813 result->memblock = pa_memblock_new(s->core->mempool, length);
814
815 ptr = pa_memblock_acquire(result->memblock);
816 result->length = pa_mix(info, n,
817 ptr, length,
818 &s->sample_spec,
819 &s->thread_info.soft_volume,
820 s->thread_info.soft_muted);
821 pa_memblock_release(result->memblock);
822
823 result->index = 0;
824 }
825
826 inputs_drop(s, info, n, result);
827
828 pa_sink_unref(s);
829 }
830
831 /* Called from IO thread context */
832 void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
833 pa_mix_info info[MAX_MIX_CHANNELS];
834 unsigned n;
835 size_t length, block_size_max;
836
837 pa_sink_assert_ref(s);
838 pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
839 pa_assert(target);
840 pa_assert(target->memblock);
841 pa_assert(target->length > 0);
842 pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
843
844 pa_sink_ref(s);
845
846 pa_assert(!s->thread_info.rewind_requested);
847 pa_assert(s->thread_info.rewind_nbytes == 0);
848
849 if (s->thread_info.state == PA_SINK_SUSPENDED) {
850 pa_silence_memchunk(target, &s->sample_spec);
851 return;
852 }
853
854 length = target->length;
855 block_size_max = pa_mempool_block_size_max(s->core->mempool);
856 if (length > block_size_max)
857 length = pa_frame_align(block_size_max, &s->sample_spec);
858
859 pa_assert(length > 0);
860
861 n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS);
862
863 if (n == 0) {
864 if (target->length > length)
865 target->length = length;
866
867 pa_silence_memchunk(target, &s->sample_spec);
868 } else if (n == 1) {
869 pa_cvolume volume;
870
871 if (target->length > length)
872 target->length = length;
873
874 pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
875
876 if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
877 pa_silence_memchunk(target, &s->sample_spec);
878 else {
879 pa_memchunk vchunk;
880
881 vchunk = info[0].chunk;
882 pa_memblock_ref(vchunk.memblock);
883
884 if (vchunk.length > length)
885 vchunk.length = length;
886
887 if (!pa_cvolume_is_norm(&volume)) {
888 pa_memchunk_make_writable(&vchunk, 0);
889 pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
890 }
891
892 pa_memchunk_memcpy(target, &vchunk);
893 pa_memblock_unref(vchunk.memblock);
894 }
895
896 } else {
897 void *ptr;
898
899 ptr = pa_memblock_acquire(target->memblock);
900
901 target->length = pa_mix(info, n,
902 (uint8_t*) ptr + target->index, length,
903 &s->sample_spec,
904 &s->thread_info.soft_volume,
905 s->thread_info.soft_muted);
906
907 pa_memblock_release(target->memblock);
908 }
909
910 inputs_drop(s, info, n, target);
911
912 pa_sink_unref(s);
913 }
914
915 /* Called from IO thread context */
916 void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
917 pa_memchunk chunk;
918 size_t l, d;
919
920 pa_sink_assert_ref(s);
921 pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
922 pa_assert(target);
923 pa_assert(target->memblock);
924 pa_assert(target->length > 0);
925 pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
926
927 pa_sink_ref(s);
928
929 pa_assert(!s->thread_info.rewind_requested);
930 pa_assert(s->thread_info.rewind_nbytes == 0);
931
932 l = target->length;
933 d = 0;
934 while (l > 0) {
935 chunk = *target;
936 chunk.index += d;
937 chunk.length -= d;
938
939 pa_sink_render_into(s, &chunk);
940
941 d += chunk.length;
942 l -= chunk.length;
943 }
944
945 pa_sink_unref(s);
946 }
947
948 /* Called from IO thread context */
949 void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
950 pa_mix_info info[MAX_MIX_CHANNELS];
951 size_t length1st = length;
952 unsigned n;
953
954 pa_sink_assert_ref(s);
955 pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
956 pa_assert(length > 0);
957 pa_assert(pa_frame_aligned(length, &s->sample_spec));
958 pa_assert(result);
959
960 pa_sink_ref(s);
961
962 pa_assert(!s->thread_info.rewind_requested);
963 pa_assert(s->thread_info.rewind_nbytes == 0);
964
965 pa_assert(length > 0);
966
967 n = fill_mix_info(s, &length1st, info, MAX_MIX_CHANNELS);
968
969 if (n == 0) {
970 pa_silence_memchunk_get(&s->core->silence_cache,
971 s->core->mempool,
972 result,
973 &s->sample_spec,
974 length1st);
975 } else if (n == 1) {
976 pa_cvolume volume;
977
978 *result = info[0].chunk;
979 pa_memblock_ref(result->memblock);
980
981 if (result->length > length)
982 result->length = length;
983
984 pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
985
986 if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {
987 if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) {
988 pa_memblock_unref(result->memblock);
989 pa_silence_memchunk_get(&s->core->silence_cache,
990 s->core->mempool,
991 result,
992 &s->sample_spec,
993 result->length);
994 } else {
995 pa_memchunk_make_writable(result, length);
996 pa_volume_memchunk(result, &s->sample_spec, &volume);
997 }
998 }
999 } else {
1000 void *ptr;
1001
1002 result->index = 0;
1003 result->memblock = pa_memblock_new(s->core->mempool, length);
1004
1005 ptr = pa_memblock_acquire(result->memblock);
1006
1007 result->length = pa_mix(info, n,
1008 (uint8_t*) ptr + result->index, length1st,
1009 &s->sample_spec,
1010 &s->thread_info.soft_volume,
1011 s->thread_info.soft_muted);
1012
1013 pa_memblock_release(result->memblock);
1014 }
1015
1016 inputs_drop(s, info, n, result);
1017
1018 if (result->length < length) {
1019 pa_memchunk chunk;
1020 size_t l, d;
1021 pa_memchunk_make_writable(result, length);
1022 result->length = length;
1023
1024 l = length - result->length;
1025 d = result->index + result->length;
1026 while (l > 0) {
1027 chunk = *result;
1028 chunk.index += d;
1029 chunk.length -= d - result->index;
1030
1031 pa_sink_render_into(s, &chunk);
1032
1033 d += chunk.length;
1034 l -= chunk.length;
1035 }
1036 result->length = length;
1037 }
1038
1039 pa_sink_unref(s);
1040 }
1041
1042 /* Called from main thread */
1043 pa_usec_t pa_sink_get_latency(pa_sink *s) {
1044 pa_usec_t usec = 0;
1045
1046 pa_sink_assert_ref(s);
1047 pa_assert(PA_SINK_IS_LINKED(s->state));
1048
1049 /* The returned value is supposed to be in the time domain of the sound card! */
1050
1051 if (s->state == PA_SINK_SUSPENDED)
1052 return 0;
1053
1054 if (!(s->flags & PA_SINK_LATENCY))
1055 return 0;
1056
1057 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
1058
1059 return usec;
1060 }
1061
1062 /* Called from IO thread */
1063 pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
1064 pa_usec_t usec = 0;
1065 pa_msgobject *o;
1066
1067 pa_sink_assert_ref(s);
1068 pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1069
1070 /* The returned value is supposed to be in the time domain of the sound card! */
1071
1072 if (s->thread_info.state == PA_SINK_SUSPENDED)
1073 return 0;
1074
1075 if (!(s->flags & PA_SINK_LATENCY))
1076 return 0;
1077
1078 o = PA_MSGOBJECT(s);
1079
1080 /* We probably should make this a proper vtable callback instead of going through process_msg() */
1081
1082 if (o->process_msg(o, PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
1083 return -1;
1084
1085 return usec;
1086 }
1087
1088 static void compute_new_soft_volume(pa_sink_input *i, const pa_cvolume *new_volume) {
1089 unsigned c;
1090
1091 pa_sink_input_assert_ref(i);
1092 pa_assert(new_volume->channels == i->sample_spec.channels);
1093
1094 /*
1095 * This basically calculates:
1096 *
1097 * i->relative_volume := i->virtual_volume / new_volume
1098 * i->soft_volume := i->relative_volume * i->volume_factor
1099 */
1100
1101 /* The new sink volume passed in here must already be remapped to
1102 * the sink input's channel map! */
1103
1104 i->soft_volume.channels = i->sample_spec.channels;
1105
1106 for (c = 0; c < i->sample_spec.channels; c++)
1107
1108 if (new_volume->values[c] <= PA_VOLUME_MUTED)
1109 /* We leave i->relative_volume untouched */
1110 i->soft_volume.values[c] = PA_VOLUME_MUTED;
1111 else {
1112 i->relative_volume[c] =
1113 pa_sw_volume_to_linear(i->virtual_volume.values[c]) /
1114 pa_sw_volume_to_linear(new_volume->values[c]);
1115
1116 i->soft_volume.values[c] = pa_sw_volume_from_linear(
1117 i->relative_volume[c] *
1118 pa_sw_volume_to_linear(i->volume_factor.values[c]));
1119 }
1120
1121 /* Hooks have the ability to play games with i->soft_volume */
1122 pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
1123
1124 /* We don't copy the soft_volume to the thread_info data
1125 * here. That must be done by the caller */
1126 }
1127
1128 /* Called from main thread */
1129 void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
1130 pa_sink_input *i;
1131 uint32_t idx;
1132
1133 pa_sink_assert_ref(s);
1134 pa_assert(new_volume);
1135 pa_assert(PA_SINK_IS_LINKED(s->state));
1136 pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
1137
1138 /* This is called whenever a sink input volume changes or a sink
1139 * input is added/removed and we might need to fix up the sink
1140 * volume accordingly. Please note that we don't actually update
1141 * the sinks volume here, we only return how it needs to be
1142 * updated. The caller should then call pa_sink_set_volume().*/
1143
1144 if (pa_idxset_isempty(s->inputs)) {
1145 /* In the special case that we have no sink input we leave the
1146 * volume unmodified. */
1147 *new_volume = s->reference_volume;
1148 return;
1149 }
1150
1151 pa_cvolume_mute(new_volume, s->channel_map.channels);
1152
1153 /* First let's determine the new maximum volume of all inputs
1154 * connected to this sink */
1155 for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
1156 unsigned c;
1157 pa_cvolume remapped_volume;
1158
1159 remapped_volume = i->virtual_volume;
1160 pa_cvolume_remap(&remapped_volume, &i->channel_map, &s->channel_map);
1161
1162 for (c = 0; c < new_volume->channels; c++)
1163 if (remapped_volume.values[c] > new_volume->values[c])
1164 new_volume->values[c] = remapped_volume.values[c];
1165 }
1166
1167 /* Then, let's update the soft volumes of all inputs connected
1168 * to this sink */
1169 for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
1170 pa_cvolume remapped_new_volume;
1171
1172 remapped_new_volume = *new_volume;
1173 pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
1174 compute_new_soft_volume(i, &remapped_new_volume);
1175
1176 /* We don't copy soft_volume to the thread_info data here
1177 * (i.e. issue PA_SINK_INPUT_MESSAGE_SET_VOLUME) because we
1178 * want the update to be atomically with the sink volume
1179 * update, hence we do it within the pa_sink_set_volume() call
1180 * below */
1181 }
1182 }
1183
1184 /* Called from main thread */
1185 void pa_sink_propagate_flat_volume(pa_sink *s) {
1186 pa_sink_input *i;
1187 uint32_t idx;
1188
1189 pa_sink_assert_ref(s);
1190 pa_assert(PA_SINK_IS_LINKED(s->state));
1191 pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
1192
1193 /* This is called whenever the sink volume changes that is not
1194 * caused by a sink input volume change. We need to fix up the
1195 * sink input volumes accordingly */
1196
1197 for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
1198 pa_cvolume sink_volume, new_virtual_volume;
1199 unsigned c;
1200
1201 /* This basically calculates i->virtual_volume := i->relative_volume * s->virtual_volume */
1202
1203 sink_volume = s->virtual_volume;
1204 pa_cvolume_remap(&sink_volume, &s->channel_map, &i->channel_map);
1205
1206 for (c = 0; c < i->sample_spec.channels; c++)
1207 new_virtual_volume.values[c] = pa_sw_volume_from_linear(
1208 i->relative_volume[c] *
1209 pa_sw_volume_to_linear(sink_volume.values[c]));
1210
1211 new_virtual_volume.channels = i->sample_spec.channels;
1212
1213 if (!pa_cvolume_equal(&new_virtual_volume, &i->virtual_volume)) {
1214 i->virtual_volume = new_virtual_volume;
1215
1216 /* Hmm, the soft volume might no longer actually match
1217 * what has been chosen as new virtual volume here,
1218 * especially when the old volume was
1219 * PA_VOLUME_MUTED. Hence let's recalculate the soft
1220 * volumes here. */
1221 compute_new_soft_volume(i, &sink_volume);
1222
1223 /* The virtual volume changed, let's tell people so */
1224 pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
1225 }
1226 }
1227
1228 /* If the soft_volume of any of the sink inputs got changed, let's
1229 * make sure the thread copies are synced up. */
1230 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SYNC_VOLUMES, NULL, 0, NULL) == 0);
1231 }
1232
1233 /* Called from main thread */
1234 void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference) {
1235 pa_bool_t virtual_volume_changed;
1236
1237 pa_sink_assert_ref(s);
1238 pa_assert(PA_SINK_IS_LINKED(s->state));
1239 pa_assert(volume);
1240 pa_assert(pa_cvolume_valid(volume));
1241 pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
1242
1243 virtual_volume_changed = !pa_cvolume_equal(volume, &s->virtual_volume);
1244 s->virtual_volume = *volume;
1245
1246 if (become_reference)
1247 s->reference_volume = s->virtual_volume;
1248
1249 /* Propagate this volume change back to the inputs */
1250 if (virtual_volume_changed)
1251 if (propagate && (s->flags & PA_SINK_FLAT_VOLUME))
1252 pa_sink_propagate_flat_volume(s);
1253
1254 if (s->set_volume) {
1255 /* If we have a function set_volume(), then we do not apply a
1256 * soft volume by default. However, set_volume() is free to
1257 * apply one to s->soft_volume */
1258
1259 pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
1260 s->set_volume(s);
1261
1262 } else
1263 /* If we have no function set_volume(), then the soft volume
1264 * becomes the virtual volume */
1265 s->soft_volume = s->virtual_volume;
1266
1267 /* This tells the sink that soft and/or virtual volume changed */
1268 if (sendmsg)
1269 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
1270
1271 if (virtual_volume_changed)
1272 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1273 }
1274
1275 /* Called from main thread. Only to be called by sink implementor */
1276 void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
1277 pa_sink_assert_ref(s);
1278 pa_assert(volume);
1279
1280 s->soft_volume = *volume;
1281
1282 if (PA_SINK_IS_LINKED(s->state))
1283 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
1284 else
1285 s->thread_info.soft_volume = *volume;
1286 }
1287
1288 /* Called from main thread */
1289 const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh, pa_bool_t reference) {
1290 pa_sink_assert_ref(s);
1291
1292 if (s->refresh_volume || force_refresh) {
1293 struct pa_cvolume old_virtual_volume = s->virtual_volume;
1294
1295 if (s->get_volume)
1296 s->get_volume(s);
1297
1298 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
1299
1300 if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) {
1301
1302 s->reference_volume = s->virtual_volume;
1303
1304 if (s->flags & PA_SINK_FLAT_VOLUME)
1305 pa_sink_propagate_flat_volume(s);
1306
1307 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1308 }
1309 }
1310
1311 return reference ? &s->reference_volume : &s->virtual_volume;
1312 }
1313
1314 /* Called from main thread */
1315 void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume) {
1316 pa_sink_assert_ref(s);
1317
1318 /* The sink implementor may call this if the volume changed to make sure everyone is notified */
1319
1320 if (pa_cvolume_equal(&s->virtual_volume, new_volume))
1321 return;
1322
1323 s->reference_volume = s->virtual_volume = *new_volume;
1324
1325 if (s->flags & PA_SINK_FLAT_VOLUME)
1326 pa_sink_propagate_flat_volume(s);
1327
1328 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1329 }
1330
1331 /* Called from main thread */
1332 void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
1333 pa_bool_t old_muted;
1334
1335 pa_sink_assert_ref(s);
1336 pa_assert(PA_SINK_IS_LINKED(s->state));
1337
1338 old_muted = s->muted;
1339 s->muted = mute;
1340
1341 if (s->set_mute)
1342 s->set_mute(s);
1343
1344 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
1345
1346 if (old_muted != s->muted)
1347 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1348 }
1349
1350 /* Called from main thread */
1351 pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
1352
1353 pa_sink_assert_ref(s);
1354
1355 if (s->refresh_muted || force_refresh) {
1356 pa_bool_t old_muted = s->muted;
1357
1358 if (s->get_mute)
1359 s->get_mute(s);
1360
1361 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
1362
1363 if (old_muted != s->muted)
1364 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1365 }
1366
1367 return s->muted;
1368 }
1369
1370 /* Called from main thread */
1371 void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted) {
1372 pa_sink_assert_ref(s);
1373
1374 /* The sink implementor may call this if the volume changed to make sure everyone is notified */
1375
1376 if (s->muted == new_muted)
1377 return;
1378
1379 s->muted = new_muted;
1380 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1381 }
1382
1383 /* Called from main thread */
1384 pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
1385 pa_sink_assert_ref(s);
1386
1387 if (p)
1388 pa_proplist_update(s->proplist, mode, p);
1389
1390 if (PA_SINK_IS_LINKED(s->state)) {
1391 pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
1392 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1393 }
1394
1395 return TRUE;
1396 }
1397
1398 /* Called from main thread */
1399 void pa_sink_set_description(pa_sink *s, const char *description) {
1400 const char *old;
1401 pa_sink_assert_ref(s);
1402
1403 if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
1404 return;
1405
1406 old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
1407
1408 if (old && description && !strcmp(old, description))
1409 return;
1410
1411 if (description)
1412 pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
1413 else
1414 pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
1415
1416 if (s->monitor_source) {
1417 char *n;
1418
1419 n = pa_sprintf_malloc("Monitor Source of %s", description ? description : s->name);
1420 pa_source_set_description(s->monitor_source, n);
1421 pa_xfree(n);
1422 }
1423
1424 if (PA_SINK_IS_LINKED(s->state)) {
1425 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1426 pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
1427 }
1428 }
1429
1430 /* Called from main thread */
1431 unsigned pa_sink_linked_by(pa_sink *s) {
1432 unsigned ret;
1433
1434 pa_sink_assert_ref(s);
1435 pa_assert(PA_SINK_IS_LINKED(s->state));
1436
1437 ret = pa_idxset_size(s->inputs);
1438
1439 /* We add in the number of streams connected to us here. Please
1440 * note the asymmmetry to pa_sink_used_by()! */
1441
1442 if (s->monitor_source)
1443 ret += pa_source_linked_by(s->monitor_source);
1444
1445 return ret;
1446 }
1447
1448 /* Called from main thread */
1449 unsigned pa_sink_used_by(pa_sink *s) {
1450 unsigned ret;
1451
1452 pa_sink_assert_ref(s);
1453 pa_assert(PA_SINK_IS_LINKED(s->state));
1454
1455 ret = pa_idxset_size(s->inputs);
1456 pa_assert(ret >= s->n_corked);
1457
1458 /* Streams connected to our monitor source do not matter for
1459 * pa_sink_used_by()!.*/
1460
1461 return ret - s->n_corked;
1462 }
1463
1464 /* Called from main thread */
1465 unsigned pa_sink_check_suspend(pa_sink *s) {
1466 unsigned ret;
1467 pa_sink_input *i;
1468 uint32_t idx;
1469
1470 pa_sink_assert_ref(s);
1471
1472 if (!PA_SINK_IS_LINKED(s->state))
1473 return 0;
1474
1475 ret = 0;
1476
1477 for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
1478 pa_sink_input_state_t st;
1479
1480 st = pa_sink_input_get_state(i);
1481 pa_assert(PA_SINK_INPUT_IS_LINKED(st));
1482
1483 if (st == PA_SINK_INPUT_CORKED)
1484 continue;
1485
1486 if (i->flags & PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND)
1487 continue;
1488
1489 ret ++;
1490 }
1491
1492 if (s->monitor_source)
1493 ret += pa_source_check_suspend(s->monitor_source);
1494
1495 return ret;
1496 }
1497
1498 /* Called from the IO thread */
1499 static void sync_input_volumes_within_thread(pa_sink *s) {
1500 pa_sink_input *i;
1501 void *state = NULL;
1502
1503 pa_sink_assert_ref(s);
1504
1505 while ((i = PA_SINK_INPUT(pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))) {
1506 if (pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume))
1507 continue;
1508
1509 i->thread_info.soft_volume = i->soft_volume;
1510 pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
1511 }
1512 }
1513
1514 /* Called from IO thread, except when it is not */
1515 int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
1516 pa_sink *s = PA_SINK(o);
1517 pa_sink_assert_ref(s);
1518
1519 switch ((pa_sink_message_t) code) {
1520
1521 case PA_SINK_MESSAGE_ADD_INPUT: {
1522 pa_sink_input *i = PA_SINK_INPUT(userdata);
1523
1524 /* If you change anything here, make sure to change the
1525 * sink input handling a few lines down at
1526 * PA_SINK_MESSAGE_FINISH_MOVE, too. */
1527
1528 pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
1529
1530 /* Since the caller sleeps in pa_sink_input_put(), we can
1531 * safely access data outside of thread_info even though
1532 * it is mutable */
1533
1534 if ((i->thread_info.sync_prev = i->sync_prev)) {
1535 pa_assert(i->sink == i->thread_info.sync_prev->sink);
1536 pa_assert(i->sync_prev->sync_next == i);
1537 i->thread_info.sync_prev->thread_info.sync_next = i;
1538 }
1539
1540 if ((i->thread_info.sync_next = i->sync_next)) {
1541 pa_assert(i->sink == i->thread_info.sync_next->sink);
1542 pa_assert(i->sync_next->sync_prev == i);
1543 i->thread_info.sync_next->thread_info.sync_prev = i;
1544 }
1545
1546 pa_assert(!i->thread_info.attached);
1547 i->thread_info.attached = TRUE;
1548
1549 if (i->attach)
1550 i->attach(i);
1551
1552 pa_sink_input_set_state_within_thread(i, i->state);
1553
1554 /* The requested latency of the sink input needs to be
1555 * fixed up and then configured on the sink */
1556
1557 if (i->thread_info.requested_sink_latency != (pa_usec_t) -1)
1558 pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
1559
1560 pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
1561 pa_sink_input_update_max_request(i, s->thread_info.max_request);
1562
1563 /* We don't rewind here automatically. This is left to the
1564 * sink input implementor because some sink inputs need a
1565 * slow start, i.e. need some time to buffer client
1566 * samples before beginning streaming. */
1567
1568 /* In flat volume mode we need to update the volume as
1569 * well */
1570 return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
1571 }
1572
1573 case PA_SINK_MESSAGE_REMOVE_INPUT: {
1574 pa_sink_input *i = PA_SINK_INPUT(userdata);
1575
1576 /* If you change anything here, make sure to change the
1577 * sink input handling a few lines down at
1578 * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */
1579
1580 if (i->detach)
1581 i->detach(i);
1582
1583 pa_sink_input_set_state_within_thread(i, i->state);
1584
1585 pa_assert(i->thread_info.attached);
1586 i->thread_info.attached = FALSE;
1587
1588 /* Since the caller sleeps in pa_sink_input_unlink(),
1589 * we can safely access data outside of thread_info even
1590 * though it is mutable */
1591
1592 pa_assert(!i->sync_prev);
1593 pa_assert(!i->sync_next);
1594
1595 if (i->thread_info.sync_prev) {
1596 i->thread_info.sync_prev->thread_info.sync_next = i->thread_info.sync_prev->sync_next;
1597 i->thread_info.sync_prev = NULL;
1598 }
1599
1600 if (i->thread_info.sync_next) {
1601 i->thread_info.sync_next->thread_info.sync_prev = i->thread_info.sync_next->sync_prev;
1602 i->thread_info.sync_next = NULL;
1603 }
1604
1605 if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
1606 pa_sink_input_unref(i);
1607
1608 pa_sink_invalidate_requested_latency(s);
1609 pa_sink_request_rewind(s, (size_t) -1);
1610
1611 /* In flat volume mode we need to update the volume as
1612 * well */
1613 return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
1614 }
1615
1616 case PA_SINK_MESSAGE_START_MOVE: {
1617 pa_sink_input *i = PA_SINK_INPUT(userdata);
1618
1619 /* We don't support moving synchronized streams. */
1620 pa_assert(!i->sync_prev);
1621 pa_assert(!i->sync_next);
1622 pa_assert(!i->thread_info.sync_next);
1623 pa_assert(!i->thread_info.sync_prev);
1624
1625 if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
1626 pa_usec_t usec = 0;
1627 size_t sink_nbytes, total_nbytes;
1628
1629 /* Get the latency of the sink */
1630 if (!(s->flags & PA_SINK_LATENCY) ||
1631 PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
1632 usec = 0;
1633
1634 sink_nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
1635 total_nbytes = sink_nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq);
1636
1637 if (total_nbytes > 0) {
1638 i->thread_info.rewrite_nbytes = i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, total_nbytes) : total_nbytes;
1639 i->thread_info.rewrite_flush = TRUE;
1640 pa_sink_input_process_rewind(i, sink_nbytes);
1641 }
1642 }
1643
1644 if (i->detach)
1645 i->detach(i);
1646
1647 pa_assert(i->thread_info.attached);
1648 i->thread_info.attached = FALSE;
1649
1650 /* Let's remove the sink input ...*/
1651 if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
1652 pa_sink_input_unref(i);
1653
1654 pa_sink_invalidate_requested_latency(s);
1655
1656 pa_log_debug("Requesting rewind due to started move");
1657 pa_sink_request_rewind(s, (size_t) -1);
1658
1659 /* In flat volume mode we need to update the volume as
1660 * well */
1661 return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
1662 }
1663
1664 case PA_SINK_MESSAGE_FINISH_MOVE: {
1665 pa_sink_input *i = PA_SINK_INPUT(userdata);
1666
1667 /* We don't support moving synchronized streams. */
1668 pa_assert(!i->sync_prev);
1669 pa_assert(!i->sync_next);
1670 pa_assert(!i->thread_info.sync_next);
1671 pa_assert(!i->thread_info.sync_prev);
1672
1673 pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
1674
1675 pa_assert(!i->thread_info.attached);
1676 i->thread_info.attached = TRUE;
1677
1678 if (i->attach)
1679 i->attach(i);
1680
1681 if (i->thread_info.requested_sink_latency != (pa_usec_t) -1)
1682 pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
1683
1684 pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
1685 pa_sink_input_update_max_request(i, s->thread_info.max_request);
1686
1687 if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
1688 pa_usec_t usec = 0;
1689 size_t nbytes;
1690
1691 /* Get the latency of the sink */
1692 if (!(s->flags & PA_SINK_LATENCY) ||
1693 PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
1694 usec = 0;
1695
1696 nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
1697
1698 if (nbytes > 0)
1699 pa_sink_input_drop(i, nbytes);
1700
1701 pa_log_debug("Requesting rewind due to finished move");
1702 pa_sink_request_rewind(s, nbytes);
1703 }
1704
1705 /* In flat volume mode we need to update the volume as
1706 * well */
1707 return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
1708 }
1709
1710 case PA_SINK_MESSAGE_SET_VOLUME:
1711
1712 if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
1713 s->thread_info.soft_volume = s->soft_volume;
1714 pa_sink_request_rewind(s, (size_t) -1);
1715 }
1716
1717 if (!(s->flags & PA_SINK_FLAT_VOLUME))
1718 return 0;
1719
1720 /* Fall through ... */
1721
1722 case PA_SINK_MESSAGE_SYNC_VOLUMES:
1723 sync_input_volumes_within_thread(s);
1724 return 0;
1725
1726 case PA_SINK_MESSAGE_GET_VOLUME:
1727 return 0;
1728
1729 case PA_SINK_MESSAGE_SET_MUTE:
1730
1731 if (s->thread_info.soft_muted != s->muted) {
1732 s->thread_info.soft_muted = s->muted;
1733 pa_sink_request_rewind(s, (size_t) -1);
1734 }
1735
1736 return 0;
1737
1738 case PA_SINK_MESSAGE_GET_MUTE:
1739 return 0;
1740
1741 case PA_SINK_MESSAGE_SET_STATE: {
1742
1743 pa_bool_t suspend_change =
1744 (s->thread_info.state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
1745 (PA_SINK_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SINK_SUSPENDED);
1746
1747 s->thread_info.state = PA_PTR_TO_UINT(userdata);
1748
1749 if (s->thread_info.state == PA_SINK_SUSPENDED) {
1750 s->thread_info.rewind_nbytes = 0;
1751 s->thread_info.rewind_requested = FALSE;
1752 }
1753
1754 if (suspend_change) {
1755 pa_sink_input *i;
1756 void *state = NULL;
1757
1758 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1759 if (i->suspend_within_thread)
1760 i->suspend_within_thread(i, s->thread_info.state == PA_SINK_SUSPENDED);
1761 }
1762
1763 return 0;
1764 }
1765
1766 case PA_SINK_MESSAGE_DETACH:
1767
1768 /* Detach all streams */
1769 pa_sink_detach_within_thread(s);
1770 return 0;
1771
1772 case PA_SINK_MESSAGE_ATTACH:
1773
1774 /* Reattach all streams */
1775 pa_sink_attach_within_thread(s);
1776 return 0;
1777
1778 case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY: {
1779
1780 pa_usec_t *usec = userdata;
1781 *usec = pa_sink_get_requested_latency_within_thread(s);
1782
1783 if (*usec == (pa_usec_t) -1)
1784 *usec = s->thread_info.max_latency;
1785
1786 return 0;
1787 }
1788
1789 case PA_SINK_MESSAGE_SET_LATENCY_RANGE: {
1790 pa_usec_t *r = userdata;
1791
1792 pa_sink_set_latency_range_within_thread(s, r[0], r[1]);
1793
1794 return 0;
1795 }
1796
1797 case PA_SINK_MESSAGE_GET_LATENCY_RANGE: {
1798 pa_usec_t *r = userdata;
1799
1800 r[0] = s->thread_info.min_latency;
1801 r[1] = s->thread_info.max_latency;
1802
1803 return 0;
1804 }
1805
1806 case PA_SINK_MESSAGE_GET_MAX_REWIND:
1807
1808 *((size_t*) userdata) = s->thread_info.max_rewind;
1809 return 0;
1810
1811 case PA_SINK_MESSAGE_GET_MAX_REQUEST:
1812
1813 *((size_t*) userdata) = s->thread_info.max_request;
1814 return 0;
1815
1816 case PA_SINK_MESSAGE_SET_MAX_REWIND:
1817
1818 pa_sink_set_max_rewind_within_thread(s, (size_t) offset);
1819 return 0;
1820
1821 case PA_SINK_MESSAGE_SET_MAX_REQUEST:
1822
1823 pa_sink_set_max_request_within_thread(s, (size_t) offset);
1824 return 0;
1825
1826 case PA_SINK_MESSAGE_GET_LATENCY:
1827 case PA_SINK_MESSAGE_MAX:
1828 ;
1829 }
1830
1831 return -1;
1832 }
1833
1834 /* Called from main thread */
1835 int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause) {
1836 pa_sink *sink;
1837 uint32_t idx;
1838 int ret = 0;
1839
1840 pa_core_assert_ref(c);
1841 pa_assert(cause != 0);
1842
1843 for (sink = PA_SINK(pa_idxset_first(c->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(c->sinks, &idx))) {
1844 int r;
1845
1846 if ((r = pa_sink_suspend(sink, suspend, cause)) < 0)
1847 ret = r;
1848 }
1849
1850 return ret;
1851 }
1852
1853 /* Called from main thread */
1854 void pa_sink_detach(pa_sink *s) {
1855 pa_sink_assert_ref(s);
1856 pa_assert(PA_SINK_IS_LINKED(s->state));
1857
1858 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL) == 0);
1859 }
1860
1861 /* Called from main thread */
1862 void pa_sink_attach(pa_sink *s) {
1863 pa_sink_assert_ref(s);
1864 pa_assert(PA_SINK_IS_LINKED(s->state));
1865
1866 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
1867 }
1868
1869 /* Called from IO thread */
1870 void pa_sink_detach_within_thread(pa_sink *s) {
1871 pa_sink_input *i;
1872 void *state = NULL;
1873
1874 pa_sink_assert_ref(s);
1875 pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1876
1877 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1878 if (i->detach)
1879 i->detach(i);
1880
1881 if (s->monitor_source)
1882 pa_source_detach_within_thread(s->monitor_source);
1883 }
1884
1885 /* Called from IO thread */
1886 void pa_sink_attach_within_thread(pa_sink *s) {
1887 pa_sink_input *i;
1888 void *state = NULL;
1889
1890 pa_sink_assert_ref(s);
1891 pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1892
1893 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1894 if (i->attach)
1895 i->attach(i);
1896
1897 if (s->monitor_source)
1898 pa_source_attach_within_thread(s->monitor_source);
1899 }
1900
1901 /* Called from IO thread */
1902 void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
1903 pa_sink_assert_ref(s);
1904 pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1905
1906 if (s->thread_info.state == PA_SINK_SUSPENDED)
1907 return;
1908
1909 if (nbytes == (size_t) -1)
1910 nbytes = s->thread_info.max_rewind;
1911
1912 nbytes = PA_MIN(nbytes, s->thread_info.max_rewind);
1913
1914 if (s->thread_info.rewind_requested &&
1915 nbytes <= s->thread_info.rewind_nbytes)
1916 return;
1917
1918 s->thread_info.rewind_nbytes = nbytes;
1919 s->thread_info.rewind_requested = TRUE;
1920
1921 if (s->request_rewind)
1922 s->request_rewind(s);
1923 }
1924
1925 /* Called from IO thread */
1926 pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
1927 pa_usec_t result = (pa_usec_t) -1;
1928 pa_sink_input *i;
1929 void *state = NULL;
1930 pa_usec_t monitor_latency;
1931
1932 pa_sink_assert_ref(s);
1933
1934 if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
1935 return PA_CLAMP(s->fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
1936
1937 if (s->thread_info.requested_latency_valid)
1938 return s->thread_info.requested_latency;
1939
1940 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1941
1942 if (i->thread_info.requested_sink_latency != (pa_usec_t) -1 &&
1943 (result == (pa_usec_t) -1 || result > i->thread_info.requested_sink_latency))
1944 result = i->thread_info.requested_sink_latency;
1945
1946 monitor_latency = pa_source_get_requested_latency_within_thread(s->monitor_source);
1947
1948 if (monitor_latency != (pa_usec_t) -1 &&
1949 (result == (pa_usec_t) -1 || result > monitor_latency))
1950 result = monitor_latency;
1951
1952 if (result != (pa_usec_t) -1)
1953 result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
1954
1955 if (PA_SINK_IS_LINKED(s->thread_info.state)) {
1956 /* Only cache if properly initialized */
1957 s->thread_info.requested_latency = result;
1958 s->thread_info.requested_latency_valid = TRUE;
1959 }
1960
1961 return result;
1962 }
1963
1964 /* Called from main thread */
1965 pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
1966 pa_usec_t usec = 0;
1967
1968 pa_sink_assert_ref(s);
1969 pa_assert(PA_SINK_IS_LINKED(s->state));
1970
1971 if (s->state == PA_SINK_SUSPENDED)
1972 return 0;
1973
1974 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
1975 return usec;
1976 }
1977
1978 /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
1979 void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind) {
1980 pa_sink_input *i;
1981 void *state = NULL;
1982
1983 pa_sink_assert_ref(s);
1984
1985 if (max_rewind == s->thread_info.max_rewind)
1986 return;
1987
1988 s->thread_info.max_rewind = max_rewind;
1989
1990 if (PA_SINK_IS_LINKED(s->thread_info.state)) {
1991 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1992 pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
1993 }
1994
1995 if (s->monitor_source)
1996 pa_source_set_max_rewind_within_thread(s->monitor_source, s->thread_info.max_rewind);
1997 }
1998
1999 /* Called from main thread */
2000 void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
2001 pa_sink_assert_ref(s);
2002
2003 if (PA_SINK_IS_LINKED(s->state))
2004 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REWIND, NULL, max_rewind, NULL) == 0);
2005 else
2006 pa_sink_set_max_rewind_within_thread(s, max_rewind);
2007 }
2008
2009 /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
2010 void pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request) {
2011 void *state = NULL;
2012
2013 pa_sink_assert_ref(s);
2014
2015 if (max_request == s->thread_info.max_request)
2016 return;
2017
2018 s->thread_info.max_request = max_request;
2019
2020 if (PA_SINK_IS_LINKED(s->thread_info.state)) {
2021 pa_sink_input *i;
2022
2023 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
2024 pa_sink_input_update_max_request(i, s->thread_info.max_request);
2025 }
2026 }
2027
2028 /* Called from main thread */
2029 void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
2030 pa_sink_assert_ref(s);
2031
2032 if (PA_SINK_IS_LINKED(s->state))
2033 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REQUEST, NULL, max_request, NULL) == 0);
2034 else
2035 pa_sink_set_max_request_within_thread(s, max_request);
2036 }
2037
2038 /* Called from IO thread */
2039 void pa_sink_invalidate_requested_latency(pa_sink *s) {
2040 pa_sink_input *i;
2041 void *state = NULL;
2042
2043 pa_sink_assert_ref(s);
2044
2045 if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
2046 return;
2047
2048 s->thread_info.requested_latency_valid = FALSE;
2049
2050 if (PA_SINK_IS_LINKED(s->thread_info.state)) {
2051
2052 if (s->update_requested_latency)
2053 s->update_requested_latency(s);
2054
2055 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
2056 if (i->update_sink_requested_latency)
2057 i->update_sink_requested_latency(i);
2058 }
2059 }
2060
2061 /* Called from main thread */
2062 void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
2063 pa_sink_assert_ref(s);
2064
2065 /* min_latency == 0: no limit
2066 * min_latency anything else: specified limit
2067 *
2068 * Similar for max_latency */
2069
2070 if (min_latency < ABSOLUTE_MIN_LATENCY)
2071 min_latency = ABSOLUTE_MIN_LATENCY;
2072
2073 if (max_latency <= 0 ||
2074 max_latency > ABSOLUTE_MAX_LATENCY)
2075 max_latency = ABSOLUTE_MAX_LATENCY;
2076
2077 pa_assert(min_latency <= max_latency);
2078
2079 /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
2080 pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
2081 max_latency == ABSOLUTE_MAX_LATENCY) ||
2082 (s->flags & PA_SINK_DYNAMIC_LATENCY));
2083
2084 if (PA_SINK_IS_LINKED(s->state)) {
2085 pa_usec_t r[2];
2086
2087 r[0] = min_latency;
2088 r[1] = max_latency;
2089
2090 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
2091 } else
2092 pa_sink_set_latency_range_within_thread(s, min_latency, max_latency);
2093 }
2094
2095 /* Called from main thread */
2096 void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
2097 pa_sink_assert_ref(s);
2098 pa_assert(min_latency);
2099 pa_assert(max_latency);
2100
2101 if (PA_SINK_IS_LINKED(s->state)) {
2102 pa_usec_t r[2] = { 0, 0 };
2103
2104 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
2105
2106 *min_latency = r[0];
2107 *max_latency = r[1];
2108 } else {
2109 *min_latency = s->thread_info.min_latency;
2110 *max_latency = s->thread_info.max_latency;
2111 }
2112 }
2113
2114 /* Called from IO thread */
2115 void pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
2116 void *state = NULL;
2117
2118 pa_sink_assert_ref(s);
2119
2120 pa_assert(min_latency >= ABSOLUTE_MIN_LATENCY);
2121 pa_assert(max_latency <= ABSOLUTE_MAX_LATENCY);
2122 pa_assert(min_latency <= max_latency);
2123
2124 /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
2125 pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
2126 max_latency == ABSOLUTE_MAX_LATENCY) ||
2127 (s->flags & PA_SINK_DYNAMIC_LATENCY));
2128
2129 s->thread_info.min_latency = min_latency;
2130 s->thread_info.max_latency = max_latency;
2131
2132 if (PA_SINK_IS_LINKED(s->thread_info.state)) {
2133 pa_sink_input *i;
2134
2135 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
2136 if (i->update_sink_latency_range)
2137 i->update_sink_latency_range(i);
2138 }
2139
2140 pa_sink_invalidate_requested_latency(s);
2141
2142 pa_source_set_latency_range_within_thread(s->monitor_source, min_latency, max_latency);
2143 }
2144
2145 /* Called from main thread, before the sink is put */
2146 void pa_sink_set_fixed_latency(pa_sink *s, pa_usec_t latency) {
2147 pa_sink_assert_ref(s);
2148
2149 pa_assert(pa_sink_get_state(s) == PA_SINK_INIT);
2150
2151 if (latency < ABSOLUTE_MIN_LATENCY)
2152 latency = ABSOLUTE_MIN_LATENCY;
2153
2154 if (latency > ABSOLUTE_MAX_LATENCY)
2155 latency = ABSOLUTE_MAX_LATENCY;
2156
2157 s->fixed_latency = latency;
2158 pa_source_set_fixed_latency(s->monitor_source, latency);
2159 }
2160
2161 /* Called from main context */
2162 size_t pa_sink_get_max_rewind(pa_sink *s) {
2163 size_t r;
2164 pa_sink_assert_ref(s);
2165
2166 if (!PA_SINK_IS_LINKED(s->state))
2167 return s->thread_info.max_rewind;
2168
2169 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
2170
2171 return r;
2172 }
2173
2174 /* Called from main context */
2175 size_t pa_sink_get_max_request(pa_sink *s) {
2176 size_t r;
2177 pa_sink_assert_ref(s);
2178
2179 if (!PA_SINK_IS_LINKED(s->state))
2180 return s->thread_info.max_request;
2181
2182 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REQUEST, &r, 0, NULL) == 0);
2183
2184 return r;
2185 }
2186
2187 /* Called from main context */
2188 pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) {
2189 const char *ff, *c, *t = NULL, *s = "", *profile, *bus;
2190
2191 pa_assert(p);
2192
2193 if (pa_proplist_contains(p, PA_PROP_DEVICE_ICON_NAME))
2194 return TRUE;
2195
2196 if ((ff = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR))) {
2197
2198 if (pa_streq(ff, "microphone"))
2199 t = "audio-input-microphone";
2200 else if (pa_streq(ff, "webcam"))
2201 t = "camera-web";
2202 else if (pa_streq(ff, "computer"))
2203 t = "computer";
2204 else if (pa_streq(ff, "handset"))
2205 t = "phone";
2206 else if (pa_streq(ff, "portable"))
2207 t = "multimedia-player";
2208 else if (pa_streq(ff, "tv"))
2209 t = "video-display";
2210
2211 /*
2212 * The following icons are not part of the icon naming spec,
2213 * because Rodney Dawes sucks as the maintainer of that spec.
2214 *
2215 * http://lists.freedesktop.org/archives/xdg/2009-May/010397.html
2216 */
2217 else if (pa_streq(ff, "headset"))
2218 t = "audio-headset";
2219 else if (pa_streq(ff, "headphone"))
2220 t = "audio-headphones";
2221 else if (pa_streq(ff, "speaker"))
2222 t = "audio-speakers";
2223 else if (pa_streq(ff, "hands-free"))
2224 t = "audio-handsfree";
2225 }
2226
2227 if (!t)
2228 if ((c = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
2229 if (pa_streq(c, "modem"))
2230 t = "modem";
2231
2232 if (!t) {
2233 if (is_sink)
2234 t = "audio-card";
2235 else
2236 t = "audio-input-microphone";
2237 }
2238
2239 if ((profile = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
2240 if (strstr(profile, "analog"))
2241 s = "-analog";
2242 else if (strstr(profile, "iec958"))
2243 s = "-iec958";
2244 else if (strstr(profile, "hdmi"))
2245 s = "-hdmi";
2246 }
2247
2248 bus = pa_proplist_gets(p, PA_PROP_DEVICE_BUS);
2249
2250 pa_proplist_setf(p, PA_PROP_DEVICE_ICON_NAME, "%s%s%s%s", t, pa_strempty(s), bus ? "-" : "", pa_strempty(bus));
2251
2252 return TRUE;
2253 }
2254
2255 pa_bool_t pa_device_init_description(pa_proplist *p) {
2256 const char *s;
2257 pa_assert(p);
2258
2259 if (pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
2260 return TRUE;
2261
2262 if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
2263 if (pa_streq(s, "internal")) {
2264 pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, _("Internal Audio"));
2265 return TRUE;
2266 }
2267
2268 if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
2269 if (pa_streq(s, "modem")) {
2270 pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, _("Modem"));
2271 return TRUE;
2272 }
2273
2274 if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_PRODUCT_NAME))) {
2275 pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, s);
2276 return TRUE;
2277 }
2278
2279 return FALSE;
2280 }