VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostALSAAudio.cpp@ 56644

Last change on this file since 56644 was 56644, checked in by vboxsync, 10 years ago

Audio/ALSA: Fix possible endless loop if the mixer buffer is already full and the ALSA capture device keeps producing samples (null capture device). Check how much we can write to the mixer buffer upfront to avoid loosing any samples

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.0 KB
Line 
1/* $Id: DrvHostALSAAudio.cpp 56644 2015-06-25 20:35:23Z vboxsync $ */
2/** @file
3 * VBox audio devices: ALSA audio driver.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 * --------------------------------------------------------------------
17 *
18 * This code is based on: alsaaudio.c
19 *
20 * QEMU ALSA audio driver
21 *
22 * Copyright (c) 2005 Vassili Karpov (malc)
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a copy
25 * of this software and associated documentation files (the "Software"), to deal
26 * in the Software without restriction, including without limitation the rights
27 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28 * copies of the Software, and to permit persons to whom the Software is
29 * furnished to do so, subject to the following conditions:
30 *
31 * The above copyright notice and this permission notice shall be included in
32 * all copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
37 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40 * THE SOFTWARE.
41 */
42
43/*******************************************************************************
44* Header Files *
45*******************************************************************************/
46
47#include <iprt/alloc.h>
48#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
49#include <VBox/vmm/pdmaudioifs.h>
50
51RT_C_DECLS_BEGIN
52 #include "alsa_stubs.h"
53 #include "alsa_mangling.h"
54RT_C_DECLS_END
55
56#include <alsa/asoundlib.h>
57
58#include "DrvAudio.h"
59#include "AudioMixBuffer.h"
60
61#include "VBoxDD.h"
62
63
64#ifdef LOG_GROUP
65# undef LOG_GROUP
66#endif
67#define LOG_GROUP LOG_GROUP_DEV_AUDIO
68#include <VBox/log.h>
69
70typedef struct ALSAAUDIOSTREAMIN
71{
72 PDMAUDIOHSTSTRMIN pStreamIn;
73 snd_pcm_t *phPCM;
74 void *pvBuf;
75 size_t cbBuf;
76} ALSAAUDIOSTREAMIN, *PALSAAUDIOSTREAMIN;
77
78typedef struct ALSAAUDIOSTREAMOUT
79{
80 PDMAUDIOHSTSTRMOUT pStreamOut;
81 snd_pcm_t *phPCM;
82 void *pvBuf;
83 size_t cbBuf;
84} ALSAAUDIOSTREAMOUT, *PALSAAUDIOSTREAMOUT;
85
86/* latency = period_size * periods / (rate * bytes_per_frame) */
87
88typedef struct ALSAAUDIOCFG
89{
90 int size_in_usec_in;
91 int size_in_usec_out;
92 const char *pcm_name_in;
93 const char *pcm_name_out;
94 unsigned int buffer_size_in;
95 unsigned int period_size_in;
96 unsigned int buffer_size_out;
97 unsigned int period_size_out;
98 unsigned int threshold;
99
100 int buffer_size_in_overriden;
101 int period_size_in_overriden;
102
103 int buffer_size_out_overriden;
104 int period_size_out_overriden;
105
106} ALSAAUDIOCFG, *PALSAAUDIOCFG;
107
108static int drvHostALSAAudioRecover(snd_pcm_t *phPCM);
109
110static ALSAAUDIOCFG s_ALSAConf =
111{
112#ifdef HIGH_LATENCY
113 1,
114 1,
115#else
116 0,
117 0,
118#endif
119 "default",
120 "default",
121#ifdef HIGH_LATENCY
122 400000,
123 400000 / 4,
124 400000,
125 400000 / 4,
126#else
127# define DEFAULT_BUFFER_SIZE 1024
128# define DEFAULT_PERIOD_SIZE 256
129 DEFAULT_BUFFER_SIZE * 4,
130 DEFAULT_PERIOD_SIZE * 4,
131 DEFAULT_BUFFER_SIZE,
132 DEFAULT_PERIOD_SIZE,
133#endif
134 0,
135 0,
136 0,
137 0,
138 0
139};
140
141/**
142 * Host Alsa audio driver instance data.
143 * @implements PDMIAUDIOCONNECTOR
144 */
145typedef struct DRVHOSTALSAAUDIO
146{
147 /** Pointer to the driver instance structure. */
148 PPDMDRVINS pDrvIns;
149 /** Pointer to host audio interface. */
150 PDMIHOSTAUDIO IHostAudio;
151 /** Error count for not flooding the release log.
152 * UINT32_MAX for unlimited logging. */
153 uint32_t cLogErrors;
154} DRVHOSTALSAAUDIO, *PDRVHOSTALSAAUDIO;
155
156typedef struct ALSAAUDIOSTREAMCFG
157{
158 unsigned int freq;
159 snd_pcm_format_t fmt;
160 int nchannels;
161 unsigned long buffer_size;
162 unsigned long period_size;
163 snd_pcm_uframes_t samples;
164} ALSAAUDIOSTREAMCFG, *PALSAAUDIOSTREAMCFG;
165
166static int drvHostALSAAudioClose(snd_pcm_t **pphPCM)
167{
168 if (!pphPCM || !*pphPCM)
169 return VINF_SUCCESS;
170
171 int rc;
172 int rc2 = snd_pcm_close(*pphPCM);
173 if (rc2)
174 {
175 LogRel(("ALSA: Closing PCM descriptor failed: %s\n",
176 snd_strerror(rc2)));
177 rc = VERR_GENERAL_FAILURE; /** @todo */
178 }
179 else
180 {
181 *pphPCM = NULL;
182 rc = VINF_SUCCESS;
183 }
184
185 return rc;
186}
187
188static snd_pcm_format_t drvHostALSAAudioFmtToALSA(PDMAUDIOFMT fmt)
189{
190 switch (fmt)
191 {
192 case AUD_FMT_S8:
193 return SND_PCM_FORMAT_S8;
194
195 case AUD_FMT_U8:
196 return SND_PCM_FORMAT_U8;
197
198 case AUD_FMT_S16:
199 return SND_PCM_FORMAT_S16_LE;
200
201 case AUD_FMT_U16:
202 return SND_PCM_FORMAT_U16_LE;
203
204 case AUD_FMT_S32:
205 return SND_PCM_FORMAT_S32_LE;
206
207 case AUD_FMT_U32:
208 return SND_PCM_FORMAT_U32_LE;
209
210 default:
211 break;
212 }
213
214 AssertMsgFailed(("Format %ld not supported\n", fmt));
215 return SND_PCM_FORMAT_U8;
216}
217
218static int drvHostALSAAudioALSAToFmt(snd_pcm_format_t fmt,
219 PDMAUDIOFMT *pFmt, PDMAUDIOENDIANNESS *pEndianness)
220{
221 AssertPtrReturn(pFmt, VERR_INVALID_POINTER);
222 /* pEndianness is optional. */
223
224 switch (fmt)
225 {
226 case SND_PCM_FORMAT_S8:
227 *pFmt = AUD_FMT_S8;
228 if (pEndianness)
229 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
230 break;
231
232 case SND_PCM_FORMAT_U8:
233 *pFmt = AUD_FMT_U8;
234 if (pEndianness)
235 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
236 break;
237
238 case SND_PCM_FORMAT_S16_LE:
239 *pFmt = AUD_FMT_S16;
240 if (pEndianness)
241 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
242 break;
243
244 case SND_PCM_FORMAT_U16_LE:
245 *pFmt = AUD_FMT_U16;
246 if (pEndianness)
247 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
248 break;
249
250 case SND_PCM_FORMAT_S16_BE:
251 *pFmt = AUD_FMT_S16;
252 if (pEndianness)
253 *pEndianness = PDMAUDIOENDIANNESS_BIG;
254 break;
255
256 case SND_PCM_FORMAT_U16_BE:
257 *pFmt = AUD_FMT_U16;
258 if (pEndianness)
259 *pEndianness = PDMAUDIOENDIANNESS_BIG;
260 break;
261
262 case SND_PCM_FORMAT_S32_LE:
263 *pFmt = AUD_FMT_S32;
264 if (pEndianness)
265 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
266 break;
267
268 case SND_PCM_FORMAT_U32_LE:
269 *pFmt = AUD_FMT_U32;
270 if (pEndianness)
271 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
272 break;
273
274 case SND_PCM_FORMAT_S32_BE:
275 *pFmt = AUD_FMT_S32;
276 if (pEndianness)
277 *pEndianness = PDMAUDIOENDIANNESS_BIG;
278 break;
279
280 case SND_PCM_FORMAT_U32_BE:
281 *pFmt = AUD_FMT_U32;
282 if (pEndianness)
283 *pEndianness = PDMAUDIOENDIANNESS_BIG;
284 break;
285
286 default:
287 AssertMsgFailed(("Format %ld not supported\n", fmt));
288 return VERR_NOT_SUPPORTED;
289 }
290
291 return VINF_SUCCESS;
292}
293
294static int drvHostALSAAudioALSAGetShift(snd_pcm_format_t fmt, unsigned *puShift)
295{
296 AssertPtrReturn(puShift, VERR_INVALID_POINTER);
297
298 switch (fmt)
299 {
300 case SND_PCM_FORMAT_S8:
301 case SND_PCM_FORMAT_U8:
302 *puShift = 0;
303 break;
304
305 case SND_PCM_FORMAT_S16_LE:
306 case SND_PCM_FORMAT_U16_LE:
307 case SND_PCM_FORMAT_S16_BE:
308 case SND_PCM_FORMAT_U16_BE:
309 *puShift = 1;
310 break;
311
312 case SND_PCM_FORMAT_S32_LE:
313 case SND_PCM_FORMAT_U32_LE:
314 case SND_PCM_FORMAT_S32_BE:
315 case SND_PCM_FORMAT_U32_BE:
316 *puShift = 2;
317 break;
318
319 default:
320 AssertMsgFailed(("Format %ld not supported\n", fmt));
321 return VERR_NOT_SUPPORTED;
322 }
323
324 return VINF_SUCCESS;
325}
326
327static int drvHostALSAAudioSetThreshold(snd_pcm_t *phPCM,
328 snd_pcm_uframes_t threshold)
329{
330 snd_pcm_sw_params_t *pSWParms = NULL;
331 snd_pcm_sw_params_alloca(&pSWParms);
332 if (!pSWParms)
333 return VERR_NO_MEMORY;
334
335 int rc;
336 do
337 {
338 int err = snd_pcm_sw_params_current(phPCM, pSWParms);
339 if (err < 0)
340 {
341 LogRel(("ALSA: Failed to get current software parameters for threshold: %s\n",
342 snd_strerror(err)));
343 rc = VERR_ACCESS_DENIED;
344 break;
345 }
346
347 err = snd_pcm_sw_params_set_start_threshold(phPCM, pSWParms, threshold);
348 if (err < 0)
349 {
350 LogRel(("ALSA: Failed to set software threshold to %ld: %s\n",
351 threshold, snd_strerror(err)));
352 rc = VERR_ACCESS_DENIED;
353 break;
354 }
355
356 err = snd_pcm_sw_params(phPCM, pSWParms);
357 if (err < 0)
358 {
359 LogRel(("ALSA: Failed to set new software parameters for threshold: %s\n",
360 snd_strerror(err)));
361 rc = VERR_ACCESS_DENIED;
362 break;
363 }
364
365 LogFlowFunc(("Setting threshold to %RU32\n", threshold));
366 rc = VINF_SUCCESS;
367 }
368 while (0);
369
370 return rc;
371}
372
373static int drvHostALSAAudioOpen(bool fIn,
374 PALSAAUDIOSTREAMCFG pCfgReq,
375 PALSAAUDIOSTREAMCFG pCfgObt,
376 snd_pcm_t **pphPCM)
377{
378 snd_pcm_t *phPCM = NULL;
379 int rc;
380
381 unsigned int cChannels = pCfgReq->nchannels;
382 unsigned int uFreq = pCfgReq->freq;
383 snd_pcm_uframes_t obt_buffer_size;
384
385 do
386 {
387 const char *pszDev = fIn ? s_ALSAConf.pcm_name_in : s_ALSAConf.pcm_name_out;
388 if (!pszDev)
389 {
390 LogRel(("ALSA: Invalid or no %s device name set\n",
391 fIn ? "input" : "output"));
392 rc = VERR_INVALID_PARAMETER;
393 break;
394 }
395
396 int err = snd_pcm_open(&phPCM, pszDev,
397 fIn ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
398 SND_PCM_NONBLOCK);
399 if (err < 0)
400 {
401 LogRel(("ALSA: Failed to open \"%s\" as %s: %s\n", pszDev,
402 fIn ? "ADC" : "DAC", snd_strerror(err)));
403 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
404 break;
405 }
406
407 snd_pcm_hw_params_t *pHWParms;
408 snd_pcm_hw_params_alloca(&pHWParms); /** @todo Check for successful allocation? */
409 err = snd_pcm_hw_params_any(phPCM, pHWParms);
410 if (err < 0)
411 {
412 LogRel(("ALSA: Failed to initialize hardware parameters: %s\n",
413 snd_strerror(err)));
414 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
415 break;
416 }
417
418 err = snd_pcm_hw_params_set_access(phPCM, pHWParms,
419 SND_PCM_ACCESS_RW_INTERLEAVED);
420 if (err < 0)
421 {
422 LogRel(("ALSA: Failed to set access type: %s\n", snd_strerror(err)));
423 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
424 break;
425 }
426
427 err = snd_pcm_hw_params_set_format(phPCM, pHWParms, pCfgReq->fmt);
428 if (err < 0)
429 {
430 LogRel(("ALSA: Failed to set audio format to %d: %s\n",
431 pCfgReq->fmt, snd_strerror(err)));
432 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
433 break;
434 }
435
436 err = snd_pcm_hw_params_set_rate_near(phPCM, pHWParms, &uFreq, 0);
437 if (err < 0)
438 {
439 LogRel(("ALSA: Failed to set frequency to %dHz: %s\n",
440 pCfgReq->freq, snd_strerror(err)));
441 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
442 break;
443 }
444
445 err = snd_pcm_hw_params_set_channels_near(phPCM, pHWParms, &cChannels);
446 if (err < 0)
447 {
448 LogRel(("ALSA: Failed to set number of channels to %d\n", pCfgReq->nchannels));
449 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
450 break;
451 }
452
453 if ( cChannels != 1
454 && cChannels != 2)
455 {
456 LogRel(("ALSA: Number of audio channels (%u) not supported\n", cChannels));
457 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
458 break;
459 }
460
461 unsigned int period_size = pCfgReq->period_size;
462 unsigned int buffer_size = pCfgReq->buffer_size;
463
464 if ( !((fIn && s_ALSAConf.size_in_usec_in)
465 || (!fIn && s_ALSAConf.size_in_usec_out)))
466 {
467 if (!buffer_size)
468 {
469 buffer_size = DEFAULT_BUFFER_SIZE;
470 period_size = DEFAULT_PERIOD_SIZE;
471 }
472 }
473
474 if (buffer_size)
475 {
476 if ( ( fIn && s_ALSAConf.size_in_usec_in)
477 || (!fIn && s_ALSAConf.size_in_usec_out))
478 {
479 if (period_size)
480 {
481 err = snd_pcm_hw_params_set_period_time_near(phPCM, pHWParms,
482 &period_size, 0);
483 if (err < 0)
484 {
485 LogRel(("ALSA: Failed to set period time %d\n", pCfgReq->period_size));
486 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
487 break;
488 }
489 }
490
491 err = snd_pcm_hw_params_set_buffer_time_near(phPCM, pHWParms,
492 &buffer_size, 0);
493 if (err < 0)
494 {
495 LogRel(("ALSA: Failed to set buffer time %d\n", pCfgReq->buffer_size));
496 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
497 break;
498 }
499 }
500 else
501 {
502 snd_pcm_uframes_t period_size_f = (snd_pcm_uframes_t)period_size;
503 snd_pcm_uframes_t buffer_size_f = (snd_pcm_uframes_t)buffer_size;
504
505 snd_pcm_uframes_t minval;
506
507 if (period_size_f)
508 {
509 minval = period_size_f;
510
511 int dir = 0;
512 err = snd_pcm_hw_params_get_period_size_min(pHWParms,
513 &minval, &dir);
514 if (err < 0)
515 {
516 LogRel(("ALSA: Could not determine minimal period size\n"));
517 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
518 break;
519 }
520 else
521 {
522 LogFunc(("Minimal period size is: %ld\n", minval));
523 if (period_size_f < minval)
524 {
525 if ( ( fIn && s_ALSAConf.period_size_in_overriden)
526 || (!fIn && s_ALSAConf.period_size_out_overriden))
527 {
528 LogFunc(("Period size %RU32 is less than minimal period size %RU32\n",
529 period_size_f, minval));
530 }
531
532 period_size_f = minval;
533 }
534 }
535
536 err = snd_pcm_hw_params_set_period_size_near(phPCM, pHWParms,
537 &period_size_f, 0);
538 LogFunc(("Period size is: %RU32\n", period_size_f));
539 if (err < 0)
540 {
541 LogRel(("ALSA: Failed to set period size %d (%s)\n",
542 period_size_f, snd_strerror(err)));
543 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
544 break;
545 }
546 }
547
548 /* Calculate default buffer size here since it might have been changed
549 * in the _near functions */
550 buffer_size_f = 4 * period_size_f;
551
552 minval = buffer_size_f;
553 err = snd_pcm_hw_params_get_buffer_size_min(pHWParms, &minval);
554 if (err < 0)
555 {
556 LogRel(("ALSA: Could not retrieve minimal buffer size\n"));
557 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
558 break;
559 }
560 else
561 {
562 LogFunc(("Minimal buffer size is: %RU32\n", minval));
563 if (buffer_size_f < minval)
564 {
565 if ( ( fIn && s_ALSAConf.buffer_size_in_overriden)
566 || (!fIn && s_ALSAConf.buffer_size_out_overriden))
567 {
568 LogFunc(("Buffer size %RU32 is less than minimal buffer size %RU32\n",
569 buffer_size_f, minval));
570 }
571
572 buffer_size_f = minval;
573 }
574 }
575
576 err = snd_pcm_hw_params_set_buffer_size_near(phPCM,
577 pHWParms, &buffer_size_f);
578 LogFunc(("Buffer size is: %RU32\n", buffer_size_f));
579 if (err < 0)
580 {
581 LogRel(("ALSA: Failed to set buffer size %d: %s\n",
582 buffer_size_f, snd_strerror(err)));
583 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
584 break;
585 }
586 }
587 }
588 else
589 LogFunc(("Warning: Buffer size is not set\n"));
590
591 err = snd_pcm_hw_params(phPCM, pHWParms);
592 if (err < 0)
593 {
594 LogRel(("ALSA: Failed to apply audio parameters\n"));
595 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
596 break;
597 }
598
599 err = snd_pcm_hw_params_get_buffer_size(pHWParms, &obt_buffer_size);
600 if (err < 0)
601 {
602 LogRel(("ALSA: Failed to get buffer size\n"));
603 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
604 break;
605 }
606
607 snd_pcm_uframes_t obt_period_size;
608 int dir = 0;
609 err = snd_pcm_hw_params_get_period_size(pHWParms, &obt_period_size, &dir);
610 if (err < 0)
611 {
612 LogRel(("ALSA: Failed to get period size\n"));
613 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
614 break;
615 }
616
617 LogFunc(("Freq=%dHz, period size=%RU32, buffer size=%RU32\n",
618 pCfgReq->freq, obt_period_size, obt_buffer_size));
619
620 err = snd_pcm_prepare(phPCM);
621 if (err < 0)
622 {
623 LogRel(("ALSA: Could not prepare hPCM %p\n", (void *)phPCM));
624 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
625 break;
626 }
627
628 if ( !fIn
629 && s_ALSAConf.threshold)
630 {
631 unsigned uShift;
632 rc = drvHostALSAAudioALSAGetShift(pCfgReq->fmt, &uShift);
633 if (RT_SUCCESS(rc))
634 {
635 int bytes_per_sec = uFreq
636 << (cChannels == 2)
637 << uShift;
638
639 snd_pcm_uframes_t threshold
640 = (s_ALSAConf.threshold * bytes_per_sec) / 1000;
641
642 rc = drvHostALSAAudioSetThreshold(phPCM, threshold);
643 }
644 }
645 else
646 rc = VINF_SUCCESS;
647 }
648 while (0);
649
650 if (RT_SUCCESS(rc))
651 {
652 pCfgObt->fmt = pCfgReq->fmt;
653 pCfgObt->nchannels = cChannels;
654 pCfgObt->freq = uFreq;
655 pCfgObt->samples = obt_buffer_size;
656
657 *pphPCM = phPCM;
658 }
659 else
660 drvHostALSAAudioClose(&phPCM);
661
662 LogFlowFuncLeaveRC(rc);
663 return rc;
664}
665
666#ifdef DEBUG
667static void drvHostALSAAudioErrorHandler(const char *file, int line, const char *function,
668 int err, const char *fmt, ...)
669{
670 /** @todo Implement me! */
671}
672#endif
673
674static int drvHostALSAAudioGetAvail(snd_pcm_t *phPCM, snd_pcm_sframes_t *pFramesAvail)
675{
676 AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
677 AssertPtrReturn(pFramesAvail, VERR_INVALID_POINTER);
678
679 int rc;
680
681 snd_pcm_sframes_t framesAvail;
682 framesAvail = snd_pcm_avail_update(phPCM);
683 if (framesAvail < 0)
684 {
685 if (framesAvail == -EPIPE)
686 {
687 rc = drvHostALSAAudioRecover(phPCM);
688 if (RT_SUCCESS(rc))
689 framesAvail = snd_pcm_avail_update(phPCM);
690 }
691 else
692 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
693 }
694 else
695 rc = VINF_SUCCESS;
696
697 if (framesAvail >= 0)
698 *pFramesAvail = framesAvail;
699
700 return rc;
701}
702
703static int drvHostALSAAudioRecover(snd_pcm_t *phPCM)
704{
705 AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
706
707 int err = snd_pcm_prepare(phPCM);
708 if (err < 0)
709 {
710 LogFunc(("Failed to recover stream %p: %s\n",
711 phPCM, snd_strerror(err)));
712 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
713 }
714
715 return VINF_SUCCESS;
716}
717
718static int drvHostALSAAudioResume(snd_pcm_t *phPCM)
719{
720 AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
721
722 int err = snd_pcm_resume(phPCM);
723 if (err < 0)
724 {
725 LogFunc(("Failed to resume stream %p: %s\n",
726 phPCM, snd_strerror(err)));
727 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
728 }
729
730 return VINF_SUCCESS;
731}
732
733static int drvHostALSAAudioStreamCtl(snd_pcm_t *phPCM, bool fPause)
734{
735 int err;
736 if (fPause)
737 {
738 err = snd_pcm_drop(phPCM);
739 if (err < 0)
740 {
741 LogFlow(("Error stopping stream %p: %s\n",
742 phPCM, snd_strerror(err)));
743 return VERR_ACCESS_DENIED;
744 }
745 }
746 else
747 {
748 err = snd_pcm_prepare (phPCM);
749 if (err < 0)
750 {
751 LogFlow(("Error preparing stream %p: %s\n",
752 phPCM, snd_strerror(err)));
753 return VERR_ACCESS_DENIED;
754 }
755 }
756
757 return VINF_SUCCESS;
758}
759
760static DECLCALLBACK(int) drvHostALSAAudioInit(PPDMIHOSTAUDIO pInterface)
761{
762 NOREF(pInterface);
763
764 LogFlowFuncEnter();
765
766 int rc = audioLoadAlsaLib();
767 if (RT_FAILURE(rc))
768 LogRel(("ALSA: Failed to load the ALSA shared library, rc=%Rrc\n", rc));
769 else
770 {
771#ifdef DEBUG
772 snd_lib_error_set_handler(drvHostALSAAudioErrorHandler);
773#endif
774 }
775
776 return rc;
777}
778
779static DECLCALLBACK(int) drvHostALSAAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
780 uint32_t *pcSamplesCaptured)
781{
782 NOREF(pInterface);
783 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
784
785 PALSAAUDIOSTREAMIN pThisStrmIn = (PALSAAUDIOSTREAMIN)pHstStrmIn;
786
787 snd_pcm_sframes_t cAvail;
788 int rc = drvHostALSAAudioGetAvail(pThisStrmIn->phPCM, &cAvail);
789 if (RT_FAILURE(rc))
790 {
791 LogFunc(("Error getting number of captured frames, rc=%Rrc\n", rc));
792 return rc;
793 }
794
795 if (!cAvail) /* No data yet? */
796 {
797 snd_pcm_state_t state = snd_pcm_state(pThisStrmIn->phPCM);
798 switch (state)
799 {
800 case SND_PCM_STATE_PREPARED:
801 cAvail = AudioMixBufFree(&pHstStrmIn->MixBuf);
802 break;
803
804 case SND_PCM_STATE_SUSPENDED:
805 {
806 rc = drvHostALSAAudioResume(pThisStrmIn->phPCM);
807 if (RT_FAILURE(rc))
808 break;
809
810 LogFlow(("Resuming suspended input stream\n"));
811 break;
812 }
813
814 default:
815 LogFlow(("No frames available, state=%d\n", state));
816 break;
817 }
818
819 if (!cAvail)
820 {
821 if (pcSamplesCaptured)
822 *pcSamplesCaptured = 0;
823 return VINF_SUCCESS;
824 }
825 }
826
827 /*
828 * Check how much we can read from the capture device without overflowing
829 * the mixer buffer.
830 */
831 Assert(cAvail);
832 size_t cbToRead = RT_MIN(AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cAvail),
833 AudioMixBufFreeBytes(&pHstStrmIn->MixBuf));
834
835 LogFlowFunc(("cbToRead=%zu, cAvail=%RI32\n", cbToRead, cAvail));
836
837 uint32_t cWrittenTotal = 0;
838 snd_pcm_uframes_t cToRead;
839 snd_pcm_sframes_t cRead;
840
841 while ( cbToRead
842 && RT_SUCCESS(rc))
843 {
844 cToRead = RT_MIN(AUDIOMIXBUF_B2S(&pHstStrmIn->MixBuf, cbToRead),
845 AUDIOMIXBUF_B2S(&pHstStrmIn->MixBuf, pThisStrmIn->cbBuf));
846 AssertBreakStmt(cToRead, rc = VERR_NO_DATA);
847 cRead = snd_pcm_readi(pThisStrmIn->phPCM, pThisStrmIn->pvBuf, cToRead);
848 if (cRead <= 0)
849 {
850 switch (cRead)
851 {
852 case 0:
853 {
854 LogFunc(("No input frames available\n"));
855 rc = VERR_ACCESS_DENIED;
856 break;
857 }
858
859 case -EAGAIN:
860 /*
861 * Don't set error here because EAGAIN means there are no further frames
862 * available at the moment, try later. As we might have read some frames
863 * already these need to be processed instead.
864 */
865 cbToRead = 0;
866 break;
867
868 case -EPIPE:
869 {
870 rc = drvHostALSAAudioRecover(pThisStrmIn->phPCM);
871 if (RT_FAILURE(rc))
872 break;
873
874 LogFlowFunc(("Recovered from capturing\n"));
875 continue;
876 }
877
878 default:
879 LogFunc(("Failed to read input frames: %s\n", snd_strerror(cRead)));
880 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
881 break;
882 }
883 }
884 else
885 {
886 uint32_t cWritten;
887 rc = AudioMixBufWriteCirc(&pHstStrmIn->MixBuf,
888 pThisStrmIn->pvBuf, AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cRead),
889 &cWritten);
890 if (RT_FAILURE(rc))
891 break;
892
893 /*
894 * We should not run into a full mixer buffer or we loose samples and
895 * run into an endless loop if ALSA keeps producing samples ("null"
896 * capture device for example).
897 */
898 AssertLogRelMsgBreakStmt(cWritten > 0, ("Mixer buffer shouldn't be full at this point!\n"),
899 rc = VERR_INTERNAL_ERROR);
900 uint32_t cbWritten = AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cWritten);
901
902 Assert(cbToRead >= cbWritten);
903 cbToRead -= cbWritten;
904 cWrittenTotal += cWritten;
905 }
906 }
907
908 if (RT_SUCCESS(rc))
909 {
910 uint32_t cProcessed = 0;
911 if (cWrittenTotal)
912 rc = AudioMixBufMixToParent(&pHstStrmIn->MixBuf, cWrittenTotal,
913 &cProcessed);
914
915 if (pcSamplesCaptured)
916 *pcSamplesCaptured = cWrittenTotal;
917
918 LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
919 cWrittenTotal, cProcessed, rc));
920 }
921
922 LogFlowFuncLeaveRC(rc);
923 return rc;
924}
925
926static DECLCALLBACK(int) drvHostALSAAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
927 uint32_t *pcSamplesPlayed)
928{
929 NOREF(pInterface);
930 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
931
932 PALSAAUDIOSTREAMOUT pThisStrmOut = (PALSAAUDIOSTREAMOUT)pHstStrmOut;
933
934 int rc = VINF_SUCCESS;
935 uint32_t cbReadTotal = 0;
936
937 do
938 {
939 snd_pcm_sframes_t cAvail;
940 rc = drvHostALSAAudioGetAvail(pThisStrmOut->phPCM, &cAvail);
941 if (RT_FAILURE(rc))
942 {
943 LogFunc(("Error getting number of playback frames, rc=%Rrc\n", rc));
944 break;
945 }
946
947 size_t cbToRead = RT_MIN(AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf,
948 cAvail),
949 AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf,
950 drvAudioHstOutSamplesLive(pHstStrmOut, NULL /* pcStreamsLive */)));
951 LogFlowFunc(("cbToRead=%zu, cbAvail=%zu\n",
952 cbToRead, AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cAvail)));
953
954 uint32_t cRead, cbRead;
955 snd_pcm_sframes_t cWritten;
956 while (cbToRead)
957 {
958 rc = AudioMixBufReadCirc(&pHstStrmOut->MixBuf, pThisStrmOut->pvBuf, cbToRead, &cRead);
959 if (RT_FAILURE(rc))
960 break;
961
962 cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
963 AssertBreak(cbRead);
964
965 cWritten = snd_pcm_writei(pThisStrmOut->phPCM, pThisStrmOut->pvBuf, cRead);
966 if (cWritten <= 0)
967 {
968 switch (cWritten)
969 {
970 case 0:
971 {
972 LogFunc(("Failed to write %RI32 frames\n", cRead));
973 rc = VERR_ACCESS_DENIED;
974 break;
975 }
976
977 case -EPIPE:
978 {
979 rc = drvHostALSAAudioRecover(pThisStrmOut->phPCM);
980 if (RT_FAILURE(rc))
981 break;
982
983 LogFlowFunc(("Recovered from playback\n"));
984 continue;
985 }
986
987 case -ESTRPIPE:
988 {
989 /* Stream was suspended and waiting for a recovery. */
990 rc = drvHostALSAAudioResume(pThisStrmOut->phPCM);
991 if (RT_FAILURE(rc))
992 {
993 LogRel(("ALSA: Failed to resume output stream\n"));
994 break;
995 }
996
997 LogFlowFunc(("Resumed suspended output stream\n"));
998 continue;
999 }
1000
1001 default:
1002 LogFlowFunc(("Failed to write %RI32 output frames, rc=%Rrc\n",
1003 cRead, rc));
1004 rc = VERR_GENERAL_FAILURE; /** @todo */
1005 break;
1006 }
1007 }
1008
1009 if (RT_FAILURE(rc))
1010 break;
1011
1012 Assert(cbToRead >= cRead);
1013 cbToRead -= cbRead;
1014 cbReadTotal += cbRead;
1015 }
1016 }
1017 while (0);
1018
1019 if (RT_SUCCESS(rc))
1020 {
1021 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbReadTotal);
1022 if (cReadTotal)
1023 AudioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
1024
1025 if (pcSamplesPlayed)
1026 *pcSamplesPlayed = cReadTotal;
1027
1028 LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n",
1029 cReadTotal, cbReadTotal, rc));
1030 }
1031
1032 LogFlowFuncLeaveRC(rc);
1033 return rc;
1034}
1035
1036static DECLCALLBACK(int) drvHostALSAAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
1037{
1038 NOREF(pInterface);
1039 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1040
1041 PALSAAUDIOSTREAMIN pThisStrmIn = (PALSAAUDIOSTREAMIN)pHstStrmIn;
1042
1043 drvHostALSAAudioClose(&pThisStrmIn->phPCM);
1044
1045 if (pThisStrmIn->pvBuf)
1046 {
1047 RTMemFree(pThisStrmIn->pvBuf);
1048 pThisStrmIn->pvBuf = NULL;
1049 }
1050
1051 return VINF_SUCCESS;
1052}
1053
1054static DECLCALLBACK(int) drvHostALSAAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
1055{
1056 NOREF(pInterface);
1057 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1058
1059 PALSAAUDIOSTREAMOUT pThisStrmOut = (PALSAAUDIOSTREAMOUT)pHstStrmOut;
1060
1061 drvHostALSAAudioClose(&pThisStrmOut->phPCM);
1062
1063 if (pThisStrmOut->pvBuf)
1064 {
1065 RTMemFree(pThisStrmOut->pvBuf);
1066 pThisStrmOut->pvBuf = NULL;
1067 }
1068
1069 return VINF_SUCCESS;
1070}
1071
1072static DECLCALLBACK(int) drvHostALSAAudioInitOut(PPDMIHOSTAUDIO pInterface,
1073 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
1074 uint32_t *pcSamples)
1075{
1076 NOREF(pInterface);
1077 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1078 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1079
1080 PALSAAUDIOSTREAMOUT pThisStrmOut = (PALSAAUDIOSTREAMOUT)pHstStrmOut;
1081 snd_pcm_t *phPCM = NULL;
1082
1083 int rc;
1084
1085 do
1086 {
1087 ALSAAUDIOSTREAMCFG req;
1088 req.fmt = drvHostALSAAudioFmtToALSA(pCfg->enmFormat);
1089 req.freq = pCfg->uHz;
1090 req.nchannels = pCfg->cChannels;
1091 req.period_size = s_ALSAConf.period_size_out;
1092 req.buffer_size = s_ALSAConf.buffer_size_out;
1093
1094 ALSAAUDIOSTREAMCFG obt;
1095 rc = drvHostALSAAudioOpen(false /* false */, &req, &obt, &phPCM);
1096 if (RT_FAILURE(rc))
1097 break;
1098
1099 PDMAUDIOFMT enmFormat;
1100 PDMAUDIOENDIANNESS enmEnd;
1101 rc = drvHostALSAAudioALSAToFmt(obt.fmt, &enmFormat, &enmEnd);
1102 if (RT_FAILURE(rc))
1103 break;
1104
1105 PDMAUDIOSTREAMCFG streamCfg;
1106 streamCfg.uHz = obt.freq;
1107 streamCfg.cChannels = obt.nchannels;
1108 streamCfg.enmFormat = enmFormat;
1109 streamCfg.enmEndianness = enmEnd;
1110
1111 rc = drvAudioStreamCfgToProps(&streamCfg, &pHstStrmOut->Props);
1112 if (RT_FAILURE(rc))
1113 break;
1114
1115 AssertBreakStmt(obt.samples, rc = VERR_INVALID_PARAMETER);
1116 size_t cbBuf = obt.samples * (1 << pHstStrmOut->Props.cShift);
1117 AssertBreakStmt(cbBuf, rc = VERR_INVALID_PARAMETER);
1118 pThisStrmOut->pvBuf = RTMemAlloc(cbBuf);
1119 if (!pThisStrmOut->pvBuf)
1120 {
1121 LogRel(("ALSA: Not enough memory for output DAC buffer (%RU32 samples, each %d bytes)\n",
1122 obt.samples, 1 << pHstStrmOut->Props.cShift));
1123 rc = VERR_NO_MEMORY;
1124 break;
1125 }
1126
1127 pThisStrmOut->cbBuf = cbBuf;
1128 pThisStrmOut->phPCM = phPCM;
1129
1130 if (pcSamples)
1131 *pcSamples = obt.samples;
1132 }
1133 while (0);
1134
1135 if (RT_FAILURE(rc))
1136 drvHostALSAAudioClose(&phPCM);
1137
1138 LogFlowFuncLeaveRC(rc);
1139 return rc;
1140}
1141
1142static DECLCALLBACK(int) drvHostALSAAudioInitIn(PPDMIHOSTAUDIO pInterface,
1143 PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
1144 PDMAUDIORECSOURCE enmRecSource,
1145 uint32_t *pcSamples)
1146{
1147 NOREF(pInterface);
1148 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1149 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1150
1151 int rc;
1152
1153 PALSAAUDIOSTREAMIN pThisStrmIn = (PALSAAUDIOSTREAMIN)pHstStrmIn;
1154 snd_pcm_t *phPCM = NULL;
1155
1156 do
1157 {
1158 ALSAAUDIOSTREAMCFG req;
1159 req.fmt = drvHostALSAAudioFmtToALSA(pCfg->enmFormat);
1160 req.freq = pCfg->uHz;
1161 req.nchannels = pCfg->cChannels;
1162 req.period_size = s_ALSAConf.period_size_in;
1163 req.buffer_size = s_ALSAConf.buffer_size_in;
1164
1165 ALSAAUDIOSTREAMCFG obt;
1166 rc = drvHostALSAAudioOpen(true /* fIn */, &req, &obt, &phPCM);
1167 if (RT_FAILURE(rc))
1168 break;
1169
1170 PDMAUDIOFMT enmFormat;
1171 PDMAUDIOENDIANNESS enmEnd;
1172 rc = drvHostALSAAudioALSAToFmt(obt.fmt, &enmFormat, &enmEnd);
1173 if (RT_FAILURE(rc))
1174 break;
1175
1176 PDMAUDIOSTREAMCFG streamCfg;
1177 streamCfg.uHz = obt.freq;
1178 streamCfg.cChannels = obt.nchannels;
1179 streamCfg.enmFormat = enmFormat;
1180 streamCfg.enmEndianness = enmEnd;
1181
1182 rc = drvAudioStreamCfgToProps(&streamCfg, &pHstStrmIn->Props);
1183 if (RT_FAILURE(rc))
1184 break;
1185
1186 AssertBreakStmt(obt.samples, rc = VERR_INVALID_PARAMETER);
1187 size_t cbBuf = obt.samples * (1 << pHstStrmIn->Props.cShift);
1188 AssertBreakStmt(cbBuf, rc = VERR_INVALID_PARAMETER);
1189 pThisStrmIn->pvBuf = RTMemAlloc(cbBuf);
1190 if (!pThisStrmIn->pvBuf)
1191 {
1192 LogRel(("ALSA: Not enough memory for input ADC buffer (%RU32 samples, each %d bytes)\n",
1193 obt.samples, 1 << pHstStrmIn->Props.cShift));
1194 rc = VERR_NO_MEMORY;
1195 break;
1196 }
1197
1198 pThisStrmIn->cbBuf = cbBuf;
1199 pThisStrmIn->phPCM = phPCM;
1200
1201 if (pcSamples)
1202 *pcSamples = obt.samples;
1203 }
1204 while (0);
1205
1206 if (RT_FAILURE(rc))
1207 drvHostALSAAudioClose(&phPCM);
1208
1209 LogFlowFuncLeaveRC(rc);
1210 return rc;
1211}
1212
1213static DECLCALLBACK(bool) drvHostALSAAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
1214{
1215 NOREF(pInterface);
1216 NOREF(enmDir);
1217 return true; /* Always all enabled. */
1218}
1219
1220static DECLCALLBACK(int) drvHostALSAAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
1221 PDMAUDIOSTREAMCMD enmStreamCmd)
1222{
1223 NOREF(pInterface);
1224 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1225 PALSAAUDIOSTREAMIN pThisStrmIn = (PALSAAUDIOSTREAMIN)pHstStrmIn;
1226
1227 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
1228
1229 int rc;
1230 switch (enmStreamCmd)
1231 {
1232 case PDMAUDIOSTREAMCMD_ENABLE:
1233 rc = drvHostALSAAudioStreamCtl(pThisStrmIn->phPCM, false /* fStop */);
1234 break;
1235
1236 case PDMAUDIOSTREAMCMD_DISABLE:
1237 rc = drvHostALSAAudioStreamCtl(pThisStrmIn->phPCM, true /* fStop */);
1238 break;
1239
1240 default:
1241 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
1242 rc = VERR_INVALID_PARAMETER;
1243 break;
1244 }
1245
1246 return rc;
1247}
1248
1249static DECLCALLBACK(int) drvHostALSAAudioControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
1250 PDMAUDIOSTREAMCMD enmStreamCmd)
1251{
1252 NOREF(pInterface);
1253 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1254 PALSAAUDIOSTREAMOUT pThisStrmOut = (PALSAAUDIOSTREAMOUT)pHstStrmOut;
1255
1256 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
1257
1258 int rc;
1259 switch (enmStreamCmd)
1260 {
1261 case PDMAUDIOSTREAMCMD_ENABLE:
1262 rc = drvHostALSAAudioStreamCtl(pThisStrmOut->phPCM, false /* fStop */);
1263 break;
1264
1265 case PDMAUDIOSTREAMCMD_DISABLE:
1266 rc = drvHostALSAAudioStreamCtl(pThisStrmOut->phPCM, true /* fStop */);
1267 break;
1268
1269 default:
1270 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
1271 rc = VERR_INVALID_PARAMETER;
1272 break;
1273 }
1274
1275 return rc;
1276}
1277
1278static DECLCALLBACK(int) drvHostALSAAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
1279{
1280 NOREF(pInterface);
1281 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1282
1283 pCfg->cbStreamOut = sizeof(ALSAAUDIOSTREAMOUT);
1284 pCfg->cbStreamIn = sizeof(ALSAAUDIOSTREAMIN);
1285 pCfg->cMaxHstStrmsOut = INT_MAX;
1286 pCfg->cMaxHstStrmsIn = INT_MAX;
1287
1288 return VINF_SUCCESS;
1289}
1290
1291static DECLCALLBACK(void) drvHostALSAAudioShutdown(PPDMIHOSTAUDIO pInterface)
1292{
1293 NOREF(pInterface);
1294}
1295
1296/**
1297 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1298 */
1299static DECLCALLBACK(void *) drvHostALSAAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1300{
1301 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1302 PDRVHOSTALSAAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTALSAAUDIO);
1303 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1304 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1305
1306 return NULL;
1307}
1308
1309/**
1310 * Construct a DirectSound Audio driver instance.
1311 *
1312 * @copydoc FNPDMDRVCONSTRUCT
1313 */
1314static DECLCALLBACK(int) drvHostAlsaAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1315{
1316 PDRVHOSTALSAAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTALSAAUDIO);
1317 LogRel(("Audio: Initializing ALSA driver\n"));
1318
1319 /*
1320 * Init the static parts.
1321 */
1322 pThis->pDrvIns = pDrvIns;
1323 /* IBase */
1324 pDrvIns->IBase.pfnQueryInterface = drvHostALSAAudioQueryInterface;
1325 /* IHostAudio */
1326 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostALSAAudio);
1327
1328 return VINF_SUCCESS;
1329}
1330
1331/**
1332 * Char driver registration record.
1333 */
1334const PDMDRVREG g_DrvHostALSAAudio =
1335{
1336 /* u32Version */
1337 PDM_DRVREG_VERSION,
1338 /* szName */
1339 "ALSAAudio",
1340 /* szRCMod */
1341 "",
1342 /* szR0Mod */
1343 "",
1344 /* pszDescription */
1345 "ALSA host audio driver",
1346 /* fFlags */
1347 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1348 /* fClass. */
1349 PDM_DRVREG_CLASS_AUDIO,
1350 /* cMaxInstances */
1351 ~0U,
1352 /* cbInstance */
1353 sizeof(DRVHOSTALSAAUDIO),
1354 /* pfnConstruct */
1355 drvHostAlsaAudioConstruct,
1356 /* pfnDestruct */
1357 NULL,
1358 /* pfnRelocate */
1359 NULL,
1360 /* pfnIOCtl */
1361 NULL,
1362 /* pfnPowerOn */
1363 NULL,
1364 /* pfnReset */
1365 NULL,
1366 /* pfnSuspend */
1367 NULL,
1368 /* pfnResume */
1369 NULL,
1370 /* pfnAttach */
1371 NULL,
1372 /* pfnDetach */
1373 NULL,
1374 /* pfnPowerOff */
1375 NULL,
1376 /* pfnSoftReset */
1377 NULL,
1378 /* u32EndVersion */
1379 PDM_DRVREG_VERSION
1380};
1381
1382static struct audio_option alsa_options[] =
1383{
1384 {"DACSizeInUsec", AUD_OPT_BOOL, &s_ALSAConf.size_in_usec_out,
1385 "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
1386 {"DACPeriodSize", AUD_OPT_INT, &s_ALSAConf.period_size_out,
1387 "DAC period size", &s_ALSAConf.period_size_out_overriden, 0},
1388 {"DACBufferSize", AUD_OPT_INT, &s_ALSAConf.buffer_size_out,
1389 "DAC buffer size", &s_ALSAConf.buffer_size_out_overriden, 0},
1390
1391 {"ADCSizeInUsec", AUD_OPT_BOOL, &s_ALSAConf.size_in_usec_in,
1392 "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
1393 {"ADCPeriodSize", AUD_OPT_INT, &s_ALSAConf.period_size_in,
1394 "ADC period size", &s_ALSAConf.period_size_in_overriden, 0},
1395 {"ADCBufferSize", AUD_OPT_INT, &s_ALSAConf.buffer_size_in,
1396 "ADC buffer size", &s_ALSAConf.buffer_size_in_overriden, 0},
1397
1398 {"Threshold", AUD_OPT_INT, &s_ALSAConf.threshold,
1399 "(undocumented)", NULL, 0},
1400
1401 {"DACDev", AUD_OPT_STR, &s_ALSAConf.pcm_name_out,
1402 "DAC device name (for instance dmix)", NULL, 0},
1403
1404 {"ADCDev", AUD_OPT_STR, &s_ALSAConf.pcm_name_in,
1405 "ADC device name", NULL, 0},
1406
1407 NULL
1408};
1409
Note: See TracBrowser for help on using the repository browser.

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