]> git.proxmox.com Git - qemu.git/blame - audio/paaudio.c
Introduce v3 of savevm protocol
[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 = {
41 1024,
42 2,
43 NULL,
44 NULL,
45 NULL
46};
47
48static 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
59static 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 st_sample_t *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
126static 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
149static int qpa_write (SWVoiceOut *sw, void *buf, int len)
150{
151 return audio_pcm_sw_write (sw, buf, len);
152}
153
154/* capture */
155static 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
221static 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
245static int qpa_read (SWVoiceIn *sw, void *buf, int len)
246{
247 return audio_pcm_sw_read (sw, buf, len);
248}
249
250static 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
275static 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
298static int qpa_init_out (HWVoiceOut *hw, audsettings_t *as)
299{
300 int error;
301 static pa_sample_spec ss;
302 audsettings_t 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
352static int qpa_init_in (HWVoiceIn *hw, audsettings_t *as)
353{
354 int error;
355 static pa_sample_spec ss;
356 audsettings_t 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
406static 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
426static 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
446static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
447{
448 (void) hw;
449 (void) cmd;
450 return 0;
451}
452
453static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
454{
455 (void) hw;
456 (void) cmd;
457 return 0;
458}
459
460/* common */
461static void *qpa_audio_init (void)
462{
463 return &conf;
464}
465
466static void qpa_audio_fini (void *opaque)
467{
468 (void) opaque;
469}
470
471struct 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
8869defe 490static const struct audio_pcm_ops qpa_pcm_ops = {
b8e59f18 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
503struct 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};