use C99 initializers for audio_pcm_ops
[qemu/mdroth.git] / audio / paaudio.c
blob942e64f2b22ef6201eb4fa20f0263a8315291091
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 1024,
43 NULL,
44 NULL,
45 NULL
48 static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
50 va_list ap;
52 va_start (ap, fmt);
53 AUD_vlog (AUDIO_CAP, fmt, ap);
54 va_end (ap);
56 AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
59 static void *qpa_thread_out (void *arg)
61 PAVoiceOut *pa = arg;
62 HWVoiceOut *hw = &pa->hw;
63 int threshold;
65 threshold = conf.divisor ? hw->samples / conf.divisor : 0;
67 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
68 return NULL;
71 for (;;) {
72 int decr, to_mix, rpos;
74 for (;;) {
75 if (pa->done) {
76 goto exit;
79 if (pa->live > threshold) {
80 break;
83 if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
84 goto exit;
88 decr = to_mix = pa->live;
89 rpos = hw->rpos;
91 if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
92 return NULL;
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;
100 hw->clip (pa->pcm_buf, src, chunk);
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;
108 rpos = (rpos + chunk) % hw->samples;
109 to_mix -= chunk;
112 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
113 return NULL;
116 pa->rpos = rpos;
117 pa->live -= decr;
118 pa->decr += decr;
121 exit:
122 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
123 return NULL;
126 static int qpa_run_out (HWVoiceOut *hw)
128 int live, decr;
129 PAVoiceOut *pa = (PAVoiceOut *) hw;
131 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
132 return 0;
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);
143 else {
144 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
146 return decr;
149 static int qpa_write (SWVoiceOut *sw, void *buf, int len)
151 return audio_pcm_sw_write (sw, buf, len);
154 /* capture */
155 static void *qpa_thread_in (void *arg)
157 PAVoiceIn *pa = arg;
158 HWVoiceIn *hw = &pa->hw;
159 int threshold;
161 threshold = conf.divisor ? hw->samples / conf.divisor : 0;
163 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
164 return NULL;
167 for (;;) {
168 int incr, to_grab, wpos;
170 for (;;) {
171 if (pa->done) {
172 goto exit;
175 if (pa->dead > threshold) {
176 break;
179 if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
180 goto exit;
184 incr = to_grab = pa->dead;
185 wpos = hw->wpos;
187 if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
188 return NULL;
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);
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;
202 hw->conv (hw->conv_buf + wpos, buf, chunk, &nominal_volume);
203 wpos = (wpos + chunk) % hw->samples;
204 to_grab -= chunk;
207 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
208 return NULL;
211 pa->wpos = wpos;
212 pa->dead -= incr;
213 pa->incr += incr;
216 exit:
217 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
218 return NULL;
221 static int qpa_run_in (HWVoiceIn *hw)
223 int live, incr, dead;
224 PAVoiceIn *pa = (PAVoiceIn *) hw;
226 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
227 return 0;
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);
239 else {
240 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
242 return incr;
245 static int qpa_read (SWVoiceIn *sw, void *buf, int len)
247 return audio_pcm_sw_read (sw, buf, len);
250 static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
252 int format;
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;
272 return format;
275 static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
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;
298 static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
300 int error;
301 static pa_sample_spec ss;
302 struct audsettings obt_as = *as;
303 PAVoiceOut *pa = (PAVoiceOut *) hw;
305 ss.format = audfmt_to_pa (as->fmt, as->endianness);
306 ss.channels = as->nchannels;
307 ss.rate = as->freq;
309 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
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
322 if (!pa->s) {
323 qpa_logerr (error, "pa_simple_new for playback failed\n");
324 goto fail1;
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;
336 if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
337 goto fail3;
340 return 0;
342 fail3:
343 qemu_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;
352 static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
354 int error;
355 static pa_sample_spec ss;
356 struct audsettings obt_as = *as;
357 PAVoiceIn *pa = (PAVoiceIn *) hw;
359 ss.format = audfmt_to_pa (as->fmt, as->endianness);
360 ss.channels = as->nchannels;
361 ss.rate = as->freq;
363 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
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
376 if (!pa->s) {
377 qpa_logerr (error, "pa_simple_new for capture failed\n");
378 goto fail1;
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;
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[] = {
472 {.name = "SAMPLES",
473 .tag = AUD_OPT_INT,
474 .valp = &conf.samples,
475 .descr = "buffer size in samples"},
476 {.name = "DIVISOR",
477 .tag = AUD_OPT_INT,
478 .valp = &conf.divisor,
479 .descr = "threshold divisor"},
480 {.name = "SERVER",
481 .tag = AUD_OPT_STR,
482 .valp = &conf.server,
483 .descr = "server address"},
484 {.name = "SINK",
485 .tag = AUD_OPT_STR,
486 .valp = &conf.sink,
487 .descr = "sink device name"},
488 {.name = "SOURCE",
489 .tag = AUD_OPT_STR,
490 .valp = &conf.source,
491 .descr = "source device name"},
492 { /* End of list */ }
495 static struct audio_pcm_ops qpa_pcm_ops = {
496 .init_out = qpa_init_out,
497 .fini_out = qpa_fini_out,
498 .run_out = qpa_run_out,
499 .write = qpa_write,
500 .ctl_out = qpa_ctl_out,
502 .init_in = qpa_init_in,
503 .fini_in = qpa_fini_in,
504 .run_in = qpa_run_in,
505 .read = qpa_read,
506 .ctl_in = qpa_ctl_in
509 struct audio_driver pa_audio_driver = {
510 .name = "pa",
511 .descr = "http://www.pulseaudio.org/",
512 .options = qpa_options,
513 .init = qpa_audio_init,
514 .fini = qpa_audio_fini,
515 .pcm_ops = &qpa_pcm_ops,
516 .can_be_default = 0,
517 .max_voices_out = INT_MAX,
518 .max_voices_in = INT_MAX,
519 .voice_size_out = sizeof (PAVoiceOut),
520 .voice_size_in = sizeof (PAVoiceIn)