]> Git Repo - qemu.git/blame - audio/paaudio.c
use C99 initializers for all audio/*
[qemu.git] / audio / paaudio.c
CommitLineData
b8e59f18 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
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 int divisor;
37 char *server;
38 char *sink;
39 char *source;
40} conf = {
1a40d5e2
JQ
41 .samples = 1024,
42 .divisor = 2,
b8e59f18 43};
44
45static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
46{
47 va_list ap;
48
49 va_start (ap, fmt);
50 AUD_vlog (AUDIO_CAP, fmt, ap);
51 va_end (ap);
52
53 AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
54}
55
56static void *qpa_thread_out (void *arg)
57{
58 PAVoiceOut *pa = arg;
59 HWVoiceOut *hw = &pa->hw;
60 int threshold;
61
62 threshold = conf.divisor ? hw->samples / conf.divisor : 0;
63
64 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
65 return NULL;
66 }
67
68 for (;;) {
69 int decr, to_mix, rpos;
70
71 for (;;) {
72 if (pa->done) {
73 goto exit;
74 }
75
76 if (pa->live > threshold) {
77 break;
78 }
79
80 if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
81 goto exit;
82 }
83 }
84
85 decr = to_mix = pa->live;
86 rpos = hw->rpos;
87
88 if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
89 return NULL;
90 }
91
92 while (to_mix) {
93 int error;
94 int chunk = audio_MIN (to_mix, hw->samples - rpos);
1ea879e5 95 struct st_sample *src = hw->mix_buf + rpos;
b8e59f18 96
97 hw->clip (pa->pcm_buf, src, chunk);
98
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;
103 }
104
105 rpos = (rpos + chunk) % hw->samples;
106 to_mix -= chunk;
107 }
108
109 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
110 return NULL;
111 }
112
113 pa->rpos = rpos;
114 pa->live -= decr;
115 pa->decr += decr;
116 }
117
118 exit:
119 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
120 return NULL;
121}
122
123static int qpa_run_out (HWVoiceOut *hw)
124{
125 int live, decr;
126 PAVoiceOut *pa = (PAVoiceOut *) hw;
127
128 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
129 return 0;
130 }
131
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);
139 }
140 else {
141 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
142 }
143 return decr;
144}
145
146static int qpa_write (SWVoiceOut *sw, void *buf, int len)
147{
148 return audio_pcm_sw_write (sw, buf, len);
149}
150
151/* capture */
152static void *qpa_thread_in (void *arg)
153{
154 PAVoiceIn *pa = arg;
155 HWVoiceIn *hw = &pa->hw;
156 int threshold;
157
158 threshold = conf.divisor ? hw->samples / conf.divisor : 0;
159
160 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
161 return NULL;
162 }
163
164 for (;;) {
165 int incr, to_grab, wpos;
166
167 for (;;) {
168 if (pa->done) {
169 goto exit;
170 }
171
172 if (pa->dead > threshold) {
173 break;
174 }
175
176 if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
177 goto exit;
178 }
179 }
180
181 incr = to_grab = pa->dead;
182 wpos = hw->wpos;
183
184 if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
185 return NULL;
186 }
187
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);
192
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;
197 }
198
199 hw->conv (hw->conv_buf + wpos, buf, chunk, &nominal_volume);
200 wpos = (wpos + chunk) % hw->samples;
201 to_grab -= chunk;
202 }
203
204 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
205 return NULL;
206 }
207
208 pa->wpos = wpos;
209 pa->dead -= incr;
210 pa->incr += incr;
211 }
212
213 exit:
214 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
215 return NULL;
216}
217
218static int qpa_run_in (HWVoiceIn *hw)
219{
220 int live, incr, dead;
221 PAVoiceIn *pa = (PAVoiceIn *) hw;
222
223 if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
224 return 0;
225 }
226
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);
235 }
236 else {
237 audio_pt_unlock (&pa->pt, AUDIO_FUNC);
238 }
239 return incr;
240}
241
242static int qpa_read (SWVoiceIn *sw, void *buf, int len)
243{
244 return audio_pcm_sw_read (sw, buf, len);
245}
246
247static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
248{
249 int format;
250
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;
268 }
269 return format;
270}
271
272static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
273{
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;
292 }
293}
294
1ea879e5 295static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
b8e59f18 296{
297 int error;
298 static pa_sample_spec ss;
1ea879e5 299 struct audsettings obt_as = *as;
b8e59f18 300 PAVoiceOut *pa = (PAVoiceOut *) hw;
301
302 ss.format = audfmt_to_pa (as->fmt, as->endianness);
303 ss.channels = as->nchannels;
304 ss.rate = as->freq;
305
306 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
307
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
318 );
319 if (!pa->s) {
320 qpa_logerr (error, "pa_simple_new for playback failed\n");
321 goto fail1;
322 }
323
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;
331 }
332
333 if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
334 goto fail3;
335 }
336
337 return 0;
338
339 fail3:
5d928867 340 qemu_free (pa->pcm_buf);
b8e59f18 341 pa->pcm_buf = NULL;
342 fail2:
343 pa_simple_free (pa->s);
344 pa->s = NULL;
345 fail1:
346 return -1;
347}
348
1ea879e5 349static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
b8e59f18 350{
351 int error;
352 static pa_sample_spec ss;
1ea879e5 353 struct audsettings obt_as = *as;
b8e59f18 354 PAVoiceIn *pa = (PAVoiceIn *) hw;
355
356 ss.format = audfmt_to_pa (as->fmt, as->endianness);
357 ss.channels = as->nchannels;
358 ss.rate = as->freq;
359
360 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
361
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
372 );
373 if (!pa->s) {
374 qpa_logerr (error, "pa_simple_new for capture failed\n");
375 goto fail1;
376 }
377
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;
385 }
386
387 if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
388 goto fail3;
389 }
390
391 return 0;
392
393 fail3:
5d928867 394 qemu_free (pa->pcm_buf);
b8e59f18 395 pa->pcm_buf = NULL;
396 fail2:
397 pa_simple_free (pa->s);
398 pa->s = NULL;
399 fail1:
400 return -1;
401}
402
403static void qpa_fini_out (HWVoiceOut *hw)
404{
405 void *ret;
406 PAVoiceOut *pa = (PAVoiceOut *) hw;
407
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);
412
413 if (pa->s) {
414 pa_simple_free (pa->s);
415 pa->s = NULL;
416 }
417
418 audio_pt_fini (&pa->pt, AUDIO_FUNC);
419 qemu_free (pa->pcm_buf);
420 pa->pcm_buf = NULL;
421}
422
423static void qpa_fini_in (HWVoiceIn *hw)
424{
425 void *ret;
426 PAVoiceIn *pa = (PAVoiceIn *) hw;
427
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);
432
433 if (pa->s) {
434 pa_simple_free (pa->s);
435 pa->s = NULL;
436 }
437
438 audio_pt_fini (&pa->pt, AUDIO_FUNC);
439 qemu_free (pa->pcm_buf);
440 pa->pcm_buf = NULL;
441}
442
443static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
444{
445 (void) hw;
446 (void) cmd;
447 return 0;
448}
449
450static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
451{
452 (void) hw;
453 (void) cmd;
454 return 0;
455}
456
457/* common */
458static void *qpa_audio_init (void)
459{
460 return &conf;
461}
462
463static void qpa_audio_fini (void *opaque)
464{
465 (void) opaque;
466}
467
468struct audio_option qpa_options[] = {
2700efa3
JQ
469 {.name = "SAMPLES",
470 .tag = AUD_OPT_INT,
471 .valp = &conf.samples,
472 .descr = "buffer size in samples"},
473 {.name = "DIVISOR",
474 .tag = AUD_OPT_INT,
475 .valp = &conf.divisor,
476 .descr = "threshold divisor"},
477 {.name = "SERVER",
478 .tag = AUD_OPT_STR,
479 .valp = &conf.server,
480 .descr = "server address"},
481 {.name = "SINK",
482 .tag = AUD_OPT_STR,
483 .valp = &conf.sink,
484 .descr = "sink device name"},
485 {.name = "SOURCE",
486 .tag = AUD_OPT_STR,
487 .valp = &conf.source,
488 .descr = "source device name"},
489 { /* End of list */ }
b8e59f18 490};
491
35f4b58c 492static struct audio_pcm_ops qpa_pcm_ops = {
1dd3e4d1
JQ
493 .init_out = qpa_init_out,
494 .fini_out = qpa_fini_out,
495 .run_out = qpa_run_out,
496 .write = qpa_write,
497 .ctl_out = qpa_ctl_out,
498
499 .init_in = qpa_init_in,
500 .fini_in = qpa_fini_in,
501 .run_in = qpa_run_in,
502 .read = qpa_read,
503 .ctl_in = qpa_ctl_in
b8e59f18 504};
505
506struct audio_driver pa_audio_driver = {
bee37f32
JQ
507 .name = "pa",
508 .descr = "http://www.pulseaudio.org/",
509 .options = qpa_options,
510 .init = qpa_audio_init,
511 .fini = qpa_audio_fini,
512 .pcm_ops = &qpa_pcm_ops,
513 .can_be_default = 0,
514 .max_voices_out = INT_MAX,
515 .max_voices_in = INT_MAX,
516 .voice_size_out = sizeof (PAVoiceOut),
517 .voice_size_in = sizeof (PAVoiceIn)
b8e59f18 518};
This page took 0.209391 seconds and 4 git commands to generate.