xref: /qemu/audio/paaudio.c (revision 99a0949b720a0936da2052cb9a46db04ffc6db29)
1 /* public domain */
2 #include "qemu-common.h"
3 #include "audio.h"
4 
5 #include <pulse/simple.h>
6 #include <pulse/error.h>
7 
8 #define AUDIO_CAP "pulseaudio"
9 #include "audio_int.h"
10 #include "audio_pt_int.h"
11 
12 typedef struct {
13     HWVoiceOut hw;
14     int done;
15     int live;
16     int decr;
17     int rpos;
18     pa_simple *s;
19     void *pcm_buf;
20     struct audio_pt pt;
21 } PAVoiceOut;
22 
23 typedef struct {
24     HWVoiceIn hw;
25     int done;
26     int dead;
27     int incr;
28     int wpos;
29     pa_simple *s;
30     void *pcm_buf;
31     struct audio_pt pt;
32 } PAVoiceIn;
33 
34 static struct {
35     int samples;
36     int divisor;
37     char *server;
38     char *sink;
39     char *source;
40 } conf = {
41     .samples = 1024,
42     .divisor = 2,
43 };
44 
45 static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
46 {
47     va_list ap;
48 
49     va_start (ap, fmt);
50     AUD_vlog (AUDIO_CAP, fmt, ap);
51     va_end (ap);
52 
53     AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
54 }
55 
56 static void *qpa_thread_out (void *arg)
57 {
58     PAVoiceOut *pa = arg;
59     HWVoiceOut *hw = &pa->hw;
60     int threshold;
61 
62     threshold = conf.divisor ? hw->samples / conf.divisor : 0;
63 
64     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
65         return NULL;
66     }
67 
68     for (;;) {
69         int decr, to_mix, rpos;
70 
71         for (;;) {
72             if (pa->done) {
73                 goto exit;
74             }
75 
76             if (pa->live > threshold) {
77                 break;
78             }
79 
80             if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
81                 goto exit;
82             }
83         }
84 
85         decr = to_mix = pa->live;
86         rpos = hw->rpos;
87 
88         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
89             return NULL;
90         }
91 
92         while (to_mix) {
93             int error;
94             int chunk = audio_MIN (to_mix, hw->samples - rpos);
95             struct st_sample *src = hw->mix_buf + rpos;
96 
97             hw->clip (pa->pcm_buf, src, chunk);
98 
99             if (pa_simple_write (pa->s, pa->pcm_buf,
100                                  chunk << hw->info.shift, &error) < 0) {
101                 qpa_logerr (error, "pa_simple_write failed\n");
102                 return NULL;
103             }
104 
105             rpos = (rpos + chunk) % hw->samples;
106             to_mix -= chunk;
107         }
108 
109         if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
110             return NULL;
111         }
112 
113         pa->rpos = rpos;
114         pa->live -= decr;
115         pa->decr += decr;
116     }
117 
118  exit:
119     audio_pt_unlock (&pa->pt, AUDIO_FUNC);
120     return NULL;
121 }
122 
123 static int qpa_run_out (HWVoiceOut *hw, int live)
124 {
125     int decr;
126     PAVoiceOut *pa = (PAVoiceOut *) hw;
127 
128     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
129         return 0;
130     }
131 
132     decr = audio_MIN (live, pa->decr);
133     pa->decr -= decr;
134     pa->live = live - decr;
135     hw->rpos = pa->rpos;
136     if (pa->live > 0) {
137         audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
138     }
139     else {
140         audio_pt_unlock (&pa->pt, AUDIO_FUNC);
141     }
142     return decr;
143 }
144 
145 static int qpa_write (SWVoiceOut *sw, void *buf, int len)
146 {
147     return audio_pcm_sw_write (sw, buf, len);
148 }
149 
150 /* capture */
151 static void *qpa_thread_in (void *arg)
152 {
153     PAVoiceIn *pa = arg;
154     HWVoiceIn *hw = &pa->hw;
155     int threshold;
156 
157     threshold = conf.divisor ? hw->samples / conf.divisor : 0;
158 
159     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
160         return NULL;
161     }
162 
163     for (;;) {
164         int incr, to_grab, wpos;
165 
166         for (;;) {
167             if (pa->done) {
168                 goto exit;
169             }
170 
171             if (pa->dead > threshold) {
172                 break;
173             }
174 
175             if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
176                 goto exit;
177             }
178         }
179 
180         incr = to_grab = pa->dead;
181         wpos = hw->wpos;
182 
183         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
184             return NULL;
185         }
186 
187         while (to_grab) {
188             int error;
189             int chunk = audio_MIN (to_grab, hw->samples - wpos);
190             void *buf = advance (pa->pcm_buf, wpos);
191 
192             if (pa_simple_read (pa->s, buf,
193                                 chunk << hw->info.shift, &error) < 0) {
194                 qpa_logerr (error, "pa_simple_read failed\n");
195                 return NULL;
196             }
197 
198             hw->conv (hw->conv_buf + wpos, buf, chunk, &nominal_volume);
199             wpos = (wpos + chunk) % hw->samples;
200             to_grab -= chunk;
201         }
202 
203         if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
204             return NULL;
205         }
206 
207         pa->wpos = wpos;
208         pa->dead -= incr;
209         pa->incr += incr;
210     }
211 
212  exit:
213     audio_pt_unlock (&pa->pt, AUDIO_FUNC);
214     return NULL;
215 }
216 
217 static int qpa_run_in (HWVoiceIn *hw)
218 {
219     int live, incr, dead;
220     PAVoiceIn *pa = (PAVoiceIn *) hw;
221 
222     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
223         return 0;
224     }
225 
226     live = audio_pcm_hw_get_live_in (hw);
227     dead = hw->samples - live;
228     incr = audio_MIN (dead, pa->incr);
229     pa->incr -= incr;
230     pa->dead = dead - incr;
231     hw->wpos = pa->wpos;
232     if (pa->dead > 0) {
233         audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
234     }
235     else {
236         audio_pt_unlock (&pa->pt, AUDIO_FUNC);
237     }
238     return incr;
239 }
240 
241 static int qpa_read (SWVoiceIn *sw, void *buf, int len)
242 {
243     return audio_pcm_sw_read (sw, buf, len);
244 }
245 
246 static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
247 {
248     int format;
249 
250     switch (afmt) {
251     case AUD_FMT_S8:
252     case AUD_FMT_U8:
253         format = PA_SAMPLE_U8;
254         break;
255     case AUD_FMT_S16:
256     case AUD_FMT_U16:
257         format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
258         break;
259     case AUD_FMT_S32:
260     case AUD_FMT_U32:
261         format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
262         break;
263     default:
264         dolog ("Internal logic error: Bad audio format %d\n", afmt);
265         format = PA_SAMPLE_U8;
266         break;
267     }
268     return format;
269 }
270 
271 static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
272 {
273     switch (fmt) {
274     case PA_SAMPLE_U8:
275         return AUD_FMT_U8;
276     case PA_SAMPLE_S16BE:
277         *endianness = 1;
278         return AUD_FMT_S16;
279     case PA_SAMPLE_S16LE:
280         *endianness = 0;
281         return AUD_FMT_S16;
282     case PA_SAMPLE_S32BE:
283         *endianness = 1;
284         return AUD_FMT_S32;
285     case PA_SAMPLE_S32LE:
286         *endianness = 0;
287         return AUD_FMT_S32;
288     default:
289         dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
290         return AUD_FMT_U8;
291     }
292 }
293 
294 static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
295 {
296     int error;
297     static pa_sample_spec ss;
298     struct audsettings obt_as = *as;
299     PAVoiceOut *pa = (PAVoiceOut *) hw;
300 
301     ss.format = audfmt_to_pa (as->fmt, as->endianness);
302     ss.channels = as->nchannels;
303     ss.rate = as->freq;
304 
305     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
306 
307     pa->s = pa_simple_new (
308         conf.server,
309         "qemu",
310         PA_STREAM_PLAYBACK,
311         conf.sink,
312         "pcm.playback",
313         &ss,
314         NULL,                   /* channel map */
315         NULL,                   /* buffering attributes */
316         &error
317         );
318     if (!pa->s) {
319         qpa_logerr (error, "pa_simple_new for playback failed\n");
320         goto fail1;
321     }
322 
323     audio_pcm_init_info (&hw->info, &obt_as);
324     hw->samples = conf.samples;
325     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
326     if (!pa->pcm_buf) {
327         dolog ("Could not allocate buffer (%d bytes)\n",
328                hw->samples << hw->info.shift);
329         goto fail2;
330     }
331 
332     if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
333         goto fail3;
334     }
335 
336     return 0;
337 
338  fail3:
339     qemu_free (pa->pcm_buf);
340     pa->pcm_buf = NULL;
341  fail2:
342     pa_simple_free (pa->s);
343     pa->s = NULL;
344  fail1:
345     return -1;
346 }
347 
348 static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
349 {
350     int error;
351     static pa_sample_spec ss;
352     struct audsettings obt_as = *as;
353     PAVoiceIn *pa = (PAVoiceIn *) hw;
354 
355     ss.format = audfmt_to_pa (as->fmt, as->endianness);
356     ss.channels = as->nchannels;
357     ss.rate = as->freq;
358 
359     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
360 
361     pa->s = pa_simple_new (
362         conf.server,
363         "qemu",
364         PA_STREAM_RECORD,
365         conf.source,
366         "pcm.capture",
367         &ss,
368         NULL,                   /* channel map */
369         NULL,                   /* buffering attributes */
370         &error
371         );
372     if (!pa->s) {
373         qpa_logerr (error, "pa_simple_new for capture failed\n");
374         goto fail1;
375     }
376 
377     audio_pcm_init_info (&hw->info, &obt_as);
378     hw->samples = conf.samples;
379     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
380     if (!pa->pcm_buf) {
381         dolog ("Could not allocate buffer (%d bytes)\n",
382                hw->samples << hw->info.shift);
383         goto fail2;
384     }
385 
386     if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
387         goto fail3;
388     }
389 
390     return 0;
391 
392  fail3:
393     qemu_free (pa->pcm_buf);
394     pa->pcm_buf = NULL;
395  fail2:
396     pa_simple_free (pa->s);
397     pa->s = NULL;
398  fail1:
399     return -1;
400 }
401 
402 static void qpa_fini_out (HWVoiceOut *hw)
403 {
404     void *ret;
405     PAVoiceOut *pa = (PAVoiceOut *) hw;
406 
407     audio_pt_lock (&pa->pt, AUDIO_FUNC);
408     pa->done = 1;
409     audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
410     audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
411 
412     if (pa->s) {
413         pa_simple_free (pa->s);
414         pa->s = NULL;
415     }
416 
417     audio_pt_fini (&pa->pt, AUDIO_FUNC);
418     qemu_free (pa->pcm_buf);
419     pa->pcm_buf = NULL;
420 }
421 
422 static void qpa_fini_in (HWVoiceIn *hw)
423 {
424     void *ret;
425     PAVoiceIn *pa = (PAVoiceIn *) hw;
426 
427     audio_pt_lock (&pa->pt, AUDIO_FUNC);
428     pa->done = 1;
429     audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
430     audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
431 
432     if (pa->s) {
433         pa_simple_free (pa->s);
434         pa->s = NULL;
435     }
436 
437     audio_pt_fini (&pa->pt, AUDIO_FUNC);
438     qemu_free (pa->pcm_buf);
439     pa->pcm_buf = NULL;
440 }
441 
442 static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
443 {
444     (void) hw;
445     (void) cmd;
446     return 0;
447 }
448 
449 static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
450 {
451     (void) hw;
452     (void) cmd;
453     return 0;
454 }
455 
456 /* common */
457 static void *qpa_audio_init (void)
458 {
459     return &conf;
460 }
461 
462 static void qpa_audio_fini (void *opaque)
463 {
464     (void) opaque;
465 }
466 
467 struct audio_option qpa_options[] = {
468     {
469         .name  = "SAMPLES",
470         .tag   = AUD_OPT_INT,
471         .valp  = &conf.samples,
472         .descr = "buffer size in samples"
473     },
474     {
475         .name  = "DIVISOR",
476         .tag   = AUD_OPT_INT,
477         .valp  = &conf.divisor,
478         .descr = "threshold divisor"
479     },
480     {
481         .name  = "SERVER",
482         .tag   = AUD_OPT_STR,
483         .valp  = &conf.server,
484         .descr = "server address"
485     },
486     {
487         .name  = "SINK",
488         .tag   = AUD_OPT_STR,
489         .valp  = &conf.sink,
490         .descr = "sink device name"
491     },
492     {
493         .name  = "SOURCE",
494         .tag   = AUD_OPT_STR,
495         .valp  = &conf.source,
496         .descr = "source device name"
497     },
498     { /* End of list */ }
499 };
500 
501 static struct audio_pcm_ops qpa_pcm_ops = {
502     .init_out = qpa_init_out,
503     .fini_out = qpa_fini_out,
504     .run_out  = qpa_run_out,
505     .write    = qpa_write,
506     .ctl_out  = qpa_ctl_out,
507 
508     .init_in  = qpa_init_in,
509     .fini_in  = qpa_fini_in,
510     .run_in   = qpa_run_in,
511     .read     = qpa_read,
512     .ctl_in   = qpa_ctl_in
513 };
514 
515 struct audio_driver pa_audio_driver = {
516     .name           = "pa",
517     .descr          = "http://www.pulseaudio.org/",
518     .options        = qpa_options,
519     .init           = qpa_audio_init,
520     .fini           = qpa_audio_fini,
521     .pcm_ops        = &qpa_pcm_ops,
522     .can_be_default = 0,
523     .max_voices_out = INT_MAX,
524     .max_voices_in  = INT_MAX,
525     .voice_size_out = sizeof (PAVoiceOut),
526     .voice_size_in  = sizeof (PAVoiceIn)
527 };
528