VirtualBox

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

Last change on this file since 78135 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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