VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/alsaaudio.c@ 9074

Last change on this file since 9074 was 6077, checked in by vboxsync, 17 years ago

pulse/alsa: resolve _all_ symbols during initialization, otherwise incompatible libraries might confuse the sound driver much later during runtime

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.2 KB
Line 
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#ifdef VBOX
25#ifndef DEBUG
26#define NDEBUG
27#endif
28#define LOG_GROUP LOG_GROUP_DEV_AUDIO
29#include <VBox/log.h>
30#endif
31
32#include <alsa/asoundlib.h>
33
34#include "Builtins.h"
35#include "../../vl_vbox.h"
36#include "audio.h"
37#ifdef VBOX
38#include "alsa_stubs.h"
39#endif
40#include <iprt/alloc.h>
41
42#define AUDIO_CAP "alsa"
43#include "audio_int.h"
44
45typedef struct ALSAVoiceOut {
46 HWVoiceOut hw;
47 void *pcm_buf;
48 snd_pcm_t *handle;
49} ALSAVoiceOut;
50
51typedef struct ALSAVoiceIn {
52 HWVoiceIn hw;
53 snd_pcm_t *handle;
54 void *pcm_buf;
55} ALSAVoiceIn;
56
57/* latency = period_size * periods / (rate * bytes_per_frame) */
58
59static struct {
60 int size_in_usec_in;
61 int size_in_usec_out;
62 const char *pcm_name_in;
63 const char *pcm_name_out;
64 unsigned int buffer_size_in;
65 unsigned int period_size_in;
66 unsigned int buffer_size_out;
67 unsigned int period_size_out;
68 unsigned int threshold;
69
70 int buffer_size_in_overriden;
71 int period_size_in_overriden;
72
73 int buffer_size_out_overriden;
74 int period_size_out_overriden;
75 int verbose;
76} conf = {
77#ifdef HIGH_LATENCY
78 INIT_FIELD (.size_in_usec_in =) 1,
79 INIT_FIELD (.size_in_usec_out =) 1,
80#else
81 INIT_FIELD (.size_in_usec_in =) 0,
82 INIT_FIELD (.size_in_usec_out =) 0,
83#endif
84 INIT_FIELD (.pcm_name_out =) "default",
85 INIT_FIELD (.pcm_name_in =) "default",
86#ifdef HIGH_LATENCY
87 INIT_FIELD (.buffer_size_in =) 400000,
88 INIT_FIELD (.period_size_in =) 400000 / 4,
89 INIT_FIELD (.buffer_size_out =) 400000,
90 INIT_FIELD (.period_size_out =) 400000 / 4,
91#else
92#define DEFAULT_BUFFER_SIZE 1024
93#define DEFAULT_PERIOD_SIZE 256
94 INIT_FIELD (.buffer_size_in =) DEFAULT_BUFFER_SIZE * 4,
95 INIT_FIELD (.period_size_in =) DEFAULT_PERIOD_SIZE * 4,
96 INIT_FIELD (.buffer_size_out =) DEFAULT_BUFFER_SIZE,
97 INIT_FIELD (.period_size_out =) DEFAULT_PERIOD_SIZE,
98#endif
99 INIT_FIELD (.threshold =) 0,
100 INIT_FIELD (.buffer_size_in_overriden =) 0,
101 INIT_FIELD (.period_size_in_overriden =) 0,
102 INIT_FIELD (.buffer_size_out_overriden =) 0,
103 INIT_FIELD (.period_size_out_overriden =) 0,
104 INIT_FIELD (.verbose =) 0
105};
106
107struct alsa_params_req {
108 int freq;
109 audfmt_e fmt;
110 int nchannels;
111 unsigned long buffer_size;
112 unsigned long period_size;
113};
114
115struct alsa_params_obt {
116 int freq;
117 audfmt_e fmt;
118 int nchannels;
119 snd_pcm_uframes_t samples;
120};
121
122static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...)
123{
124 va_list ap;
125
126 va_start (ap, fmt);
127 AUD_vlog (AUDIO_CAP, fmt, ap);
128 va_end (ap);
129
130 AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
131}
132
133static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
134 int err,
135 const char *typ,
136 const char *fmt,
137 ...
138 )
139{
140 va_list ap;
141
142 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
143
144 va_start (ap, fmt);
145 AUD_vlog (AUDIO_CAP, fmt, ap);
146 va_end (ap);
147
148 AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
149}
150
151static void alsa_anal_close (snd_pcm_t **handlep)
152{
153 int err = snd_pcm_close (*handlep);
154 if (err) {
155 alsa_logerr (err, "Failed to close PCM handle %p\n",
156 (void *) *handlep);
157 }
158 *handlep = NULL;
159}
160
161static int alsa_write (SWVoiceOut *sw, void *buf, int len)
162{
163 return audio_pcm_sw_write (sw, buf, len);
164}
165
166static int aud_to_alsafmt (audfmt_e fmt)
167{
168 switch (fmt) {
169 case AUD_FMT_S8:
170 return SND_PCM_FORMAT_S8;
171
172 case AUD_FMT_U8:
173 return SND_PCM_FORMAT_U8;
174
175 case AUD_FMT_S16:
176 return SND_PCM_FORMAT_S16_LE;
177
178 case AUD_FMT_U16:
179 return SND_PCM_FORMAT_U16_LE;
180
181 case AUD_FMT_S32:
182 return SND_PCM_FORMAT_S32_LE;
183
184 case AUD_FMT_U32:
185 return SND_PCM_FORMAT_U32_LE;
186
187 default:
188 dolog ("Internal logic error: Bad audio format %d\n", fmt);
189#ifdef DEBUG_AUDIO
190 abort ();
191#endif
192 return SND_PCM_FORMAT_U8;
193 }
194}
195
196static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness)
197{
198 switch (alsafmt) {
199 case SND_PCM_FORMAT_S8:
200 *endianness = 0;
201 *fmt = AUD_FMT_S8;
202 break;
203
204 case SND_PCM_FORMAT_U8:
205 *endianness = 0;
206 *fmt = AUD_FMT_U8;
207 break;
208
209 case SND_PCM_FORMAT_S16_LE:
210 *endianness = 0;
211 *fmt = AUD_FMT_S16;
212 break;
213
214 case SND_PCM_FORMAT_U16_LE:
215 *endianness = 0;
216 *fmt = AUD_FMT_U16;
217 break;
218
219 case SND_PCM_FORMAT_S16_BE:
220 *endianness = 1;
221 *fmt = AUD_FMT_S16;
222 break;
223
224 case SND_PCM_FORMAT_U16_BE:
225 *endianness = 1;
226 *fmt = AUD_FMT_U16;
227 break;
228
229 case SND_PCM_FORMAT_S32_LE:
230 *endianness = 0;
231 *fmt = AUD_FMT_S32;
232 break;
233
234 case SND_PCM_FORMAT_U32_LE:
235 *endianness = 0;
236 *fmt = AUD_FMT_U32;
237 break;
238
239 case SND_PCM_FORMAT_S32_BE:
240 *endianness = 1;
241 *fmt = AUD_FMT_S32;
242 break;
243
244 case SND_PCM_FORMAT_U32_BE:
245 *endianness = 1;
246 *fmt = AUD_FMT_U32;
247 break;
248
249 default:
250 dolog ("Unrecognized audio format %d\n", alsafmt);
251 return -1;
252 }
253
254 return 0;
255}
256
257#if defined DEBUG_MISMATCHES || defined DEBUG
258static void alsa_dump_info (struct alsa_params_req *req,
259 struct alsa_params_obt *obt)
260{
261 dolog ("parameter | requested value | obtained value\n");
262 dolog ("format | %10d | %10d\n", req->fmt, obt->fmt);
263 dolog ("channels | %10d | %10d\n",
264 req->nchannels, obt->nchannels);
265 dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
266 dolog ("============================================\n");
267 dolog ("requested: buffer size %d period size %d\n",
268 req->buffer_size, req->period_size);
269 dolog ("obtained: samples %ld\n", obt->samples);
270}
271#endif
272
273static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
274{
275 int err;
276 snd_pcm_sw_params_t *sw_params;
277
278 snd_pcm_sw_params_alloca (&sw_params);
279
280 err = snd_pcm_sw_params_current (handle, sw_params);
281 if (err < 0) {
282 dolog ("Could not fully initialize DAC\n");
283 alsa_logerr (err, "Failed to get current software parameters\n");
284 return;
285 }
286
287 err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold);
288 if (err < 0) {
289 dolog ("Could not fully initialize DAC\n");
290 alsa_logerr (err, "Failed to set software threshold to %ld\n",
291 threshold);
292 return;
293 }
294
295 err = snd_pcm_sw_params (handle, sw_params);
296 if (err < 0) {
297 dolog ("Could not fully initialize DAC\n");
298 alsa_logerr (err, "Failed to set software parameters\n");
299 return;
300 }
301}
302
303static int alsa_open (int in, struct alsa_params_req *req,
304 struct alsa_params_obt *obt, snd_pcm_t **handlep)
305{
306 snd_pcm_t *handle;
307 snd_pcm_hw_params_t *hw_params;
308 int err, dir;
309 unsigned int freq, nchannels;
310 const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out;
311 unsigned int period_size, buffer_size;
312 snd_pcm_uframes_t period_size_f, buffer_size_f;
313 snd_pcm_uframes_t obt_buffer_size, obt_period_size;
314 const char *typ = in ? "ADC" : "DAC";
315
316 freq = req->freq;
317 period_size = req->period_size;
318 buffer_size = req->buffer_size;
319 period_size_f = (snd_pcm_uframes_t)period_size;
320 buffer_size_f = (snd_pcm_uframes_t)buffer_size;
321 nchannels = req->nchannels;
322
323 snd_pcm_hw_params_alloca (&hw_params);
324
325 err = snd_pcm_open (
326 &handle,
327 pcm_name,
328 in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
329 SND_PCM_NONBLOCK
330 );
331 if (err < 0) {
332#ifndef VBOX
333 alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name);
334#else
335 LogRel(("ALSA: Failed to open '%s' as %s\n", pcm_name, typ));
336#endif
337 return -1;
338 }
339
340 err = snd_pcm_hw_params_any (handle, hw_params);
341 if (err < 0) {
342#ifndef VBOX
343 alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n");
344#else
345 LogRel(("ALSA: Failed to initialize hardware parameters\n"));
346#endif
347 goto err;
348 }
349
350 err = snd_pcm_hw_params_set_access (
351 handle,
352 hw_params,
353 SND_PCM_ACCESS_RW_INTERLEAVED
354 );
355 if (err < 0) {
356#ifndef VBOX
357 alsa_logerr2 (err, typ, "Failed to set access type\n");
358#else
359 LogRel(("ALSA: Failed to set access type\n"));
360#endif
361 goto err;
362 }
363
364 err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
365 if (err < 0) {
366#ifndef VBOX
367 alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
368#else
369 LogRel(("ALSA: Failed to set format %d\n", req->fmt));
370#endif
371 goto err;
372 }
373
374 err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0);
375 if (err < 0) {
376#ifndef VBOX
377 alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq);
378#else
379 LogRel(("ALSA: Failed to set frequency %dHz\n", req->freq));
380#endif
381 goto err;
382 }
383
384 err = snd_pcm_hw_params_set_channels_near (
385 handle,
386 hw_params,
387 &nchannels
388 );
389 if (err < 0) {
390#ifndef VBOX
391 alsa_logerr2 (err, typ, "Failed to set number of channels %d\n",
392 req->nchannels);
393#else
394 LogRel(("ALSA: Failed to set number of channels to %d\n", req->nchannels));
395#endif
396 goto err;
397 }
398
399 if (nchannels != 1 && nchannels != 2) {
400#ifndef VBOX
401 alsa_logerr2 (err, typ,
402 "Can not handle obtained number of channels %d\n",
403 nchannels);
404#else
405 LogRel(("ALSA: Cannot handle obtained number of channels (%d)\n", nchannels));
406#endif
407 goto err;
408 }
409
410 if (!((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out))) {
411 if (!buffer_size) {
412 buffer_size = DEFAULT_BUFFER_SIZE;
413 period_size= DEFAULT_PERIOD_SIZE;
414 }
415 }
416
417 if (buffer_size) {
418 if ((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out)) {
419 if (period_size) {
420 err = snd_pcm_hw_params_set_period_time_near (
421 handle,
422 hw_params,
423 &period_size,
424 0
425 );
426 if (err < 0) {
427#ifndef VBOX
428 alsa_logerr2 (err, typ,
429 "Failed to set period time %d\n",
430 req->period_size);
431#else
432 LogRel(("ALSA: Failed to set period time %d\n", req->period_size));
433#endif
434 goto err;
435 }
436 }
437
438 err = snd_pcm_hw_params_set_buffer_time_near (
439 handle,
440 hw_params,
441 &buffer_size,
442 0
443 );
444
445 if (err < 0) {
446#ifndef VBOX
447 alsa_logerr2 (err, typ,
448 "Failed to set buffer time %d\n",
449 req->buffer_size);
450#else
451 LogRel(("ALSA: Failed to set buffer time %d\n", req->buffer_size));
452#endif
453 goto err;
454 }
455 }
456 else {
457 snd_pcm_uframes_t minval;
458
459 if (period_size_f) {
460 minval = period_size_f;
461 dir = 0;
462
463 err = snd_pcm_hw_params_get_period_size_min (
464 hw_params,
465 &minval,
466 &dir
467 );
468 if (err < 0) {
469#ifndef VBOX
470 alsa_logerr (
471 err,
472 "Could not get minmal period size for %s\n",
473 typ
474 );
475#else
476 LogRel(("ALSA: Could not get minimal period size for %s\n", typ));
477#endif
478 }
479 else {
480 dolog("minimal period size %ld\n", minval);
481 if (period_size_f < minval) {
482 if ((in && conf.period_size_in_overriden)
483 || (!in && conf.period_size_out_overriden)) {
484 dolog ("%s period size(%d) is less "
485 "than minmal period size(%ld)\n",
486 typ,
487 period_size_f,
488 minval);
489 }
490 period_size_f = minval;
491 }
492 }
493
494#ifndef VBOX
495 err = snd_pcm_hw_params_set_period_size (
496 handle,
497 hw_params,
498 period_size_f,
499 0
500 );
501#else
502 err = snd_pcm_hw_params_set_period_size_near (
503 handle,
504 hw_params,
505 &period_size_f,
506 0
507 );
508#endif
509 dolog("PERIOD_SIZE %d\n", period_size_f);
510 if (err < 0) {
511#ifndef VBOX
512 alsa_logerr2 (err, typ, "Failed to set period size %d\n",
513 period_size_f);
514#else
515 LogRel(("ALSA: Failed to set period size %d (%s)\n",
516 period_size_f, snd_strerror(err)));
517#endif
518 goto err;
519 }
520 }
521
522#ifdef VBOX
523 /* Calculate default buffer size here since it might have been changed
524 * in the _near functions */
525 buffer_size_f = 4 * period_size_f;
526#endif
527
528 minval = buffer_size_f;
529 err = snd_pcm_hw_params_get_buffer_size_min (
530 hw_params,
531 &minval
532 );
533 if (err < 0) {
534#ifndef VBOX
535 alsa_logerr (err, "Could not get minmal buffer size for %s\n",
536 typ);
537#else
538 LogRel(("ALSA: Could not get minimal buffer size for %s\n", typ));
539#endif
540 }
541 else {
542 if (buffer_size_f < minval) {
543 if ((in && conf.buffer_size_in_overriden)
544 || (!in && conf.buffer_size_out_overriden)) {
545 dolog (
546 "%s buffer size(%d) is less "
547 "than minimal buffer size(%ld)\n",
548 typ,
549 buffer_size_f,
550 minval
551 );
552 }
553 buffer_size_f = minval;
554 }
555 }
556
557 err = snd_pcm_hw_params_set_buffer_size_near (
558 handle,
559 hw_params,
560 &buffer_size_f
561 );
562 dolog("BUFFER_SIZE %d\n", buffer_size_f);
563 if (err < 0) {
564#ifndef VBOX
565 alsa_logerr2 (err, typ, "Failed to set buffer size %d\n",
566 buffer_size_f);
567#else
568 LogRel(("ALSA: Failed to set buffer size %d (%s)\n",
569 buffer_size_f, snd_strerror(err)));
570#endif
571 goto err;
572 }
573 }
574 }
575 else {
576 dolog ("warning: Buffer size is not set\n");
577 }
578
579 err = snd_pcm_hw_params (handle, hw_params);
580 if (err < 0) {
581#ifndef VBOX
582 alsa_logerr2 (err, typ, "Failed to apply audio parameters\n");
583#else
584 LogRel(("ALSA: Failed to apply audio parameters\n"));
585#endif
586 goto err;
587 }
588
589 err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size);
590 if (err < 0) {
591#ifndef VBOX
592 alsa_logerr2 (err, typ, "Failed to get buffer size\n");
593#else
594 LogRel(("ALSA: Failed to get buffer size\n"));
595#endif
596 goto err;
597 }
598
599#ifdef VBOX
600 dir = 0;
601 err = snd_pcm_hw_params_get_period_size (hw_params, &obt_period_size, &dir);
602 if (err < 0)
603 {
604 LogRel(("ALSA: Failed to get period size\n"));
605 goto err;
606 }
607 LogRel(("ALSA: %s frequency %dHz, period size %ld, buffer size %ld\n",
608 typ, req->freq, obt_period_size, obt_buffer_size));
609#endif
610
611 err = snd_pcm_prepare (handle);
612 if (err < 0) {
613 alsa_logerr2 (err, typ, "Could not prepare handle %p\n",
614 (void *) handle);
615 goto err;
616 }
617
618 if (!in && conf.threshold) {
619 snd_pcm_uframes_t threshold;
620 int bytes_per_sec;
621
622 bytes_per_sec = freq
623 << (nchannels == 2)
624 << (req->fmt == AUD_FMT_S16 || req->fmt == AUD_FMT_U16);
625
626 threshold = (conf.threshold * bytes_per_sec) / 1000;
627 alsa_set_threshold (handle, threshold);
628 }
629
630 obt->fmt = req->fmt;
631 obt->nchannels = nchannels;
632 obt->freq = freq;
633 obt->samples = obt_buffer_size;
634 *handlep = handle;
635
636#if defined DEBUG_MISMATCHES || defined DEBUG
637 if (obt->fmt != req->fmt ||
638 obt->nchannels != req->nchannels ||
639 obt->freq != req->freq) {
640 dolog ("Audio paramters mismatch for %s\n", typ);
641 alsa_dump_info (req, obt);
642 }
643#endif
644
645#ifdef DEBUG
646 alsa_dump_info (req, obt);
647#endif
648 return 0;
649
650 err:
651 alsa_anal_close (&handle);
652 return -1;
653}
654
655static int alsa_recover (snd_pcm_t *handle)
656{
657 int err = snd_pcm_prepare (handle);
658 if (err < 0) {
659 alsa_logerr (err, "Failed to prepare handle %p\n",
660 (void *) handle);
661 return -1;
662 }
663 return 0;
664}
665
666static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
667{
668 snd_pcm_sframes_t avail;
669
670 avail = snd_pcm_avail_update (handle);
671 if (avail < 0) {
672 if (avail == -EPIPE) {
673 if (!alsa_recover (handle)) {
674 avail = snd_pcm_avail_update (handle);
675 }
676 }
677
678 if (avail < 0) {
679 alsa_logerr (avail,
680 "Could not obtain number of available frames\n");
681 return -1;
682 }
683 }
684
685 return avail;
686}
687
688static int alsa_run_out (HWVoiceOut *hw)
689{
690 ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
691 int rpos, live, decr;
692 int samples;
693 uint8_t *dst;
694 st_sample_t *src;
695 snd_pcm_sframes_t avail;
696
697 live = audio_pcm_hw_get_live_out (hw);
698 if (!live) {
699 return 0;
700 }
701
702 avail = alsa_get_avail (alsa->handle);
703 if (avail < 0) {
704 dolog ("Could not get number of available playback frames\n");
705 return 0;
706 }
707
708 decr = audio_MIN (live, avail);
709 samples = decr;
710 rpos = hw->rpos;
711 while (samples) {
712 int left_till_end_samples = hw->samples - rpos;
713 int len = audio_MIN (samples, left_till_end_samples);
714 snd_pcm_sframes_t written;
715
716 src = hw->mix_buf + rpos;
717 dst = advance (alsa->pcm_buf, rpos << hw->info.shift);
718
719 hw->clip (dst, src, len);
720
721 while (len) {
722 written = snd_pcm_writei (alsa->handle, dst, len);
723
724 if (written <= 0) {
725 switch (written) {
726 case 0:
727 if (conf.verbose) {
728 dolog ("Failed to write %d frames (wrote zero)\n", len);
729 }
730 goto exit;
731
732 case -EPIPE:
733 if (alsa_recover (alsa->handle)) {
734 alsa_logerr (written, "Failed to write %d frames\n",
735 len);
736 goto exit;
737 }
738 if (conf.verbose) {
739 dolog ("Recovering from playback xrun\n");
740 }
741 continue;
742
743 case -EAGAIN:
744 goto exit;
745
746 default:
747 alsa_logerr (written, "Failed to write %d frames to %p\n",
748 len, dst);
749 goto exit;
750 }
751 }
752
753 rpos = (rpos + written) % hw->samples;
754 samples -= written;
755 len -= written;
756 dst = advance (dst, written << hw->info.shift);
757 src += written;
758 }
759 }
760
761 exit:
762 hw->rpos = rpos;
763 return decr;
764}
765
766static void alsa_fini_out (HWVoiceOut *hw)
767{
768 ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
769
770 ldebug ("alsa_fini\n");
771 alsa_anal_close (&alsa->handle);
772
773 if (alsa->pcm_buf) {
774 qemu_free (alsa->pcm_buf);
775 alsa->pcm_buf = NULL;
776 }
777}
778
779static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
780{
781 ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
782 struct alsa_params_req req;
783 struct alsa_params_obt obt;
784 audfmt_e effective_fmt;
785 int endianness;
786 int err;
787 snd_pcm_t *handle;
788 audsettings_t obt_as;
789
790 req.fmt = aud_to_alsafmt (as->fmt);
791 req.freq = as->freq;
792 req.nchannels = as->nchannels;
793 req.period_size = conf.period_size_out;
794 req.buffer_size = conf.buffer_size_out;
795
796 if (alsa_open (0, &req, &obt, &handle)) {
797 return -1;
798 }
799
800 err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
801 if (err) {
802 alsa_anal_close (&handle);
803 return -1;
804 }
805
806 obt_as.freq = obt.freq;
807 obt_as.nchannels = obt.nchannels;
808 obt_as.fmt = effective_fmt;
809 obt_as.endianness = endianness;
810
811 audio_pcm_init_info (&hw->info, &obt_as);
812 hw->samples = obt.samples;
813
814 alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift);
815 if (!alsa->pcm_buf) {
816 dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
817 hw->samples, 1 << hw->info.shift);
818 alsa_anal_close (&handle);
819 return -1;
820 }
821
822 alsa->handle = handle;
823 return 0;
824}
825
826static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause)
827{
828 int err;
829
830 if (pause) {
831 err = snd_pcm_drop (handle);
832 if (err < 0) {
833 alsa_logerr (err, "Could not stop %s\n", typ);
834 return -1;
835 }
836 }
837 else {
838 err = snd_pcm_prepare (handle);
839 if (err < 0) {
840 alsa_logerr (err, "Could not prepare handle for %s\n", typ);
841 return -1;
842 }
843 }
844
845 return 0;
846}
847
848static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
849{
850 ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
851
852 switch (cmd) {
853 case VOICE_ENABLE:
854 ldebug ("enabling voice\n");
855 return alsa_voice_ctl (alsa->handle, "playback", 0);
856
857 case VOICE_DISABLE:
858 ldebug ("disabling voice\n");
859 return alsa_voice_ctl (alsa->handle, "playback", 1);
860 }
861
862 return -1;
863}
864
865static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
866{
867 ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
868 struct alsa_params_req req;
869 struct alsa_params_obt obt;
870 int endianness;
871 int err;
872 audfmt_e effective_fmt;
873 snd_pcm_t *handle;
874 audsettings_t obt_as;
875
876 req.fmt = aud_to_alsafmt (as->fmt);
877 req.freq = as->freq;
878 req.nchannels = as->nchannels;
879 req.period_size = conf.period_size_in;
880 req.buffer_size = conf.buffer_size_in;
881
882 if (alsa_open (1, &req, &obt, &handle)) {
883 return -1;
884 }
885
886 err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
887 if (err) {
888 alsa_anal_close (&handle);
889 return -1;
890 }
891
892 obt_as.freq = obt.freq;
893 obt_as.nchannels = obt.nchannels;
894 obt_as.fmt = effective_fmt;
895 obt_as.endianness = endianness;
896
897 audio_pcm_init_info (&hw->info, &obt_as);
898 hw->samples = obt.samples;
899
900 alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
901 if (!alsa->pcm_buf) {
902 dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
903 hw->samples, 1 << hw->info.shift);
904 alsa_anal_close (&handle);
905 return -1;
906 }
907
908 alsa->handle = handle;
909 return 0;
910}
911
912static void alsa_fini_in (HWVoiceIn *hw)
913{
914 ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
915
916 alsa_anal_close (&alsa->handle);
917
918 if (alsa->pcm_buf) {
919 qemu_free (alsa->pcm_buf);
920 alsa->pcm_buf = NULL;
921 }
922}
923
924static int alsa_run_in (HWVoiceIn *hw)
925{
926 ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
927 int hwshift = hw->info.shift;
928 int i;
929 int live = audio_pcm_hw_get_live_in (hw);
930 int dead = hw->samples - live;
931 int decr;
932 struct {
933 int add;
934 int len;
935 } bufs[2];
936
937 snd_pcm_sframes_t avail;
938 snd_pcm_uframes_t read_samples = 0;
939
940 bufs[0].add = hw->wpos;
941 bufs[0].len = 0;
942 bufs[1].add = 0;
943 bufs[1].len = 0;
944
945 if (!dead) {
946 return 0;
947 }
948
949 avail = alsa_get_avail (alsa->handle);
950 if (avail < 0) {
951 dolog ("Could not get number of captured frames\n");
952 return 0;
953 }
954
955 if (!avail && (snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) {
956 avail = hw->samples;
957 }
958
959 decr = audio_MIN (dead, avail);
960 if (!decr) {
961 return 0;
962 }
963
964 if (hw->wpos + decr > hw->samples) {
965 bufs[0].len = (hw->samples - hw->wpos);
966 bufs[1].len = (decr - (hw->samples - hw->wpos));
967 }
968 else {
969 bufs[0].len = decr;
970 }
971
972 for (i = 0; i < 2; ++i) {
973 void *src;
974 st_sample_t *dst;
975 snd_pcm_sframes_t nread;
976 snd_pcm_uframes_t len;
977
978 len = bufs[i].len;
979
980 src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
981 dst = hw->conv_buf + bufs[i].add;
982
983 while (len) {
984 nread = snd_pcm_readi (alsa->handle, src, len);
985
986 if (nread <= 0) {
987 switch (nread) {
988 case 0:
989 if (conf.verbose) {
990 dolog ("Failed to read %ld frames (read zero)\n", len);
991 }
992 goto exit;
993
994 case -EPIPE:
995 if (alsa_recover (alsa->handle)) {
996 alsa_logerr (nread, "Failed to read %ld frames\n", len);
997 goto exit;
998 }
999 if (conf.verbose) {
1000 dolog ("Recovering from capture xrun\n");
1001 }
1002 continue;
1003
1004 case -EAGAIN:
1005 goto exit;
1006
1007 default:
1008 alsa_logerr (
1009 nread,
1010 "Failed to read %ld frames from %p\n",
1011 len,
1012 src
1013 );
1014 goto exit;
1015 }
1016 }
1017
1018 hw->conv (dst, src, nread, &nominal_volume);
1019
1020 src = advance (src, nread << hwshift);
1021 dst += nread;
1022
1023 read_samples += nread;
1024 len -= nread;
1025 }
1026 }
1027
1028 exit:
1029 hw->wpos = (hw->wpos + read_samples) % hw->samples;
1030 return read_samples;
1031}
1032
1033static int alsa_read (SWVoiceIn *sw, void *buf, int size)
1034{
1035 return audio_pcm_sw_read (sw, buf, size);
1036}
1037
1038static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
1039{
1040 ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
1041
1042 switch (cmd) {
1043 case VOICE_ENABLE:
1044 ldebug ("enabling voice\n");
1045 return alsa_voice_ctl (alsa->handle, "capture", 0);
1046
1047 case VOICE_DISABLE:
1048 ldebug ("disabling voice\n");
1049 return alsa_voice_ctl (alsa->handle, "capture", 1);
1050 }
1051
1052 return -1;
1053}
1054
1055#ifdef VBOX
1056static void alsa_error_handler(const char *file, int line, const char *function,
1057 int err, const char *fmt, ...)
1058{
1059 /* ignore */
1060}
1061#endif
1062
1063static void *alsa_audio_init (void)
1064{
1065#ifdef VBOX
1066 int rc;
1067
1068 rc = audioLoadAlsaLib();
1069 if (RT_FAILURE(rc)) {
1070 LogRel(("ALSA: Failed to load the ALSA shared library! Error %Rrc\n", rc));
1071 return NULL;
1072 }
1073 snd_lib_error_set_handler (alsa_error_handler);
1074#endif
1075 return &conf;
1076}
1077
1078static void alsa_audio_fini (void *opaque)
1079{
1080 (void) opaque;
1081}
1082
1083static struct audio_option alsa_options[] = {
1084 {"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out,
1085 "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
1086 {"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out,
1087 "DAC period size", &conf.period_size_out_overriden, 0},
1088 {"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out,
1089 "DAC buffer size", &conf.buffer_size_out_overriden, 0},
1090
1091 {"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in,
1092 "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
1093 {"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in,
1094 "ADC period size", &conf.period_size_in_overriden, 0},
1095 {"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in,
1096 "ADC buffer size", &conf.buffer_size_in_overriden, 0},
1097
1098 {"THRESHOLD", AUD_OPT_INT, &conf.threshold,
1099 "(undocumented)", NULL, 0},
1100
1101 {"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out,
1102 "DAC device name (for instance dmix)", NULL, 0},
1103
1104 {"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in,
1105 "ADC device name", NULL, 0},
1106
1107 {"VERBOSE", AUD_OPT_BOOL, &conf.verbose,
1108 "Behave in a more verbose way", NULL, 0},
1109
1110 {NULL, 0, NULL, NULL, NULL, 0}
1111};
1112
1113static struct audio_pcm_ops alsa_pcm_ops = {
1114 alsa_init_out,
1115 alsa_fini_out,
1116 alsa_run_out,
1117 alsa_write,
1118 alsa_ctl_out,
1119
1120 alsa_init_in,
1121 alsa_fini_in,
1122 alsa_run_in,
1123 alsa_read,
1124 alsa_ctl_in
1125};
1126
1127struct audio_driver alsa_audio_driver = {
1128 INIT_FIELD (name = ) "alsa",
1129 INIT_FIELD (descr = ) "ALSA http://www.alsa-project.org",
1130 INIT_FIELD (options = ) alsa_options,
1131 INIT_FIELD (init = ) alsa_audio_init,
1132 INIT_FIELD (fini = ) alsa_audio_fini,
1133 INIT_FIELD (pcm_ops = ) &alsa_pcm_ops,
1134 INIT_FIELD (can_be_default = ) 1,
1135 INIT_FIELD (max_voices_out = ) INT_MAX,
1136 INIT_FIELD (max_voices_in = ) INT_MAX,
1137 INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut),
1138 INIT_FIELD (voice_size_in = ) sizeof (ALSAVoiceIn)
1139};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette