VirtualBox

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

Last change on this file since 57451 was 57451, checked in by vboxsync, 9 years ago

Audio: Try to fix chopped off samples when playing short sound files.

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