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