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