VirtualBox

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

最後變更 在這個檔案從87299是 87299,由 vboxsync 提交於 4 年 前

Audio/ALSA: Resolved a @todo (Initialize fSwapEndian in alsaALSAToAudioProps()).

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

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