• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, see <http://www.gnu.org/licenses/>.
19 ***/
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <signal.h>
28 
29 #include <pulse/rtclock.h>
30 #include <pulse/timeval.h>
31 #include <pulse/xmalloc.h>
32 
33 #include <pulsecore/module.h>
34 #include <pulsecore/core-rtclock.h>
35 #include <pulsecore/core-util.h>
36 #include <pulsecore/message-handler.h>
37 #include <pulsecore/core-scache.h>
38 #include <pulsecore/core-subscribe.h>
39 #include <pulsecore/random.h>
40 #include <pulsecore/log.h>
41 #include <pulsecore/macro.h>
42 #include <pulsecore/strbuf.h>
43 
44 #include "log/audio_log.h"
45 
46 #include "core.h"
47 
48 PA_DEFINE_PUBLIC_CLASS(pa_core, pa_msgobject);
49 
core_process_msg(pa_msgobject * o,int code,void * userdata,int64_t offset,pa_memchunk * chunk)50 static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
51     pa_core *c = PA_CORE(o);
52 
53     pa_core_assert_ref(c);
54 
55     switch (code) {
56 
57         case PA_CORE_MESSAGE_UNLOAD_MODULE:
58             pa_module_unload(userdata, true);
59             return 0;
60 
61         default:
62             return -1;
63     }
64 }
65 
66 static void core_free(pa_object *o);
67 
68 /* Returns a list of handlers. */
message_handler_list(pa_core * c)69 static char *message_handler_list(pa_core *c) {
70     pa_json_encoder *encoder;
71     void *state = NULL;
72     struct pa_message_handler *handler;
73 
74     encoder = pa_json_encoder_new();
75 
76     pa_json_encoder_begin_element_array(encoder);
77     PA_HASHMAP_FOREACH(handler, c->message_handlers, state) {
78         pa_json_encoder_begin_element_object(encoder);
79 
80         pa_json_encoder_add_member_string(encoder, "name", handler->object_path);
81         pa_json_encoder_add_member_string(encoder, "description", handler->description);
82 
83         pa_json_encoder_end_object(encoder);
84     }
85     pa_json_encoder_end_array(encoder);
86 
87     return pa_json_encoder_to_string_free(encoder);
88 }
89 
core_message_handler(const char * object_path,const char * message,const pa_json_object * parameters,char ** response,void * userdata)90 static int core_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata) {
91     pa_core *c = userdata;
92 
93     pa_assert(c);
94     pa_assert(message);
95     pa_assert(response);
96     pa_assert(pa_safe_streq(object_path, "/core"));
97 
98     if (pa_streq(message, "list-handlers")) {
99         *response = message_handler_list(c);
100         return PA_OK;
101     }
102 
103     return -PA_ERR_NOTIMPLEMENTED;
104 }
105 
pa_core_new(pa_mainloop_api * m,bool shared,bool enable_memfd,size_t shm_size)106 pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t shm_size) {
107     AUDIO_INFO_LOG("start pa_core_new, shared: %{public}d, enable_memfd: %{public}d", shared, enable_memfd);
108     pa_core* c;
109     pa_mempool *pool;
110     pa_mem_type_t type;
111     int j;
112 
113     pa_assert(m);
114 
115     if (shared) {
116         type = (enable_memfd) ? PA_MEM_TYPE_SHARED_MEMFD : PA_MEM_TYPE_SHARED_POSIX;
117         if (!(pool = pa_mempool_new(type, shm_size, false))) {
118             AUDIO_WARNING_LOG("Failed to allocate %{public}s memory pool. Falling back to a normal memory pool.",
119                         pa_mem_type_to_string(type));
120             shared = false;
121         }
122     }
123 
124     if (!shared) {
125         if (!(pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, shm_size, false))) {
126             AUDIO_ERR_LOG("pa_mempool_new() failed.");
127             return NULL;
128         }
129     }
130 
131     c = pa_msgobject_new(pa_core);
132     c->parent.parent.free = core_free;
133     c->parent.process_msg = core_process_msg;
134 
135     c->state = PA_CORE_STARTUP;
136     c->mainloop = m;
137 
138     c->clients = pa_idxset_new(NULL, NULL);
139     c->cards = pa_idxset_new(NULL, NULL);
140     c->sinks = pa_idxset_new(NULL, NULL);
141     c->sources = pa_idxset_new(NULL, NULL);
142     c->sink_inputs = pa_idxset_new(NULL, NULL);
143     c->source_outputs = pa_idxset_new(NULL, NULL);
144     c->modules = pa_idxset_new(NULL, NULL);
145     c->scache = pa_idxset_new(NULL, NULL);
146 
147     c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
148     c->shared = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
149     c->message_handlers = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
150 
151     pa_message_handler_register(c, "/core", "Core message handler", core_message_handler, (void *) c);
152 
153     c->default_source = NULL;
154     c->default_sink = NULL;
155 
156     c->default_sample_spec.format = PA_SAMPLE_S16NE;
157     c->default_sample_spec.rate = 44100;
158     c->default_sample_spec.channels = 2;
159     pa_channel_map_init_extend(&c->default_channel_map, c->default_sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
160     c->default_n_fragments = 4;
161     c->default_fragment_size_msec = 25;
162 
163     c->deferred_volume_safety_margin_usec = 8000;
164     c->deferred_volume_extra_delay_usec = 0;
165 
166     c->module_defer_unload_event = NULL;
167     c->modules_pending_unload = pa_hashmap_new(NULL, NULL);
168 
169     c->subscription_defer_event = NULL;
170     PA_LLIST_HEAD_INIT(pa_subscription, c->subscriptions);
171     PA_LLIST_HEAD_INIT(pa_subscription_event, c->subscription_event_queue);
172     c->subscription_event_last = NULL;
173 
174     c->mempool = pool;
175     c->shm_size = shm_size;
176     pa_silence_cache_init(&c->silence_cache);
177 
178     c->exit_event = NULL;
179     c->scache_auto_unload_event = NULL;
180 
181     c->exit_idle_time = -1;
182     c->scache_idle_time = 20;
183 
184     c->flat_volumes = true;
185     c->disallow_module_loading = false;
186     c->disallow_exit = false;
187     c->running_as_daemon = false;
188     c->realtime_scheduling = false;
189     c->realtime_priority = 5;
190     c->disable_remixing = false;
191     c->remixing_use_all_sink_channels = true;
192     c->remixing_produce_lfe = false;
193     c->remixing_consume_lfe = false;
194     c->lfe_crossover_freq = 0;
195     c->deferred_volume = true;
196     c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 1;
197 
198     for (j = 0; j < PA_CORE_HOOK_MAX; j++)
199         pa_hook_init(&c->hooks[j], c);
200 
201     pa_random(&c->cookie, sizeof(c->cookie));
202 
203 #ifdef SIGPIPE
204     pa_check_signal_is_blocked(SIGPIPE);
205 #endif
206 
207     return c;
208 }
209 
core_free(pa_object * o)210 static void core_free(pa_object *o) {
211     pa_core *c = PA_CORE(o);
212     int j;
213     pa_assert(c);
214 
215     c->state = PA_CORE_SHUTDOWN;
216 
217     /* Note: All modules and samples in the cache should be unloaded before
218      * we get here */
219 
220     pa_assert(pa_idxset_isempty(c->scache));
221     pa_idxset_free(c->scache, NULL);
222 
223     pa_assert(pa_idxset_isempty(c->modules));
224     pa_idxset_free(c->modules, NULL);
225 
226     pa_assert(pa_idxset_isempty(c->clients));
227     pa_idxset_free(c->clients, NULL);
228 
229     pa_assert(pa_idxset_isempty(c->cards));
230     pa_idxset_free(c->cards, NULL);
231 
232     pa_assert(pa_idxset_isempty(c->sinks));
233     pa_idxset_free(c->sinks, NULL);
234 
235     pa_assert(pa_idxset_isempty(c->sources));
236     pa_idxset_free(c->sources, NULL);
237 
238     pa_assert(pa_idxset_isempty(c->source_outputs));
239     pa_idxset_free(c->source_outputs, NULL);
240 
241     pa_assert(pa_idxset_isempty(c->sink_inputs));
242     pa_idxset_free(c->sink_inputs, NULL);
243 
244     pa_assert(pa_hashmap_isempty(c->namereg));
245     pa_hashmap_free(c->namereg);
246 
247     pa_assert(pa_hashmap_isempty(c->shared));
248     pa_hashmap_free(c->shared);
249 
250     pa_message_handler_unregister(c, "/core");
251 
252     pa_assert(pa_hashmap_isempty(c->message_handlers));
253     pa_hashmap_free(c->message_handlers);
254 
255     pa_assert(pa_hashmap_isempty(c->modules_pending_unload));
256     pa_hashmap_free(c->modules_pending_unload);
257 
258     pa_subscription_free_all(c);
259 
260     if (c->exit_event)
261         c->mainloop->time_free(c->exit_event);
262 
263     pa_assert(!c->default_source);
264     pa_assert(!c->default_sink);
265     pa_xfree(c->configured_default_source);
266     pa_xfree(c->configured_default_sink);
267 
268     pa_silence_cache_done(&c->silence_cache);
269     pa_mempool_unref(c->mempool);
270 
271     for (j = 0; j < PA_CORE_HOOK_MAX; j++)
272         pa_hook_done(&c->hooks[j]);
273 
274     pa_xfree(c);
275 }
276 
pa_core_set_configured_default_sink(pa_core * core,const char * sink)277 void pa_core_set_configured_default_sink(pa_core *core, const char *sink) {
278     char *old_sink;
279 
280     pa_assert(core);
281 
282     old_sink = pa_xstrdup(core->configured_default_sink);
283 
284     if (pa_safe_streq(sink, old_sink))
285         goto finish;
286 
287     pa_xfree(core->configured_default_sink);
288     core->configured_default_sink = pa_xstrdup(sink);
289     AUDIO_INFO_LOG("configured_default_sink: %{public}s -> %{public}s",
290                 old_sink ? old_sink : "(unset)", sink ? sink : "(unset)");
291     pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
292 
293     pa_core_update_default_sink(core);
294 
295 finish:
296     pa_xfree(old_sink);
297 }
298 
pa_core_set_configured_default_source(pa_core * core,const char * source)299 void pa_core_set_configured_default_source(pa_core *core, const char *source) {
300     char *old_source;
301 
302     pa_assert(core);
303 
304     old_source = pa_xstrdup(core->configured_default_source);
305 
306     if (pa_safe_streq(source, old_source))
307         goto finish;
308 
309     pa_xfree(core->configured_default_source);
310     core->configured_default_source = pa_xstrdup(source);
311     AUDIO_INFO_LOG("configured_default_source: %{public}s -> %{public}s",
312                 old_source ? old_source : "(unset)", source ? source : "(unset)");
313     pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
314 
315     pa_core_update_default_source(core);
316 
317 finish:
318     pa_xfree(old_source);
319 }
320 
321 /* a  < b  ->  return -1
322  * a == b  ->  return  0
323  * a  > b  ->  return  1 */
compare_sinks(pa_sink * a,pa_sink * b)324 static int compare_sinks(pa_sink *a, pa_sink *b) {
325     pa_core *core;
326 
327     core = a->core;
328 
329     /* Available sinks always beat unavailable sinks. */
330     if (a->active_port && a->active_port->available == PA_AVAILABLE_NO
331             && (!b->active_port || b->active_port->available != PA_AVAILABLE_NO))
332         return -1;
333     if (b->active_port && b->active_port->available == PA_AVAILABLE_NO
334             && (!a->active_port || a->active_port->available != PA_AVAILABLE_NO))
335         return 1;
336 
337     /* The configured default sink is preferred over any other sink. */
338     if (pa_safe_streq(b->name, core->configured_default_sink))
339         return -1;
340     if (pa_safe_streq(a->name, core->configured_default_sink))
341         return 1;
342 
343     if (a->priority < b->priority)
344         return -1;
345     if (a->priority > b->priority)
346         return 1;
347 
348     /* It's hard to find any difference between these sinks, but maybe one of
349      * them is already the default sink? If so, it's best to keep it as the
350      * default to avoid changing the routing for no good reason. */
351     if (b == core->default_sink)
352         return -1;
353     if (a == core->default_sink)
354         return 1;
355 
356     return 0;
357 }
358 
pa_core_update_default_sink(pa_core * core)359 void pa_core_update_default_sink(pa_core *core) {
360     pa_sink *best = NULL;
361     pa_sink *sink;
362     uint32_t idx;
363     pa_sink *old_default_sink;
364 
365     pa_assert(core);
366 
367     PA_IDXSET_FOREACH(sink, core->sinks, idx) {
368         if (!PA_SINK_IS_LINKED(sink->state))
369             continue;
370 
371         if (!best) {
372             best = sink;
373             continue;
374         }
375 
376         if (compare_sinks(sink, best) > 0)
377             best = sink;
378     }
379 
380     old_default_sink = core->default_sink;
381 
382     if (best == old_default_sink)
383         return;
384 
385     core->default_sink = best;
386     AUDIO_INFO_LOG("default_sink: %{public}s -> %{public}s",
387                 old_default_sink ? old_default_sink->name : "(unset)", best ? best->name : "(unset)");
388 
389     /* If the default sink changed, it may be that the default source has to be
390      * changed too, because monitor sources are prioritized partly based on the
391      * priorities of the monitored sinks. */
392     pa_core_update_default_source(core);
393 
394     pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
395     pa_hook_fire(&core->hooks[PA_CORE_HOOK_DEFAULT_SINK_CHANGED], core->default_sink);
396 
397     /* try to move the streams from old_default_sink to the new default_sink conditionally */
398     if (old_default_sink)
399         pa_sink_move_streams_to_default_sink(core, old_default_sink, true);
400 }
401 
402 /* a  < b  ->  return -1
403  * a == b  ->  return  0
404  * a  > b  ->  return  1 */
compare_sources(pa_source * a,pa_source * b)405 static int compare_sources(pa_source *a, pa_source *b) {
406     pa_core *core;
407 
408     core = a->core;
409 
410     /* Available sources always beat unavailable sources. */
411     if (a->active_port && a->active_port->available == PA_AVAILABLE_NO
412             && (!b->active_port || b->active_port->available != PA_AVAILABLE_NO))
413         return -1;
414     if (b->active_port && b->active_port->available == PA_AVAILABLE_NO
415             && (!a->active_port || a->active_port->available != PA_AVAILABLE_NO))
416         return 1;
417 
418     /* The configured default source is preferred over any other source. */
419     if (pa_safe_streq(b->name, core->configured_default_source))
420         return -1;
421     if (pa_safe_streq(a->name, core->configured_default_source))
422         return 1;
423 
424     /* Monitor sources lose to non-monitor sources. */
425     if (a->monitor_of && !b->monitor_of)
426         return -1;
427     if (!a->monitor_of && b->monitor_of)
428         return 1;
429 
430     if (a->priority < b->priority)
431         return -1;
432     if (a->priority > b->priority)
433         return 1;
434 
435     /* If the sources are monitors, we can compare the monitored sinks. */
436     if (a->monitor_of)
437         return compare_sinks(a->monitor_of, b->monitor_of);
438 
439     /* It's hard to find any difference between these sources, but maybe one of
440      * them is already the default source? If so, it's best to keep it as the
441      * default to avoid changing the routing for no good reason. */
442     if (b == core->default_source)
443         return -1;
444     if (a == core->default_source)
445         return 1;
446 
447     return 0;
448 }
449 
pa_core_update_default_source(pa_core * core)450 void pa_core_update_default_source(pa_core *core) {
451     pa_source *best = NULL;
452     pa_source *source;
453     uint32_t idx;
454     pa_source *old_default_source;
455 
456     pa_assert(core);
457 
458     PA_IDXSET_FOREACH(source, core->sources, idx) {
459         if (!PA_SOURCE_IS_LINKED(source->state))
460             continue;
461 
462         if (!best) {
463             best = source;
464             continue;
465         }
466 
467         if (compare_sources(source, best) > 0)
468             best = source;
469     }
470 
471     old_default_source = core->default_source;
472 
473     if (best == old_default_source)
474         return;
475 
476     core->default_source = best;
477     AUDIO_INFO_LOG("default_source: %{public}s -> %{public}s",
478                 old_default_source ? old_default_source->name : "(unset)", best ? best->name : "(unset)");
479     pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
480     pa_hook_fire(&core->hooks[PA_CORE_HOOK_DEFAULT_SOURCE_CHANGED], core->default_source);
481 
482     /* try to move the streams from old_default_source to the new default_source conditionally */
483     if (old_default_source)
484 	pa_source_move_streams_to_default_source(core, old_default_source, true);
485 }
486 
pa_core_set_exit_idle_time(pa_core * core,int time)487 void pa_core_set_exit_idle_time(pa_core *core, int time) {
488     pa_assert(core);
489 
490     if (time == core->exit_idle_time)
491         return;
492 
493     AUDIO_INFO_LOG("exit_idle_time: %{public}i -> %{public}i", core->exit_idle_time, time);
494     core->exit_idle_time = time;
495 }
496 
exit_callback(pa_mainloop_api * m,pa_time_event * e,const struct timeval * t,void * userdata)497 static void exit_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
498     pa_core *c = userdata;
499     pa_assert(c->exit_event == e);
500 
501     AUDIO_INFO_LOG("We are idle, quitting...");
502     pa_core_exit(c, true, 0);
503 }
504 
pa_core_check_idle(pa_core * c)505 void pa_core_check_idle(pa_core *c) {
506     pa_assert(c);
507 
508     if (!c->exit_event &&
509         c->exit_idle_time >= 0 &&
510         pa_idxset_size(c->clients) == 0) {
511 
512         c->exit_event = pa_core_rttime_new(c, pa_rtclock_now() + c->exit_idle_time * PA_USEC_PER_SEC, exit_callback, c);
513 
514     } else if (c->exit_event && pa_idxset_size(c->clients) > 0) {
515         c->mainloop->time_free(c->exit_event);
516         c->exit_event = NULL;
517     }
518 }
519 
pa_core_exit(pa_core * c,bool force,int retval)520 int pa_core_exit(pa_core *c, bool force, int retval) {
521     pa_assert(c);
522 
523     if (c->disallow_exit && !force)
524         return -1;
525 
526     c->mainloop->quit(c->mainloop, retval);
527     return 0;
528 }
529 
pa_core_maybe_vacuum(pa_core * c)530 void pa_core_maybe_vacuum(pa_core *c) {
531     pa_assert(c);
532 
533     if (pa_idxset_isempty(c->sink_inputs) && pa_idxset_isempty(c->source_outputs)) {
534         AUDIO_DEBUG_LOG("Hmm, no streams around, trying to vacuum.");
535     } else {
536         pa_sink *si;
537         pa_source *so;
538         uint32_t idx;
539 
540         idx = 0;
541         PA_IDXSET_FOREACH(si, c->sinks, idx)
542             if (si->state != PA_SINK_SUSPENDED)
543                 return;
544 
545         idx = 0;
546         PA_IDXSET_FOREACH(so, c->sources, idx)
547             if (so->state != PA_SOURCE_SUSPENDED)
548                 return;
549 
550         AUDIO_INFO_LOG("All sinks and sources are suspended, vacuuming memory");
551     }
552 
553     pa_mempool_vacuum(c->mempool);
554 }
555 
pa_core_rttime_new(pa_core * c,pa_usec_t usec,pa_time_event_cb_t cb,void * userdata)556 pa_time_event* pa_core_rttime_new(pa_core *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) {
557     struct timeval tv;
558 
559     pa_assert(c);
560     pa_assert(c->mainloop);
561 
562     return c->mainloop->time_new(c->mainloop, pa_timeval_rtstore(&tv, usec, true), cb, userdata);
563 }
564 
pa_core_rttime_restart(pa_core * c,pa_time_event * e,pa_usec_t usec)565 void pa_core_rttime_restart(pa_core *c, pa_time_event *e, pa_usec_t usec) {
566     struct timeval tv;
567 
568     pa_assert(c);
569     pa_assert(c->mainloop);
570 
571     c->mainloop->time_restart(e, pa_timeval_rtstore(&tv, usec, true));
572 }
573 
pa_core_move_streams_to_newly_available_preferred_sink(pa_core * c,pa_sink * s)574 void pa_core_move_streams_to_newly_available_preferred_sink(pa_core *c, pa_sink *s) {
575     pa_sink_input *si;
576     uint32_t idx;
577 
578     pa_assert(c);
579     pa_assert(s);
580 
581     PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
582         if (si->sink == s)
583             continue;
584 
585         if (!si->sink)
586             continue;
587 
588         /* Skip this sink input if it is connecting a filter sink to
589          * the master */
590         if (si->origin_sink)
591             continue;
592 
593         /* It might happen that a stream and a sink are set up at the
594            same time, in which case we want to make sure we don't
595            interfere with that */
596         if (!PA_SINK_INPUT_IS_LINKED(si->state))
597             continue;
598 
599         if (pa_safe_streq(si->preferred_sink, s->name))
600             pa_sink_input_move_to(si, s, false);
601     }
602 
603 }
604 
pa_core_move_streams_to_newly_available_preferred_source(pa_core * c,pa_source * s)605 void pa_core_move_streams_to_newly_available_preferred_source(pa_core *c, pa_source *s) {
606     pa_source_output *so;
607     uint32_t idx;
608 
609     pa_assert(c);
610     pa_assert(s);
611 
612     PA_IDXSET_FOREACH(so, c->source_outputs, idx) {
613         if (so->source == s)
614             continue;
615 
616         if (so->direct_on_input)
617             continue;
618 
619         if (!so->source)
620             continue;
621 
622         /* Skip this source output if it is connecting a filter source to
623          * the master */
624         if (so->destination_source)
625             continue;
626 
627         /* It might happen that a stream and a source are set up at the
628            same time, in which case we want to make sure we don't
629            interfere with that */
630         if (!PA_SOURCE_OUTPUT_IS_LINKED(so->state))
631             continue;
632 
633         if (pa_safe_streq(so->preferred_source, s->name))
634             pa_source_output_move_to(so, s, false);
635     }
636 
637 }
638 
639 
640 /* Helper macro to reduce repetition in pa_suspend_cause_to_string().
641  * Parameters:
642  *   char *p: the current position in the write buffer
643  *   bool first: is cause_to_check the first cause to be written?
644  *   pa_suspend_cause_t cause_bitfield: the causes given to pa_suspend_cause_to_string()
645  *   pa_suspend_cause_t cause_to_check: the cause whose presence in cause_bitfield is to be checked
646  */
647 #define CHECK_CAUSE(p, first, cause_bitfield, cause_to_check) \
648     if (cause_bitfield & PA_SUSPEND_##cause_to_check) {       \
649         size_t len = sizeof(#cause_to_check) - 1;             \
650         if (!first) {                                         \
651             *p = '|';                                         \
652             p++;                                              \
653         }                                                     \
654         first = false;                                        \
655         memcpy(p, #cause_to_check, len);                      \
656         p += len;                                             \
657     }
658 
pa_suspend_cause_to_string(pa_suspend_cause_t cause_bitfield,char buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE])659 const char *pa_suspend_cause_to_string(pa_suspend_cause_t cause_bitfield, char buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE]) {
660     char *p = buf;
661     bool first = true;
662 
663     CHECK_CAUSE(p, first, cause_bitfield, USER);
664     CHECK_CAUSE(p, first, cause_bitfield, APPLICATION);
665     CHECK_CAUSE(p, first, cause_bitfield, IDLE);
666     CHECK_CAUSE(p, first, cause_bitfield, SESSION);
667     CHECK_CAUSE(p, first, cause_bitfield, PASSTHROUGH);
668     CHECK_CAUSE(p, first, cause_bitfield, INTERNAL);
669     CHECK_CAUSE(p, first, cause_bitfield, UNAVAILABLE);
670 
671     if (p == buf) {
672         memcpy(p, "(none)", 6);
673         p += 6;
674     }
675 
676     *p = 0;
677 
678     return buf;
679 }
680