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