xref: /openbmc/qemu/audio/paaudio.c (revision 1fd6bb44)
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 #ifndef PA_CONTEXT_IS_GOOD
60 static 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
71 static 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 
107 static 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 
156 unlock_and_fail:
157     pa_threaded_mainloop_unlock (g->mainloop);
158     return -1;
159 }
160 
161 static 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 
194 unlock_and_fail:
195     pa_threaded_mainloop_unlock (g->mainloop);
196     return -1;
197 }
198 
199 static 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 
263 static 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 
285 static int qpa_write (SWVoiceOut *sw, void *buf, int len)
286 {
287     return audio_pcm_sw_write (sw, buf, len);
288 }
289 
290 /* capture */
291 static 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 
354 static 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 
378 static int qpa_read (SWVoiceIn *sw, void *buf, int len)
379 {
380     return audio_pcm_sw_read (sw, buf, len);
381 }
382 
383 static 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 
408 static 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 
431 static 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 
450 static 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 
468 static 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 
475 static 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 
525 fail:
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 
537 static 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      * qemu audio tick runs at 250 Hz (by default), so processing
551      * data chunks worth 4 ms of sound should be a good fit.
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,                   /* channel map */
568         &ba,                    /* buffering attributes */
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 
604 static 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,                   /* channel map */
625         NULL,                   /* buffering attributes */
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 
661 static 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 
681 static 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 
701 static 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    /* macro is present in 0.9.16+ */
709     pa_cvolume_init (&v);  /* function is present in 0.9.13+ */
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 
753 static 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             /* FIXME: use the upcoming "set_source_output_{volume,mute}" */
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 /* common */
808 static 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         /* Wait until the context is ready */
852         pa_threaded_mainloop_wait (g->mainloop);
853     }
854 
855     pa_threaded_mainloop_unlock (g->mainloop);
856 
857     return &glob_paaudio;
858 
859 unlock_and_fail:
860     pa_threaded_mainloop_unlock (g->mainloop);
861 fail:
862     AUD_log (AUDIO_CAP, "Failed to initialize PA context");
863     return NULL;
864 }
865 
866 static 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 
887 struct 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     { /* End of list */ }
913 };
914 
915 static 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 
929 struct 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