1/* public domain */
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#include "audio_pt_int.h"
13
14typedef struct PAConnection {
15 char *server;
16 int refcount;
17 QTAILQ_ENTRY(PAConnection) list;
18
19 pa_threaded_mainloop *mainloop;
20 pa_context *context;
21} PAConnection;
22
23static QTAILQ_HEAD(PAConnectionHead, PAConnection) pa_conns =
24 QTAILQ_HEAD_INITIALIZER(pa_conns);
25
26typedef struct {
27 Audiodev *dev;
28 PAConnection *conn;
29} paaudio;
30
31typedef struct {
32 HWVoiceOut hw;
33 size_t done;
34 size_t live;
35 size_t decr;
36 size_t rpos;
37 pa_stream *stream;
38 void *pcm_buf;
39 struct audio_pt pt;
40 paaudio *g;
41 size_t samples;
42} PAVoiceOut;
43
44typedef struct {
45 HWVoiceIn hw;
46 size_t done;
47 size_t dead;
48 size_t incr;
49 size_t wpos;
50 pa_stream *stream;
51 void *pcm_buf;
52 struct audio_pt pt;
53 const void *read_data;
54 size_t read_index, read_length;
55 paaudio *g;
56 size_t samples;
57} PAVoiceIn;
58
59static void qpa_conn_fini(PAConnection *c);
60
61static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
62{
63 va_list ap;
64
65 va_start (ap, fmt);
66 AUD_vlog (AUDIO_CAP, fmt, ap);
67 va_end (ap);
68
69 AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
70}
71
72#ifndef PA_CONTEXT_IS_GOOD
73static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x)
74{
75 return
76 x == PA_CONTEXT_CONNECTING ||
77 x == PA_CONTEXT_AUTHORIZING ||
78 x == PA_CONTEXT_SETTING_NAME ||
79 x == PA_CONTEXT_READY;
80}
81#endif
82
83#ifndef PA_STREAM_IS_GOOD
84static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
85{
86 return
87 x == PA_STREAM_CREATING ||
88 x == PA_STREAM_READY;
89}
90#endif
91
92#define CHECK_SUCCESS_GOTO(c, rerror, expression, label) \
93 do { \
94 if (!(expression)) { \
95 if (rerror) { \
96 *(rerror) = pa_context_errno ((c)->context); \
97 } \
98 goto label; \
99 } \
100 } while (0)
101
102#define CHECK_DEAD_GOTO(c, stream, rerror, label) \
103 do { \
104 if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
105 !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \
106 if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \
107 ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \
108 if (rerror) { \
109 *(rerror) = pa_context_errno ((c)->context); \
110 } \
111 } else { \
112 if (rerror) { \
113 *(rerror) = PA_ERR_BADSTATE; \
114 } \
115 } \
116 goto label; \
117 } \
118 } while (0)
119
120static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
121{
122 PAConnection *c = p->g->conn;
123
124 pa_threaded_mainloop_lock(c->mainloop);
125
126 CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
127
128 while (length > 0) {
129 size_t l;
130
131 while (!p->read_data) {
132 int r;
133
134 r = pa_stream_peek (p->stream, &p->read_data, &p->read_length);
135 CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail);
136
137 if (!p->read_data) {
138 pa_threaded_mainloop_wait(c->mainloop);
139 CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
140 } else {
141 p->read_index = 0;
142 }
143 }
144
145 l = p->read_length < length ? p->read_length : length;
146 memcpy (data, (const uint8_t *) p->read_data+p->read_index, l);
147
148 data = (uint8_t *) data + l;
149 length -= l;
150
151 p->read_index += l;
152 p->read_length -= l;
153
154 if (!p->read_length) {
155 int r;
156
157 r = pa_stream_drop (p->stream);
158 p->read_data = NULL;
159 p->read_length = 0;
160 p->read_index = 0;
161
162 CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail);
163 }
164 }
165
166 pa_threaded_mainloop_unlock(c->mainloop);
167 return 0;
168
169unlock_and_fail:
170 pa_threaded_mainloop_unlock(c->mainloop);
171 return -1;
172}
173
174static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
175{
176 PAConnection *c = p->g->conn;
177
178 pa_threaded_mainloop_lock(c->mainloop);
179
180 CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
181
182 while (length > 0) {
183 size_t l;
184 int r;
185
186 while (!(l = pa_stream_writable_size (p->stream))) {
187 pa_threaded_mainloop_wait(c->mainloop);
188 CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
189 }
190
191 CHECK_SUCCESS_GOTO(c, rerror, l != (size_t) -1, unlock_and_fail);
192
193 if (l > length) {
194 l = length;
195 }
196
197 r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
198 CHECK_SUCCESS_GOTO(c, rerror, r >= 0, unlock_and_fail);
199
200 data = (const uint8_t *) data + l;
201 length -= l;
202 }
203
204 pa_threaded_mainloop_unlock(c->mainloop);
205 return 0;
206
207unlock_and_fail:
208 pa_threaded_mainloop_unlock(c->mainloop);
209 return -1;
210}
211
212static void *qpa_thread_out (void *arg)
213{
214 PAVoiceOut *pa = arg;
215 HWVoiceOut *hw = &pa->hw;
216
217 if (audio_pt_lock(&pa->pt, __func__)) {
218 return NULL;
219 }
220
221 for (;;) {
222 size_t decr, to_mix, rpos;
223
224 for (;;) {
225 if (pa->done) {
226 goto exit;
227 }
228
229 if (pa->live > 0) {
230 break;
231 }
232
233 if (audio_pt_wait(&pa->pt, __func__)) {
234 goto exit;
235 }
236 }
237
238 decr = to_mix = MIN(pa->live, pa->samples >> 5);
239 rpos = pa->rpos;
240
241 if (audio_pt_unlock(&pa->pt, __func__)) {
242 return NULL;
243 }
244
245 while (to_mix) {
246 int error;
247 size_t chunk = MIN (to_mix, hw->samples - rpos);
248 struct st_sample *src = hw->mix_buf + rpos;
249
250 hw->clip (pa->pcm_buf, src, chunk);
251
252 if (qpa_simple_write (pa, pa->pcm_buf,
253 chunk << hw->info.shift, &error) < 0) {
254 qpa_logerr (error, "pa_simple_write failed\n");
255 return NULL;
256 }
257
258 rpos = (rpos + chunk) % hw->samples;
259 to_mix -= chunk;
260 }
261
262 if (audio_pt_lock(&pa->pt, __func__)) {
263 return NULL;
264 }
265
266 pa->rpos = rpos;
267 pa->live -= decr;
268 pa->decr += decr;
269 }
270
271 exit:
272 audio_pt_unlock(&pa->pt, __func__);
273 return NULL;
274}
275
276static size_t qpa_run_out(HWVoiceOut *hw, size_t live)
277{
278 size_t decr;
279 PAVoiceOut *pa = (PAVoiceOut *) hw;
280
281 if (audio_pt_lock(&pa->pt, __func__)) {
282 return 0;
283 }
284
285 decr = MIN (live, pa->decr);
286 pa->decr -= decr;
287 pa->live = live - decr;
288 hw->rpos = pa->rpos;
289 if (pa->live > 0) {
290 audio_pt_unlock_and_signal(&pa->pt, __func__);
291 }
292 else {
293 audio_pt_unlock(&pa->pt, __func__);
294 }
295 return decr;
296}
297
298/* capture */
299static void *qpa_thread_in (void *arg)
300{
301 PAVoiceIn *pa = arg;
302 HWVoiceIn *hw = &pa->hw;
303
304 if (audio_pt_lock(&pa->pt, __func__)) {
305 return NULL;
306 }
307
308 for (;;) {
309 size_t incr, to_grab, wpos;
310
311 for (;;) {
312 if (pa->done) {
313 goto exit;
314 }
315
316 if (pa->dead > 0) {
317 break;
318 }
319
320 if (audio_pt_wait(&pa->pt, __func__)) {
321 goto exit;
322 }
323 }
324
325 incr = to_grab = MIN(pa->dead, pa->samples >> 5);
326 wpos = pa->wpos;
327
328 if (audio_pt_unlock(&pa->pt, __func__)) {
329 return NULL;
330 }
331
332 while (to_grab) {
333 int error;
334 size_t chunk = MIN (to_grab, hw->samples - wpos);
335 void *buf = advance (pa->pcm_buf, wpos);
336
337 if (qpa_simple_read (pa, buf,
338 chunk << hw->info.shift, &error) < 0) {
339 qpa_logerr (error, "pa_simple_read failed\n");
340 return NULL;
341 }
342
343 hw->conv (hw->conv_buf + wpos, buf, chunk);
344 wpos = (wpos + chunk) % hw->samples;
345 to_grab -= chunk;
346 }
347
348 if (audio_pt_lock(&pa->pt, __func__)) {
349 return NULL;
350 }
351
352 pa->wpos = wpos;
353 pa->dead -= incr;
354 pa->incr += incr;
355 }
356
357 exit:
358 audio_pt_unlock(&pa->pt, __func__);
359 return NULL;
360}
361
362static size_t qpa_run_in(HWVoiceIn *hw)
363{
364 size_t live, incr, dead;
365 PAVoiceIn *pa = (PAVoiceIn *) hw;
366
367 if (audio_pt_lock(&pa->pt, __func__)) {
368 return 0;
369 }
370
371 live = audio_pcm_hw_get_live_in (hw);
372 dead = hw->samples - live;
373 incr = MIN (dead, pa->incr);
374 pa->incr -= incr;
375 pa->dead = dead - incr;
376 hw->wpos = pa->wpos;
377 if (pa->dead > 0) {
378 audio_pt_unlock_and_signal(&pa->pt, __func__);
379 }
380 else {
381 audio_pt_unlock(&pa->pt, __func__);
382 }
383 return incr;
384}
385
386static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
387{
388 int format;
389
390 switch (afmt) {
391 case AUDIO_FORMAT_S8:
392 case AUDIO_FORMAT_U8:
393 format = PA_SAMPLE_U8;
394 break;
395 case AUDIO_FORMAT_S16:
396 case AUDIO_FORMAT_U16:
397 format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
398 break;
399 case AUDIO_FORMAT_S32:
400 case AUDIO_FORMAT_U32:
401 format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
402 break;
403 default:
404 dolog ("Internal logic error: Bad audio format %d\n", afmt);
405 format = PA_SAMPLE_U8;
406 break;
407 }
408 return format;
409}
410
411static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
412{
413 switch (fmt) {
414 case PA_SAMPLE_U8:
415 return AUDIO_FORMAT_U8;
416 case PA_SAMPLE_S16BE:
417 *endianness = 1;
418 return AUDIO_FORMAT_S16;
419 case PA_SAMPLE_S16LE:
420 *endianness = 0;
421 return AUDIO_FORMAT_S16;
422 case PA_SAMPLE_S32BE:
423 *endianness = 1;
424 return AUDIO_FORMAT_S32;
425 case PA_SAMPLE_S32LE:
426 *endianness = 0;
427 return AUDIO_FORMAT_S32;
428 default:
429 dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
430 return AUDIO_FORMAT_U8;
431 }
432}
433
434static void context_state_cb (pa_context *c, void *userdata)
435{
436 PAConnection *conn = userdata;
437
438 switch (pa_context_get_state(c)) {
439 case PA_CONTEXT_READY:
440 case PA_CONTEXT_TERMINATED:
441 case PA_CONTEXT_FAILED:
442 pa_threaded_mainloop_signal(conn->mainloop, 0);
443 break;
444
445 case PA_CONTEXT_UNCONNECTED:
446 case PA_CONTEXT_CONNECTING:
447 case PA_CONTEXT_AUTHORIZING:
448 case PA_CONTEXT_SETTING_NAME:
449 break;
450 }
451}
452
453static void stream_state_cb (pa_stream *s, void * userdata)
454{
455 PAConnection *c = userdata;
456
457 switch (pa_stream_get_state (s)) {
458
459 case PA_STREAM_READY:
460 case PA_STREAM_FAILED:
461 case PA_STREAM_TERMINATED:
462 pa_threaded_mainloop_signal(c->mainloop, 0);
463 break;
464
465 case PA_STREAM_UNCONNECTED:
466 case PA_STREAM_CREATING:
467 break;
468 }
469}
470
471static void stream_request_cb (pa_stream *s, size_t length, void *userdata)
472{
473 PAConnection *c = userdata;
474
475 pa_threaded_mainloop_signal(c->mainloop, 0);
476}
477
478static pa_stream *qpa_simple_new (
479 PAConnection *c,
480 const char *name,
481 pa_stream_direction_t dir,
482 const char *dev,
483 const pa_sample_spec *ss,
484 const pa_channel_map *map,
485 const pa_buffer_attr *attr,
486 int *rerror)
487{
488 int r;
489 pa_stream *stream;
490 pa_stream_flags_t flags;
491
492 pa_threaded_mainloop_lock(c->mainloop);
493
494 stream = pa_stream_new(c->context, name, ss, map);
495 if (!stream) {
496 goto fail;
497 }
498
499 pa_stream_set_state_callback(stream, stream_state_cb, c);
500 pa_stream_set_read_callback(stream, stream_request_cb, c);
501 pa_stream_set_write_callback(stream, stream_request_cb, c);
502
503 flags =
504 PA_STREAM_INTERPOLATE_TIMING
505 | PA_STREAM_AUTO_TIMING_UPDATE
506 | PA_STREAM_EARLY_REQUESTS;
507
508 if (dev) {
509 /* don't move the stream if the user specified a sink/source */
510 flags |= PA_STREAM_DONT_MOVE;
511 }
512
513 if (dir == PA_STREAM_PLAYBACK) {
514 r = pa_stream_connect_playback(stream, dev, attr, flags, NULL, NULL);
515 } else {
516 r = pa_stream_connect_record(stream, dev, attr, flags);
517 }
518
519 if (r < 0) {
520 goto fail;
521 }
522
523 pa_threaded_mainloop_unlock(c->mainloop);
524
525 return stream;
526
527fail:
528 pa_threaded_mainloop_unlock(c->mainloop);
529
530 if (stream) {
531 pa_stream_unref (stream);
532 }
533
534 *rerror = pa_context_errno(c->context);
535
536 return NULL;
537}
538
539static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
540 void *drv_opaque)
541{
542 int error;
543 pa_sample_spec ss;
544 pa_buffer_attr ba;
545 struct audsettings obt_as = *as;
546 PAVoiceOut *pa = (PAVoiceOut *) hw;
547 paaudio *g = pa->g = drv_opaque;
548 AudiodevPaOptions *popts = &g->dev->u.pa;
549 AudiodevPaPerDirectionOptions *ppdo = popts->out;
550 PAConnection *c = g->conn;
551
552 ss.format = audfmt_to_pa (as->fmt, as->endianness);
553 ss.channels = as->nchannels;
554 ss.rate = as->freq;
555
556 ba.tlength = pa_usec_to_bytes(ppdo->latency, &ss);
557 ba.minreq = -1;
558 ba.maxlength = -1;
559 ba.prebuf = -1;
560
561 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
562
563 pa->stream = qpa_simple_new (
564 c,
565 "qemu",
566 PA_STREAM_PLAYBACK,
567 ppdo->has_name ? ppdo->name : NULL,
568 &ss,
569 NULL, /* channel map */
570 &ba, /* buffering attributes */
571 &error
572 );
573 if (!pa->stream) {
574 qpa_logerr (error, "pa_simple_new for playback failed\n");
575 goto fail1;
576 }
577
578 audio_pcm_init_info (&hw->info, &obt_as);
579 hw->samples = pa->samples = audio_buffer_samples(
580 qapi_AudiodevPaPerDirectionOptions_base(ppdo),
581 &obt_as, ppdo->buffer_length);
582 pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
583 pa->rpos = hw->rpos;
584 if (!pa->pcm_buf) {
585 dolog("Could not allocate buffer (%zu bytes)\n",
586 hw->samples << hw->info.shift);
587 goto fail2;
588 }
589
590 if (audio_pt_init(&pa->pt, qpa_thread_out, hw, AUDIO_CAP, __func__)) {
591 goto fail3;
592 }
593
594 return 0;
595
596 fail3:
597 g_free (pa->pcm_buf);
598 pa->pcm_buf = NULL;
599 fail2:
600 if (pa->stream) {
601 pa_stream_unref (pa->stream);
602 pa->stream = NULL;
603 }
604 fail1:
605 return -1;
606}
607
608static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
609{
610 int error;
611 pa_sample_spec ss;
612 pa_buffer_attr ba;
613 struct audsettings obt_as = *as;
614 PAVoiceIn *pa = (PAVoiceIn *) hw;
615 paaudio *g = pa->g = drv_opaque;
616 AudiodevPaOptions *popts = &g->dev->u.pa;
617 AudiodevPaPerDirectionOptions *ppdo = popts->in;
618 PAConnection *c = g->conn;
619
620 ss.format = audfmt_to_pa (as->fmt, as->endianness);
621 ss.channels = as->nchannels;
622 ss.rate = as->freq;
623
624 ba.fragsize = pa_usec_to_bytes(ppdo->latency, &ss);
625 ba.maxlength = pa_usec_to_bytes(ppdo->latency * 2, &ss);
626 ba.minreq = -1;
627 ba.prebuf = -1;
628
629 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
630
631 pa->stream = qpa_simple_new (
632 c,
633 "qemu",
634 PA_STREAM_RECORD,
635 ppdo->has_name ? ppdo->name : NULL,
636 &ss,
637 NULL, /* channel map */
638 &ba, /* buffering attributes */
639 &error
640 );
641 if (!pa->stream) {
642 qpa_logerr (error, "pa_simple_new for capture failed\n");
643 goto fail1;
644 }
645
646 audio_pcm_init_info (&hw->info, &obt_as);
647 hw->samples = pa->samples = audio_buffer_samples(
648 qapi_AudiodevPaPerDirectionOptions_base(ppdo),
649 &obt_as, ppdo->buffer_length);
650 pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
651 pa->wpos = hw->wpos;
652 if (!pa->pcm_buf) {
653 dolog("Could not allocate buffer (%zu bytes)\n",
654 hw->samples << hw->info.shift);
655 goto fail2;
656 }
657
658 if (audio_pt_init(&pa->pt, qpa_thread_in, hw, AUDIO_CAP, __func__)) {
659 goto fail3;
660 }
661
662 return 0;
663
664 fail3:
665 g_free (pa->pcm_buf);
666 pa->pcm_buf = NULL;
667 fail2:
668 if (pa->stream) {
669 pa_stream_unref (pa->stream);
670 pa->stream = NULL;
671 }
672 fail1:
673 return -1;
674}
675
676static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream)
677{
678 int err;
679
680 pa_threaded_mainloop_lock(c->mainloop);
681 /*
682 * wait until actually connects. workaround pa bug #247
683 * https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/247
684 */
685 while (pa_stream_get_state(stream) == PA_STREAM_CREATING) {
686 pa_threaded_mainloop_wait(c->mainloop);
687 }
688
689 err = pa_stream_disconnect(stream);
690 if (err != 0) {
691 dolog("Failed to disconnect! err=%d\n", err);
692 }
693 pa_stream_unref(stream);
694 pa_threaded_mainloop_unlock(c->mainloop);
695}
696
697static void qpa_fini_out (HWVoiceOut *hw)
698{
699 void *ret;
700 PAVoiceOut *pa = (PAVoiceOut *) hw;
701
702 audio_pt_lock(&pa->pt, __func__);
703 pa->done = 1;
704 audio_pt_unlock_and_signal(&pa->pt, __func__);
705 audio_pt_join(&pa->pt, &ret, __func__);
706
707 if (pa->stream) {
708 qpa_simple_disconnect(pa->g->conn, pa->stream);
709 pa->stream = NULL;
710 }
711
712 audio_pt_fini(&pa->pt, __func__);
713 g_free (pa->pcm_buf);
714 pa->pcm_buf = NULL;
715}
716
717static void qpa_fini_in (HWVoiceIn *hw)
718{
719 void *ret;
720 PAVoiceIn *pa = (PAVoiceIn *) hw;
721
722 audio_pt_lock(&pa->pt, __func__);
723 pa->done = 1;
724 audio_pt_unlock_and_signal(&pa->pt, __func__);
725 audio_pt_join(&pa->pt, &ret, __func__);
726
727 if (pa->stream) {
728 qpa_simple_disconnect(pa->g->conn, pa->stream);
729 pa->stream = NULL;
730 }
731
732 audio_pt_fini(&pa->pt, __func__);
733 g_free (pa->pcm_buf);
734 pa->pcm_buf = NULL;
735}
736
737static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
738{
739 PAVoiceOut *pa = (PAVoiceOut *) hw;
740 pa_operation *op;
741 pa_cvolume v;
742 PAConnection *c = pa->g->conn;
743
744#ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */
745 pa_cvolume_init (&v); /* function is present in 0.9.13+ */
746#endif
747
748 switch (cmd) {
749 case VOICE_VOLUME:
750 {
751 SWVoiceOut *sw;
752 va_list ap;
753
754 va_start (ap, cmd);
755 sw = va_arg (ap, SWVoiceOut *);
756 va_end (ap);
757
758 v.channels = 2;
759 v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
760 v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
761
762 pa_threaded_mainloop_lock(c->mainloop);
763
764 op = pa_context_set_sink_input_volume(c->context,
765 pa_stream_get_index (pa->stream),
766 &v, NULL, NULL);
767 if (!op) {
768 qpa_logerr(pa_context_errno(c->context),
769 "set_sink_input_volume() failed\n");
770 } else {
771 pa_operation_unref(op);
772 }
773
774 op = pa_context_set_sink_input_mute(c->context,
775 pa_stream_get_index (pa->stream),
776 sw->vol.mute, NULL, NULL);
777 if (!op) {
778 qpa_logerr(pa_context_errno(c->context),
779 "set_sink_input_mute() failed\n");
780 } else {
781 pa_operation_unref(op);
782 }
783
784 pa_threaded_mainloop_unlock(c->mainloop);
785 }
786 }
787 return 0;
788}
789
790static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
791{
792 PAVoiceIn *pa = (PAVoiceIn *) hw;
793 pa_operation *op;
794 pa_cvolume v;
795 PAConnection *c = pa->g->conn;
796
797#ifdef PA_CHECK_VERSION
798 pa_cvolume_init (&v);
799#endif
800
801 switch (cmd) {
802 case VOICE_VOLUME:
803 {
804 SWVoiceIn *sw;
805 va_list ap;
806
807 va_start (ap, cmd);
808 sw = va_arg (ap, SWVoiceIn *);
809 va_end (ap);
810
811 v.channels = 2;
812 v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
813 v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
814
815 pa_threaded_mainloop_lock(c->mainloop);
816
817 op = pa_context_set_source_output_volume(c->context,
818 pa_stream_get_index(pa->stream),
819 &v, NULL, NULL);
820 if (!op) {
821 qpa_logerr(pa_context_errno(c->context),
822 "set_source_output_volume() failed\n");
823 } else {
824 pa_operation_unref(op);
825 }
826
827 op = pa_context_set_source_output_mute(c->context,
828 pa_stream_get_index (pa->stream),
829 sw->vol.mute, NULL, NULL);
830 if (!op) {
831 qpa_logerr(pa_context_errno(c->context),
832 "set_source_output_mute() failed\n");
833 } else {
834 pa_operation_unref (op);
835 }
836
837 pa_threaded_mainloop_unlock(c->mainloop);
838 }
839 }
840 return 0;
841}
842
843static int qpa_validate_per_direction_opts(Audiodev *dev,
844 AudiodevPaPerDirectionOptions *pdo)
845{
846 if (!pdo->has_buffer_length) {
847 pdo->has_buffer_length = true;
848 pdo->buffer_length = 46440;
849 }
850 if (!pdo->has_latency) {
851 pdo->has_latency = true;
852 pdo->latency = 15000;
853 }
854 return 1;
855}
856
857/* common */
858static void *qpa_conn_init(const char *server)
859{
860 PAConnection *c = g_malloc0(sizeof(PAConnection));
861 QTAILQ_INSERT_TAIL(&pa_conns, c, list);
862
863 c->mainloop = pa_threaded_mainloop_new();
864 if (!c->mainloop) {
865 goto fail;
866 }
867
868 c->context = pa_context_new(pa_threaded_mainloop_get_api(c->mainloop),
869 server);
870 if (!c->context) {
871 goto fail;
872 }
873
874 pa_context_set_state_callback(c->context, context_state_cb, c);
875
876 if (pa_context_connect(c->context, server, 0, NULL) < 0) {
877 qpa_logerr(pa_context_errno(c->context),
878 "pa_context_connect() failed\n");
879 goto fail;
880 }
881
882 pa_threaded_mainloop_lock(c->mainloop);
883
884 if (pa_threaded_mainloop_start(c->mainloop) < 0) {
885 goto unlock_and_fail;
886 }
887
888 for (;;) {
889 pa_context_state_t state;
890
891 state = pa_context_get_state(c->context);
892
893 if (state == PA_CONTEXT_READY) {
894 break;
895 }
896
897 if (!PA_CONTEXT_IS_GOOD(state)) {
898 qpa_logerr(pa_context_errno(c->context),
899 "Wrong context state\n");
900 goto unlock_and_fail;
901 }
902
903 /* Wait until the context is ready */
904 pa_threaded_mainloop_wait(c->mainloop);
905 }
906
907 pa_threaded_mainloop_unlock(c->mainloop);
908 return c;
909
910unlock_and_fail:
911 pa_threaded_mainloop_unlock(c->mainloop);
912fail:
913 AUD_log (AUDIO_CAP, "Failed to initialize PA context");
914 qpa_conn_fini(c);
915 return NULL;
916}
917
918static void *qpa_audio_init(Audiodev *dev)
919{
920 paaudio *g;
921 AudiodevPaOptions *popts = &dev->u.pa;
922 const char *server;
923 PAConnection *c;
924
925 assert(dev->driver == AUDIODEV_DRIVER_PA);
926
927 if (!popts->has_server) {
928 char pidfile[64];
929 char *runtime;
930 struct stat st;
931
932 runtime = getenv("XDG_RUNTIME_DIR");
933 if (!runtime) {
934 return NULL;
935 }
936 snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime);
937 if (stat(pidfile, &st) != 0) {
938 return NULL;
939 }
940 }
941
942 if (!qpa_validate_per_direction_opts(dev, popts->in)) {
943 return NULL;
944 }
945 if (!qpa_validate_per_direction_opts(dev, popts->out)) {
946 return NULL;
947 }
948
949 g = g_malloc0(sizeof(paaudio));
950 server = popts->has_server ? popts->server : NULL;
951
952 g->dev = dev;
953
954 QTAILQ_FOREACH(c, &pa_conns, list) {
955 if (server == NULL || c->server == NULL ?
956 server == c->server :
957 strcmp(server, c->server) == 0) {
958 g->conn = c;
959 break;
960 }
961 }
962 if (!g->conn) {
963 g->conn = qpa_conn_init(server);
964 }
965 if (!g->conn) {
966 g_free(g);
967 return NULL;
968 }
969
970 ++g->conn->refcount;
971 return g;
972}
973
974static void qpa_conn_fini(PAConnection *c)
975{
976 if (c->mainloop) {
977 pa_threaded_mainloop_stop(c->mainloop);
978 }
979
980 if (c->context) {
981 pa_context_disconnect(c->context);
982 pa_context_unref(c->context);
983 }
984
985 if (c->mainloop) {
986 pa_threaded_mainloop_free(c->mainloop);
987 }
988
989 QTAILQ_REMOVE(&pa_conns, c, list);
990 g_free(c);
991}
992
993static void qpa_audio_fini (void *opaque)
994{
995 paaudio *g = opaque;
996 PAConnection *c = g->conn;
997
998 if (--c->refcount == 0) {
999 qpa_conn_fini(c);
1000 }
1001
1002 g_free(g);
1003}
1004
1005static struct audio_pcm_ops qpa_pcm_ops = {
1006 .init_out = qpa_init_out,
1007 .fini_out = qpa_fini_out,
1008 .run_out = qpa_run_out,
1009 .ctl_out = qpa_ctl_out,
1010
1011 .init_in = qpa_init_in,
1012 .fini_in = qpa_fini_in,
1013 .run_in = qpa_run_in,
1014 .ctl_in = qpa_ctl_in
1015};
1016
1017static struct audio_driver pa_audio_driver = {
1018 .name = "pa",
1019 .descr = "http://www.pulseaudio.org/",
1020 .init = qpa_audio_init,
1021 .fini = qpa_audio_fini,
1022 .pcm_ops = &qpa_pcm_ops,
1023 .can_be_default = 1,
1024 .max_voices_out = INT_MAX,
1025 .max_voices_in = INT_MAX,
1026 .voice_size_out = sizeof (PAVoiceOut),
1027 .voice_size_in = sizeof (PAVoiceIn),
1028 .ctl_caps = VOICE_VOLUME_CAP
1029};
1030
1031static void register_audio_pa(void)
1032{
1033 audio_driver_register(&pa_audio_driver);
1034}
1035type_init(register_audio_pa);
1036