notifier: Pass data argument to callback
[qemu/stefanha.git] / audio / paaudio.c
blobfb4510e426cbe8fe4e46121147497a11465845ba
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 char *server;
37 char *sink;
38 char *source;
39 } conf = {
40 .samples = 4096,
43 static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
45 va_list ap;
47 va_start (ap, fmt);
48 AUD_vlog (AUDIO_CAP, fmt, ap);
49 va_end (ap);
51 AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
54 static void *qpa_thread_out (void *arg)
56 PAVoiceOut *pa = arg;
57 HWVoiceOut *hw = &pa->hw;
59 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
60 return NULL;
63 for (;;) {
64 int decr, to_mix, rpos;
66 for (;;) {
67 if (pa->done) {
68 goto exit;
71 if (pa->live > 0) {
72 break;
75 if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
76 goto exit;
80 decr = to_mix = audio_MIN (pa->live, conf.samples >> 2);
81 rpos = pa->rpos;
83 if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
84 return NULL;
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;
92 hw->clip (pa->pcm_buf, src, chunk);
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;
100 rpos = (rpos + chunk) % hw->samples;
101 to_mix -= chunk;
104 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
105 return NULL;
108 pa->rpos = rpos;
109 pa->live -= decr;
110 pa->decr += decr;
113 exit:
114 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
115 return NULL;
118 static int qpa_run_out (HWVoiceOut *hw, int live)
120 int decr;
121 PAVoiceOut *pa = (PAVoiceOut *) hw;
123 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
124 return 0;
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);
134 else {
135 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
137 return decr;
140 static int qpa_write (SWVoiceOut *sw, void *buf, int len)
142 return audio_pcm_sw_write (sw, buf, len);
145 /* capture */
146 static void *qpa_thread_in (void *arg)
148 PAVoiceIn *pa = arg;
149 HWVoiceIn *hw = &pa->hw;
151 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
152 return NULL;
155 for (;;) {
156 int incr, to_grab, wpos;
158 for (;;) {
159 if (pa->done) {
160 goto exit;
163 if (pa->dead > 0) {
164 break;
167 if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
168 goto exit;
172 incr = to_grab = audio_MIN (pa->dead, conf.samples >> 2);
173 wpos = pa->wpos;
175 if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
176 return NULL;
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);
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;
190 hw->conv (hw->conv_buf + wpos, buf, chunk);
191 wpos = (wpos + chunk) % hw->samples;
192 to_grab -= chunk;
195 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
196 return NULL;
199 pa->wpos = wpos;
200 pa->dead -= incr;
201 pa->incr += incr;
204 exit:
205 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
206 return NULL;
209 static int qpa_run_in (HWVoiceIn *hw)
211 int live, incr, dead;
212 PAVoiceIn *pa = (PAVoiceIn *) hw;
214 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
215 return 0;
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);
227 else {
228 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
230 return incr;
233 static int qpa_read (SWVoiceIn *sw, void *buf, int len)
235 return audio_pcm_sw_read (sw, buf, len);
238 static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
240 int format;
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;
260 return format;
263 static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
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;
286 static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
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;
294 ss.format = audfmt_to_pa (as->fmt, as->endianness);
295 ss.channels = as->nchannels;
296 ss.rate = as->freq;
299 * qemu audio tick runs at 250 Hz (by default), so processing
300 * data chunks worth 4 ms of sound should be a good fit.
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;
307 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
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, /* channel map */
317 &ba, /* buffering attributes */
318 &error
320 if (!pa->s) {
321 qpa_logerr (error, "pa_simple_new for playback failed\n");
322 goto fail1;
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;
335 if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
336 goto fail3;
339 return 0;
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;
351 static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
353 int error;
354 static pa_sample_spec ss;
355 struct audsettings obt_as = *as;
356 PAVoiceIn *pa = (PAVoiceIn *) hw;
358 ss.format = audfmt_to_pa (as->fmt, as->endianness);
359 ss.channels = as->nchannels;
360 ss.rate = as->freq;
362 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
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, /* channel map */
372 NULL, /* buffering attributes */
373 &error
375 if (!pa->s) {
376 qpa_logerr (error, "pa_simple_new for capture failed\n");
377 goto fail1;
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;
390 if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
391 goto fail3;
394 return 0;
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;
406 static void qpa_fini_out (HWVoiceOut *hw)
408 void *ret;
409 PAVoiceOut *pa = (PAVoiceOut *) hw;
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);
416 if (pa->s) {
417 pa_simple_free (pa->s);
418 pa->s = NULL;
421 audio_pt_fini (&pa->pt, AUDIO_FUNC);
422 qemu_free (pa->pcm_buf);
423 pa->pcm_buf = NULL;
426 static void qpa_fini_in (HWVoiceIn *hw)
428 void *ret;
429 PAVoiceIn *pa = (PAVoiceIn *) hw;
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);
436 if (pa->s) {
437 pa_simple_free (pa->s);
438 pa->s = NULL;
441 audio_pt_fini (&pa->pt, AUDIO_FUNC);
442 qemu_free (pa->pcm_buf);
443 pa->pcm_buf = NULL;
446 static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
448 (void) hw;
449 (void) cmd;
450 return 0;
453 static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
455 (void) hw;
456 (void) cmd;
457 return 0;
460 /* common */
461 static void *qpa_audio_init (void)
463 return &conf;
466 static void qpa_audio_fini (void *opaque)
468 (void) opaque;
471 struct audio_option qpa_options[] = {
473 .name = "SAMPLES",
474 .tag = AUD_OPT_INT,
475 .valp = &conf.samples,
476 .descr = "buffer size in samples"
479 .name = "SERVER",
480 .tag = AUD_OPT_STR,
481 .valp = &conf.server,
482 .descr = "server address"
485 .name = "SINK",
486 .tag = AUD_OPT_STR,
487 .valp = &conf.sink,
488 .descr = "sink device name"
491 .name = "SOURCE",
492 .tag = AUD_OPT_STR,
493 .valp = &conf.source,
494 .descr = "source device name"
496 { /* End of list */ }
499 static 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,
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
513 struct 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)