xref: /openbmc/qemu/audio/paaudio.c (revision 4fabffc11234d0587a213418574095e036110cd2)
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     qpa_logerr (pa_context_errno (g->context),
509                 "stream_new() failed\n");
510 
511     return NULL;
512 }
513 
514 static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
515 {
516     int error;
517     static pa_sample_spec ss;
518     static pa_buffer_attr ba;
519     struct audsettings obt_as = *as;
520     PAVoiceOut *pa = (PAVoiceOut *) hw;
521 
522     ss.format = audfmt_to_pa (as->fmt, as->endianness);
523     ss.channels = as->nchannels;
524     ss.rate = as->freq;
525 
526     /*
527      * qemu audio tick runs at 250 Hz (by default), so processing
528      * data chunks worth 4 ms of sound should be a good fit.
529      */
530     ba.tlength = pa_usec_to_bytes (4 * 1000, &ss);
531     ba.minreq = pa_usec_to_bytes (2 * 1000, &ss);
532     ba.maxlength = -1;
533     ba.prebuf = -1;
534 
535     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
536 
537     pa->stream = qpa_simple_new (
538         glob_paaudio.server,
539         "qemu",
540         PA_STREAM_PLAYBACK,
541         glob_paaudio.sink,
542         "pcm.playback",
543         &ss,
544         NULL,                   /* channel map */
545         &ba,                    /* buffering attributes */
546         &error
547         );
548     if (!pa->stream) {
549         qpa_logerr (error, "pa_simple_new for playback failed\n");
550         goto fail1;
551     }
552 
553     audio_pcm_init_info (&hw->info, &obt_as);
554     hw->samples = glob_paaudio.samples;
555     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
556     pa->rpos = hw->rpos;
557     if (!pa->pcm_buf) {
558         dolog ("Could not allocate buffer (%d bytes)\n",
559                hw->samples << hw->info.shift);
560         goto fail2;
561     }
562 
563     if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
564         goto fail3;
565     }
566 
567     return 0;
568 
569  fail3:
570     g_free (pa->pcm_buf);
571     pa->pcm_buf = NULL;
572  fail2:
573     if (pa->stream) {
574         pa_stream_unref (pa->stream);
575         pa->stream = NULL;
576     }
577  fail1:
578     return -1;
579 }
580 
581 static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
582 {
583     int error;
584     static pa_sample_spec ss;
585     struct audsettings obt_as = *as;
586     PAVoiceIn *pa = (PAVoiceIn *) hw;
587 
588     ss.format = audfmt_to_pa (as->fmt, as->endianness);
589     ss.channels = as->nchannels;
590     ss.rate = as->freq;
591 
592     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
593 
594     pa->stream = qpa_simple_new (
595         glob_paaudio.server,
596         "qemu",
597         PA_STREAM_RECORD,
598         glob_paaudio.source,
599         "pcm.capture",
600         &ss,
601         NULL,                   /* channel map */
602         NULL,                   /* buffering attributes */
603         &error
604         );
605     if (!pa->stream) {
606         qpa_logerr (error, "pa_simple_new for capture failed\n");
607         goto fail1;
608     }
609 
610     audio_pcm_init_info (&hw->info, &obt_as);
611     hw->samples = glob_paaudio.samples;
612     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
613     pa->wpos = hw->wpos;
614     if (!pa->pcm_buf) {
615         dolog ("Could not allocate buffer (%d bytes)\n",
616                hw->samples << hw->info.shift);
617         goto fail2;
618     }
619 
620     if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
621         goto fail3;
622     }
623 
624     return 0;
625 
626  fail3:
627     g_free (pa->pcm_buf);
628     pa->pcm_buf = NULL;
629  fail2:
630     if (pa->stream) {
631         pa_stream_unref (pa->stream);
632         pa->stream = NULL;
633     }
634  fail1:
635     return -1;
636 }
637 
638 static void qpa_fini_out (HWVoiceOut *hw)
639 {
640     void *ret;
641     PAVoiceOut *pa = (PAVoiceOut *) hw;
642 
643     audio_pt_lock (&pa->pt, AUDIO_FUNC);
644     pa->done = 1;
645     audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
646     audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
647 
648     if (pa->stream) {
649         pa_stream_unref (pa->stream);
650         pa->stream = NULL;
651     }
652 
653     audio_pt_fini (&pa->pt, AUDIO_FUNC);
654     g_free (pa->pcm_buf);
655     pa->pcm_buf = NULL;
656 }
657 
658 static void qpa_fini_in (HWVoiceIn *hw)
659 {
660     void *ret;
661     PAVoiceIn *pa = (PAVoiceIn *) hw;
662 
663     audio_pt_lock (&pa->pt, AUDIO_FUNC);
664     pa->done = 1;
665     audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
666     audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
667 
668     if (pa->stream) {
669         pa_stream_unref (pa->stream);
670         pa->stream = NULL;
671     }
672 
673     audio_pt_fini (&pa->pt, AUDIO_FUNC);
674     g_free (pa->pcm_buf);
675     pa->pcm_buf = NULL;
676 }
677 
678 static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
679 {
680     PAVoiceOut *pa = (PAVoiceOut *) hw;
681     pa_operation *op;
682     pa_cvolume v;
683     paaudio *g = &glob_paaudio;
684 
685     pa_cvolume_init (&v);
686 
687     switch (cmd) {
688     case VOICE_VOLUME:
689         {
690             SWVoiceOut *sw;
691             va_list ap;
692 
693             va_start (ap, cmd);
694             sw = va_arg (ap, SWVoiceOut *);
695             va_end (ap);
696 
697             v.channels = 2;
698             v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
699             v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
700 
701             pa_threaded_mainloop_lock (g->mainloop);
702 
703             op = pa_context_set_sink_input_volume (g->context,
704                 pa_stream_get_index (pa->stream),
705                 &v, NULL, NULL);
706             if (!op)
707                 qpa_logerr (pa_context_errno (g->context),
708                             "set_sink_input_volume() failed\n");
709             else
710                 pa_operation_unref (op);
711 
712             op = pa_context_set_sink_input_mute (g->context,
713                 pa_stream_get_index (pa->stream),
714                sw->vol.mute, NULL, NULL);
715             if (!op) {
716                 qpa_logerr (pa_context_errno (g->context),
717                             "set_sink_input_mute() failed\n");
718             } else {
719                 pa_operation_unref (op);
720             }
721 
722             pa_threaded_mainloop_unlock (g->mainloop);
723         }
724     }
725     return 0;
726 }
727 
728 static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
729 {
730     PAVoiceIn *pa = (PAVoiceIn *) hw;
731     pa_operation *op;
732     pa_cvolume v;
733     paaudio *g = &glob_paaudio;
734 
735     pa_cvolume_init (&v);
736 
737     switch (cmd) {
738     case VOICE_VOLUME:
739         {
740             SWVoiceIn *sw;
741             va_list ap;
742 
743             va_start (ap, cmd);
744             sw = va_arg (ap, SWVoiceIn *);
745             va_end (ap);
746 
747             v.channels = 2;
748             v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
749             v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
750 
751             pa_threaded_mainloop_lock (g->mainloop);
752 
753             /* FIXME: use the upcoming "set_source_output_{volume,mute}" */
754             op = pa_context_set_source_volume_by_index (g->context,
755                 pa_stream_get_device_index (pa->stream),
756                 &v, NULL, NULL);
757             if (!op) {
758                 qpa_logerr (pa_context_errno (g->context),
759                             "set_source_volume() failed\n");
760             } else {
761                 pa_operation_unref(op);
762             }
763 
764             op = pa_context_set_source_mute_by_index (g->context,
765                 pa_stream_get_index (pa->stream),
766                 sw->vol.mute, NULL, NULL);
767             if (!op) {
768                 qpa_logerr (pa_context_errno (g->context),
769                             "set_source_mute() failed\n");
770             } else {
771                 pa_operation_unref (op);
772             }
773 
774             pa_threaded_mainloop_unlock (g->mainloop);
775         }
776     }
777     return 0;
778 }
779 
780 /* common */
781 static void *qpa_audio_init (void)
782 {
783     paaudio *g = &glob_paaudio;
784 
785     g->mainloop = pa_threaded_mainloop_new ();
786     if (!g->mainloop) {
787         goto fail;
788     }
789 
790     g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), glob_paaudio.server);
791     if (!g->context) {
792         goto fail;
793     }
794 
795     pa_context_set_state_callback (g->context, context_state_cb, g);
796 
797     if (pa_context_connect (g->context, glob_paaudio.server, 0, NULL) < 0) {
798         qpa_logerr (pa_context_errno (g->context),
799                     "pa_context_connect() failed\n");
800         goto fail;
801     }
802 
803     pa_threaded_mainloop_lock (g->mainloop);
804 
805     if (pa_threaded_mainloop_start (g->mainloop) < 0) {
806         goto unlock_and_fail;
807     }
808 
809     for (;;) {
810         pa_context_state_t state;
811 
812         state = pa_context_get_state (g->context);
813 
814         if (state == PA_CONTEXT_READY) {
815             break;
816         }
817 
818         if (!PA_CONTEXT_IS_GOOD (state)) {
819             qpa_logerr (pa_context_errno (g->context),
820                         "Wrong context state\n");
821             goto unlock_and_fail;
822         }
823 
824         /* Wait until the context is ready */
825         pa_threaded_mainloop_wait (g->mainloop);
826     }
827 
828     pa_threaded_mainloop_unlock (g->mainloop);
829 
830     return &glob_paaudio;
831 
832 unlock_and_fail:
833     pa_threaded_mainloop_unlock (g->mainloop);
834 fail:
835     AUD_log (AUDIO_CAP, "Failed to initialize PA context");
836     return NULL;
837 }
838 
839 static void qpa_audio_fini (void *opaque)
840 {
841     paaudio *g = opaque;
842 
843     if (g->mainloop) {
844         pa_threaded_mainloop_stop (g->mainloop);
845     }
846 
847     if (g->context) {
848         pa_context_disconnect (g->context);
849         pa_context_unref (g->context);
850         g->context = NULL;
851     }
852 
853     if (g->mainloop) {
854         pa_threaded_mainloop_free (g->mainloop);
855     }
856 
857     g->mainloop = NULL;
858 }
859 
860 struct audio_option qpa_options[] = {
861     {
862         .name  = "SAMPLES",
863         .tag   = AUD_OPT_INT,
864         .valp  = &glob_paaudio.samples,
865         .descr = "buffer size in samples"
866     },
867     {
868         .name  = "SERVER",
869         .tag   = AUD_OPT_STR,
870         .valp  = &glob_paaudio.server,
871         .descr = "server address"
872     },
873     {
874         .name  = "SINK",
875         .tag   = AUD_OPT_STR,
876         .valp  = &glob_paaudio.sink,
877         .descr = "sink device name"
878     },
879     {
880         .name  = "SOURCE",
881         .tag   = AUD_OPT_STR,
882         .valp  = &glob_paaudio.source,
883         .descr = "source device name"
884     },
885     { /* End of list */ }
886 };
887 
888 static struct audio_pcm_ops qpa_pcm_ops = {
889     .init_out = qpa_init_out,
890     .fini_out = qpa_fini_out,
891     .run_out  = qpa_run_out,
892     .write    = qpa_write,
893     .ctl_out  = qpa_ctl_out,
894 
895     .init_in  = qpa_init_in,
896     .fini_in  = qpa_fini_in,
897     .run_in   = qpa_run_in,
898     .read     = qpa_read,
899     .ctl_in   = qpa_ctl_in
900 };
901 
902 struct audio_driver pa_audio_driver = {
903     .name           = "pa",
904     .descr          = "http://www.pulseaudio.org/",
905     .options        = qpa_options,
906     .init           = qpa_audio_init,
907     .fini           = qpa_audio_fini,
908     .pcm_ops        = &qpa_pcm_ops,
909     .can_be_default = 1,
910     .max_voices_out = INT_MAX,
911     .max_voices_in  = INT_MAX,
912     .voice_size_out = sizeof (PAVoiceOut),
913     .voice_size_in  = sizeof (PAVoiceIn),
914     .ctl_caps       = VOICE_VOLUME_CAP
915 };
916