qcow2: Order concurrent AIO requests on the same unallocated cluster
[armpft.git] / audio / paaudio.c
blobcf415f4402162b11f50eb4a191c02296fdada899
1 /* public domain */
2 #include "qemu-common.h"
3 #include "audio.h"
5 #include <pulse/simple.h>
6 #include <pulse/error.h>
8 #define AUDIO_CAP "pulseaudio"
9 #include "audio_int.h"
10 #include "audio_pt_int.h"
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;
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;
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,
45 static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
47 va_list ap;
49 va_start (ap, fmt);
50 AUD_vlog (AUDIO_CAP, fmt, ap);
51 va_end (ap);
53 AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
56 static void *qpa_thread_out (void *arg)
58 PAVoiceOut *pa = arg;
59 HWVoiceOut *hw = &pa->hw;
60 int threshold;
62 threshold = conf.divisor ? hw->samples / conf.divisor : 0;
64 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
65 return NULL;
68 for (;;) {
69 int decr, to_mix, rpos;
71 for (;;) {
72 if (pa->done) {
73 goto exit;
76 if (pa->live > threshold) {
77 break;
80 if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
81 goto exit;
85 decr = to_mix = pa->live;
86 rpos = hw->rpos;
88 if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
89 return NULL;
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;
97 hw->clip (pa->pcm_buf, src, chunk);
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;
105 rpos = (rpos + chunk) % hw->samples;
106 to_mix -= chunk;
109 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
110 return NULL;
113 pa->rpos = rpos;
114 pa->live -= decr;
115 pa->decr += decr;
118 exit:
119 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
120 return NULL;
123 static int qpa_run_out (HWVoiceOut *hw)
125 int live, decr;
126 PAVoiceOut *pa = (PAVoiceOut *) hw;
128 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
129 return 0;
132 live = audio_pcm_hw_get_live_out (hw);
133 decr = audio_MIN (live, pa->decr);
134 pa->decr -= decr;
135 pa->live = live - decr;
136 hw->rpos = pa->rpos;
137 if (pa->live > 0) {
138 audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
140 else {
141 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
143 return decr;
146 static int qpa_write (SWVoiceOut *sw, void *buf, int len)
148 return audio_pcm_sw_write (sw, buf, len);
151 /* capture */
152 static void *qpa_thread_in (void *arg)
154 PAVoiceIn *pa = arg;
155 HWVoiceIn *hw = &pa->hw;
156 int threshold;
158 threshold = conf.divisor ? hw->samples / conf.divisor : 0;
160 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
161 return NULL;
164 for (;;) {
165 int incr, to_grab, wpos;
167 for (;;) {
168 if (pa->done) {
169 goto exit;
172 if (pa->dead > threshold) {
173 break;
176 if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
177 goto exit;
181 incr = to_grab = pa->dead;
182 wpos = hw->wpos;
184 if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
185 return NULL;
188 while (to_grab) {
189 int error;
190 int chunk = audio_MIN (to_grab, hw->samples - wpos);
191 void *buf = advance (pa->pcm_buf, wpos);
193 if (pa_simple_read (pa->s, buf,
194 chunk << hw->info.shift, &error) < 0) {
195 qpa_logerr (error, "pa_simple_read failed\n");
196 return NULL;
199 hw->conv (hw->conv_buf + wpos, buf, chunk, &nominal_volume);
200 wpos = (wpos + chunk) % hw->samples;
201 to_grab -= chunk;
204 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
205 return NULL;
208 pa->wpos = wpos;
209 pa->dead -= incr;
210 pa->incr += incr;
213 exit:
214 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
215 return NULL;
218 static int qpa_run_in (HWVoiceIn *hw)
220 int live, incr, dead;
221 PAVoiceIn *pa = (PAVoiceIn *) hw;
223 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
224 return 0;
227 live = audio_pcm_hw_get_live_in (hw);
228 dead = hw->samples - live;
229 incr = audio_MIN (dead, pa->incr);
230 pa->incr -= incr;
231 pa->dead = dead - incr;
232 hw->wpos = pa->wpos;
233 if (pa->dead > 0) {
234 audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
236 else {
237 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
239 return incr;
242 static int qpa_read (SWVoiceIn *sw, void *buf, int len)
244 return audio_pcm_sw_read (sw, buf, len);
247 static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
249 int format;
251 switch (afmt) {
252 case AUD_FMT_S8:
253 case AUD_FMT_U8:
254 format = PA_SAMPLE_U8;
255 break;
256 case AUD_FMT_S16:
257 case AUD_FMT_U16:
258 format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
259 break;
260 case AUD_FMT_S32:
261 case AUD_FMT_U32:
262 format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
263 break;
264 default:
265 dolog ("Internal logic error: Bad audio format %d\n", afmt);
266 format = PA_SAMPLE_U8;
267 break;
269 return format;
272 static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
274 switch (fmt) {
275 case PA_SAMPLE_U8:
276 return AUD_FMT_U8;
277 case PA_SAMPLE_S16BE:
278 *endianness = 1;
279 return AUD_FMT_S16;
280 case PA_SAMPLE_S16LE:
281 *endianness = 0;
282 return AUD_FMT_S16;
283 case PA_SAMPLE_S32BE:
284 *endianness = 1;
285 return AUD_FMT_S32;
286 case PA_SAMPLE_S32LE:
287 *endianness = 0;
288 return AUD_FMT_S32;
289 default:
290 dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
291 return AUD_FMT_U8;
295 static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
297 int error;
298 static pa_sample_spec ss;
299 struct audsettings obt_as = *as;
300 PAVoiceOut *pa = (PAVoiceOut *) hw;
302 ss.format = audfmt_to_pa (as->fmt, as->endianness);
303 ss.channels = as->nchannels;
304 ss.rate = as->freq;
306 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
308 pa->s = pa_simple_new (
309 conf.server,
310 "qemu",
311 PA_STREAM_PLAYBACK,
312 conf.sink,
313 "pcm.playback",
314 &ss,
315 NULL, /* channel map */
316 NULL, /* buffering attributes */
317 &error
319 if (!pa->s) {
320 qpa_logerr (error, "pa_simple_new for playback failed\n");
321 goto fail1;
324 audio_pcm_init_info (&hw->info, &obt_as);
325 hw->samples = conf.samples;
326 pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
327 if (!pa->pcm_buf) {
328 dolog ("Could not allocate buffer (%d bytes)\n",
329 hw->samples << hw->info.shift);
330 goto fail2;
333 if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
334 goto fail3;
337 return 0;
339 fail3:
340 qemu_free (pa->pcm_buf);
341 pa->pcm_buf = NULL;
342 fail2:
343 pa_simple_free (pa->s);
344 pa->s = NULL;
345 fail1:
346 return -1;
349 static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
351 int error;
352 static pa_sample_spec ss;
353 struct audsettings obt_as = *as;
354 PAVoiceIn *pa = (PAVoiceIn *) hw;
356 ss.format = audfmt_to_pa (as->fmt, as->endianness);
357 ss.channels = as->nchannels;
358 ss.rate = as->freq;
360 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
362 pa->s = pa_simple_new (
363 conf.server,
364 "qemu",
365 PA_STREAM_RECORD,
366 conf.source,
367 "pcm.capture",
368 &ss,
369 NULL, /* channel map */
370 NULL, /* buffering attributes */
371 &error
373 if (!pa->s) {
374 qpa_logerr (error, "pa_simple_new for capture failed\n");
375 goto fail1;
378 audio_pcm_init_info (&hw->info, &obt_as);
379 hw->samples = conf.samples;
380 pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
381 if (!pa->pcm_buf) {
382 dolog ("Could not allocate buffer (%d bytes)\n",
383 hw->samples << hw->info.shift);
384 goto fail2;
387 if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
388 goto fail3;
391 return 0;
393 fail3:
394 qemu_free (pa->pcm_buf);
395 pa->pcm_buf = NULL;
396 fail2:
397 pa_simple_free (pa->s);
398 pa->s = NULL;
399 fail1:
400 return -1;
403 static void qpa_fini_out (HWVoiceOut *hw)
405 void *ret;
406 PAVoiceOut *pa = (PAVoiceOut *) hw;
408 audio_pt_lock (&pa->pt, AUDIO_FUNC);
409 pa->done = 1;
410 audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
411 audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
413 if (pa->s) {
414 pa_simple_free (pa->s);
415 pa->s = NULL;
418 audio_pt_fini (&pa->pt, AUDIO_FUNC);
419 qemu_free (pa->pcm_buf);
420 pa->pcm_buf = NULL;
423 static void qpa_fini_in (HWVoiceIn *hw)
425 void *ret;
426 PAVoiceIn *pa = (PAVoiceIn *) hw;
428 audio_pt_lock (&pa->pt, AUDIO_FUNC);
429 pa->done = 1;
430 audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
431 audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
433 if (pa->s) {
434 pa_simple_free (pa->s);
435 pa->s = NULL;
438 audio_pt_fini (&pa->pt, AUDIO_FUNC);
439 qemu_free (pa->pcm_buf);
440 pa->pcm_buf = NULL;
443 static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
445 (void) hw;
446 (void) cmd;
447 return 0;
450 static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
452 (void) hw;
453 (void) cmd;
454 return 0;
457 /* common */
458 static void *qpa_audio_init (void)
460 return &conf;
463 static void qpa_audio_fini (void *opaque)
465 (void) opaque;
468 struct audio_option qpa_options[] = {
470 .name = "SAMPLES",
471 .tag = AUD_OPT_INT,
472 .valp = &conf.samples,
473 .descr = "buffer size in samples"
476 .name = "DIVISOR",
477 .tag = AUD_OPT_INT,
478 .valp = &conf.divisor,
479 .descr = "threshold divisor"
482 .name = "SERVER",
483 .tag = AUD_OPT_STR,
484 .valp = &conf.server,
485 .descr = "server address"
488 .name = "SINK",
489 .tag = AUD_OPT_STR,
490 .valp = &conf.sink,
491 .descr = "sink device name"
494 .name = "SOURCE",
495 .tag = AUD_OPT_STR,
496 .valp = &conf.source,
497 .descr = "source device name"
499 { /* End of list */ }
502 static struct audio_pcm_ops qpa_pcm_ops = {
503 .init_out = qpa_init_out,
504 .fini_out = qpa_fini_out,
505 .run_out = qpa_run_out,
506 .write = qpa_write,
507 .ctl_out = qpa_ctl_out,
509 .init_in = qpa_init_in,
510 .fini_in = qpa_fini_in,
511 .run_in = qpa_run_in,
512 .read = qpa_read,
513 .ctl_in = qpa_ctl_in
516 struct audio_driver pa_audio_driver = {
517 .name = "pa",
518 .descr = "http://www.pulseaudio.org/",
519 .options = qpa_options,
520 .init = qpa_audio_init,
521 .fini = qpa_audio_fini,
522 .pcm_ops = &qpa_pcm_ops,
523 .can_be_default = 0,
524 .max_voices_out = INT_MAX,
525 .max_voices_in = INT_MAX,
526 .voice_size_out = sizeof (PAVoiceOut),
527 .voice_size_in = sizeof (PAVoiceIn)