VirtualBox

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

最後變更 在這個檔案從787是 787,由 vboxsync 提交於 18 年 前

made ALSA work again

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

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