VirtualBox

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

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

Audio: Update.

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