VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio_old/DrvHostALSAAudio.cpp@ 61541

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

Audio: Use the old audio code for now when doing a release. Set VBOX_WITH_AUDIO_STABLE to an empty value to enable the new audio code (default for non-release builds for now).

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