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