1
2
3#include "qemu/osdep.h"
4#include "qemu/module.h"
5#include "audio.h"
6#include "qapi/opts-visitor.h"
7
8#include <pulse/pulseaudio.h>
9
10#define AUDIO_CAP "pulseaudio"
11#include "audio_int.h"
12
13typedef struct PAConnection {
14 char *server;
15 int refcount;
16 QTAILQ_ENTRY(PAConnection) list;
17
18 pa_threaded_mainloop *mainloop;
19 pa_context *context;
20} PAConnection;
21
22static QTAILQ_HEAD(PAConnectionHead, PAConnection) pa_conns =
23 QTAILQ_HEAD_INITIALIZER(pa_conns);
24
25typedef struct {
26 Audiodev *dev;
27 PAConnection *conn;
28} paaudio;
29
30typedef struct {
31 HWVoiceOut hw;
32 pa_stream *stream;
33 paaudio *g;
34} PAVoiceOut;
35
36typedef struct {
37 HWVoiceIn hw;
38 pa_stream *stream;
39 const void *read_data;
40 size_t read_length;
41 paaudio *g;
42} PAVoiceIn;
43
44static void qpa_conn_fini(PAConnection *c);
45
46static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
47{
48 va_list ap;
49
50 va_start (ap, fmt);
51 AUD_vlog (AUDIO_CAP, fmt, ap);
52 va_end (ap);
53
54 AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
55}
56
57#ifndef PA_CONTEXT_IS_GOOD
58static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x)
59{
60 return
61 x == PA_CONTEXT_CONNECTING ||
62 x == PA_CONTEXT_AUTHORIZING ||
63 x == PA_CONTEXT_SETTING_NAME ||
64 x == PA_CONTEXT_READY;
65}
66#endif
67
68#ifndef PA_STREAM_IS_GOOD
69static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
70{
71 return
72 x == PA_STREAM_CREATING ||
73 x == PA_STREAM_READY;
74}
75#endif
76
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)
84
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)
98
99static void *qpa_get_buffer_in(HWVoiceIn *hw, size_t *size)
100{
101 PAVoiceIn *p = (PAVoiceIn *) hw;
102 PAConnection *c = p->g->conn;
103 int r;
104
105 pa_threaded_mainloop_lock(c->mainloop);
106
107 CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
108 "pa_threaded_mainloop_lock failed\n");
109
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");
114 }
115
116 *size = MIN(p->read_length, *size);
117
118 pa_threaded_mainloop_unlock(c->mainloop);
119 return (void *) p->read_data;
120
121unlock_and_fail:
122 pa_threaded_mainloop_unlock(c->mainloop);
123 *size = 0;
124 return NULL;
125}
126
127static void qpa_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
128{
129 PAVoiceIn *p = (PAVoiceIn *) hw;
130 PAConnection *c = p->g->conn;
131 int r;
132
133 pa_threaded_mainloop_lock(c->mainloop);
134
135 CHECK_DEAD_GOTO(c, p->stream, unlock,
136 "pa_threaded_mainloop_lock failed\n");
137
138 assert(buf == p->read_data && size <= p->read_length);
139
140 p->read_data += size;
141 p->read_length -= size;
142
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");
146 }
147
148unlock:
149 pa_threaded_mainloop_unlock(c->mainloop);
150}
151
152static size_t qpa_read(HWVoiceIn *hw, void *data, size_t length)
153{
154 PAVoiceIn *p = (PAVoiceIn *) hw;
155 PAConnection *c = p->g->conn;
156 size_t total = 0;
157
158 pa_threaded_mainloop_lock(c->mainloop);
159
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
164 goto unlock;
165 }
166
167 while (total < length) {
168 size_t l;
169 int r;
170
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
177 break;
178 }
179 }
180
181 l = MIN(p->read_length, length - total);
182 memcpy((char *)data + total, p->read_data, l);
183
184 p->read_data += l;
185 p->read_length -= l;
186 total += l;
187
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");
192 }
193 }
194
195unlock:
196 pa_threaded_mainloop_unlock(c->mainloop);
197 return total;
198
199unlock_and_fail:
200 pa_threaded_mainloop_unlock(c->mainloop);
201 return 0;
202}
203
204static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
205{
206 PAVoiceOut *p = (PAVoiceOut *) hw;
207 PAConnection *c = p->g->conn;
208 void *ret;
209 size_t l;
210 int r;
211
212 pa_threaded_mainloop_lock(c->mainloop);
213
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
218 l = 0;
219 ret = NULL;
220 goto unlock;
221 }
222
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");
226
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");
231
232unlock:
233 pa_threaded_mainloop_unlock(c->mainloop);
234 if (*size > l) {
235 *size = l;
236 }
237 return ret;
238
239unlock_and_fail:
240 pa_threaded_mainloop_unlock(c->mainloop);
241 *size = 0;
242 return NULL;
243}
244
245static size_t qpa_put_buffer_out(HWVoiceOut *hw, void *data, size_t length)
246{
247 PAVoiceOut *p = (PAVoiceOut *)hw;
248 PAConnection *c = p->g->conn;
249 int r;
250
251 pa_threaded_mainloop_lock(c->mainloop);
252
253 CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
254 "pa_threaded_mainloop_lock failed\n");
255
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");
258
259 pa_threaded_mainloop_unlock(c->mainloop);
260 return length;
261
262unlock_and_fail:
263 pa_threaded_mainloop_unlock(c->mainloop);
264 return 0;
265}
266
267static size_t qpa_write(HWVoiceOut *hw, void *data, size_t length)
268{
269 PAVoiceOut *p = (PAVoiceOut *) hw;
270 PAConnection *c = p->g->conn;
271 size_t l;
272 int r;
273
274 pa_threaded_mainloop_lock(c->mainloop);
275
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
280 l = 0;
281 goto unlock;
282 }
283
284 l = pa_stream_writable_size(p->stream);
285
286 CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail,
287 "pa_stream_writable_size failed\n");
288
289 if (l > length) {
290 l = length;
291 }
292
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");
295
296unlock:
297 pa_threaded_mainloop_unlock(c->mainloop);
298 return l;
299
300unlock_and_fail:
301 pa_threaded_mainloop_unlock(c->mainloop);
302 return 0;
303}
304
305static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
306{
307 int format;
308
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;
329 }
330 return format;
331}
332
333static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
334{
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;
359 }
360}
361
362static void context_state_cb (pa_context *c, void *userdata)
363{
364 PAConnection *conn = userdata;
365
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;
372
373 case PA_CONTEXT_UNCONNECTED:
374 case PA_CONTEXT_CONNECTING:
375 case PA_CONTEXT_AUTHORIZING:
376 case PA_CONTEXT_SETTING_NAME:
377 break;
378 }
379}
380
381static void stream_state_cb (pa_stream *s, void * userdata)
382{
383 PAConnection *c = userdata;
384
385 switch (pa_stream_get_state (s)) {
386
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;
392
393 case PA_STREAM_UNCONNECTED:
394 case PA_STREAM_CREATING:
395 break;
396 }
397}
398
399static 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)
407{
408 int r;
409 pa_stream *stream = NULL;
410 pa_stream_flags_t flags;
411 pa_channel_map map;
412
413 pa_threaded_mainloop_lock(c->mainloop);
414
415 pa_channel_map_init(&map);
416 map.channels = ss->channels;
417
418
419
420
421
422
423 switch (ss->channels) {
424 case 1:
425 map.map[0] = PA_CHANNEL_POSITION_MONO;
426 break;
427
428 case 2:
429 map.map[0] = PA_CHANNEL_POSITION_LEFT;
430 map.map[1] = PA_CHANNEL_POSITION_RIGHT;
431 break;
432
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;
441
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;
452
453 default:
454 dolog("Internal error: unsupported channel count %d\n", ss->channels);
455 goto fail;
456 }
457
458 stream = pa_stream_new(c->context, name, ss, &map);
459 if (!stream) {
460 goto fail;
461 }
462
463 pa_stream_set_state_callback(stream, stream_state_cb, c);
464
465 flags = PA_STREAM_EARLY_REQUESTS;
466
467 if (dev) {
468
469 flags |= PA_STREAM_DONT_MOVE;
470 }
471
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);
476 }
477
478 if (r < 0) {
479 goto fail;
480 }
481
482 pa_threaded_mainloop_unlock(c->mainloop);
483
484 return stream;
485
486fail:
487 pa_threaded_mainloop_unlock(c->mainloop);
488
489 if (stream) {
490 pa_stream_unref (stream);
491 }
492
493 *rerror = pa_context_errno(c->context);
494
495 return NULL;
496}
497
498static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
499 void *drv_opaque)
500{
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;
510
511 ss.format = audfmt_to_pa (as->fmt, as->endianness);
512 ss.channels = as->nchannels;
513 ss.rate = as->freq;
514
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;
520
521 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
522
523 pa->stream = qpa_simple_new (
524 c,
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,
530 &error
531 );
532 if (!pa->stream) {
533 qpa_logerr (error, "pa_simple_new for playback failed\n");
534 goto fail1;
535 }
536
537 audio_pcm_init_info (&hw->info, &obt_as);
538
539
540
541
542 hw->samples = audio_buffer_samples(
543 qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
544
545 return 0;
546
547 fail1:
548 return -1;
549}
550
551static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
552{
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;
562
563 ss.format = audfmt_to_pa (as->fmt, as->endianness);
564 ss.channels = as->nchannels;
565 ss.rate = as->freq;
566
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;
572
573 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
574
575 pa->stream = qpa_simple_new (
576 c,
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,
582 &error
583 );
584 if (!pa->stream) {
585 qpa_logerr (error, "pa_simple_new for capture failed\n");
586 goto fail1;
587 }
588
589 audio_pcm_init_info (&hw->info, &obt_as);
590
591
592
593
594 hw->samples = audio_buffer_samples(
595 qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
596
597 return 0;
598
599 fail1:
600 return -1;
601}
602
603static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream)
604{
605 int err;
606
607
608
609
610
611 while (pa_stream_get_state(stream) == PA_STREAM_CREATING) {
612 pa_threaded_mainloop_wait(c->mainloop);
613 }
614
615 err = pa_stream_disconnect(stream);
616 if (err != 0) {
617 dolog("Failed to disconnect! err=%d\n", err);
618 }
619 pa_stream_unref(stream);
620}
621
622static void qpa_fini_out (HWVoiceOut *hw)
623{
624 PAVoiceOut *pa = (PAVoiceOut *) hw;
625
626 if (pa->stream) {
627 PAConnection *c = pa->g->conn;
628
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);
633 }
634}
635
636static void qpa_fini_in (HWVoiceIn *hw)
637{
638 PAVoiceIn *pa = (PAVoiceIn *) hw;
639
640 if (pa->stream) {
641 PAConnection *c = pa->g->conn;
642
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");
649 }
650 pa->read_length = 0;
651 }
652 qpa_simple_disconnect(c, pa->stream);
653 pa->stream = NULL;
654 pa_threaded_mainloop_unlock(c->mainloop);
655 }
656}
657
658static void qpa_volume_out(HWVoiceOut *hw, Volume *vol)
659{
660 PAVoiceOut *pa = (PAVoiceOut *) hw;
661 pa_operation *op;
662 pa_cvolume v;
663 PAConnection *c = pa->g->conn;
664 int i;
665
666#ifdef PA_CHECK_VERSION
667 pa_cvolume_init (&v);
668#endif
669
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;
673 }
674
675 pa_threaded_mainloop_lock(c->mainloop);
676
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);
685 }
686
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);
695 }
696
697 pa_threaded_mainloop_unlock(c->mainloop);
698}
699
700static void qpa_volume_in(HWVoiceIn *hw, Volume *vol)
701{
702 PAVoiceIn *pa = (PAVoiceIn *) hw;
703 pa_operation *op;
704 pa_cvolume v;
705 PAConnection *c = pa->g->conn;
706 int i;
707
708#ifdef PA_CHECK_VERSION
709 pa_cvolume_init (&v);
710#endif
711
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;
715 }
716
717 pa_threaded_mainloop_lock(c->mainloop);
718
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);
727 }
728
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);
737 }
738
739 pa_threaded_mainloop_unlock(c->mainloop);
740}
741
742static int qpa_validate_per_direction_opts(Audiodev *dev,
743 AudiodevPaPerDirectionOptions *pdo)
744{
745 if (!pdo->has_latency) {
746 pdo->has_latency = true;
747 pdo->latency = 15000;
748 }
749 return 1;
750}
751
752
753static void *qpa_conn_init(const char *server)
754{
755 PAConnection *c = g_malloc0(sizeof(PAConnection));
756 QTAILQ_INSERT_TAIL(&pa_conns, c, list);
757
758 c->mainloop = pa_threaded_mainloop_new();
759 if (!c->mainloop) {
760 goto fail;
761 }
762
763 c->context = pa_context_new(pa_threaded_mainloop_get_api(c->mainloop),
764 audio_application_name());
765 if (!c->context) {
766 goto fail;
767 }
768
769 pa_context_set_state_callback(c->context, context_state_cb, c);
770
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;
775 }
776
777 pa_threaded_mainloop_lock(c->mainloop);
778
779 if (pa_threaded_mainloop_start(c->mainloop) < 0) {
780 goto unlock_and_fail;
781 }
782
783 for (;;) {
784 pa_context_state_t state;
785
786 state = pa_context_get_state(c->context);
787
788 if (state == PA_CONTEXT_READY) {
789 break;
790 }
791
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;
796 }
797
798
799 pa_threaded_mainloop_wait(c->mainloop);
800 }
801
802 pa_threaded_mainloop_unlock(c->mainloop);
803 return c;
804
805unlock_and_fail:
806 pa_threaded_mainloop_unlock(c->mainloop);
807fail:
808 AUD_log (AUDIO_CAP, "Failed to initialize PA context");
809 qpa_conn_fini(c);
810 return NULL;
811}
812
813static void *qpa_audio_init(Audiodev *dev)
814{
815 paaudio *g;
816 AudiodevPaOptions *popts = &dev->u.pa;
817 const char *server;
818 PAConnection *c;
819
820 assert(dev->driver == AUDIODEV_DRIVER_PA);
821
822 if (!popts->has_server) {
823 char pidfile[64];
824 char *runtime;
825 struct stat st;
826
827 runtime = getenv("XDG_RUNTIME_DIR");
828 if (!runtime) {
829 return NULL;
830 }
831 snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime);
832 if (stat(pidfile, &st) != 0) {
833 return NULL;
834 }
835 }
836
837 if (!qpa_validate_per_direction_opts(dev, popts->in)) {
838 return NULL;
839 }
840 if (!qpa_validate_per_direction_opts(dev, popts->out)) {
841 return NULL;
842 }
843
844 g = g_malloc0(sizeof(paaudio));
845 server = popts->has_server ? popts->server : NULL;
846
847 g->dev = dev;
848
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;
855 }
856 }
857 if (!g->conn) {
858 g->conn = qpa_conn_init(server);
859 }
860 if (!g->conn) {
861 g_free(g);
862 return NULL;
863 }
864
865 ++g->conn->refcount;
866 return g;
867}
868
869static void qpa_conn_fini(PAConnection *c)
870{
871 if (c->mainloop) {
872 pa_threaded_mainloop_stop(c->mainloop);
873 }
874
875 if (c->context) {
876 pa_context_disconnect(c->context);
877 pa_context_unref(c->context);
878 }
879
880 if (c->mainloop) {
881 pa_threaded_mainloop_free(c->mainloop);
882 }
883
884 QTAILQ_REMOVE(&pa_conns, c, list);
885 g_free(c);
886}
887
888static void qpa_audio_fini (void *opaque)
889{
890 paaudio *g = opaque;
891 PAConnection *c = g->conn;
892
893 if (--c->refcount == 0) {
894 qpa_conn_fini(c);
895 }
896
897 g_free(g);
898}
899
900static 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,
907
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
914};
915
916static 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),
927};
928
929static void register_audio_pa(void)
930{
931 audio_driver_register(&pa_audio_driver);
932}
933type_init(register_audio_pa);
934