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