]> git.proxmox.com Git - qemu.git/blame - audio/alsaaudio.c
Warn about rejected buffer/period size/time when it was specified by the user
[qemu.git] / audio / alsaaudio.c
CommitLineData
1d14ffa9
FB
1/*
2 * QEMU ALSA audio driver
3 *
4 * Copyright (c) 2005 Vassili Karpov (malc)
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24#include <alsa/asoundlib.h>
749bc4bf
PB
25#include "qemu-common.h"
26#include "audio.h"
1d14ffa9
FB
27
28#define AUDIO_CAP "alsa"
29#include "audio_int.h"
30
31typedef struct ALSAVoiceOut {
32 HWVoiceOut hw;
33 void *pcm_buf;
34 snd_pcm_t *handle;
1d14ffa9
FB
35} ALSAVoiceOut;
36
37typedef struct ALSAVoiceIn {
38 HWVoiceIn hw;
39 snd_pcm_t *handle;
40 void *pcm_buf;
1d14ffa9
FB
41} ALSAVoiceIn;
42
43static struct {
44 int size_in_usec_in;
45 int size_in_usec_out;
46 const char *pcm_name_in;
47 const char *pcm_name_out;
48 unsigned int buffer_size_in;
49 unsigned int period_size_in;
50 unsigned int buffer_size_out;
51 unsigned int period_size_out;
52 unsigned int threshold;
53
fe8f096b
TS
54 int buffer_size_in_overridden;
55 int period_size_in_overridden;
1d14ffa9 56
fe8f096b
TS
57 int buffer_size_out_overridden;
58 int period_size_out_overridden;
571ec3d6 59 int verbose;
1d14ffa9 60} conf = {
8ead62cf
FB
61 .pcm_name_out = "default",
62 .pcm_name_in = "default",
1d14ffa9
FB
63};
64
65struct alsa_params_req {
ca9cc28c
AZ
66 int freq;
67 snd_pcm_format_t fmt;
68 int nchannels;
7a24c800 69 int size_in_usec;
64333899 70 int override_mask;
1d14ffa9
FB
71 unsigned int buffer_size;
72 unsigned int period_size;
73};
74
75struct alsa_params_obt {
76 int freq;
77 audfmt_e fmt;
ca9cc28c 78 int endianness;
1d14ffa9 79 int nchannels;
c0fe3827 80 snd_pcm_uframes_t samples;
1d14ffa9
FB
81};
82
83static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...)
84{
85 va_list ap;
86
87 va_start (ap, fmt);
88 AUD_vlog (AUDIO_CAP, fmt, ap);
89 va_end (ap);
90
91 AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
92}
93
94static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
95 int err,
96 const char *typ,
97 const char *fmt,
98 ...
99 )
100{
101 va_list ap;
102
c0fe3827 103 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
1d14ffa9
FB
104
105 va_start (ap, fmt);
106 AUD_vlog (AUDIO_CAP, fmt, ap);
107 va_end (ap);
108
109 AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
110}
111
112static void alsa_anal_close (snd_pcm_t **handlep)
113{
114 int err = snd_pcm_close (*handlep);
115 if (err) {
116 alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep);
117 }
118 *handlep = NULL;
119}
120
121static int alsa_write (SWVoiceOut *sw, void *buf, int len)
122{
123 return audio_pcm_sw_write (sw, buf, len);
124}
125
ca9cc28c 126static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt)
1d14ffa9
FB
127{
128 switch (fmt) {
129 case AUD_FMT_S8:
130 return SND_PCM_FORMAT_S8;
131
132 case AUD_FMT_U8:
133 return SND_PCM_FORMAT_U8;
134
135 case AUD_FMT_S16:
136 return SND_PCM_FORMAT_S16_LE;
137
138 case AUD_FMT_U16:
139 return SND_PCM_FORMAT_U16_LE;
140
f941aa25
TS
141 case AUD_FMT_S32:
142 return SND_PCM_FORMAT_S32_LE;
143
144 case AUD_FMT_U32:
145 return SND_PCM_FORMAT_U32_LE;
146
1d14ffa9
FB
147 default:
148 dolog ("Internal logic error: Bad audio format %d\n", fmt);
149#ifdef DEBUG_AUDIO
150 abort ();
151#endif
152 return SND_PCM_FORMAT_U8;
153 }
154}
155
ca9cc28c
AZ
156static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt,
157 int *endianness)
1d14ffa9
FB
158{
159 switch (alsafmt) {
160 case SND_PCM_FORMAT_S8:
161 *endianness = 0;
162 *fmt = AUD_FMT_S8;
163 break;
164
165 case SND_PCM_FORMAT_U8:
166 *endianness = 0;
167 *fmt = AUD_FMT_U8;
168 break;
169
170 case SND_PCM_FORMAT_S16_LE:
171 *endianness = 0;
172 *fmt = AUD_FMT_S16;
173 break;
174
175 case SND_PCM_FORMAT_U16_LE:
176 *endianness = 0;
177 *fmt = AUD_FMT_U16;
178 break;
179
180 case SND_PCM_FORMAT_S16_BE:
181 *endianness = 1;
182 *fmt = AUD_FMT_S16;
183 break;
184
185 case SND_PCM_FORMAT_U16_BE:
186 *endianness = 1;
187 *fmt = AUD_FMT_U16;
188 break;
189
f941aa25
TS
190 case SND_PCM_FORMAT_S32_LE:
191 *endianness = 0;
192 *fmt = AUD_FMT_S32;
193 break;
194
195 case SND_PCM_FORMAT_U32_LE:
196 *endianness = 0;
197 *fmt = AUD_FMT_U32;
198 break;
199
200 case SND_PCM_FORMAT_S32_BE:
201 *endianness = 1;
202 *fmt = AUD_FMT_S32;
203 break;
204
205 case SND_PCM_FORMAT_U32_BE:
206 *endianness = 1;
207 *fmt = AUD_FMT_U32;
208 break;
209
1d14ffa9
FB
210 default:
211 dolog ("Unrecognized audio format %d\n", alsafmt);
212 return -1;
213 }
214
215 return 0;
216}
217
1d14ffa9
FB
218static void alsa_dump_info (struct alsa_params_req *req,
219 struct alsa_params_obt *obt)
220{
221 dolog ("parameter | requested value | obtained value\n");
222 dolog ("format | %10d | %10d\n", req->fmt, obt->fmt);
223 dolog ("channels | %10d | %10d\n",
224 req->nchannels, obt->nchannels);
225 dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
226 dolog ("============================================\n");
227 dolog ("requested: buffer size %d period size %d\n",
228 req->buffer_size, req->period_size);
c0fe3827 229 dolog ("obtained: samples %ld\n", obt->samples);
1d14ffa9 230}
1d14ffa9
FB
231
232static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
233{
234 int err;
235 snd_pcm_sw_params_t *sw_params;
236
237 snd_pcm_sw_params_alloca (&sw_params);
238
239 err = snd_pcm_sw_params_current (handle, sw_params);
240 if (err < 0) {
c0fe3827 241 dolog ("Could not fully initialize DAC\n");
1d14ffa9
FB
242 alsa_logerr (err, "Failed to get current software parameters\n");
243 return;
244 }
245
246 err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold);
247 if (err < 0) {
c0fe3827 248 dolog ("Could not fully initialize DAC\n");
1d14ffa9
FB
249 alsa_logerr (err, "Failed to set software threshold to %ld\n",
250 threshold);
251 return;
252 }
253
254 err = snd_pcm_sw_params (handle, sw_params);
255 if (err < 0) {
c0fe3827 256 dolog ("Could not fully initialize DAC\n");
1d14ffa9
FB
257 alsa_logerr (err, "Failed to set software parameters\n");
258 return;
259 }
260}
261
262static int alsa_open (int in, struct alsa_params_req *req,
263 struct alsa_params_obt *obt, snd_pcm_t **handlep)
264{
265 snd_pcm_t *handle;
266 snd_pcm_hw_params_t *hw_params;
60fe76f3 267 int err;
7a24c800 268 int size_in_usec;
60fe76f3 269 unsigned int freq, nchannels;
1d14ffa9 270 const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out;
1d14ffa9
FB
271 snd_pcm_uframes_t obt_buffer_size;
272 const char *typ = in ? "ADC" : "DAC";
ca9cc28c 273 snd_pcm_format_t obtfmt;
1d14ffa9
FB
274
275 freq = req->freq;
1d14ffa9 276 nchannels = req->nchannels;
7a24c800 277 size_in_usec = req->size_in_usec;
1d14ffa9
FB
278
279 snd_pcm_hw_params_alloca (&hw_params);
280
281 err = snd_pcm_open (
282 &handle,
283 pcm_name,
284 in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
285 SND_PCM_NONBLOCK
286 );
287 if (err < 0) {
288 alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name);
289 return -1;
290 }
291
292 err = snd_pcm_hw_params_any (handle, hw_params);
293 if (err < 0) {
294 alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n");
295 goto err;
296 }
297
298 err = snd_pcm_hw_params_set_access (
299 handle,
300 hw_params,
301 SND_PCM_ACCESS_RW_INTERLEAVED
302 );
303 if (err < 0) {
304 alsa_logerr2 (err, typ, "Failed to set access type\n");
305 goto err;
306 }
307
308 err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
ca9cc28c 309 if (err < 0 && conf.verbose) {
1d14ffa9 310 alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
1d14ffa9
FB
311 }
312
313 err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0);
314 if (err < 0) {
315 alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq);
316 goto err;
317 }
318
319 err = snd_pcm_hw_params_set_channels_near (
320 handle,
321 hw_params,
322 &nchannels
323 );
324 if (err < 0) {
325 alsa_logerr2 (err, typ, "Failed to set number of channels %d\n",
326 req->nchannels);
327 goto err;
328 }
329
330 if (nchannels != 1 && nchannels != 2) {
331 alsa_logerr2 (err, typ,
332 "Can not handle obtained number of channels %d\n",
333 nchannels);
334 goto err;
335 }
336
7a24c800 337 if (req->buffer_size) {
f3b52983 338 unsigned long obt;
339
7a24c800 340 if (size_in_usec) {
341 int dir = 0;
342 unsigned int btime = req->buffer_size;
1d14ffa9
FB
343
344 err = snd_pcm_hw_params_set_buffer_time_near (
345 handle,
346 hw_params,
7a24c800 347 &btime,
348 &dir
c0fe3827 349 );
f3b52983 350 obt = btime;
1d14ffa9
FB
351 }
352 else {
7a24c800 353 snd_pcm_uframes_t bsize = req->buffer_size;
1d14ffa9 354
7a24c800 355 err = snd_pcm_hw_params_set_buffer_size_near (
356 handle,
357 hw_params,
358 &bsize
359 );
f3b52983 360 obt = bsize;
7a24c800 361 }
362 if (err < 0) {
363 alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n",
364 size_in_usec ? "time" : "size", req->buffer_size);
365 goto err;
366 }
f3b52983 367
64333899 368 if ((req->override_mask & 2) && (obt - req->buffer_size))
f3b52983 369 dolog ("Requested buffer %s %u was rejected, using %lu\n",
370 size_in_usec ? "time" : "size", req->buffer_size, obt);
7a24c800 371 }
372
373 if (req->period_size) {
f3b52983 374 unsigned long obt;
375
7a24c800 376 if (size_in_usec) {
377 int dir = 0;
378 unsigned int ptime = req->period_size;
1d14ffa9 379
7a24c800 380 err = snd_pcm_hw_params_set_period_time_near (
381 handle,
1d14ffa9 382 hw_params,
7a24c800 383 &ptime,
384 &dir
1d14ffa9 385 );
f3b52983 386 obt = ptime;
7a24c800 387 }
388 else {
a7bb29ba 389 int dir = 0;
7a24c800 390 snd_pcm_uframes_t psize = req->period_size;
1d14ffa9 391
a7bb29ba 392 err = snd_pcm_hw_params_set_period_size_near (
1d14ffa9
FB
393 handle,
394 hw_params,
a7bb29ba 395 &psize,
396 &dir
1d14ffa9 397 );
f3b52983 398 obt = psize;
1d14ffa9 399 }
7a24c800 400
401 if (err < 0) {
402 alsa_logerr2 (err, typ, "Failed to set period %s to %d\n",
403 size_in_usec ? "time" : "size", req->period_size);
404 goto err;
405 }
f3b52983 406
64333899 407 if ((req->override_mask & 1) && (obt - req->period_size))
f3b52983 408 dolog ("Requested period %s %u was rejected, using %lu\n",
409 size_in_usec ? "time" : "size", req->period_size, obt);
1d14ffa9
FB
410 }
411
412 err = snd_pcm_hw_params (handle, hw_params);
413 if (err < 0) {
414 alsa_logerr2 (err, typ, "Failed to apply audio parameters\n");
415 goto err;
416 }
417
418 err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size);
419 if (err < 0) {
420 alsa_logerr2 (err, typ, "Failed to get buffer size\n");
421 goto err;
422 }
423
ca9cc28c
AZ
424 err = snd_pcm_hw_params_get_format (hw_params, &obtfmt);
425 if (err < 0) {
426 alsa_logerr2 (err, typ, "Failed to get format\n");
427 goto err;
428 }
429
430 if (alsa_to_audfmt (obtfmt, &obt->fmt, &obt->endianness)) {
431 dolog ("Invalid format was returned %d\n", obtfmt);
432 goto err;
433 }
434
1d14ffa9
FB
435 err = snd_pcm_prepare (handle);
436 if (err < 0) {
c0fe3827 437 alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle);
1d14ffa9
FB
438 goto err;
439 }
440
1d14ffa9
FB
441 if (!in && conf.threshold) {
442 snd_pcm_uframes_t threshold;
443 int bytes_per_sec;
444
ca9cc28c
AZ
445 bytes_per_sec = freq << (nchannels == 2);
446
447 switch (obt->fmt) {
448 case AUD_FMT_S8:
449 case AUD_FMT_U8:
450 break;
451
452 case AUD_FMT_S16:
453 case AUD_FMT_U16:
454 bytes_per_sec <<= 1;
455 break;
456
457 case AUD_FMT_S32:
458 case AUD_FMT_U32:
459 bytes_per_sec <<= 2;
460 break;
461 }
1d14ffa9
FB
462
463 threshold = (conf.threshold * bytes_per_sec) / 1000;
464 alsa_set_threshold (handle, threshold);
465 }
466
1d14ffa9
FB
467 obt->nchannels = nchannels;
468 obt->freq = freq;
c0fe3827 469 obt->samples = obt_buffer_size;
ca9cc28c 470
1d14ffa9
FB
471 *handlep = handle;
472
ca9cc28c
AZ
473 if (conf.verbose &&
474 (obt->fmt != req->fmt ||
475 obt->nchannels != req->nchannels ||
476 obt->freq != req->freq)) {
477 dolog ("Audio paramters for %s\n", typ);
1d14ffa9 478 alsa_dump_info (req, obt);
1d14ffa9
FB
479 }
480
481#ifdef DEBUG
482 alsa_dump_info (req, obt);
483#endif
484 return 0;
485
486 err:
487 alsa_anal_close (&handle);
488 return -1;
489}
490
491static int alsa_recover (snd_pcm_t *handle)
492{
493 int err = snd_pcm_prepare (handle);
494 if (err < 0) {
495 alsa_logerr (err, "Failed to prepare handle %p\n", handle);
496 return -1;
497 }
498 return 0;
499}
500
571ec3d6
FB
501static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
502{
503 snd_pcm_sframes_t avail;
504
505 avail = snd_pcm_avail_update (handle);
506 if (avail < 0) {
507 if (avail == -EPIPE) {
508 if (!alsa_recover (handle)) {
509 avail = snd_pcm_avail_update (handle);
510 }
511 }
512
513 if (avail < 0) {
514 alsa_logerr (avail,
515 "Could not obtain number of available frames\n");
516 return -1;
517 }
518 }
519
520 return avail;
521}
522
1d14ffa9
FB
523static int alsa_run_out (HWVoiceOut *hw)
524{
525 ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
526 int rpos, live, decr;
527 int samples;
528 uint8_t *dst;
529 st_sample_t *src;
530 snd_pcm_sframes_t avail;
531
532 live = audio_pcm_hw_get_live_out (hw);
533 if (!live) {
534 return 0;
535 }
536
571ec3d6 537 avail = alsa_get_avail (alsa->handle);
1d14ffa9 538 if (avail < 0) {
571ec3d6 539 dolog ("Could not get number of available playback frames\n");
1d14ffa9
FB
540 return 0;
541 }
542
1d14ffa9
FB
543 decr = audio_MIN (live, avail);
544 samples = decr;
545 rpos = hw->rpos;
546 while (samples) {
547 int left_till_end_samples = hw->samples - rpos;
571ec3d6 548 int len = audio_MIN (samples, left_till_end_samples);
1d14ffa9
FB
549 snd_pcm_sframes_t written;
550
551 src = hw->mix_buf + rpos;
552 dst = advance (alsa->pcm_buf, rpos << hw->info.shift);
553
571ec3d6 554 hw->clip (dst, src, len);
1d14ffa9 555
571ec3d6
FB
556 while (len) {
557 written = snd_pcm_writei (alsa->handle, dst, len);
4787c71d 558
571ec3d6 559 if (written <= 0) {
4787c71d 560 switch (written) {
571ec3d6
FB
561 case 0:
562 if (conf.verbose) {
563 dolog ("Failed to write %d frames (wrote zero)\n", len);
4787c71d 564 }
4787c71d
FB
565 goto exit;
566
571ec3d6
FB
567 case -EPIPE:
568 if (alsa_recover (alsa->handle)) {
569 alsa_logerr (written, "Failed to write %d frames\n",
570 len);
571 goto exit;
572 }
573 if (conf.verbose) {
574 dolog ("Recovering from playback xrun\n");
575 }
4787c71d
FB
576 continue;
577
571ec3d6
FB
578 case -EAGAIN:
579 goto exit;
580
4787c71d
FB
581 default:
582 alsa_logerr (written, "Failed to write %d frames to %p\n",
571ec3d6 583 len, dst);
4787c71d 584 goto exit;
1d14ffa9 585 }
1d14ffa9 586 }
1d14ffa9 587
4787c71d
FB
588 rpos = (rpos + written) % hw->samples;
589 samples -= written;
571ec3d6 590 len -= written;
4787c71d
FB
591 dst = advance (dst, written << hw->info.shift);
592 src += written;
593 }
1d14ffa9
FB
594 }
595
596 exit:
597 hw->rpos = rpos;
598 return decr;
599}
600
601static void alsa_fini_out (HWVoiceOut *hw)
602{
603 ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
604
605 ldebug ("alsa_fini\n");
606 alsa_anal_close (&alsa->handle);
607
608 if (alsa->pcm_buf) {
609 qemu_free (alsa->pcm_buf);
610 alsa->pcm_buf = NULL;
611 }
612}
613
c0fe3827 614static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
1d14ffa9
FB
615{
616 ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
617 struct alsa_params_req req;
618 struct alsa_params_obt obt;
1d14ffa9 619 snd_pcm_t *handle;
c0fe3827 620 audsettings_t obt_as;
1d14ffa9 621
c0fe3827
FB
622 req.fmt = aud_to_alsafmt (as->fmt);
623 req.freq = as->freq;
624 req.nchannels = as->nchannels;
1d14ffa9
FB
625 req.period_size = conf.period_size_out;
626 req.buffer_size = conf.buffer_size_out;
23fb600b 627 req.size_in_usec = conf.size_in_usec_out;
64333899 628 req.override_mask = !!conf.period_size_out_overridden
629 | (!!conf.buffer_size_out_overridden << 1);
1d14ffa9
FB
630
631 if (alsa_open (0, &req, &obt, &handle)) {
632 return -1;
633 }
634
c0fe3827
FB
635 obt_as.freq = obt.freq;
636 obt_as.nchannels = obt.nchannels;
ca9cc28c
AZ
637 obt_as.fmt = obt.fmt;
638 obt_as.endianness = obt.endianness;
c0fe3827 639
d929eba5 640 audio_pcm_init_info (&hw->info, &obt_as);
c0fe3827 641 hw->samples = obt.samples;
1d14ffa9 642
c0fe3827 643 alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift);
1d14ffa9 644 if (!alsa->pcm_buf) {
4787c71d
FB
645 dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
646 hw->samples, 1 << hw->info.shift);
1d14ffa9
FB
647 alsa_anal_close (&handle);
648 return -1;
649 }
650
651 alsa->handle = handle;
1d14ffa9
FB
652 return 0;
653}
654
571ec3d6 655static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause)
1d14ffa9
FB
656{
657 int err;
571ec3d6
FB
658
659 if (pause) {
660 err = snd_pcm_drop (handle);
661 if (err < 0) {
32d448c4 662 alsa_logerr (err, "Could not stop %s\n", typ);
571ec3d6
FB
663 return -1;
664 }
665 }
666 else {
667 err = snd_pcm_prepare (handle);
668 if (err < 0) {
32d448c4 669 alsa_logerr (err, "Could not prepare handle for %s\n", typ);
571ec3d6
FB
670 return -1;
671 }
672 }
673
674 return 0;
675}
676
677static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
678{
1d14ffa9
FB
679 ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
680
681 switch (cmd) {
682 case VOICE_ENABLE:
683 ldebug ("enabling voice\n");
571ec3d6 684 return alsa_voice_ctl (alsa->handle, "playback", 0);
1d14ffa9
FB
685
686 case VOICE_DISABLE:
687 ldebug ("disabling voice\n");
571ec3d6 688 return alsa_voice_ctl (alsa->handle, "playback", 1);
1d14ffa9 689 }
571ec3d6
FB
690
691 return -1;
1d14ffa9
FB
692}
693
c0fe3827 694static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
1d14ffa9
FB
695{
696 ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
697 struct alsa_params_req req;
698 struct alsa_params_obt obt;
1d14ffa9 699 snd_pcm_t *handle;
c0fe3827 700 audsettings_t obt_as;
1d14ffa9 701
c0fe3827
FB
702 req.fmt = aud_to_alsafmt (as->fmt);
703 req.freq = as->freq;
704 req.nchannels = as->nchannels;
1d14ffa9
FB
705 req.period_size = conf.period_size_in;
706 req.buffer_size = conf.buffer_size_in;
7a24c800 707 req.size_in_usec = conf.size_in_usec_in;
64333899 708 req.override_mask = !!conf.period_size_in_overridden
709 | (!!conf.buffer_size_in_overridden << 1);
1d14ffa9
FB
710
711 if (alsa_open (1, &req, &obt, &handle)) {
712 return -1;
713 }
714
c0fe3827
FB
715 obt_as.freq = obt.freq;
716 obt_as.nchannels = obt.nchannels;
ca9cc28c
AZ
717 obt_as.fmt = obt.fmt;
718 obt_as.endianness = obt.endianness;
c0fe3827 719
d929eba5 720 audio_pcm_init_info (&hw->info, &obt_as);
c0fe3827
FB
721 hw->samples = obt.samples;
722
723 alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
1d14ffa9 724 if (!alsa->pcm_buf) {
4787c71d
FB
725 dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
726 hw->samples, 1 << hw->info.shift);
1d14ffa9
FB
727 alsa_anal_close (&handle);
728 return -1;
729 }
730
731 alsa->handle = handle;
732 return 0;
733}
734
735static void alsa_fini_in (HWVoiceIn *hw)
736{
737 ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
738
739 alsa_anal_close (&alsa->handle);
740
741 if (alsa->pcm_buf) {
742 qemu_free (alsa->pcm_buf);
743 alsa->pcm_buf = NULL;
744 }
745}
746
747static int alsa_run_in (HWVoiceIn *hw)
748{
749 ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
750 int hwshift = hw->info.shift;
751 int i;
752 int live = audio_pcm_hw_get_live_in (hw);
753 int dead = hw->samples - live;
571ec3d6 754 int decr;
1d14ffa9
FB
755 struct {
756 int add;
757 int len;
758 } bufs[2] = {
759 { hw->wpos, 0 },
760 { 0, 0 }
761 };
571ec3d6 762 snd_pcm_sframes_t avail;
1d14ffa9
FB
763 snd_pcm_uframes_t read_samples = 0;
764
765 if (!dead) {
766 return 0;
767 }
768
571ec3d6
FB
769 avail = alsa_get_avail (alsa->handle);
770 if (avail < 0) {
771 dolog ("Could not get number of captured frames\n");
772 return 0;
773 }
774
775 if (!avail && (snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) {
776 avail = hw->samples;
777 }
778
779 decr = audio_MIN (dead, avail);
780 if (!decr) {
781 return 0;
782 }
783
784 if (hw->wpos + decr > hw->samples) {
1d14ffa9 785 bufs[0].len = (hw->samples - hw->wpos);
571ec3d6 786 bufs[1].len = (decr - (hw->samples - hw->wpos));
1d14ffa9
FB
787 }
788 else {
571ec3d6 789 bufs[0].len = decr;
1d14ffa9
FB
790 }
791
1d14ffa9
FB
792 for (i = 0; i < 2; ++i) {
793 void *src;
794 st_sample_t *dst;
795 snd_pcm_sframes_t nread;
796 snd_pcm_uframes_t len;
797
798 len = bufs[i].len;
799
800 src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
801 dst = hw->conv_buf + bufs[i].add;
802
803 while (len) {
804 nread = snd_pcm_readi (alsa->handle, src, len);
805
571ec3d6 806 if (nread <= 0) {
1d14ffa9 807 switch (nread) {
571ec3d6
FB
808 case 0:
809 if (conf.verbose) {
810 dolog ("Failed to read %ld frames (read zero)\n", len);
1d14ffa9 811 }
1d14ffa9
FB
812 goto exit;
813
571ec3d6
FB
814 case -EPIPE:
815 if (alsa_recover (alsa->handle)) {
816 alsa_logerr (nread, "Failed to read %ld frames\n", len);
817 goto exit;
818 }
819 if (conf.verbose) {
820 dolog ("Recovering from capture xrun\n");
821 }
1d14ffa9
FB
822 continue;
823
571ec3d6
FB
824 case -EAGAIN:
825 goto exit;
826
1d14ffa9
FB
827 default:
828 alsa_logerr (
829 nread,
830 "Failed to read %ld frames from %p\n",
831 len,
832 src
833 );
834 goto exit;
835 }
836 }
837
838 hw->conv (dst, src, nread, &nominal_volume);
839
840 src = advance (src, nread << hwshift);
841 dst += nread;
842
843 read_samples += nread;
844 len -= nread;
845 }
846 }
847
848 exit:
849 hw->wpos = (hw->wpos + read_samples) % hw->samples;
850 return read_samples;
851}
852
853static int alsa_read (SWVoiceIn *sw, void *buf, int size)
854{
855 return audio_pcm_sw_read (sw, buf, size);
856}
857
858static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
859{
571ec3d6
FB
860 ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
861
862 switch (cmd) {
863 case VOICE_ENABLE:
864 ldebug ("enabling voice\n");
865 return alsa_voice_ctl (alsa->handle, "capture", 0);
866
867 case VOICE_DISABLE:
868 ldebug ("disabling voice\n");
869 return alsa_voice_ctl (alsa->handle, "capture", 1);
870 }
871
872 return -1;
1d14ffa9
FB
873}
874
875static void *alsa_audio_init (void)
876{
877 return &conf;
878}
879
880static void alsa_audio_fini (void *opaque)
881{
882 (void) opaque;
883}
884
885static struct audio_option alsa_options[] = {
886 {"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out,
887 "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
888 {"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out,
7a24c800 889 "DAC period size (0 to go with system default)",
890 &conf.period_size_out_overridden, 0},
1d14ffa9 891 {"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out,
7a24c800 892 "DAC buffer size (0 to go with system default)",
893 &conf.buffer_size_out_overridden, 0},
1d14ffa9
FB
894
895 {"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in,
896 "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
897 {"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in,
7a24c800 898 "ADC period size (0 to go with system default)",
899 &conf.period_size_in_overridden, 0},
1d14ffa9 900 {"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in,
7a24c800 901 "ADC buffer size (0 to go with system default)",
902 &conf.buffer_size_in_overridden, 0},
1d14ffa9
FB
903
904 {"THRESHOLD", AUD_OPT_INT, &conf.threshold,
905 "(undocumented)", NULL, 0},
906
907 {"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out,
908 "DAC device name (for instance dmix)", NULL, 0},
909
910 {"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in,
911 "ADC device name", NULL, 0},
571ec3d6
FB
912
913 {"VERBOSE", AUD_OPT_BOOL, &conf.verbose,
914 "Behave in a more verbose way", NULL, 0},
915
1d14ffa9
FB
916 {NULL, 0, NULL, NULL, NULL, 0}
917};
918
919static struct audio_pcm_ops alsa_pcm_ops = {
920 alsa_init_out,
921 alsa_fini_out,
922 alsa_run_out,
923 alsa_write,
924 alsa_ctl_out,
925
926 alsa_init_in,
927 alsa_fini_in,
928 alsa_run_in,
929 alsa_read,
930 alsa_ctl_in
931};
932
933struct audio_driver alsa_audio_driver = {
934 INIT_FIELD (name = ) "alsa",
935 INIT_FIELD (descr = ) "ALSA http://www.alsa-project.org",
936 INIT_FIELD (options = ) alsa_options,
937 INIT_FIELD (init = ) alsa_audio_init,
938 INIT_FIELD (fini = ) alsa_audio_fini,
939 INIT_FIELD (pcm_ops = ) &alsa_pcm_ops,
940 INIT_FIELD (can_be_default = ) 1,
941 INIT_FIELD (max_voices_out = ) INT_MAX,
942 INIT_FIELD (max_voices_in = ) INT_MAX,
943 INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut),
944 INIT_FIELD (voice_size_in = ) sizeof (ALSAVoiceIn)
945};