linux-aio: add `dev_max_batch` parameter to laio_co_submit()
[qemu/ar7.git] / audio / paaudio.c
blob75401d53910a0d55c80c3a34507ddc2469f0bd11
1 /* public domain */
3 #include "qemu/osdep.h"
4 #include "qemu/module.h"
5 #include "audio.h"
6 #include "qapi/opts-visitor.h"
8 #include <pulse/pulseaudio.h>
10 #define AUDIO_CAP "pulseaudio"
11 #include "audio_int.h"
13 typedef struct PAConnection {
14 char *server;
15 int refcount;
16 QTAILQ_ENTRY(PAConnection) list;
18 pa_threaded_mainloop *mainloop;
19 pa_context *context;
20 } PAConnection;
22 static QTAILQ_HEAD(PAConnectionHead, PAConnection) pa_conns =
23 QTAILQ_HEAD_INITIALIZER(pa_conns);
25 typedef struct {
26 Audiodev *dev;
27 PAConnection *conn;
28 } paaudio;
30 typedef struct {
31 HWVoiceOut hw;
32 pa_stream *stream;
33 paaudio *g;
34 } PAVoiceOut;
36 typedef struct {
37 HWVoiceIn hw;
38 pa_stream *stream;
39 const void *read_data;
40 size_t read_length;
41 paaudio *g;
42 } PAVoiceIn;
44 static void qpa_conn_fini(PAConnection *c);
46 static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
48 va_list ap;
50 va_start (ap, fmt);
51 AUD_vlog (AUDIO_CAP, fmt, ap);
52 va_end (ap);
54 AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
57 #ifndef PA_CONTEXT_IS_GOOD
58 static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x)
60 return
61 x == PA_CONTEXT_CONNECTING ||
62 x == PA_CONTEXT_AUTHORIZING ||
63 x == PA_CONTEXT_SETTING_NAME ||
64 x == PA_CONTEXT_READY;
66 #endif
68 #ifndef PA_STREAM_IS_GOOD
69 static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
71 return
72 x == PA_STREAM_CREATING ||
73 x == PA_STREAM_READY;
75 #endif
77 #define CHECK_SUCCESS_GOTO(c, expression, label, msg) \
78 do { \
79 if (!(expression)) { \
80 qpa_logerr(pa_context_errno((c)->context), msg); \
81 goto label; \
82 } \
83 } while (0)
85 #define CHECK_DEAD_GOTO(c, stream, label, msg) \
86 do { \
87 if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
88 !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \
89 if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \
90 ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \
91 qpa_logerr(pa_context_errno((c)->context), msg); \
92 } else { \
93 qpa_logerr(PA_ERR_BADSTATE, msg); \
94 } \
95 goto label; \
96 } \
97 } while (0)
99 static void *qpa_get_buffer_in(HWVoiceIn *hw, size_t *size)
101 PAVoiceIn *p = (PAVoiceIn *) hw;
102 PAConnection *c = p->g->conn;
103 int r;
105 pa_threaded_mainloop_lock(c->mainloop);
107 CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
108 "pa_threaded_mainloop_lock failed\n");
110 if (!p->read_length) {
111 r = pa_stream_peek(p->stream, &p->read_data, &p->read_length);
112 CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
113 "pa_stream_peek failed\n");
116 *size = MIN(p->read_length, *size);
118 pa_threaded_mainloop_unlock(c->mainloop);
119 return (void *) p->read_data;
121 unlock_and_fail:
122 pa_threaded_mainloop_unlock(c->mainloop);
123 *size = 0;
124 return NULL;
127 static void qpa_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
129 PAVoiceIn *p = (PAVoiceIn *) hw;
130 PAConnection *c = p->g->conn;
131 int r;
133 pa_threaded_mainloop_lock(c->mainloop);
135 CHECK_DEAD_GOTO(c, p->stream, unlock,
136 "pa_threaded_mainloop_lock failed\n");
138 assert(buf == p->read_data && size <= p->read_length);
140 p->read_data += size;
141 p->read_length -= size;
143 if (size && !p->read_length) {
144 r = pa_stream_drop(p->stream);
145 CHECK_SUCCESS_GOTO(c, r == 0, unlock, "pa_stream_drop failed\n");
148 unlock:
149 pa_threaded_mainloop_unlock(c->mainloop);
152 static size_t qpa_read(HWVoiceIn *hw, void *data, size_t length)
154 PAVoiceIn *p = (PAVoiceIn *) hw;
155 PAConnection *c = p->g->conn;
156 size_t total = 0;
158 pa_threaded_mainloop_lock(c->mainloop);
160 CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
161 "pa_threaded_mainloop_lock failed\n");
162 if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
163 /* wait for stream to become ready */
164 goto unlock;
167 while (total < length) {
168 size_t l;
169 int r;
171 if (!p->read_length) {
172 r = pa_stream_peek(p->stream, &p->read_data, &p->read_length);
173 CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
174 "pa_stream_peek failed\n");
175 if (!p->read_length) {
176 /* buffer is empty */
177 break;
181 l = MIN(p->read_length, length - total);
182 memcpy((char *)data + total, p->read_data, l);
184 p->read_data += l;
185 p->read_length -= l;
186 total += l;
188 if (!p->read_length) {
189 r = pa_stream_drop(p->stream);
190 CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
191 "pa_stream_drop failed\n");
195 unlock:
196 pa_threaded_mainloop_unlock(c->mainloop);
197 return total;
199 unlock_and_fail:
200 pa_threaded_mainloop_unlock(c->mainloop);
201 return 0;
204 static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
206 PAVoiceOut *p = (PAVoiceOut *) hw;
207 PAConnection *c = p->g->conn;
208 void *ret;
209 size_t l;
210 int r;
212 pa_threaded_mainloop_lock(c->mainloop);
214 CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
215 "pa_threaded_mainloop_lock failed\n");
216 if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
217 /* wait for stream to become ready */
218 l = 0;
219 ret = NULL;
220 goto unlock;
223 l = pa_stream_writable_size(p->stream);
224 CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail,
225 "pa_stream_writable_size failed\n");
227 *size = -1;
228 r = pa_stream_begin_write(p->stream, &ret, size);
229 CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail,
230 "pa_stream_begin_write failed\n");
232 unlock:
233 pa_threaded_mainloop_unlock(c->mainloop);
234 if (*size > l) {
235 *size = l;
237 return ret;
239 unlock_and_fail:
240 pa_threaded_mainloop_unlock(c->mainloop);
241 *size = 0;
242 return NULL;
245 static size_t qpa_put_buffer_out(HWVoiceOut *hw, void *data, size_t length)
247 PAVoiceOut *p = (PAVoiceOut *)hw;
248 PAConnection *c = p->g->conn;
249 int r;
251 pa_threaded_mainloop_lock(c->mainloop);
253 CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
254 "pa_threaded_mainloop_lock failed\n");
256 r = pa_stream_write(p->stream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
257 CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, "pa_stream_write failed\n");
259 pa_threaded_mainloop_unlock(c->mainloop);
260 return length;
262 unlock_and_fail:
263 pa_threaded_mainloop_unlock(c->mainloop);
264 return 0;
267 static size_t qpa_write(HWVoiceOut *hw, void *data, size_t length)
269 PAVoiceOut *p = (PAVoiceOut *) hw;
270 PAConnection *c = p->g->conn;
271 size_t l;
272 int r;
274 pa_threaded_mainloop_lock(c->mainloop);
276 CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
277 "pa_threaded_mainloop_lock failed\n");
278 if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
279 /* wait for stream to become ready */
280 l = 0;
281 goto unlock;
284 l = pa_stream_writable_size(p->stream);
286 CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail,
287 "pa_stream_writable_size failed\n");
289 if (l > length) {
290 l = length;
293 r = pa_stream_write(p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
294 CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, "pa_stream_write failed\n");
296 unlock:
297 pa_threaded_mainloop_unlock(c->mainloop);
298 return l;
300 unlock_and_fail:
301 pa_threaded_mainloop_unlock(c->mainloop);
302 return 0;
305 static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
307 int format;
309 switch (afmt) {
310 case AUDIO_FORMAT_S8:
311 case AUDIO_FORMAT_U8:
312 format = PA_SAMPLE_U8;
313 break;
314 case AUDIO_FORMAT_S16:
315 case AUDIO_FORMAT_U16:
316 format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
317 break;
318 case AUDIO_FORMAT_S32:
319 case AUDIO_FORMAT_U32:
320 format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
321 break;
322 case AUDIO_FORMAT_F32:
323 format = endianness ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE;
324 break;
325 default:
326 dolog ("Internal logic error: Bad audio format %d\n", afmt);
327 format = PA_SAMPLE_U8;
328 break;
330 return format;
333 static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
335 switch (fmt) {
336 case PA_SAMPLE_U8:
337 return AUDIO_FORMAT_U8;
338 case PA_SAMPLE_S16BE:
339 *endianness = 1;
340 return AUDIO_FORMAT_S16;
341 case PA_SAMPLE_S16LE:
342 *endianness = 0;
343 return AUDIO_FORMAT_S16;
344 case PA_SAMPLE_S32BE:
345 *endianness = 1;
346 return AUDIO_FORMAT_S32;
347 case PA_SAMPLE_S32LE:
348 *endianness = 0;
349 return AUDIO_FORMAT_S32;
350 case PA_SAMPLE_FLOAT32BE:
351 *endianness = 1;
352 return AUDIO_FORMAT_F32;
353 case PA_SAMPLE_FLOAT32LE:
354 *endianness = 0;
355 return AUDIO_FORMAT_F32;
356 default:
357 dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
358 return AUDIO_FORMAT_U8;
362 static void context_state_cb (pa_context *c, void *userdata)
364 PAConnection *conn = userdata;
366 switch (pa_context_get_state(c)) {
367 case PA_CONTEXT_READY:
368 case PA_CONTEXT_TERMINATED:
369 case PA_CONTEXT_FAILED:
370 pa_threaded_mainloop_signal(conn->mainloop, 0);
371 break;
373 case PA_CONTEXT_UNCONNECTED:
374 case PA_CONTEXT_CONNECTING:
375 case PA_CONTEXT_AUTHORIZING:
376 case PA_CONTEXT_SETTING_NAME:
377 break;
381 static void stream_state_cb (pa_stream *s, void * userdata)
383 PAConnection *c = userdata;
385 switch (pa_stream_get_state (s)) {
387 case PA_STREAM_READY:
388 case PA_STREAM_FAILED:
389 case PA_STREAM_TERMINATED:
390 pa_threaded_mainloop_signal(c->mainloop, 0);
391 break;
393 case PA_STREAM_UNCONNECTED:
394 case PA_STREAM_CREATING:
395 break;
399 static pa_stream *qpa_simple_new (
400 PAConnection *c,
401 const char *name,
402 pa_stream_direction_t dir,
403 const char *dev,
404 const pa_sample_spec *ss,
405 const pa_buffer_attr *attr,
406 int *rerror)
408 int r;
409 pa_stream *stream = NULL;
410 pa_stream_flags_t flags;
411 pa_channel_map map;
413 pa_threaded_mainloop_lock(c->mainloop);
415 pa_channel_map_init(&map);
416 map.channels = ss->channels;
419 * TODO: This currently expects the only frontend supporting more than 2
420 * channels is the usb-audio. We will need some means to set channel
421 * order when a new frontend gains multi-channel support.
423 switch (ss->channels) {
424 case 1:
425 map.map[0] = PA_CHANNEL_POSITION_MONO;
426 break;
428 case 2:
429 map.map[0] = PA_CHANNEL_POSITION_LEFT;
430 map.map[1] = PA_CHANNEL_POSITION_RIGHT;
431 break;
433 case 6:
434 map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
435 map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
436 map.map[2] = PA_CHANNEL_POSITION_CENTER;
437 map.map[3] = PA_CHANNEL_POSITION_LFE;
438 map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
439 map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
440 break;
442 case 8:
443 map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
444 map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
445 map.map[2] = PA_CHANNEL_POSITION_CENTER;
446 map.map[3] = PA_CHANNEL_POSITION_LFE;
447 map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
448 map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
449 map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
450 map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
451 break;
453 default:
454 dolog("Internal error: unsupported channel count %d\n", ss->channels);
455 goto fail;
458 stream = pa_stream_new(c->context, name, ss, &map);
459 if (!stream) {
460 goto fail;
463 pa_stream_set_state_callback(stream, stream_state_cb, c);
465 flags = PA_STREAM_EARLY_REQUESTS;
467 if (dev) {
468 /* don't move the stream if the user specified a sink/source */
469 flags |= PA_STREAM_DONT_MOVE;
472 if (dir == PA_STREAM_PLAYBACK) {
473 r = pa_stream_connect_playback(stream, dev, attr, flags, NULL, NULL);
474 } else {
475 r = pa_stream_connect_record(stream, dev, attr, flags);
478 if (r < 0) {
479 goto fail;
482 pa_threaded_mainloop_unlock(c->mainloop);
484 return stream;
486 fail:
487 pa_threaded_mainloop_unlock(c->mainloop);
489 if (stream) {
490 pa_stream_unref (stream);
493 *rerror = pa_context_errno(c->context);
495 return NULL;
498 static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
499 void *drv_opaque)
501 int error;
502 pa_sample_spec ss;
503 pa_buffer_attr ba;
504 struct audsettings obt_as = *as;
505 PAVoiceOut *pa = (PAVoiceOut *) hw;
506 paaudio *g = pa->g = drv_opaque;
507 AudiodevPaOptions *popts = &g->dev->u.pa;
508 AudiodevPaPerDirectionOptions *ppdo = popts->out;
509 PAConnection *c = g->conn;
511 ss.format = audfmt_to_pa (as->fmt, as->endianness);
512 ss.channels = as->nchannels;
513 ss.rate = as->freq;
515 ba.tlength = pa_usec_to_bytes(ppdo->latency, &ss);
516 ba.minreq = pa_usec_to_bytes(MIN(ppdo->latency >> 2,
517 (g->dev->timer_period >> 2) * 3), &ss);
518 ba.maxlength = -1;
519 ba.prebuf = -1;
521 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
523 pa->stream = qpa_simple_new (
525 ppdo->has_stream_name ? ppdo->stream_name : g->dev->id,
526 PA_STREAM_PLAYBACK,
527 ppdo->has_name ? ppdo->name : NULL,
528 &ss,
529 &ba, /* buffering attributes */
530 &error
532 if (!pa->stream) {
533 qpa_logerr (error, "pa_simple_new for playback failed\n");
534 goto fail1;
537 audio_pcm_init_info (&hw->info, &obt_as);
539 * This is wrong. hw->samples counts in frames. hw->samples will be
540 * number of channels times larger than expected.
542 hw->samples = audio_buffer_samples(
543 qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
545 return 0;
547 fail1:
548 return -1;
551 static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
553 int error;
554 pa_sample_spec ss;
555 pa_buffer_attr ba;
556 struct audsettings obt_as = *as;
557 PAVoiceIn *pa = (PAVoiceIn *) hw;
558 paaudio *g = pa->g = drv_opaque;
559 AudiodevPaOptions *popts = &g->dev->u.pa;
560 AudiodevPaPerDirectionOptions *ppdo = popts->in;
561 PAConnection *c = g->conn;
563 ss.format = audfmt_to_pa (as->fmt, as->endianness);
564 ss.channels = as->nchannels;
565 ss.rate = as->freq;
567 ba.fragsize = pa_usec_to_bytes((g->dev->timer_period >> 1) * 3, &ss);
568 ba.maxlength = pa_usec_to_bytes(
569 MAX(ppdo->latency, g->dev->timer_period * 3), &ss);
570 ba.minreq = -1;
571 ba.prebuf = -1;
573 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
575 pa->stream = qpa_simple_new (
577 ppdo->has_stream_name ? ppdo->stream_name : g->dev->id,
578 PA_STREAM_RECORD,
579 ppdo->has_name ? ppdo->name : NULL,
580 &ss,
581 &ba, /* buffering attributes */
582 &error
584 if (!pa->stream) {
585 qpa_logerr (error, "pa_simple_new for capture failed\n");
586 goto fail1;
589 audio_pcm_init_info (&hw->info, &obt_as);
591 * This is wrong. hw->samples counts in frames. hw->samples will be
592 * number of channels times larger than expected.
594 hw->samples = audio_buffer_samples(
595 qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
597 return 0;
599 fail1:
600 return -1;
603 static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream)
605 int err;
608 * wait until actually connects. workaround pa bug #247
609 * https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/247
611 while (pa_stream_get_state(stream) == PA_STREAM_CREATING) {
612 pa_threaded_mainloop_wait(c->mainloop);
615 err = pa_stream_disconnect(stream);
616 if (err != 0) {
617 dolog("Failed to disconnect! err=%d\n", err);
619 pa_stream_unref(stream);
622 static void qpa_fini_out (HWVoiceOut *hw)
624 PAVoiceOut *pa = (PAVoiceOut *) hw;
626 if (pa->stream) {
627 PAConnection *c = pa->g->conn;
629 pa_threaded_mainloop_lock(c->mainloop);
630 qpa_simple_disconnect(c, pa->stream);
631 pa->stream = NULL;
632 pa_threaded_mainloop_unlock(c->mainloop);
636 static void qpa_fini_in (HWVoiceIn *hw)
638 PAVoiceIn *pa = (PAVoiceIn *) hw;
640 if (pa->stream) {
641 PAConnection *c = pa->g->conn;
643 pa_threaded_mainloop_lock(c->mainloop);
644 if (pa->read_length) {
645 int r = pa_stream_drop(pa->stream);
646 if (r) {
647 qpa_logerr(pa_context_errno(c->context),
648 "pa_stream_drop failed\n");
650 pa->read_length = 0;
652 qpa_simple_disconnect(c, pa->stream);
653 pa->stream = NULL;
654 pa_threaded_mainloop_unlock(c->mainloop);
658 static void qpa_volume_out(HWVoiceOut *hw, Volume *vol)
660 PAVoiceOut *pa = (PAVoiceOut *) hw;
661 pa_operation *op;
662 pa_cvolume v;
663 PAConnection *c = pa->g->conn;
664 int i;
666 #ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */
667 pa_cvolume_init (&v); /* function is present in 0.9.13+ */
668 #endif
670 v.channels = vol->channels;
671 for (i = 0; i < vol->channels; ++i) {
672 v.values[i] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->vol[i]) / 255;
675 pa_threaded_mainloop_lock(c->mainloop);
677 op = pa_context_set_sink_input_volume(c->context,
678 pa_stream_get_index(pa->stream),
679 &v, NULL, NULL);
680 if (!op) {
681 qpa_logerr(pa_context_errno(c->context),
682 "set_sink_input_volume() failed\n");
683 } else {
684 pa_operation_unref(op);
687 op = pa_context_set_sink_input_mute(c->context,
688 pa_stream_get_index(pa->stream),
689 vol->mute, NULL, NULL);
690 if (!op) {
691 qpa_logerr(pa_context_errno(c->context),
692 "set_sink_input_mute() failed\n");
693 } else {
694 pa_operation_unref(op);
697 pa_threaded_mainloop_unlock(c->mainloop);
700 static void qpa_volume_in(HWVoiceIn *hw, Volume *vol)
702 PAVoiceIn *pa = (PAVoiceIn *) hw;
703 pa_operation *op;
704 pa_cvolume v;
705 PAConnection *c = pa->g->conn;
706 int i;
708 #ifdef PA_CHECK_VERSION
709 pa_cvolume_init (&v);
710 #endif
712 v.channels = vol->channels;
713 for (i = 0; i < vol->channels; ++i) {
714 v.values[i] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->vol[i]) / 255;
717 pa_threaded_mainloop_lock(c->mainloop);
719 op = pa_context_set_source_output_volume(c->context,
720 pa_stream_get_index(pa->stream),
721 &v, NULL, NULL);
722 if (!op) {
723 qpa_logerr(pa_context_errno(c->context),
724 "set_source_output_volume() failed\n");
725 } else {
726 pa_operation_unref(op);
729 op = pa_context_set_source_output_mute(c->context,
730 pa_stream_get_index(pa->stream),
731 vol->mute, NULL, NULL);
732 if (!op) {
733 qpa_logerr(pa_context_errno(c->context),
734 "set_source_output_mute() failed\n");
735 } else {
736 pa_operation_unref(op);
739 pa_threaded_mainloop_unlock(c->mainloop);
742 static int qpa_validate_per_direction_opts(Audiodev *dev,
743 AudiodevPaPerDirectionOptions *pdo)
745 if (!pdo->has_latency) {
746 pdo->has_latency = true;
747 pdo->latency = 15000;
749 return 1;
752 /* common */
753 static void *qpa_conn_init(const char *server)
755 PAConnection *c = g_malloc0(sizeof(PAConnection));
756 QTAILQ_INSERT_TAIL(&pa_conns, c, list);
758 c->mainloop = pa_threaded_mainloop_new();
759 if (!c->mainloop) {
760 goto fail;
763 c->context = pa_context_new(pa_threaded_mainloop_get_api(c->mainloop),
764 audio_application_name());
765 if (!c->context) {
766 goto fail;
769 pa_context_set_state_callback(c->context, context_state_cb, c);
771 if (pa_context_connect(c->context, server, 0, NULL) < 0) {
772 qpa_logerr(pa_context_errno(c->context),
773 "pa_context_connect() failed\n");
774 goto fail;
777 pa_threaded_mainloop_lock(c->mainloop);
779 if (pa_threaded_mainloop_start(c->mainloop) < 0) {
780 goto unlock_and_fail;
783 for (;;) {
784 pa_context_state_t state;
786 state = pa_context_get_state(c->context);
788 if (state == PA_CONTEXT_READY) {
789 break;
792 if (!PA_CONTEXT_IS_GOOD(state)) {
793 qpa_logerr(pa_context_errno(c->context),
794 "Wrong context state\n");
795 goto unlock_and_fail;
798 /* Wait until the context is ready */
799 pa_threaded_mainloop_wait(c->mainloop);
802 pa_threaded_mainloop_unlock(c->mainloop);
803 return c;
805 unlock_and_fail:
806 pa_threaded_mainloop_unlock(c->mainloop);
807 fail:
808 AUD_log (AUDIO_CAP, "Failed to initialize PA context");
809 qpa_conn_fini(c);
810 return NULL;
813 static void *qpa_audio_init(Audiodev *dev)
815 paaudio *g;
816 AudiodevPaOptions *popts = &dev->u.pa;
817 const char *server;
818 PAConnection *c;
820 assert(dev->driver == AUDIODEV_DRIVER_PA);
822 if (!popts->has_server) {
823 char pidfile[64];
824 char *runtime;
825 struct stat st;
827 runtime = getenv("XDG_RUNTIME_DIR");
828 if (!runtime) {
829 return NULL;
831 snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime);
832 if (stat(pidfile, &st) != 0) {
833 return NULL;
837 if (!qpa_validate_per_direction_opts(dev, popts->in)) {
838 return NULL;
840 if (!qpa_validate_per_direction_opts(dev, popts->out)) {
841 return NULL;
844 g = g_malloc0(sizeof(paaudio));
845 server = popts->has_server ? popts->server : NULL;
847 g->dev = dev;
849 QTAILQ_FOREACH(c, &pa_conns, list) {
850 if (server == NULL || c->server == NULL ?
851 server == c->server :
852 strcmp(server, c->server) == 0) {
853 g->conn = c;
854 break;
857 if (!g->conn) {
858 g->conn = qpa_conn_init(server);
860 if (!g->conn) {
861 g_free(g);
862 return NULL;
865 ++g->conn->refcount;
866 return g;
869 static void qpa_conn_fini(PAConnection *c)
871 if (c->mainloop) {
872 pa_threaded_mainloop_stop(c->mainloop);
875 if (c->context) {
876 pa_context_disconnect(c->context);
877 pa_context_unref(c->context);
880 if (c->mainloop) {
881 pa_threaded_mainloop_free(c->mainloop);
884 QTAILQ_REMOVE(&pa_conns, c, list);
885 g_free(c);
888 static void qpa_audio_fini (void *opaque)
890 paaudio *g = opaque;
891 PAConnection *c = g->conn;
893 if (--c->refcount == 0) {
894 qpa_conn_fini(c);
897 g_free(g);
900 static struct audio_pcm_ops qpa_pcm_ops = {
901 .init_out = qpa_init_out,
902 .fini_out = qpa_fini_out,
903 .write = qpa_write,
904 .get_buffer_out = qpa_get_buffer_out,
905 .put_buffer_out = qpa_put_buffer_out,
906 .volume_out = qpa_volume_out,
908 .init_in = qpa_init_in,
909 .fini_in = qpa_fini_in,
910 .read = qpa_read,
911 .get_buffer_in = qpa_get_buffer_in,
912 .put_buffer_in = qpa_put_buffer_in,
913 .volume_in = qpa_volume_in
916 static struct audio_driver pa_audio_driver = {
917 .name = "pa",
918 .descr = "http://www.pulseaudio.org/",
919 .init = qpa_audio_init,
920 .fini = qpa_audio_fini,
921 .pcm_ops = &qpa_pcm_ops,
922 .can_be_default = 1,
923 .max_voices_out = INT_MAX,
924 .max_voices_in = INT_MAX,
925 .voice_size_out = sizeof (PAVoiceOut),
926 .voice_size_in = sizeof (PAVoiceIn),
929 static void register_audio_pa(void)
931 audio_driver_register(&pa_audio_driver);
933 type_init(register_audio_pa);