VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvAudio.cpp@ 73483

Last change on this file since 73483 was 73480, checked in by vboxsync, 7 years ago

DrvAudio: Changed the default stream period size from 50ms to 200 (can be changed via extra-data).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 123.9 KB
Line 
1/* $Id: DrvAudio.cpp 73480 2018-08-03 12:31:54Z vboxsync $ */
2/** @file
3 * Intermediate audio driver header.
4 *
5 * @remarks Intermediate audio driver for connecting the audio device emulation
6 * with the host backend.
7 */
8
9/*
10 * Copyright (C) 2006-2018 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 */
20
21#define LOG_GROUP LOG_GROUP_DRV_AUDIO
22#include <VBox/log.h>
23#include <VBox/vmm/pdm.h>
24#include <VBox/err.h>
25#include <VBox/vmm/mm.h>
26#include <VBox/vmm/pdmaudioifs.h>
27
28#include <iprt/alloc.h>
29#include <iprt/asm-math.h>
30#include <iprt/assert.h>
31#include <iprt/circbuf.h>
32#include <iprt/string.h>
33#include <iprt/uuid.h>
34
35#include "VBoxDD.h"
36
37#include <ctype.h>
38#include <stdlib.h>
39
40#include "DrvAudio.h"
41#include "AudioMixBuffer.h"
42
43#ifdef VBOX_WITH_AUDIO_ENUM
44static int drvAudioDevicesEnumerateInternal(PDRVAUDIO pThis, bool fLog, PPDMAUDIODEVICEENUM pDevEnum);
45#endif
46
47static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream);
48static int drvAudioStreamControlInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd);
49static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd);
50static int drvAudioStreamCreateInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq);
51static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream);
52static void drvAudioStreamFree(PPDMAUDIOSTREAM pStream);
53static int drvAudioStreamUninitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream);
54static int drvAudioStreamInitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest);
55static int drvAudioStreamIterateInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream);
56static int drvAudioStreamReInitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream);
57static void drvAudioStreamResetInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream);
58
59#ifndef VBOX_AUDIO_TESTCASE
60
61# if 0 /* unused */
62
63static PDMAUDIOFMT drvAudioGetConfFormat(PCFGMNODE pCfgHandle, const char *pszKey,
64 PDMAUDIOFMT enmDefault, bool *pfDefault)
65{
66 if ( pCfgHandle == NULL
67 || pszKey == NULL)
68 {
69 *pfDefault = true;
70 return enmDefault;
71 }
72
73 char *pszValue = NULL;
74 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
75 if (RT_FAILURE(rc))
76 {
77 *pfDefault = true;
78 return enmDefault;
79 }
80
81 PDMAUDIOFMT fmt = DrvAudioHlpStrToAudFmt(pszValue);
82 if (fmt == PDMAUDIOFMT_INVALID)
83 {
84 *pfDefault = true;
85 return enmDefault;
86 }
87
88 *pfDefault = false;
89 return fmt;
90}
91
92static int drvAudioGetConfInt(PCFGMNODE pCfgHandle, const char *pszKey,
93 int iDefault, bool *pfDefault)
94{
95
96 if ( pCfgHandle == NULL
97 || pszKey == NULL)
98 {
99 *pfDefault = true;
100 return iDefault;
101 }
102
103 uint64_t u64Data = 0;
104 int rc = CFGMR3QueryInteger(pCfgHandle, pszKey, &u64Data);
105 if (RT_FAILURE(rc))
106 {
107 *pfDefault = true;
108 return iDefault;
109
110 }
111
112 *pfDefault = false;
113 return u64Data;
114}
115
116static const char *drvAudioGetConfStr(PCFGMNODE pCfgHandle, const char *pszKey,
117 const char *pszDefault, bool *pfDefault)
118{
119 if ( pCfgHandle == NULL
120 || pszKey == NULL)
121 {
122 *pfDefault = true;
123 return pszDefault;
124 }
125
126 char *pszValue = NULL;
127 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
128 if (RT_FAILURE(rc))
129 {
130 *pfDefault = true;
131 return pszDefault;
132 }
133
134 *pfDefault = false;
135 return pszValue;
136}
137
138# endif /* unused */
139
140#ifdef LOG_ENABLED
141/**
142 * Converts an audio stream status to a string.
143 *
144 * @returns Stringified stream status flags. Must be free'd with RTStrFree().
145 * "NONE" if no flags set.
146 * @param fStatus Stream status flags to convert.
147 */
148static char *dbgAudioStreamStatusToStr(PDMAUDIOSTREAMSTS fStatus)
149{
150#define APPEND_FLAG_TO_STR(_aFlag) \
151 if (fStatus & PDMAUDIOSTREAMSTS_FLAG_##_aFlag) \
152 { \
153 if (pszFlags) \
154 { \
155 rc2 = RTStrAAppend(&pszFlags, " "); \
156 if (RT_FAILURE(rc2)) \
157 break; \
158 } \
159 \
160 rc2 = RTStrAAppend(&pszFlags, #_aFlag); \
161 if (RT_FAILURE(rc2)) \
162 break; \
163 } \
164
165 char *pszFlags = NULL;
166 int rc2 = VINF_SUCCESS;
167
168 do
169 {
170 APPEND_FLAG_TO_STR(INITIALIZED );
171 APPEND_FLAG_TO_STR(ENABLED );
172 APPEND_FLAG_TO_STR(PAUSED );
173 APPEND_FLAG_TO_STR(PENDING_DISABLE);
174 APPEND_FLAG_TO_STR(PENDING_REINIT );
175 } while (0);
176
177 if (!pszFlags)
178 rc2 = RTStrAAppend(&pszFlags, "NONE");
179
180 if ( RT_FAILURE(rc2)
181 && pszFlags)
182 {
183 RTStrFree(pszFlags);
184 pszFlags = NULL;
185 }
186
187#undef APPEND_FLAG_TO_STR
188
189 return pszFlags;
190}
191#endif /* defined(VBOX_STRICT) || defined(LOG_ENABLED) */
192
193# if 0 /* unused */
194static int drvAudioProcessOptions(PCFGMNODE pCfgHandle, const char *pszPrefix, audio_option *paOpts, size_t cOpts)
195{
196 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
197 AssertPtrReturn(pszPrefix, VERR_INVALID_POINTER);
198 /* oaOpts and cOpts are optional. */
199
200 PCFGMNODE pCfgChildHandle = NULL;
201 PCFGMNODE pCfgChildChildHandle = NULL;
202
203 /* If pCfgHandle is NULL, let NULL be passed to get int and get string functions..
204 * The getter function will return default values.
205 */
206 if (pCfgHandle != NULL)
207 {
208 /* If its audio general setting, need to traverse to one child node.
209 * /Devices/ichac97/0/LUN#0/Config/Audio
210 */
211 if(!strncmp(pszPrefix, "AUDIO", 5)) /** @todo Use a \#define */
212 {
213 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
214 if(pCfgChildHandle)
215 pCfgHandle = pCfgChildHandle;
216 }
217 else
218 {
219 /* If its driver specific configuration , then need to traverse two level deep child
220 * child nodes. for eg. in case of DirectSoundConfiguration item
221 * /Devices/ichac97/0/LUN#0/Config/Audio/DirectSoundConfig
222 */
223 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
224 if (pCfgChildHandle)
225 {
226 pCfgChildChildHandle = CFGMR3GetFirstChild(pCfgChildHandle);
227 if (pCfgChildChildHandle)
228 pCfgHandle = pCfgChildChildHandle;
229 }
230 }
231 }
232
233 for (size_t i = 0; i < cOpts; i++)
234 {
235 audio_option *pOpt = &paOpts[i];
236 if (!pOpt->valp)
237 {
238 LogFlowFunc(("Option value pointer for `%s' is not set\n", pOpt->name));
239 continue;
240 }
241
242 bool fUseDefault;
243
244 switch (pOpt->tag)
245 {
246 case AUD_OPT_BOOL:
247 case AUD_OPT_INT:
248 {
249 int *intp = (int *)pOpt->valp;
250 *intp = drvAudioGetConfInt(pCfgHandle, pOpt->name, *intp, &fUseDefault);
251
252 break;
253 }
254
255 case AUD_OPT_FMT:
256 {
257 PDMAUDIOFMT *fmtp = (PDMAUDIOFMT *)pOpt->valp;
258 *fmtp = drvAudioGetConfFormat(pCfgHandle, pOpt->name, *fmtp, &fUseDefault);
259
260 break;
261 }
262
263 case AUD_OPT_STR:
264 {
265 const char **strp = (const char **)pOpt->valp;
266 *strp = drvAudioGetConfStr(pCfgHandle, pOpt->name, *strp, &fUseDefault);
267
268 break;
269 }
270
271 default:
272 LogFlowFunc(("Bad value tag for option `%s' - %d\n", pOpt->name, pOpt->tag));
273 fUseDefault = false;
274 break;
275 }
276
277 if (!pOpt->overridenp)
278 pOpt->overridenp = &pOpt->overriden;
279
280 *pOpt->overridenp = !fUseDefault;
281 }
282
283 return VINF_SUCCESS;
284}
285# endif /* unused */
286#endif /* !VBOX_AUDIO_TESTCASE */
287
288/**
289 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamControl}
290 */
291static DECLCALLBACK(int) drvAudioStreamControl(PPDMIAUDIOCONNECTOR pInterface,
292 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
293{
294 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
295
296 if (!pStream)
297 return VINF_SUCCESS;
298
299 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
300
301 int rc = RTCritSectEnter(&pThis->CritSect);
302 if (RT_FAILURE(rc))
303 return rc;
304
305 LogFlowFunc(("[%s] enmStreamCmd=%s\n", pStream->szName, DrvAudioHlpStreamCmdToStr(enmStreamCmd)));
306
307 rc = drvAudioStreamControlInternal(pThis, pStream, enmStreamCmd);
308
309 int rc2 = RTCritSectLeave(&pThis->CritSect);
310 if (RT_SUCCESS(rc))
311 rc = rc2;
312
313 return rc;
314}
315
316/**
317 * Controls an audio stream.
318 *
319 * @returns IPRT status code.
320 * @param pThis Pointer to driver instance.
321 * @param pStream Stream to control.
322 * @param enmStreamCmd Control command.
323 */
324static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
325{
326 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
327 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
328
329 LogFunc(("[%s] enmStreamCmd=%s\n", pStream->szName, DrvAudioHlpStreamCmdToStr(enmStreamCmd)));
330
331#ifdef LOG_ENABLED
332 char *pszStreamSts = dbgAudioStreamStatusToStr(pStream->fStatus);
333 LogFlowFunc(("fStatus=%s\n", pszStreamSts));
334 RTStrFree(pszStreamSts);
335#endif /* LOG_ENABLED */
336
337 int rc = VINF_SUCCESS;
338
339 switch (enmStreamCmd)
340 {
341 case PDMAUDIOSTREAMCMD_ENABLE:
342 {
343 if (!(pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
344 {
345 /* Is a pending disable outstanding? Then disable first. */
346 if (pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE)
347 rc = drvAudioStreamControlInternalBackend(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
348
349 if (RT_SUCCESS(rc))
350 rc = drvAudioStreamControlInternalBackend(pThis, pStream, PDMAUDIOSTREAMCMD_ENABLE);
351
352 if (RT_SUCCESS(rc))
353 pStream->fStatus |= PDMAUDIOSTREAMSTS_FLAG_ENABLED;
354 }
355 break;
356 }
357
358 case PDMAUDIOSTREAMCMD_DISABLE:
359 {
360 if (pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_ENABLED)
361 {
362 /*
363 * For playback (output) streams first mark the host stream as pending disable,
364 * so that the rest of the remaining audio data will be played first before
365 * closing the stream.
366 */
367 if (pStream->enmDir == PDMAUDIODIR_OUT)
368 {
369 LogFunc(("[%s] Pending disable/pause\n", pStream->szName));
370 pStream->fStatus |= PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE;
371 }
372
373 /* Can we close the host stream as well (not in pending disable mode)? */
374 if (!(pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE))
375 {
376 rc = drvAudioStreamControlInternalBackend(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
377 if (RT_SUCCESS(rc))
378 pStream->fStatus &= ~PDMAUDIOSTREAMSTS_FLAG_ENABLED;
379 }
380 }
381 break;
382 }
383
384 case PDMAUDIOSTREAMCMD_PAUSE:
385 {
386 if (!(pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PAUSED))
387 {
388 rc = drvAudioStreamControlInternalBackend(pThis, pStream, PDMAUDIOSTREAMCMD_PAUSE);
389 if (RT_SUCCESS(rc))
390 pStream->fStatus |= PDMAUDIOSTREAMSTS_FLAG_PAUSED;
391 }
392 break;
393 }
394
395 case PDMAUDIOSTREAMCMD_RESUME:
396 {
397 if (pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PAUSED)
398 {
399 rc = drvAudioStreamControlInternalBackend(pThis, pStream, PDMAUDIOSTREAMCMD_RESUME);
400 if (RT_SUCCESS(rc))
401 pStream->fStatus &= ~PDMAUDIOSTREAMSTS_FLAG_PAUSED;
402 }
403 break;
404 }
405
406 default:
407 AssertMsgFailed(("Command %s (%RU32) not implemented\n", DrvAudioHlpStreamCmdToStr(enmStreamCmd), enmStreamCmd));
408 rc = VERR_NOT_IMPLEMENTED;
409 break;
410 }
411
412 if (RT_FAILURE(rc))
413 LogFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
414
415 return rc;
416}
417
418/**
419 * Controls a stream's backend.
420 * If the stream has no backend available, VERR_NOT_FOUND is returned.
421 *
422 * @returns IPRT status code.
423 * @param pThis Pointer to driver instance.
424 * @param pStream Stream to control.
425 * @param enmStreamCmd Control command.
426 */
427static int drvAudioStreamControlInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
428{
429 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
430 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
431
432#ifdef LOG_ENABLED
433 char *pszStreamSts = dbgAudioStreamStatusToStr(pStream->fStatus);
434 LogFlowFunc(("[%s] enmStreamCmd=%s, fStatus=%s\n", pStream->szName, DrvAudioHlpStreamCmdToStr(enmStreamCmd), pszStreamSts));
435 RTStrFree(pszStreamSts);
436#endif /* LOG_ENABLED */
437
438 if (!pThis->pHostDrvAudio) /* If not lower driver is configured, bail out. */
439 return VINF_SUCCESS;
440
441 LogRel2(("Audio: %s stream '%s'\n", DrvAudioHlpStreamCmdToStr(enmStreamCmd), pStream->szName));
442
443 int rc = VINF_SUCCESS;
444
445 switch (enmStreamCmd)
446 {
447 case PDMAUDIOSTREAMCMD_ENABLE:
448 {
449 if (!(pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
450 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStream->pvBackend, PDMAUDIOSTREAMCMD_ENABLE);
451 break;
452 }
453
454 case PDMAUDIOSTREAMCMD_DISABLE:
455 {
456 if (pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_ENABLED)
457 {
458 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStream->pvBackend, PDMAUDIOSTREAMCMD_DISABLE);
459 if (RT_SUCCESS(rc))
460 AudioMixBufReset(&pStream->Host.MixBuf);
461 }
462 break;
463 }
464
465 case PDMAUDIOSTREAMCMD_PAUSE:
466 {
467 /* Only pause if the stream is enabled. */
468 if (!(pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
469 break;
470
471 if (!(pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PAUSED))
472 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStream->pvBackend, PDMAUDIOSTREAMCMD_PAUSE);
473 break;
474 }
475
476 case PDMAUDIOSTREAMCMD_RESUME:
477 {
478 /* Only need to resume if the stream is enabled. */
479 if (!(pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
480 break;
481
482 if (pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PAUSED)
483 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStream->pvBackend, PDMAUDIOSTREAMCMD_RESUME);
484 break;
485 }
486
487 case PDMAUDIOSTREAMCMD_DRAIN:
488 {
489 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStream->pvBackend, PDMAUDIOSTREAMCMD_DRAIN);
490 break;
491 }
492
493 default:
494 {
495 AssertMsgFailed(("Command %RU32 not implemented\n", enmStreamCmd));
496 rc = VERR_NOT_IMPLEMENTED;
497 break;
498 }
499 }
500
501 if (RT_FAILURE(rc))
502 {
503 if ( rc != VERR_NOT_IMPLEMENTED
504 && rc != VERR_NOT_SUPPORTED)
505 LogRel(("Audio: %s stream '%s' failed with %Rrc\n", DrvAudioHlpStreamCmdToStr(enmStreamCmd), pStream->szName, rc));
506
507 LogFunc(("[%s] %s failed with %Rrc\n", pStream->szName, DrvAudioHlpStreamCmdToStr(enmStreamCmd), rc));
508 }
509
510 return rc;
511}
512
513/**
514 * Initializes an audio stream with a given host and guest stream configuration.
515 *
516 * @returns IPRT status code.
517 * @param pThis Pointer to driver instance.
518 * @param pStream Stream to initialize.
519 * @param pCfgHost Stream configuration to use for the host side (backend).
520 * @param pCfgGuest Stream configuration to use for the guest side.
521 */
522static int drvAudioStreamInitInternal(PDRVAUDIO pThis,
523 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest)
524{
525 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
526 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
527 AssertPtrReturn(pCfgHost, VERR_INVALID_POINTER);
528 AssertPtrReturn(pCfgGuest, VERR_INVALID_POINTER);
529
530 /*
531 * Init host stream.
532 */
533
534 /* Set the host's default audio data layout. */
535 pCfgHost->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
536
537#ifdef DEBUG
538 LogFunc(("[%s] Requested host format:\n", pStream->szName));
539 DrvAudioHlpStreamCfgPrint(pCfgHost);
540#endif
541
542 LogRel2(("Audio: Creating stream '%s'\n", pStream->szName));
543 LogRel2(("Audio: Guest %s format for '%s': %RU32Hz, %RU8%s, %RU8 %s\n",
544 pCfgGuest->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStream->szName,
545 pCfgGuest->Props.uHz, pCfgGuest->Props.cBits, pCfgGuest->Props.fSigned ? "S" : "U",
546 pCfgGuest->Props.cChannels, pCfgGuest->Props.cChannels == 1 ? "Channel" : "Channels"));
547 LogRel2(("Audio: Requested host %s format for '%s': %RU32Hz, %RU8%s, %RU8 %s\n",
548 pCfgHost->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStream->szName,
549 pCfgHost->Props.uHz, pCfgHost->Props.cBits, pCfgHost->Props.fSigned ? "S" : "U",
550 pCfgHost->Props.cChannels, pCfgHost->Props.cChannels == 1 ? "Channel" : "Channels"));
551
552 PDMAUDIOSTREAMCFG CfgHostAcq;
553 int rc = drvAudioStreamCreateInternalBackend(pThis, pStream, pCfgHost, &CfgHostAcq);
554 if (RT_FAILURE(rc))
555 return rc;
556
557#ifdef DEBUG
558 LogFunc(("[%s] Acquired host format:\n", pStream->szName));
559 DrvAudioHlpStreamCfgPrint(&CfgHostAcq);
560#endif
561
562 LogRel2(("Audio: Acquired host %s format for '%s': %RU32Hz, %RU8%s, %RU8 %s\n",
563 CfgHostAcq.enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStream->szName,
564 CfgHostAcq.Props.uHz, CfgHostAcq.Props.cBits, CfgHostAcq.Props.fSigned ? "S" : "U",
565 CfgHostAcq.Props.cChannels, CfgHostAcq.Props.cChannels == 1 ? "Channel" : "Channels"));
566
567 /* Let the user know if the backend changed some of the tweakable values. */
568 if (CfgHostAcq.Backend.cfBufferSize != pCfgHost->Backend.cfBufferSize)
569 LogRel2(("Audio: Backend changed buffer size from (%RU64ms, %RU32 frames) to (%RU64ms, %RU32 frames) frames\n",
570 DrvAudioHlpFramesToMilli(pCfgHost->Backend.cfBufferSize, &pCfgHost->Props), pCfgHost->Backend.cfBufferSize,
571 DrvAudioHlpFramesToMilli(CfgHostAcq.Backend.cfBufferSize, &CfgHostAcq.Props), CfgHostAcq.Backend.cfBufferSize));
572
573 if (CfgHostAcq.Backend.cfPeriod != pCfgHost->Backend.cfPeriod)
574 LogRel2(("Audio: Backend changed period size from (%RU64ms, %RU32 frames) to (%RU64ms, %RU32 frames) frames\n",
575 DrvAudioHlpFramesToMilli(pCfgHost->Backend.cfPeriod, &pCfgHost->Props), pCfgHost->Backend.cfPeriod,
576 DrvAudioHlpFramesToMilli(CfgHostAcq.Backend.cfPeriod, &CfgHostAcq.Props), CfgHostAcq.Backend.cfPeriod));
577
578 if (CfgHostAcq.Backend.cfPreBuf != pCfgHost->Backend.cfPreBuf)
579 LogRel2(("Audio: Backend changed pre-buffering size from (%RU64ms, %RU32 frames) to (%RU64ms, %RU32 frames) frames\n",
580 DrvAudioHlpFramesToMilli(pCfgHost->Backend.cfPreBuf, &pCfgHost->Props), pCfgHost->Backend.cfPreBuf,
581 DrvAudioHlpFramesToMilli(CfgHostAcq.Backend.cfPreBuf, &CfgHostAcq.Props), CfgHostAcq.Backend.cfPreBuf));
582 /*
583 * Configure host buffers.
584 */
585
586 /* Check if the backend did return sane values and correct if necessary.
587 * Should never happen with our own backends, but you never know ... */
588 if (CfgHostAcq.Backend.cfBufferSize < CfgHostAcq.Backend.cfPreBuf)
589 {
590 LogRel2(("Audio: Warning: Pre-buffering size (%RU32 frames) of stream '%s' does not match buffer size (%RU32 frames), "
591 "setting pre-buffering size to %RU32 frames\n",
592 CfgHostAcq.Backend.cfPreBuf, pStream->szName, CfgHostAcq.Backend.cfBufferSize, CfgHostAcq.Backend.cfBufferSize));
593 CfgHostAcq.Backend.cfPreBuf = CfgHostAcq.Backend.cfBufferSize;
594 }
595
596 if (CfgHostAcq.Backend.cfPeriod > CfgHostAcq.Backend.cfBufferSize)
597 {
598 LogRel2(("Audio: Warning: Period size (%RU32 frames) of stream '%s' does not match buffer size (%RU32 frames), setting to %RU32 frames\n",
599 CfgHostAcq.Backend.cfPeriod, pStream->szName, CfgHostAcq.Backend.cfBufferSize, CfgHostAcq.Backend.cfBufferSize));
600 CfgHostAcq.Backend.cfPeriod = CfgHostAcq.Backend.cfBufferSize;
601 }
602
603 uint64_t msBufferSize = DrvAudioHlpFramesToMilli(CfgHostAcq.Backend.cfBufferSize, &pCfgHost->Props);
604
605 LogRel2(("Audio: Buffer size of stream '%s' is %RU64ms (%RU32 frames)\n",
606 pStream->szName, msBufferSize, CfgHostAcq.Backend.cfBufferSize));
607
608 /* If no own pre-buffer is set, let the backend choose. */
609 uint64_t msPreBuf = DrvAudioHlpFramesToMilli(CfgHostAcq.Backend.cfPreBuf, &pCfgHost->Props);
610 LogRel2(("Audio: Pre-buffering size of stream '%s' is %RU64ms (%RU32 frames)\n",
611 pStream->szName, msPreBuf, CfgHostAcq.Backend.cfPreBuf));
612
613 /* Make sure the configured buffer size by the backend at least can hold the configured latency. */
614 const uint32_t msPeriod = DrvAudioHlpFramesToMilli(CfgHostAcq.Backend.cfPeriod, &pCfgHost->Props);
615
616 LogRel2(("Audio: Period size of stream '%s' is %RU64ms (%RU32 frames)\n",
617 pStream->szName, msPeriod, CfgHostAcq.Backend.cfPeriod));
618
619 /* Set set host buffer size multiplicator. */
620 const unsigned cHstBufferFactor = 2; /** @todo Make this configurable. */
621
622 /* Destroy any former mixing buffer. */
623 AudioMixBufDestroy(&pStream->Host.MixBuf);
624
625 /* Make sure to (re-)set the host buffer's shift size. */
626 CfgHostAcq.Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(CfgHostAcq.Props.cBits, CfgHostAcq.Props.cChannels);
627
628 rc = AudioMixBufInit(&pStream->Host.MixBuf, pStream->szName, &CfgHostAcq.Props,
629 CfgHostAcq.Backend.cfBufferSize * cHstBufferFactor);
630 AssertRC(rc);
631
632 /* Make a copy of the acquired host stream configuration. */
633 rc = DrvAudioHlpStreamCfgCopy(&pStream->Host.Cfg, &CfgHostAcq);
634 AssertRC(rc);
635
636 /*
637 * Init guest stream.
638 */
639
640 if (pCfgGuest->Device.uSchedulingHintMs)
641 LogRel2(("Audio: Stream '%s' got a scheduling hint of %RU32ms\n", pStream->szName, pCfgGuest->Device.uSchedulingHintMs));
642
643 /* Destroy any former mixing buffer. */
644 AudioMixBufDestroy(&pStream->Guest.MixBuf);
645
646 /* Set the guests's default audio data layout. */
647 pCfgGuest->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
648
649 /* Make sure to (re-)set the guest buffer's shift size. */
650 pCfgGuest->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfgGuest->Props.cBits, pCfgGuest->Props.cChannels);
651
652 /* Set set guest buffer size multiplicator. */
653 const unsigned cGstBufferFactor = 4; /** @todo Make this configurable. */
654
655 rc = AudioMixBufInit(&pStream->Guest.MixBuf, pStream->szName, &pCfgGuest->Props,
656 CfgHostAcq.Backend.cfBufferSize * cGstBufferFactor);
657 AssertRC(rc);
658
659 /* Make a copy of the guest stream configuration. */
660 rc = DrvAudioHlpStreamCfgCopy(&pStream->Guest.Cfg, pCfgGuest);
661 AssertRC(rc);
662
663 if (RT_FAILURE(rc))
664 LogRel(("Audio: Creating stream '%s' failed with %Rrc\n", pStream->szName, rc));
665
666 if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
667 {
668 /* Host (Parent) -> Guest (Child). */
669 rc = AudioMixBufLinkTo(&pStream->Host.MixBuf, &pStream->Guest.MixBuf);
670 AssertRC(rc);
671 }
672 else
673 {
674 /* Guest (Parent) -> Host (Child). */
675 rc = AudioMixBufLinkTo(&pStream->Guest.MixBuf, &pStream->Host.MixBuf);
676 AssertRC(rc);
677 }
678
679#ifdef VBOX_WITH_STATISTICS
680 char szStatName[255];
681
682 if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
683 {
684 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pStream->szName);
685 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pStream->In.Stats.BytesElapsed,
686 szStatName, STAMUNIT_BYTES, "Elapsed bytes read.");
687
688 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesRead", pStream->szName);
689 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pStream->In.Stats.BytesTotalRead,
690 szStatName, STAMUNIT_BYTES, "Total bytes read.");
691
692 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/FramesCaptured", pStream->szName);
693 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pStream->In.Stats.FramesCaptured,
694 szStatName, STAMUNIT_COUNT, "Total frames captured.");
695 }
696 else if (pCfgGuest->enmDir == PDMAUDIODIR_OUT)
697 {
698 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pStream->szName);
699 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pStream->Out.Stats.BytesElapsed,
700 szStatName, STAMUNIT_BYTES, "Elapsed bytes written.");
701
702 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesWritten", pStream->szName);
703 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pStream->Out.Stats.BytesTotalWritten,
704 szStatName, STAMUNIT_BYTES, "Total bytes written.");
705
706 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/FramesPlayed", pStream->szName);
707 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pStream->Out.Stats.FramesPlayed,
708 szStatName, STAMUNIT_COUNT, "Total frames played.");
709 }
710 else
711 AssertFailed();
712#endif
713
714 LogFlowFunc(("[%s] Returning %Rrc\n", pStream->szName, rc));
715 return rc;
716}
717
718/**
719 * Frees an audio stream and its allocated resources.
720 *
721 * @param pStream Audio stream to free.
722 * After this call the pointer will not be valid anymore.
723 */
724static void drvAudioStreamFree(PPDMAUDIOSTREAM pStream)
725{
726 if (!pStream)
727 return;
728
729 if (pStream->pvBackend)
730 {
731 Assert(pStream->cbBackend);
732 RTMemFree(pStream->pvBackend);
733 pStream->pvBackend = NULL;
734 }
735
736 RTMemFree(pStream);
737 pStream = NULL;
738}
739
740#ifdef VBOX_WITH_AUDIO_CALLBACKS
741/**
742 * Schedules a re-initialization of all current audio streams.
743 * The actual re-initialization will happen at some later point in time.
744 *
745 * @returns IPRT status code.
746 * @param pThis Pointer to driver instance.
747 */
748static int drvAudioScheduleReInitInternal(PDRVAUDIO pThis)
749{
750 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
751
752 LogFunc(("\n"));
753
754 /* Mark all host streams to re-initialize. */
755 PPDMAUDIOSTREAM pStream;
756 RTListForEach(&pThis->lstStreams, pStream, PDMAUDIOSTREAM, Node)
757 pStream->fStatus |= PDMAUDIOSTREAMSTS_FLAG_PENDING_REINIT;
758
759# ifdef VBOX_WITH_AUDIO_ENUM
760 /* Re-enumerate all host devices as soon as possible. */
761 pThis->fEnumerateDevices = true;
762# endif
763
764 return VINF_SUCCESS;
765}
766#endif /* VBOX_WITH_AUDIO_CALLBACKS */
767
768/**
769 * Re-initializes an audio stream with its existing host and guest stream configuration.
770 * This might be the case if the backend told us we need to re-initialize because something
771 * on the host side has changed.
772 *
773 * @returns IPRT status code.
774 * @param pThis Pointer to driver instance.
775 * @param pStream Stream to re-initialize.
776 */
777static int drvAudioStreamReInitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
778{
779 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
780 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
781
782 LogFlowFunc(("[%s]\n", pStream->szName));
783
784 /*
785 * Gather current stream status.
786 */
787 bool fIsEnabled = RT_BOOL(pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_ENABLED); /* Stream is enabled? */
788
789 /*
790 * Destroy and re-create stream on backend side.
791 */
792 int rc = drvAudioStreamControlInternalBackend(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
793 if (RT_SUCCESS(rc))
794 {
795 rc = drvAudioStreamDestroyInternalBackend(pThis, pStream);
796 if (RT_SUCCESS(rc))
797 {
798 rc = drvAudioStreamCreateInternalBackend(pThis, pStream, &pStream->Host.Cfg, NULL /* pCfgAcq */);
799 /** @todo Validate (re-)acquired configuration with pStream->Host.Cfg? */
800 }
801 }
802
803 /* Do the internal reset. */
804 drvAudioStreamResetInternal(pThis, pStream);
805
806 /*
807 * Restore previous stream state.
808 */
809 if (fIsEnabled)
810 rc = drvAudioStreamControlInternalBackend(pThis, pStream, PDMAUDIOSTREAMCMD_ENABLE);
811
812 if (RT_FAILURE(rc))
813 LogRel(("Audio: Re-initializing stream '%s' failed with %Rrc\n", pStream->szName, rc));
814
815 LogFunc(("[%s] Returning %Rrc\n", pStream->szName, rc));
816 return rc;
817}
818
819static void drvAudioStreamResetInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
820{
821 RT_NOREF(pThis);
822
823 AudioMixBufReset(&pStream->Guest.MixBuf);
824 AudioMixBufReset(&pStream->Host.MixBuf);
825
826#ifdef VBOX_WITH_STATISTICS
827 /*
828 * Reset statistics.
829 */
830 if (pStream->enmDir == PDMAUDIODIR_IN)
831 {
832 STAM_COUNTER_RESET(&pStream->In.Stats.BytesElapsed);
833 STAM_COUNTER_RESET(&pStream->In.Stats.BytesTotalRead);
834 STAM_COUNTER_RESET(&pStream->In.Stats.FramesCaptured);
835 }
836 else if (pStream->enmDir == PDMAUDIODIR_OUT)
837 {
838 STAM_COUNTER_RESET(&pStream->Out.Stats.BytesElapsed);
839 STAM_COUNTER_RESET(&pStream->Out.Stats.BytesTotalWritten);
840 STAM_COUNTER_RESET(&pStream->Out.Stats.FramesPlayed);
841 }
842 else
843 AssertFailed();
844#endif
845}
846
847/**
848 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamWrite}
849 */
850static DECLCALLBACK(int) drvAudioStreamWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
851 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
852{
853 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
854 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
855 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
856 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
857 /* pcbWritten is optional. */
858
859 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
860
861 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT,
862 ("Stream '%s' is not an output stream and therefore cannot be written to (direction is 0x%x)\n",
863 pStream->szName, pStream->enmDir));
864
865 uint32_t cbWrittenTotal = 0;
866
867 int rc = RTCritSectEnter(&pThis->CritSect);
868 if (RT_FAILURE(rc))
869 return rc;
870
871#ifdef VBOX_WITH_STATISTICS
872 STAM_PROFILE_ADV_START(&pThis->Stats.DelayOut, out);
873#endif
874
875#ifdef LOG_ENABLED
876 char *pszStreamSts = NULL;
877#endif
878
879 do
880 {
881 if (!pThis->Out.fEnabled)
882 {
883 cbWrittenTotal = cbBuf;
884 break;
885 }
886
887 if ( pThis->pHostDrvAudio
888 && pThis->pHostDrvAudio->pfnGetStatus
889 && pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_OUT) != PDMAUDIOBACKENDSTS_RUNNING)
890 {
891 rc = VERR_NOT_AVAILABLE;
892 break;
893 }
894
895 AssertMsg(DrvAudioHlpBytesIsAligned(cbBuf, &pStream->Guest.Cfg.Props),
896 ("Writing audio data (%RU32 bytes) to stream '%s' is not properly aligned\n", cbBuf, pStream->szName));
897
898#ifdef LOG_ENABLED
899 pszStreamSts = dbgAudioStreamStatusToStr(pStream->fStatus);
900 AssertPtr(pszStreamSts);
901#endif
902 if (!(pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
903 {
904 Log3Func(("[%s] Writing to disabled guest output stream not possible (status is %s)\n",
905 pStream->szName, pszStreamSts));
906#ifdef DEBUG_andy
907 AssertFailed();
908#endif
909 rc = VERR_NOT_AVAILABLE;
910 break;
911 }
912
913 const uint32_t cbFree = AudioMixBufFreeBytes(&pStream->Host.MixBuf);
914 if (cbFree < cbBuf)
915 LogRel2(("Audio: Lost audio output (%RU64ms, %RU32 free but needs %RU32) due to full host stream buffer '%s'\n",
916 DrvAudioHlpBytesToMilli(cbBuf - cbFree, &pStream->Host.Cfg.Props), cbFree, cbBuf, pStream->szName));
917
918 uint32_t cbToWrite = RT_MIN(cbBuf, cbFree);
919 if (cbToWrite > cbBuf) /* Paranoia. */
920 cbToWrite = cbBuf;
921
922 if (!cbToWrite)
923 break;
924
925 /* We use the guest side mixing buffer as an intermediate buffer to do some
926 * (first) processing (if needed), so always write the incoming data at offset 0. */
927 uint32_t cfGstWritten = 0;
928 rc = AudioMixBufWriteAt(&pStream->Guest.MixBuf, 0 /* offFrames */, pvBuf, cbToWrite, &cfGstWritten);
929 if ( RT_FAILURE(rc)
930 || !cfGstWritten)
931 {
932 AssertMsgFailed(("[%s] Write failed: cbToWrite=%RU32, cfWritten=%RU32, rc=%Rrc\n",
933 pStream->szName, cbToWrite, cfGstWritten, rc));
934 break;
935 }
936
937 if (pThis->Out.Cfg.Dbg.fEnabled)
938 DrvAudioHlpFileWrite(pStream->Out.Dbg.pFileStreamWrite, pvBuf, cbToWrite, 0 /* fFlags */);
939
940#ifdef VBOX_WITH_STATISTICS
941 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesWritten, cfGstWritten);
942#endif
943 uint32_t cfGstMixed = 0;
944 if (cfGstWritten)
945 {
946 int rc2 = AudioMixBufMixToParentEx(&pStream->Guest.MixBuf, 0 /* cSrcOffset */, cfGstWritten /* cSrcFrames */,
947 &cfGstMixed /* pcSrcMixed */);
948 if (RT_FAILURE(rc2))
949 {
950 AssertMsgFailed(("[%s] Mixing failed: cbToWrite=%RU32, cfWritten=%RU32, cfMixed=%RU32, rc=%Rrc\n",
951 pStream->szName, cbToWrite, cfGstWritten, cfGstMixed, rc2));
952 }
953 else
954 {
955 Log3Func(("[%s] Buffer: Writing %RU32 frames (%RU64ms), now filled with %RU64ms -- %RU8%%\n",
956 pStream->szName, cfGstWritten, DrvAudioHlpFramesToMilli(cfGstWritten, &pStream->Guest.Cfg.Props),
957 DrvAudioHlpFramesToMilli(AudioMixBufUsed(&pStream->Host.MixBuf), &pStream->Host.Cfg.Props),
958 AudioMixBufUsed(&pStream->Host.MixBuf) * 100 / AudioMixBufSize(&pStream->Host.MixBuf)));
959
960 pStream->tsLastReadWrittenNs = RTTimeNanoTS();
961
962 /* Keep going. */
963 }
964
965 if (RT_SUCCESS(rc))
966 rc = rc2;
967
968 cbWrittenTotal = AUDIOMIXBUF_F2B(&pStream->Guest.MixBuf, cfGstWritten);
969#ifdef DEBUG
970 if (pStream->fThresholdReached)
971 pStream->Out.Dbg.cbJitterWrittenPlayed += cbWrittenTotal;
972#endif
973
974#ifdef VBOX_WITH_STATISTICS
975 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesMixedOut, cfGstMixed);
976 Assert(cfGstWritten >= cfGstMixed);
977 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesLostOut, cfGstWritten - cfGstMixed);
978 STAM_COUNTER_ADD(&pThis->Stats.TotalBytesWritten, cbWrittenTotal);
979 STAM_COUNTER_ADD(&pStream->Out.Stats.BytesTotalWritten, cbWrittenTotal);
980#endif
981 }
982
983 Log3Func(("[%s] cbToWrite=%RU32, cfHstUsed=%RU32, cfHstfLive=%RU32, cfGstWritten=%RU32, cfGstMixed=%RU32, rc=%Rrc\n",
984 pStream->szName, cbToWrite, AudioMixBufUsed(&pStream->Host.MixBuf),
985 AudioMixBufLive(&pStream->Host.MixBuf), cfGstWritten, cfGstMixed, rc));
986
987 } while (0);
988
989 Log3Func(("[%s] fEnabled=%RTbool, cbWrittenTotal=%RU32, rc=%Rrc\n",
990 pStream->szName, pThis->Out.fEnabled, cbWrittenTotal, rc));
991
992#ifdef LOG_ENABLED
993 RTStrFree(pszStreamSts);
994#endif
995
996 int rc2 = RTCritSectLeave(&pThis->CritSect);
997 if (RT_SUCCESS(rc))
998 rc = rc2;
999
1000 if (RT_SUCCESS(rc))
1001 {
1002 if (pcbWritten)
1003 *pcbWritten = cbWrittenTotal;
1004 }
1005
1006 return rc;
1007}
1008
1009/**
1010 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRetain}
1011 */
1012static DECLCALLBACK(uint32_t) drvAudioStreamRetain(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1013{
1014 AssertPtrReturn(pInterface, UINT32_MAX);
1015 AssertPtrReturn(pStream, UINT32_MAX);
1016
1017 NOREF(pInterface);
1018
1019 return ++pStream->cRefs;
1020}
1021
1022/**
1023 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRelease}
1024 */
1025static DECLCALLBACK(uint32_t) drvAudioStreamRelease(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1026{
1027 AssertPtrReturn(pInterface, UINT32_MAX);
1028 AssertPtrReturn(pStream, UINT32_MAX);
1029
1030 NOREF(pInterface);
1031
1032 if (pStream->cRefs > 1) /* 1 reference always is kept by this audio driver. */
1033 pStream->cRefs--;
1034
1035 return pStream->cRefs;
1036}
1037
1038/**
1039 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamIterate}
1040 */
1041static DECLCALLBACK(int) drvAudioStreamIterate(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1042{
1043 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1044 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1045
1046 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1047
1048 int rc = RTCritSectEnter(&pThis->CritSect);
1049 if (RT_FAILURE(rc))
1050 return rc;
1051
1052 rc = drvAudioStreamIterateInternal(pThis, pStream);
1053
1054 int rc2 = RTCritSectLeave(&pThis->CritSect);
1055 if (RT_SUCCESS(rc))
1056 rc = rc2;
1057
1058 if (RT_FAILURE(rc))
1059 LogFlowFuncLeaveRC(rc);
1060
1061 return rc;
1062}
1063
1064/**
1065 * Does one iteration of an audio stream.
1066 * This function gives the backend the chance of iterating / altering data and
1067 * does the actual mixing between the guest <-> host mixing buffers.
1068 *
1069 * @returns IPRT status code.
1070 * @param pThis Pointer to driver instance.
1071 * @param pStream Stream to iterate.
1072 */
1073static int drvAudioStreamIterateInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
1074{
1075 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1076
1077 if (!pThis->pHostDrvAudio)
1078 return VINF_SUCCESS;
1079
1080 if (!pStream)
1081 return VINF_SUCCESS;
1082
1083 int rc;
1084
1085 /* Is the stream scheduled for re-initialization? Do so now. */
1086 if (pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PENDING_REINIT)
1087 {
1088#ifdef VBOX_WITH_AUDIO_ENUM
1089 if (pThis->fEnumerateDevices)
1090 {
1091 /* Re-enumerate all host devices. */
1092 drvAudioDevicesEnumerateInternal(pThis, true /* fLog */, NULL /* pDevEnum */);
1093
1094 pThis->fEnumerateDevices = false;
1095 }
1096#endif /* VBOX_WITH_AUDIO_ENUM */
1097
1098 /* Remove the pending re-init flag in any case, regardless whether the actual re-initialization succeeded
1099 * or not. If it failed, the backend needs to notify us again to try again at some later point in time. */
1100 pStream->fStatus &= ~PDMAUDIOSTREAMSTS_FLAG_PENDING_REINIT;
1101
1102 rc = drvAudioStreamReInitInternal(pThis, pStream);
1103 if (RT_FAILURE(rc))
1104 return rc;
1105 }
1106
1107#ifdef LOG_ENABLED
1108 char *pszStreamSts = dbgAudioStreamStatusToStr(pStream->fStatus);
1109 Log3Func(("[%s] fStatus=%s\n", pStream->szName, pszStreamSts));
1110 RTStrFree(pszStreamSts);
1111#endif /* LOG_ENABLED */
1112
1113 /* Not enabled or paused? Skip iteration. */
1114 if ( !(pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_ENABLED)
1115 || (pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PAUSED))
1116 {
1117 return VINF_SUCCESS;
1118 }
1119
1120 /* Whether to try closing a pending to close stream. */
1121 bool fTryClosePending = false;
1122
1123 do
1124 {
1125 uint32_t cfMixed = 0;
1126
1127 rc = pThis->pHostDrvAudio->pfnStreamIterate(pThis->pHostDrvAudio, pStream->pvBackend);
1128 if (RT_FAILURE(rc))
1129 break;
1130
1131 if (pStream->enmDir == PDMAUDIODIR_IN)
1132 {
1133 /* Has the host captured any frames which were not mixed to the guest side yet? */
1134 uint32_t cfCaptured = AudioMixBufUsed(&pStream->Host.MixBuf);
1135 if (cfCaptured)
1136 {
1137 /* When capturing frames, the guest is the parent while the host is the child.
1138 * So try mixing not yet mixed host-side frames to the guest-side buffer. */
1139 rc = AudioMixBufMixToParent(&pStream->Host.MixBuf, cfCaptured, &cfMixed);
1140 if (RT_FAILURE(rc))
1141 {
1142 if (rc == VERR_BUFFER_OVERFLOW)
1143 LogRel2(("Audio: Guest input stream '%s' full\n", pStream->szName));
1144 else
1145 LogRel2(("Audio: Mixing to guest input stream '%s' failed: %Rrc\n", pStream->szName, rc));
1146#ifdef DEBUG_andy_disabled
1147 AssertFailed();
1148#endif
1149 }
1150
1151#ifdef VBOX_WITH_STATISTICS
1152 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesMixedIn, cfMixed);
1153 Assert(cfCaptured >= cfMixed);
1154 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesLostIn, cfCaptured - cfMixed);
1155#endif
1156 Log3Func(("[%s] %RU32/%RU32 input frames mixed, rc=%Rrc\n", pStream->szName, cfMixed, cfCaptured, rc));
1157 }
1158 else
1159 {
1160 /* No audio frames to transfer host to the guest (anymore)?
1161 * Then try closing this stream if marked so in the next block. */
1162 fTryClosePending = true;
1163 }
1164 }
1165 else if (pStream->enmDir == PDMAUDIODIR_OUT)
1166 {
1167 /* No audio frames to transfer from guest to host (anymore)?
1168 * Then try closing this stream if marked so in the next block. */
1169 const uint32_t cfLive = AudioMixBufLive(&pStream->Host.MixBuf);
1170 fTryClosePending = cfLive == 0;
1171 Log3Func(("[%s] fTryClosePending=%RTbool, cfLive=%RU32\n", pStream->szName, fTryClosePending, cfLive));
1172 }
1173 else
1174 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1175
1176 /* Has the host stream marked as pending to disable?
1177 * Try disabling the stream then. */
1178 if ( pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE
1179 && fTryClosePending)
1180 {
1181 /* Tell the backend to drain the stream, that is, play the remaining (buffered) data
1182 * on the backend side. */
1183 rc = drvAudioStreamControlInternalBackend(pThis, pStream, PDMAUDIOSTREAMCMD_DRAIN);
1184 if (rc == VERR_NOT_SUPPORTED) /* Not all backends support draining. */
1185 rc = VINF_SUCCESS;
1186
1187 if (RT_SUCCESS(rc))
1188 {
1189 if (pThis->pHostDrvAudio->pfnStreamGetPending) /* Optional to implement. */
1190 {
1191 const uint32_t cxPending = pThis->pHostDrvAudio->pfnStreamGetPending(pThis->pHostDrvAudio, pStream->pvBackend);
1192 Log3Func(("[%s] cxPending=%RU32\n", pStream->szName, cxPending));
1193
1194 /* Only try close pending if no audio data is pending on the backend-side anymore. */
1195 fTryClosePending = cxPending == 0;
1196 }
1197
1198 if (fTryClosePending)
1199 {
1200 LogFunc(("[%s] Closing pending stream\n", pStream->szName));
1201 rc = drvAudioStreamControlInternalBackend(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
1202 if (RT_SUCCESS(rc))
1203 {
1204 pStream->fStatus &= ~(PDMAUDIOSTREAMSTS_FLAG_ENABLED | PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE);
1205 }
1206 else
1207 LogFunc(("[%s] Backend vetoed against closing pending input stream, rc=%Rrc\n", pStream->szName, rc));
1208 }
1209 }
1210 }
1211
1212 } while (0);
1213
1214 /* Update timestamps. */
1215 pStream->tsLastIteratedNs = RTTimeNanoTS();
1216
1217 if (RT_FAILURE(rc))
1218 LogFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
1219
1220 return rc;
1221}
1222
1223/**
1224 * Plays an audio host output stream which has been configured for non-interleaved (layout) data.
1225 *
1226 * @return IPRT status code.
1227 * @param pThis Pointer to driver instance.
1228 * @param pStream Stream to play.
1229 * @param cfToPlay Number of audio frames to play.
1230 * @param pcfPlayed Returns number of audio frames played. Optional.
1231 */
1232static int drvAudioStreamPlayNonInterleaved(PDRVAUDIO pThis,
1233 PPDMAUDIOSTREAM pStream, uint32_t cfToPlay, uint32_t *pcfPlayed)
1234{
1235 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1236 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1237 /* pcfPlayed is optional. */
1238
1239 if (!cfToPlay)
1240 {
1241 if (pcfPlayed)
1242 *pcfPlayed = 0;
1243 return VINF_SUCCESS;
1244 }
1245
1246 /* Sanity. */
1247 Assert(pStream->enmDir == PDMAUDIODIR_OUT);
1248 Assert(pStream->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED);
1249
1250 int rc = VINF_SUCCESS;
1251
1252 uint32_t cfPlayedTotal = 0;
1253
1254 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetWritable);
1255 uint32_t cfWritable = AUDIOMIXBUF_B2F(&pStream->Host.MixBuf,
1256 pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStream->pvBackend));
1257
1258 Log3Func(("[%s] cfToPlay=%RU32 (%RU64ms), cfWritable=%RU32 (%RU64ms)\n", pStream->szName,
1259 cfToPlay, DrvAudioHlpFramesToMilli(cfToPlay, &pStream->Host.Cfg.Props),
1260 cfWritable, DrvAudioHlpFramesToMilli(cfWritable, &pStream->Host.Cfg.Props)));
1261
1262 if (cfWritable)
1263 {
1264 if (cfToPlay > cfWritable) /* More frames available than we can write? Limit. */
1265 cfToPlay = cfWritable;
1266
1267 if (cfToPlay)
1268 {
1269 uint8_t auBuf[256]; /** @todo Get rid of this here. */
1270
1271 uint32_t cfLeft = cfToPlay;
1272 uint32_t cbChunk = sizeof(auBuf);
1273
1274 while (cfLeft)
1275 {
1276 uint32_t cfRead = 0;
1277 rc = AudioMixBufAcquireReadBlock(&pStream->Host.MixBuf,
1278 auBuf, RT_MIN(cbChunk, AUDIOMIXBUF_F2B(&pStream->Host.MixBuf, cfLeft)),
1279 &cfRead);
1280 if (RT_FAILURE(rc))
1281 break;
1282
1283 uint32_t cbRead = AUDIOMIXBUF_F2B(&pStream->Host.MixBuf, cfRead);
1284 Assert(cbRead <= cbChunk);
1285
1286 uint32_t cfPlayed = 0;
1287 uint32_t cbPlayed = 0;
1288 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pStream->pvBackend,
1289 auBuf, cbRead, &cbPlayed);
1290 if ( RT_SUCCESS(rc)
1291 && cbPlayed)
1292 {
1293 if (pThis->Out.Cfg.Dbg.fEnabled)
1294 DrvAudioHlpFileWrite(pStream->Out.Dbg.pFilePlayNonInterleaved, auBuf, cbPlayed, 0 /* fFlags */);
1295
1296 if (cbRead != cbPlayed)
1297 LogRel2(("Audio: Host stream '%s' played wrong amount (%RU32 bytes read but played %RU32)\n",
1298 pStream->szName, cbRead, cbPlayed));
1299
1300 cfPlayed = AUDIOMIXBUF_B2F(&pStream->Host.MixBuf, cbPlayed);
1301 cfPlayedTotal += cfPlayed;
1302
1303 Assert(cfLeft >= cfPlayed);
1304 cfLeft -= cfPlayed;
1305 }
1306
1307 AudioMixBufReleaseReadBlock(&pStream->Host.MixBuf, cfPlayed);
1308
1309 if (RT_FAILURE(rc))
1310 break;
1311 }
1312 }
1313 }
1314
1315 Log3Func(("[%s] Played %RU32/%RU32 frames, rc=%Rrc\n", pStream->szName, cfPlayedTotal, cfToPlay, rc));
1316
1317 if (RT_SUCCESS(rc))
1318 {
1319 if (pcfPlayed)
1320 *pcfPlayed = cfPlayedTotal;
1321 }
1322
1323 return rc;
1324}
1325
1326/**
1327 * Plays an audio host output stream which has been configured for raw audio (layout) data.
1328 *
1329 * @return IPRT status code.
1330 * @param pThis Pointer to driver instance.
1331 * @param pStream Stream to play.
1332 * @param cfToPlay Number of audio frames to play.
1333 * @param pcfPlayed Returns number of audio frames played. Optional.
1334 */
1335static int drvAudioStreamPlayRaw(PDRVAUDIO pThis,
1336 PPDMAUDIOSTREAM pStream, uint32_t cfToPlay, uint32_t *pcfPlayed)
1337{
1338 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1339 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1340 /* pcfPlayed is optional. */
1341
1342 /* Sanity. */
1343 Assert(pStream->enmDir == PDMAUDIODIR_OUT);
1344 Assert(pStream->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
1345
1346 if (!cfToPlay)
1347 {
1348 if (pcfPlayed)
1349 *pcfPlayed = 0;
1350 return VINF_SUCCESS;
1351 }
1352
1353 int rc = VINF_SUCCESS;
1354
1355 uint32_t cfPlayedTotal = 0;
1356
1357 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetWritable);
1358 uint32_t cfWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStream->pvBackend);
1359 if (cfWritable)
1360 {
1361 if (cfToPlay > cfWritable) /* More frames available than we can write? Limit. */
1362 cfToPlay = cfWritable;
1363
1364 PDMAUDIOFRAME aFrameBuf[_4K]; /** @todo Get rid of this here. */
1365
1366 uint32_t cfLeft = cfToPlay;
1367 while (cfLeft)
1368 {
1369 uint32_t cfRead = 0;
1370 rc = AudioMixBufPeek(&pStream->Host.MixBuf, cfLeft, aFrameBuf,
1371 RT_MIN(cfLeft, RT_ELEMENTS(aFrameBuf)), &cfRead);
1372
1373 if (RT_SUCCESS(rc))
1374 {
1375 if (cfRead)
1376 {
1377 uint32_t cfPlayed;
1378
1379 /* Note: As the stream layout is RPDMAUDIOSTREAMLAYOUT_RAW, operate on audio frames
1380 * rather on bytes. */
1381 Assert(cfRead <= RT_ELEMENTS(aFrameBuf));
1382 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pStream->pvBackend,
1383 aFrameBuf, cfRead, &cfPlayed);
1384 if ( RT_FAILURE(rc)
1385 || !cfPlayed)
1386 {
1387 break;
1388 }
1389
1390 cfPlayedTotal += cfPlayed;
1391 Assert(cfPlayedTotal <= cfToPlay);
1392
1393 Assert(cfLeft >= cfRead);
1394 cfLeft -= cfRead;
1395 }
1396 else
1397 {
1398 if (rc == VINF_AUDIO_MORE_DATA_AVAILABLE) /* Do another peeking round if there is more data available. */
1399 continue;
1400
1401 break;
1402 }
1403 }
1404 else if (RT_FAILURE(rc))
1405 break;
1406 }
1407 }
1408
1409 Log3Func(("[%s] Played %RU32/%RU32 frames, rc=%Rrc\n", pStream->szName, cfPlayedTotal, cfToPlay, rc));
1410
1411 if (RT_SUCCESS(rc))
1412 {
1413 if (pcfPlayed)
1414 *pcfPlayed = cfPlayedTotal;
1415
1416 }
1417
1418 return rc;
1419}
1420
1421/**
1422 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamPlay}
1423 */
1424static DECLCALLBACK(int) drvAudioStreamPlay(PPDMIAUDIOCONNECTOR pInterface,
1425 PPDMAUDIOSTREAM pStream, uint32_t *pcFramesPlayed)
1426{
1427 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1428 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1429 /* pcFramesPlayed is optional. */
1430
1431 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1432
1433 int rc = RTCritSectEnter(&pThis->CritSect);
1434 if (RT_FAILURE(rc))
1435 return rc;
1436
1437 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT,
1438 ("Stream '%s' is not an output stream and therefore cannot be played back (direction is 0x%x)\n",
1439 pStream->szName, pStream->enmDir));
1440
1441 uint32_t cfPlayedTotal = 0;
1442
1443 if (!pThis->pHostDrvAudio)
1444 return VINF_SUCCESS;
1445
1446 PDMAUDIOSTREAMSTS stsStream = pStream->fStatus;
1447#ifdef LOG_ENABLED
1448 char *pszStreamSts = dbgAudioStreamStatusToStr(stsStream);
1449 Log3Func(("[%s] Buffer: Start fStatus=%s\n", pStream->szName, pszStreamSts));
1450 RTStrFree(pszStreamSts);
1451#endif /* LOG_ENABLED */
1452
1453 do
1454 {
1455 /*
1456 * Check if the backend is ready to operate.
1457 */
1458 if (!(stsStream & PDMAUDIOSTREAMSTS_FLAG_ENABLED)) /* Stream disabled? Bail out. */
1459 break;
1460
1461 uint32_t cfLive = AudioMixBufLive(&pStream->Host.MixBuf);
1462 const uint8_t uLivePercent = (100 * cfLive) / AudioMixBufSize(&pStream->Host.MixBuf);
1463
1464 const uint64_t tsDeltaPlayedCapturedNs = RTTimeNanoTS() - pStream->tsLastPlayedCapturedNs;
1465
1466 pStream->tsLastPlayedCapturedNs = RTTimeNanoTS();
1467
1468 Log3Func(("[%s] Buffer: Last played %RU64ns (%RU64ms), filled with %RU64ms (%RU8%%) total, "
1469 "(cfLive=%RU32, fThresholdReached=%RTbool)\n",
1470 pStream->szName, tsDeltaPlayedCapturedNs, tsDeltaPlayedCapturedNs / RT_NS_1MS_64,
1471 DrvAudioHlpFramesToMilli(cfLive, &pStream->Host.Cfg.Props),
1472 uLivePercent, cfLive, pStream->fThresholdReached));
1473
1474 bool fDoPlay = pStream->fThresholdReached;
1475 bool fJustStarted = false;
1476 if (!fDoPlay)
1477 {
1478 /* Did we reach the backend's playback (pre-buffering) threshold? Can be 0 if no threshold set. */
1479 if (cfLive >= pStream->Host.Cfg.Backend.cfPreBuf)
1480 {
1481 LogRel2(("Audio: Stream '%s' buffering complete\n", pStream->szName));
1482 fDoPlay = true;
1483 }
1484 /* Some audio files are shorter than the pre-buffering level (e.g. the "click" Explorer sounds on some Windows guests),
1485 * so make sure that we also play those by checking if the stream already is pending disable mode, even if we didn't
1486 * hit the pre-buffering watermark yet.
1487 *
1488 * To reproduce, use "Windows Navigation Start.wav" on Windows 7 (2824 samples). */
1489 else if ( cfLive
1490 && pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE)
1491 {
1492 LogRel2(("Audio: Stream '%s' buffering complete (short sound)\n", pStream->szName));
1493 fDoPlay = true;
1494 }
1495
1496 if (fDoPlay)
1497 {
1498 pStream->fThresholdReached = true;
1499 fJustStarted = true;
1500 LogRel2(("Audio: Stream '%s' started playing\n", pStream->szName));
1501 }
1502 else /* Not yet, so still buffering audio data. */
1503 LogRel2(("Audio: Stream '%s' is buffering (%RU8%% complete)\n",
1504 pStream->szName, (100 * cfLive) / pStream->Host.Cfg.Backend.cfPreBuf));
1505 }
1506
1507 if (fDoPlay)
1508 {
1509 uint32_t cfToPlay = 0;
1510 if (fJustStarted)
1511 cfToPlay = pStream->Host.Cfg.Backend.cfPeriod; /* cfPeriod can be 0. */
1512
1513 if (!cfToPlay)
1514 {
1515 cfToPlay = DrvAudioHlpNanoToFrames(tsDeltaPlayedCapturedNs, &pStream->Host.Cfg.Props);
1516 if (pStream->Host.Cfg.Device.uSchedulingHintMs)
1517 cfToPlay = RT_MIN(cfToPlay, DrvAudioHlpMilliToFrames(pStream->Host.Cfg.Device.uSchedulingHintMs, &pStream->Host.Cfg.Props));
1518 }
1519
1520 Log3Func(("[%s] Buffer: fJustStarted=%RTbool, cfLive=%RU32, cfToPlay=%RU32\n",
1521 pStream->szName, fJustStarted, cfLive, cfToPlay));
1522
1523 /* Did we reach a buffer underrun? Do pre-buffering again.
1524 * If we're in pending disabled mode, try to play (drain) the remaining audio data. */
1525 if ( !(pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE)
1526 && !fJustStarted)
1527 {
1528 if (cfLive < cfToPlay)
1529 {
1530 pStream->fThresholdReached = false;
1531 Log3Func(("[%s] Buffer: Underrun (cfLive=%RU32, cfToPlay=%RU32)\n", pStream->szName, cfLive, cfToPlay));
1532 LogRel2(("Audio: Stream '%s' buffer underrun (total %RU8%%, which is %RU8%% of a period), buffering ...\n",
1533 pStream->szName, uLivePercent, (100 * cfLive) / pStream->Host.Cfg.Backend.cfPeriod));
1534 break;
1535 }
1536 }
1537
1538 if (cfToPlay > cfLive) /* Don't try to play more than available. */
1539 cfToPlay = cfLive;
1540
1541 if (!cfToPlay)
1542 break;
1543#ifdef DEBUG
1544 if (!fJustStarted)
1545 pStream->Out.Dbg.cbJitterWrittenPlayed -= AUDIOMIXBUF_F2B(&pStream->Host.MixBuf, cfToPlay);
1546 Log3Func(("[%s] Buffer: Playing %RU32 frames (%RU64ms), now filled with %RU64ms -- %RU8%% (cbJitterWrittenPlayed=%RI64)\n",
1547 pStream->szName, cfToPlay, DrvAudioHlpFramesToMilli(cfToPlay, &pStream->Host.Cfg.Props),
1548 DrvAudioHlpFramesToMilli(AudioMixBufUsed(&pStream->Host.MixBuf), &pStream->Host.Cfg.Props),
1549 AudioMixBufUsed(&pStream->Host.MixBuf) * 100 / AudioMixBufSize(&pStream->Host.MixBuf),
1550 pStream->Out.Dbg.cbJitterWrittenPlayed));
1551#endif
1552 if (pThis->pHostDrvAudio->pfnStreamPlayBegin)
1553 pThis->pHostDrvAudio->pfnStreamPlayBegin(pThis->pHostDrvAudio, pStream->pvBackend);
1554
1555 if (RT_LIKELY(pStream->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED))
1556 {
1557 rc = drvAudioStreamPlayNonInterleaved(pThis, pStream, cfToPlay, &cfPlayedTotal);
1558 }
1559 else if (pStream->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW)
1560 {
1561 rc = drvAudioStreamPlayRaw(pThis, pStream, cfToPlay, &cfPlayedTotal);
1562 }
1563 else
1564 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1565
1566 if (pThis->pHostDrvAudio->pfnStreamPlayEnd)
1567 pThis->pHostDrvAudio->pfnStreamPlayEnd(pThis->pHostDrvAudio, pStream->pvBackend);
1568 }
1569
1570 if (RT_SUCCESS(rc))
1571 {
1572 AudioMixBufFinish(&pStream->Host.MixBuf, cfPlayedTotal);
1573
1574#ifdef VBOX_WITH_STATISTICS
1575 STAM_COUNTER_ADD (&pThis->Stats.TotalFramesOut, cfPlayedTotal);
1576 STAM_PROFILE_ADV_STOP(&pThis->Stats.DelayOut, out);
1577 STAM_COUNTER_ADD (&pStream->Out.Stats.FramesPlayed, cfPlayedTotal);
1578#endif
1579 }
1580
1581 } while (0);
1582
1583#ifdef LOG_ENABLED
1584 uint32_t cfLive = AudioMixBufLive(&pStream->Host.MixBuf);
1585 pszStreamSts = dbgAudioStreamStatusToStr(stsStream);
1586 Log3Func(("[%s] Buffer: End fStatus=%s, cfLive=%RU32, cfPlayedTotal=%RU32, rc=%Rrc\n",
1587 pStream->szName, pszStreamSts, cfLive, cfPlayedTotal, rc));
1588 RTStrFree(pszStreamSts);
1589#endif /* LOG_ENABLED */
1590
1591 int rc2 = RTCritSectLeave(&pThis->CritSect);
1592 if (RT_SUCCESS(rc))
1593 rc = rc2;
1594
1595 if (RT_SUCCESS(rc))
1596 {
1597 if (pcFramesPlayed)
1598 *pcFramesPlayed = cfPlayedTotal;
1599 }
1600
1601 if (RT_FAILURE(rc))
1602 LogFlowFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
1603
1604 return rc;
1605}
1606
1607/**
1608 * Captures non-interleaved input from a host stream.
1609 *
1610 * @returns IPRT status code.
1611 * @param pThis Driver instance.
1612 * @param pStream Stream to capture from.
1613 * @param pcfCaptured Number of (host) audio frames captured. Optional.
1614 */
1615static int drvAudioStreamCaptureNonInterleaved(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, uint32_t *pcfCaptured)
1616{
1617 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1618 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1619 /* pcfCaptured is optional. */
1620
1621 /* Sanity. */
1622 Assert(pStream->enmDir == PDMAUDIODIR_IN);
1623 Assert(pStream->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED);
1624
1625 int rc = VINF_SUCCESS;
1626
1627 uint32_t cfCapturedTotal = 0;
1628
1629 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
1630
1631 uint8_t auBuf[_1K]; /** @todo Get rid of this. */
1632 size_t cbBuf = sizeof(auBuf);
1633
1634 for (;;)
1635 {
1636 uint32_t cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStream->pvBackend);
1637 if (!cbReadable)
1638 {
1639 Log2Func(("[%s] No readable data available, skipping\n", pStream->szName));
1640 break;
1641 }
1642
1643 uint32_t cbFree = AUDIOMIXBUF_F2B(&pStream->Host.MixBuf, AudioMixBufFree(&pStream->Host.MixBuf));
1644 if (!cbFree)
1645 {
1646 Log2Func(("[%s] Host buffer full, skipping\n", pStream->szName));
1647 break;
1648 }
1649
1650 if (cbFree < cbReadable) /* More data captured than we can read? */
1651 {
1652 /** @todo Warn? */
1653 }
1654
1655 if (cbFree > cbBuf) /* Limit to buffer size. */
1656 cbFree = (uint32_t)cbBuf;
1657
1658 uint32_t cbCaptured;
1659 rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pStream->pvBackend,
1660 auBuf, cbFree, &cbCaptured);
1661 if (RT_FAILURE(rc))
1662 {
1663 int rc2 = drvAudioStreamControlInternalBackend(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
1664 AssertRC(rc2);
1665 }
1666 else if (cbCaptured)
1667 {
1668 if (pThis->In.Cfg.Dbg.fEnabled)
1669 DrvAudioHlpFileWrite(pStream->In.Dbg.pFileCaptureNonInterleaved, auBuf, cbCaptured, 0 /* fFlags */);
1670
1671 Assert(cbCaptured <= cbBuf);
1672 if (cbCaptured > cbBuf) /* Paranoia. */
1673 cbCaptured = (uint32_t)cbBuf;
1674
1675 uint32_t cfCaptured = 0;
1676 rc = AudioMixBufWriteCirc(&pStream->Host.MixBuf, auBuf, cbCaptured, &cfCaptured);
1677 if (RT_SUCCESS(rc))
1678 cfCapturedTotal += cfCaptured;
1679 }
1680 else /* Nothing captured -- bail out. */
1681 break;
1682
1683 if (RT_FAILURE(rc))
1684 break;
1685 }
1686
1687 if (RT_SUCCESS(rc))
1688 {
1689 if (cfCapturedTotal)
1690 Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStream->szName, cfCapturedTotal, rc));
1691 }
1692 else
1693 LogFunc(("[%s] Capturing failed with rc=%Rrc\n", pStream->szName, rc));
1694
1695 if (pcfCaptured)
1696 *pcfCaptured = cfCapturedTotal;
1697
1698 return rc;
1699}
1700
1701/**
1702 * Captures raw input from a host stream.
1703 * Raw input means that the backend directly operates on PDMAUDIOFRAME structs without
1704 * no data layout processing done in between.
1705 *
1706 * Needed for e.g. the VRDP audio backend (in Main).
1707 *
1708 * @returns IPRT status code.
1709 * @param pThis Driver instance.
1710 * @param pStream Stream to capture from.
1711 * @param pcfCaptured Number of (host) audio frames captured. Optional.
1712 */
1713static int drvAudioStreamCaptureRaw(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, uint32_t *pcfCaptured)
1714{
1715 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1716 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1717 /* pcfCaptured is optional. */
1718
1719 /* Sanity. */
1720 Assert(pStream->enmDir == PDMAUDIODIR_IN);
1721 Assert(pStream->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
1722
1723 int rc = VINF_SUCCESS;
1724
1725 uint32_t cfCapturedTotal = 0;
1726
1727 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
1728
1729 for (;;)
1730 {
1731 uint32_t cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStream->pvBackend);
1732 if (!cbReadable) /* Nothing to read on the backend side? Bail out. */
1733 break;
1734
1735 const uint32_t cbFree = AudioMixBufFreeBytes(&pStream->Host.MixBuf);
1736 if (!cbFree) /* No space left in the host stream? */
1737 break;
1738
1739 if (cbReadable > cbFree) /* Don't capture more than the host stream currently can hold. */
1740 cbReadable = cbFree;
1741
1742 PPDMAUDIOFRAME paFrames;
1743 uint32_t cfWritable;
1744 rc = AudioMixBufPeekMutable(&pStream->Host.MixBuf, AUDIOMIXBUF_B2F(&pStream->Host.MixBuf, cbReadable),
1745 &paFrames, &cfWritable);
1746 if ( RT_FAILURE(rc)
1747 || !cfWritable)
1748 {
1749 break;
1750 }
1751
1752 uint32_t cfCaptured;
1753 rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pStream->pvBackend,
1754 paFrames, cfWritable, &cfCaptured);
1755 if (RT_FAILURE(rc))
1756 {
1757 int rc2 = drvAudioStreamControlInternalBackend(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
1758 AssertRC(rc2);
1759 }
1760 else if (cfCaptured)
1761 {
1762 Assert(cfCaptured <= cfWritable);
1763 if (cfCaptured > cfWritable) /* Paranoia. */
1764 cfCaptured = cfWritable;
1765
1766 cfCapturedTotal += cfCaptured;
1767 }
1768 else /* Nothing captured -- bail out. */
1769 break;
1770
1771 if (RT_FAILURE(rc))
1772 break;
1773 }
1774
1775 Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStream->szName, cfCapturedTotal, rc));
1776
1777 if (pcfCaptured)
1778 *pcfCaptured = cfCapturedTotal;
1779
1780 return rc;
1781}
1782
1783/**
1784 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCapture}
1785 */
1786static DECLCALLBACK(int) drvAudioStreamCapture(PPDMIAUDIOCONNECTOR pInterface,
1787 PPDMAUDIOSTREAM pStream, uint32_t *pcFramesCaptured)
1788{
1789 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1790
1791 int rc = RTCritSectEnter(&pThis->CritSect);
1792 if (RT_FAILURE(rc))
1793 return rc;
1794
1795 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
1796 ("Stream '%s' is not an input stream and therefore cannot be captured (direction is 0x%x)\n",
1797 pStream->szName, pStream->enmDir));
1798
1799 uint32_t cfCaptured = 0;
1800
1801 do
1802 {
1803 if (!pThis->pHostDrvAudio)
1804 break;
1805
1806 /*
1807 * Check if the backend is ready to operate.
1808 */
1809
1810 PDMAUDIOSTREAMSTS stsStream = pStream->fStatus;
1811#ifdef LOG_ENABLED
1812 char *pszStreamSts = dbgAudioStreamStatusToStr(stsStream);
1813 Log3Func(("[%s] Start: fStatus=%s\n", pStream->szName, pszStreamSts));
1814 RTStrFree(pszStreamSts);
1815#endif /* LOG_ENABLED */
1816 if (!(stsStream & PDMAUDIOSTREAMSTS_FLAG_ENABLED)) /* Stream disabled? Bail out. */
1817 break;
1818
1819 /*
1820 * Do the actual capturing.
1821 */
1822
1823 if (pThis->pHostDrvAudio->pfnStreamCaptureBegin)
1824 pThis->pHostDrvAudio->pfnStreamCaptureBegin(pThis->pHostDrvAudio, pStream->pvBackend);
1825
1826 if (RT_LIKELY(pStream->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED))
1827 {
1828 rc = drvAudioStreamCaptureNonInterleaved(pThis, pStream, &cfCaptured);
1829 }
1830 else if (pStream->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW)
1831 {
1832 rc = drvAudioStreamCaptureRaw(pThis, pStream, &cfCaptured);
1833 }
1834 else
1835 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1836
1837 if (pThis->pHostDrvAudio->pfnStreamCaptureEnd)
1838 pThis->pHostDrvAudio->pfnStreamCaptureEnd(pThis->pHostDrvAudio, pStream->pvBackend);
1839
1840#ifdef LOG_ENABLED
1841 pszStreamSts = dbgAudioStreamStatusToStr(stsStream);
1842 Log3Func(("[%s] End: fStatus=%s, cfCaptured=%RU32, rc=%Rrc\n",
1843 pStream->szName, pszStreamSts, cfCaptured, rc));
1844 RTStrFree(pszStreamSts);
1845#endif /* LOG_ENABLED */
1846
1847 if (RT_SUCCESS(rc))
1848 {
1849 Log3Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStream->szName, cfCaptured, rc));
1850
1851#ifdef VBOX_WITH_STATISTICS
1852 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesIn, cfCaptured);
1853 STAM_COUNTER_ADD(&pStream->In.Stats.FramesCaptured, cfCaptured);
1854#endif
1855 }
1856 else if (RT_UNLIKELY(RT_FAILURE(rc)))
1857 {
1858 LogRel(("Audio: Capturing stream '%s' failed with %Rrc\n", pStream->szName, rc));
1859 }
1860
1861 } while (0);
1862
1863 if (pcFramesCaptured)
1864 *pcFramesCaptured = cfCaptured;
1865
1866 int rc2 = RTCritSectLeave(&pThis->CritSect);
1867 if (RT_SUCCESS(rc))
1868 rc = rc2;
1869
1870 if (RT_FAILURE(rc))
1871 LogFlowFuncLeaveRC(rc);
1872
1873 return rc;
1874}
1875
1876#ifdef VBOX_WITH_AUDIO_CALLBACKS
1877/**
1878 * Duplicates an audio callback.
1879 *
1880 * @returns Pointer to duplicated callback, or NULL on failure.
1881 * @param pCB Callback to duplicate.
1882 */
1883static PPDMAUDIOCBRECORD drvAudioCallbackDuplicate(PPDMAUDIOCBRECORD pCB)
1884{
1885 AssertPtrReturn(pCB, NULL);
1886
1887 PPDMAUDIOCBRECORD pCBCopy = (PPDMAUDIOCBRECORD)RTMemDup((void *)pCB, sizeof(PDMAUDIOCBRECORD));
1888 if (!pCBCopy)
1889 return NULL;
1890
1891 if (pCB->pvCtx)
1892 {
1893 pCBCopy->pvCtx = RTMemDup(pCB->pvCtx, pCB->cbCtx);
1894 if (!pCBCopy->pvCtx)
1895 {
1896 RTMemFree(pCBCopy);
1897 return NULL;
1898 }
1899
1900 pCBCopy->cbCtx = pCB->cbCtx;
1901 }
1902
1903 return pCBCopy;
1904}
1905
1906/**
1907 * Destroys a given callback.
1908 *
1909 * @param pCB Callback to destroy.
1910 */
1911static void drvAudioCallbackDestroy(PPDMAUDIOCBRECORD pCB)
1912{
1913 if (!pCB)
1914 return;
1915
1916 RTListNodeRemove(&pCB->Node);
1917 if (pCB->pvCtx)
1918 {
1919 Assert(pCB->cbCtx);
1920 RTMemFree(pCB->pvCtx);
1921 }
1922 RTMemFree(pCB);
1923}
1924
1925/**
1926 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnRegisterCallbacks}
1927 */
1928static DECLCALLBACK(int) drvAudioRegisterCallbacks(PPDMIAUDIOCONNECTOR pInterface,
1929 PPDMAUDIOCBRECORD paCallbacks, size_t cCallbacks)
1930{
1931 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1932 AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER);
1933 AssertReturn(cCallbacks, VERR_INVALID_PARAMETER);
1934
1935 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1936
1937 int rc = RTCritSectEnter(&pThis->CritSect);
1938 if (RT_FAILURE(rc))
1939 return rc;
1940
1941 for (size_t i = 0; i < cCallbacks; i++)
1942 {
1943 PPDMAUDIOCBRECORD pCB = drvAudioCallbackDuplicate(&paCallbacks[i]);
1944 if (!pCB)
1945 {
1946 rc = VERR_NO_MEMORY;
1947 break;
1948 }
1949
1950 switch (pCB->enmSource)
1951 {
1952 case PDMAUDIOCBSOURCE_DEVICE:
1953 {
1954 switch (pCB->Device.enmType)
1955 {
1956 case PDMAUDIODEVICECBTYPE_DATA_INPUT:
1957 RTListAppend(&pThis->In.lstCB, &pCB->Node);
1958 break;
1959
1960 case PDMAUDIODEVICECBTYPE_DATA_OUTPUT:
1961 RTListAppend(&pThis->Out.lstCB, &pCB->Node);
1962 break;
1963
1964 default:
1965 AssertMsgFailed(("Not supported\n"));
1966 break;
1967 }
1968
1969 break;
1970 }
1971
1972 default:
1973 AssertMsgFailed(("Not supported\n"));
1974 break;
1975 }
1976 }
1977
1978 /** @todo Undo allocations on error. */
1979
1980 int rc2 = RTCritSectLeave(&pThis->CritSect);
1981 if (RT_SUCCESS(rc))
1982 rc = rc2;
1983
1984 return rc;
1985}
1986#endif /* VBOX_WITH_AUDIO_CALLBACKS */
1987
1988#ifdef VBOX_WITH_AUDIO_CALLBACKS
1989/**
1990 * Backend callback implementation.
1991 *
1992 * Important: No calls back to the backend within this function, as the backend
1993 * might hold any locks / critical sections while executing this callback.
1994 * Will result in some ugly deadlocks (or at least locking order violations) then.
1995 *
1996 * @copydoc FNPDMHOSTAUDIOCALLBACK
1997 */
1998static DECLCALLBACK(int) drvAudioBackendCallback(PPDMDRVINS pDrvIns,
1999 PDMAUDIOBACKENDCBTYPE enmType, void *pvUser, size_t cbUser)
2000{
2001 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
2002 RT_NOREF(pvUser, cbUser);
2003 /* pvUser and cbUser are optional. */
2004
2005 /* Get the upper driver (PDMIAUDIOCONNECTOR). */
2006 AssertPtr(pDrvIns->pUpBase);
2007 PPDMIAUDIOCONNECTOR pInterface = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
2008 AssertPtr(pInterface);
2009 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2010
2011 int rc = RTCritSectEnter(&pThis->CritSect);
2012 AssertRCReturn(rc, rc);
2013
2014 LogFunc(("pThis=%p, enmType=%RU32, pvUser=%p, cbUser=%zu\n", pThis, enmType, pvUser, cbUser));
2015
2016 switch (enmType)
2017 {
2018 case PDMAUDIOBACKENDCBTYPE_DEVICES_CHANGED:
2019 LogRel(("Audio: Host audio device configuration has changed\n"));
2020 rc = drvAudioScheduleReInitInternal(pThis);
2021 break;
2022
2023 default:
2024 AssertMsgFailed(("Not supported\n"));
2025 break;
2026 }
2027
2028 int rc2 = RTCritSectLeave(&pThis->CritSect);
2029 if (RT_SUCCESS(rc))
2030 rc = rc2;
2031
2032 LogFlowFunc(("Returning %Rrc\n", rc));
2033 return rc;
2034}
2035#endif /* VBOX_WITH_AUDIO_CALLBACKS */
2036
2037#ifdef VBOX_WITH_AUDIO_ENUM
2038/**
2039 * Enumerates all host audio devices.
2040 * This functionality might not be implemented by all backends and will return VERR_NOT_SUPPORTED
2041 * if not being supported.
2042 *
2043 * @returns IPRT status code.
2044 * @param pThis Driver instance to be called.
2045 * @param fLog Whether to print the enumerated device to the release log or not.
2046 * @param pDevEnum Where to store the device enumeration.
2047 */
2048static int drvAudioDevicesEnumerateInternal(PDRVAUDIO pThis, bool fLog, PPDMAUDIODEVICEENUM pDevEnum)
2049{
2050 int rc;
2051
2052 /*
2053 * If the backend supports it, do a device enumeration.
2054 */
2055 if (pThis->pHostDrvAudio->pfnGetDevices)
2056 {
2057 PDMAUDIODEVICEENUM DevEnum;
2058 rc = pThis->pHostDrvAudio->pfnGetDevices(pThis->pHostDrvAudio, &DevEnum);
2059 if (RT_SUCCESS(rc))
2060 {
2061 if (fLog)
2062 LogRel(("Audio: Found %RU16 devices\n", DevEnum.cDevices));
2063
2064 PPDMAUDIODEVICE pDev;
2065 RTListForEach(&DevEnum.lstDevices, pDev, PDMAUDIODEVICE, Node)
2066 {
2067 if (fLog)
2068 {
2069 char *pszFlags = DrvAudioHlpAudDevFlagsToStrA(pDev->fFlags);
2070
2071 LogRel(("Audio: Device '%s':\n", pDev->szName));
2072 LogRel(("Audio: \tUsage = %s\n", DrvAudioHlpAudDirToStr(pDev->enmUsage)));
2073 LogRel(("Audio: \tFlags = %s\n", pszFlags ? pszFlags : "<NONE>"));
2074 LogRel(("Audio: \tInput channels = %RU8\n", pDev->cMaxInputChannels));
2075 LogRel(("Audio: \tOutput channels = %RU8\n", pDev->cMaxOutputChannels));
2076
2077 if (pszFlags)
2078 RTStrFree(pszFlags);
2079 }
2080 }
2081
2082 if (pDevEnum)
2083 rc = DrvAudioHlpDeviceEnumCopy(pDevEnum, &DevEnum);
2084
2085 DrvAudioHlpDeviceEnumFree(&DevEnum);
2086 }
2087 else
2088 {
2089 if (fLog)
2090 LogRel(("Audio: Device enumeration failed with %Rrc\n", rc));
2091 /* Not fatal. */
2092 }
2093 }
2094 else
2095 {
2096 rc = VERR_NOT_SUPPORTED;
2097
2098 if (fLog)
2099 LogRel3(("Audio: Host audio backend does not support audio device enumeration, skipping\n"));
2100 }
2101
2102 LogFunc(("Returning %Rrc\n", rc));
2103 return rc;
2104}
2105#endif /* VBOX_WITH_AUDIO_ENUM */
2106
2107/**
2108 * Initializes the host backend and queries its initial configuration.
2109 * If the host backend fails, VERR_AUDIO_BACKEND_INIT_FAILED will be returned.
2110 *
2111 * Note: As this routine is called when attaching to the device LUN in the
2112 * device emulation, we either check for success or VERR_AUDIO_BACKEND_INIT_FAILED.
2113 * Everything else is considered as fatal and must be handled separately in
2114 * the device emulation!
2115 *
2116 * @return IPRT status code.
2117 * @param pThis Driver instance to be called.
2118 * @param pCfgHandle CFGM configuration handle to use for this driver.
2119 */
2120static int drvAudioHostInit(PDRVAUDIO pThis, PCFGMNODE pCfgHandle)
2121{
2122 /* pCfgHandle is optional. */
2123 NOREF(pCfgHandle);
2124 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2125
2126 LogFlowFuncEnter();
2127
2128 AssertPtr(pThis->pHostDrvAudio);
2129 int rc = pThis->pHostDrvAudio->pfnInit(pThis->pHostDrvAudio);
2130 if (RT_FAILURE(rc))
2131 {
2132 LogRel(("Audio: Initialization of host backend failed with %Rrc\n", rc));
2133 return VERR_AUDIO_BACKEND_INIT_FAILED;
2134 }
2135
2136 /*
2137 * Get the backend configuration.
2138 */
2139 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
2140 if (RT_FAILURE(rc))
2141 {
2142 LogRel(("Audio: Getting host backend configuration failed with %Rrc\n", rc));
2143 return VERR_AUDIO_BACKEND_INIT_FAILED;
2144 }
2145
2146 pThis->In.cStreamsFree = pThis->BackendCfg.cMaxStreamsIn;
2147 pThis->Out.cStreamsFree = pThis->BackendCfg.cMaxStreamsOut;
2148
2149 LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->In.cStreamsFree, pThis->Out.cStreamsFree));
2150
2151 LogRel2(("Audio: Host audio backend supports %RU32 input streams and %RU32 output streams at once\n",
2152 /* Clamp for logging. Unlimited streams are defined by UINT32_MAX. */
2153 RT_MIN(64, pThis->In.cStreamsFree), RT_MIN(64, pThis->Out.cStreamsFree)));
2154
2155#ifdef VBOX_WITH_AUDIO_ENUM
2156 int rc2 = drvAudioDevicesEnumerateInternal(pThis, true /* fLog */, NULL /* pDevEnum */);
2157 if (rc2 != VERR_NOT_SUPPORTED) /* Some backends don't implement device enumeration. */
2158 AssertRC(rc2);
2159
2160 RT_NOREF(rc2);
2161 /* Ignore rc. */
2162#endif
2163
2164#ifdef VBOX_WITH_AUDIO_CALLBACKS
2165 /*
2166 * If the backend supports it, offer a callback to this connector.
2167 */
2168 if (pThis->pHostDrvAudio->pfnSetCallback)
2169 {
2170 rc2 = pThis->pHostDrvAudio->pfnSetCallback(pThis->pHostDrvAudio, drvAudioBackendCallback);
2171 if (RT_FAILURE(rc2))
2172 LogRel(("Audio: Error registering backend callback, rc=%Rrc\n", rc2));
2173 /* Not fatal. */
2174 }
2175#endif
2176
2177 LogFlowFuncLeave();
2178 return VINF_SUCCESS;
2179}
2180
2181/**
2182 * Handles state changes for all audio streams.
2183 *
2184 * @param pDrvIns Pointer to driver instance.
2185 * @param enmCmd Stream command to set for all streams.
2186 */
2187static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
2188{
2189 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2190 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2191
2192 LogFlowFunc(("enmCmd=%s\n", DrvAudioHlpStreamCmdToStr(enmCmd)));
2193
2194 int rc2 = RTCritSectEnter(&pThis->CritSect);
2195 AssertRC(rc2);
2196
2197 if (pThis->pHostDrvAudio)
2198 {
2199 PPDMAUDIOSTREAM pStream;
2200 RTListForEach(&pThis->lstStreams, pStream, PDMAUDIOSTREAM, Node)
2201 drvAudioStreamControlInternalBackend(pThis, pStream, enmCmd);
2202 }
2203
2204 rc2 = RTCritSectLeave(&pThis->CritSect);
2205 AssertRC(rc2);
2206}
2207
2208/**
2209 * Retrieves an audio configuration from the specified CFGM node.
2210 *
2211 * @return VBox status code.
2212 * @param pThis Driver instance to be called.
2213 * @param pCfg Where to store the retrieved audio configuration to.
2214 * @param pNode Where to get the audio configuration from.
2215 */
2216static int drvAudioGetCfgFromCFGM(PDRVAUDIO pThis, PDRVAUDIOCFG pCfg, PCFGMNODE pNode)
2217{
2218 RT_NOREF(pThis);
2219
2220 /* Debug stuff. */
2221 CFGMR3QueryBoolDef(pNode, "DebugEnabled", &pCfg->Dbg.fEnabled, false);
2222 int rc2 = CFGMR3QueryString(pNode, "DebugPathOut", pCfg->Dbg.szPathOut, sizeof(pCfg->Dbg.szPathOut));
2223 if ( RT_FAILURE(rc2)
2224 || !strlen(pCfg->Dbg.szPathOut))
2225 {
2226 RTStrPrintf(pCfg->Dbg.szPathOut, sizeof(pCfg->Dbg.szPathOut), VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH);
2227 }
2228
2229 if (pCfg->Dbg.fEnabled)
2230 LogRel(("Audio: Debugging enabled (audio data written to '%s')\n", pCfg->Dbg.szPathOut));
2231
2232 /* Buffering stuff. */
2233 CFGMR3QueryU32Def(pNode, "PeriodMs", &pCfg->uPeriodMs, 0);
2234 CFGMR3QueryU32Def(pNode, "BufferSizeMs", &pCfg->uBufferSizeMs, 0);
2235 CFGMR3QueryU32Def(pNode, "PreBufferMs", &pCfg->uPreBufMs, UINT32_MAX /* No custom value set */);
2236
2237 LogFunc(("pCfg=%p, uPeriodMs=%RU32, uBufferSizeMs=%RU32, uPreBufMs=%RU32\n",
2238 pCfg, pCfg->uPeriodMs, pCfg->uBufferSizeMs, pCfg->uPreBufMs));
2239
2240 return VINF_SUCCESS;
2241}
2242
2243/**
2244 * Intializes an audio driver instance.
2245 *
2246 * @returns IPRT status code.
2247 * @param pDrvIns Pointer to driver instance.
2248 * @param pCfgHandle CFGM handle to use for configuration.
2249 */
2250static int drvAudioInit(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
2251{
2252 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
2253 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
2254
2255 LogRel2(("Audio: Verbose logging enabled\n"));
2256
2257 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2258
2259 int rc = RTCritSectInit(&pThis->CritSect);
2260 AssertRCReturn(rc, rc);
2261
2262 /*
2263 * Configure driver from CFGM.
2264 */
2265#ifdef DEBUG
2266 CFGMR3Dump(pCfgHandle);
2267#endif
2268
2269 pThis->fTerminate = false;
2270 pThis->pCFGMNode = pCfgHandle;
2271
2272 int rc2 = CFGMR3QueryString(pThis->pCFGMNode, "DriverName", pThis->szName, sizeof(pThis->szName));
2273 if (RT_FAILURE(rc2))
2274 RTStrPrintf(pThis->szName, sizeof(pThis->szName), "Untitled");
2275
2276 /* By default we don't enable anything if wrongly / not set-up. */
2277 CFGMR3QueryBoolDef(pThis->pCFGMNode, "InputEnabled", &pThis->In.fEnabled, false);
2278 CFGMR3QueryBoolDef(pThis->pCFGMNode, "OutputEnabled", &pThis->Out.fEnabled, false);
2279
2280 LogRel2(("Audio: Initial status for driver '%s': Input is %s, output is %s\n",
2281 pThis->szName, pThis->In.fEnabled ? "enabled" : "disabled", pThis->Out.fEnabled ? "enabled" : "disabled"));
2282
2283 /*
2284 * Load configurations.
2285 */
2286 rc = drvAudioGetCfgFromCFGM(pThis, &pThis->In.Cfg, pThis->pCFGMNode);
2287 if (RT_SUCCESS(rc))
2288 rc = drvAudioGetCfgFromCFGM(pThis, &pThis->Out.Cfg, pThis->pCFGMNode);
2289
2290 LogFunc(("[%s] rc=%Rrc\n", pThis->szName, rc));
2291 return rc;
2292}
2293
2294/**
2295 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRead}
2296 */
2297static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
2298 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
2299{
2300 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2301 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2302
2303 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2304 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2305 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
2306 /* pcbRead is optional. */
2307
2308 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
2309 ("Stream '%s' is not an input stream and therefore cannot be read from (direction is 0x%x)\n",
2310 pStream->szName, pStream->enmDir));
2311
2312 uint32_t cbReadTotal = 0;
2313
2314 int rc = RTCritSectEnter(&pThis->CritSect);
2315 if (RT_FAILURE(rc))
2316 return rc;
2317
2318 do
2319 {
2320 if (!pThis->In.fEnabled)
2321 {
2322 RT_BZERO(pvBuf, cbBuf);
2323 cbReadTotal = cbBuf;
2324 break;
2325 }
2326
2327 if ( pThis->pHostDrvAudio
2328 && pThis->pHostDrvAudio->pfnGetStatus
2329 && pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_IN) != PDMAUDIOBACKENDSTS_RUNNING)
2330 {
2331 rc = VERR_NOT_AVAILABLE;
2332 break;
2333 }
2334
2335 /*
2336 * Read from the parent buffer (that is, the guest buffer) which
2337 * should have the audio data in the format the guest needs.
2338 */
2339 uint32_t cReadTotal = 0;
2340
2341 uint32_t cfToRead = RT_MIN(AUDIOMIXBUF_B2F(&pStream->Guest.MixBuf, cbBuf), AudioMixBufUsed(&pStream->Guest.MixBuf));
2342 while (cfToRead)
2343 {
2344 uint32_t cRead;
2345 rc = AudioMixBufAcquireReadBlock(&pStream->Guest.MixBuf,
2346 (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pStream->Guest.MixBuf, cReadTotal),
2347 AUDIOMIXBUF_F2B(&pStream->Guest.MixBuf, cfToRead), &cRead);
2348 if (RT_FAILURE(rc))
2349 break;
2350
2351#ifdef VBOX_WITH_STATISTICS
2352 const uint32_t cbRead = AUDIOMIXBUF_F2B(&pStream->Guest.MixBuf, cRead);
2353
2354 STAM_COUNTER_ADD(&pThis->Stats.TotalBytesRead, cbRead);
2355 STAM_COUNTER_ADD(&pStream->In.Stats.BytesTotalRead, cbRead);
2356#endif
2357 Assert(cfToRead >= cRead);
2358 cfToRead -= cRead;
2359
2360 cReadTotal += cRead;
2361
2362 AudioMixBufReleaseReadBlock(&pStream->Guest.MixBuf, cRead);
2363 }
2364
2365 if (cReadTotal)
2366 {
2367 if (pThis->In.Cfg.Dbg.fEnabled)
2368 DrvAudioHlpFileWrite(pStream->In.Dbg.pFileStreamRead,
2369 pvBuf, AUDIOMIXBUF_F2B(&pStream->Guest.MixBuf, cReadTotal), 0 /* fFlags */);
2370
2371 AudioMixBufFinish(&pStream->Guest.MixBuf, cReadTotal);
2372
2373 cbReadTotal = AUDIOMIXBUF_F2B(&pStream->Guest.MixBuf, cReadTotal);
2374 }
2375
2376 } while (0);
2377
2378 Log3Func(("[%s] fEnabled=%RTbool, cbReadTotal=%RU32, rc=%Rrc\n", pStream->szName, pThis->In.fEnabled, cbReadTotal, rc));
2379
2380 int rc2 = RTCritSectLeave(&pThis->CritSect);
2381 if (RT_SUCCESS(rc))
2382 rc = rc2;
2383
2384 if (RT_SUCCESS(rc))
2385 {
2386 if (pcbRead)
2387 *pcbRead = cbReadTotal;
2388 }
2389
2390 return rc;
2391}
2392
2393/**
2394 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCreate}
2395 */
2396static DECLCALLBACK(int) drvAudioStreamCreate(PPDMIAUDIOCONNECTOR pInterface,
2397 PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest,
2398 PPDMAUDIOSTREAM *ppStream)
2399{
2400 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2401 AssertPtrReturn(pCfgHost, VERR_INVALID_POINTER);
2402 AssertPtrReturn(pCfgGuest, VERR_INVALID_POINTER);
2403 AssertPtrReturn(ppStream, VERR_INVALID_POINTER);
2404
2405 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2406
2407 int rc = RTCritSectEnter(&pThis->CritSect);
2408 if (RT_FAILURE(rc))
2409 return rc;
2410
2411 LogFlowFunc(("Host=%s, Guest=%s\n", pCfgHost->szName, pCfgGuest->szName));
2412#ifdef DEBUG
2413 DrvAudioHlpStreamCfgPrint(pCfgHost);
2414 DrvAudioHlpStreamCfgPrint(pCfgGuest);
2415#endif
2416
2417 PPDMAUDIOSTREAM pStream = NULL;
2418
2419#define RC_BREAK(x) { rc = x; break; }
2420
2421 do
2422 {
2423 if ( !DrvAudioHlpStreamCfgIsValid(pCfgHost)
2424 || !DrvAudioHlpStreamCfgIsValid(pCfgGuest))
2425 {
2426 RC_BREAK(VERR_INVALID_PARAMETER);
2427 }
2428
2429 /* Make sure that both configurations actually intend the same thing. */
2430 if (pCfgHost->enmDir != pCfgGuest->enmDir)
2431 {
2432 AssertMsgFailed(("Stream configuration directions do not match\n"));
2433 RC_BREAK(VERR_INVALID_PARAMETER);
2434 }
2435
2436 /* Note: cbHstStrm will contain the size of the data the backend needs to operate on. */
2437 size_t cbHstStrm = 0;
2438 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
2439 {
2440 if (!pThis->In.cStreamsFree)
2441 LogFunc(("Warning: No more input streams free to use\n"));
2442
2443 cbHstStrm = pThis->BackendCfg.cbStreamIn;
2444 }
2445 else /* Out */
2446 {
2447 if (!pThis->Out.cStreamsFree)
2448 {
2449 LogFlowFunc(("Maximum number of host output streams reached\n"));
2450 RC_BREAK(VERR_AUDIO_NO_FREE_OUTPUT_STREAMS);
2451 }
2452
2453 cbHstStrm = pThis->BackendCfg.cbStreamOut;
2454 }
2455
2456 /*
2457 * Allocate and initialize common state.
2458 */
2459
2460 pStream = (PPDMAUDIOSTREAM)RTMemAllocZ(sizeof(PDMAUDIOSTREAM));
2461 AssertPtrBreakStmt(pStream, rc = VERR_NO_MEMORY);
2462
2463 /* Retrieve host driver name for easier identification. */
2464 AssertPtr(pThis->pHostDrvAudio);
2465 PPDMDRVINS pDrvAudioInst = PDMIBASE_2_PDMDRV(pThis->pDrvIns->pDownBase);
2466 AssertPtr(pDrvAudioInst);
2467 AssertPtr(pDrvAudioInst->pReg);
2468
2469 char szDriver[64];
2470 RTStrPrintf(szDriver, RT_ELEMENTS(szDriver), "%s", pDrvAudioInst->pReg->szName);
2471 if (!strlen(szDriver))
2472 {
2473 RTStrPrintf(szDriver, RT_ELEMENTS(szDriver), "Untitled");
2474 AssertFailed(); /* Should never happen. */
2475 }
2476
2477 RTStrPrintf(pStream->szName, RT_ELEMENTS(pStream->szName), "[%s] %s",
2478 szDriver, strlen(pCfgHost->szName) ? pCfgHost->szName : "<Untitled>");
2479 pStream->enmDir = pCfgHost->enmDir;
2480
2481 /*
2482 * Allocate and init backend-specific data.
2483 */
2484
2485 if (cbHstStrm) /* High unlikely that backends do not have an own space for data, but better check. */
2486 {
2487 pStream->pvBackend = RTMemAllocZ(cbHstStrm);
2488 AssertPtrBreakStmt(pStream->pvBackend, rc = VERR_NO_MEMORY);
2489
2490 pStream->cbBackend = cbHstStrm;
2491 }
2492
2493 /*
2494 * Try to init the rest.
2495 */
2496
2497 rc = drvAudioStreamInitInternal(pThis, pStream, pCfgHost, pCfgGuest);
2498 if (RT_FAILURE(rc))
2499 break;
2500
2501 } while (0);
2502
2503#undef RC_BREAK
2504
2505 if (RT_FAILURE(rc))
2506 {
2507 if (pStream)
2508 {
2509 int rc2 = drvAudioStreamUninitInternal(pThis, pStream);
2510 if (RT_SUCCESS(rc2))
2511 {
2512 drvAudioStreamFree(pStream);
2513 pStream = NULL;
2514 }
2515 }
2516 }
2517 else
2518 {
2519 /* Append the stream to our stream list. */
2520 RTListAppend(&pThis->lstStreams, &pStream->Node);
2521
2522 /* Set initial reference counts. */
2523 pStream->cRefs = 1;
2524
2525 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
2526 {
2527 if (pThis->In.Cfg.Dbg.fEnabled)
2528 {
2529 char szFile[RTPATH_MAX + 1];
2530
2531 int rc2 = DrvAudioHlpFileNameGet(szFile, RT_ELEMENTS(szFile), pThis->In.Cfg.Dbg.szPathOut, "CaptureNonInterleaved",
2532 pThis->pDrvIns->iInstance, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
2533 if (RT_SUCCESS(rc2))
2534 {
2535 rc2 = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE,
2536 &pStream->In.Dbg.pFileCaptureNonInterleaved);
2537 if (RT_SUCCESS(rc2))
2538 rc2 = DrvAudioHlpFileOpen(pStream->In.Dbg.pFileCaptureNonInterleaved, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS,
2539 &pStream->Host.Cfg.Props);
2540 }
2541
2542 if (RT_SUCCESS(rc2))
2543 {
2544 rc2 = DrvAudioHlpFileNameGet(szFile, RT_ELEMENTS(szFile), pThis->In.Cfg.Dbg.szPathOut, "StreamRead",
2545 pThis->pDrvIns->iInstance, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
2546 if (RT_SUCCESS(rc2))
2547 {
2548 rc2 = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE,
2549 &pStream->In.Dbg.pFileStreamRead);
2550 if (RT_SUCCESS(rc2))
2551 rc2 = DrvAudioHlpFileOpen(pStream->In.Dbg.pFileStreamRead, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS,
2552 &pStream->Host.Cfg.Props);
2553 }
2554 }
2555 }
2556
2557 if (pThis->In.cStreamsFree)
2558 pThis->In.cStreamsFree--;
2559 }
2560 else /* Out */
2561 {
2562 if (pThis->Out.Cfg.Dbg.fEnabled)
2563 {
2564 char szFile[RTPATH_MAX + 1];
2565
2566 int rc2 = DrvAudioHlpFileNameGet(szFile, RT_ELEMENTS(szFile), pThis->Out.Cfg.Dbg.szPathOut, "PlayNonInterleaved",
2567 pThis->pDrvIns->iInstance, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
2568 if (RT_SUCCESS(rc2))
2569 {
2570 rc2 = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE,
2571 &pStream->Out.Dbg.pFilePlayNonInterleaved);
2572 if (RT_SUCCESS(rc2))
2573 rc = DrvAudioHlpFileOpen(pStream->Out.Dbg.pFilePlayNonInterleaved, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS,
2574 &pStream->Host.Cfg.Props);
2575 }
2576
2577 if (RT_SUCCESS(rc2))
2578 {
2579 rc2 = DrvAudioHlpFileNameGet(szFile, RT_ELEMENTS(szFile), pThis->Out.Cfg.Dbg.szPathOut, "StreamWrite",
2580 pThis->pDrvIns->iInstance, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
2581 if (RT_SUCCESS(rc2))
2582 {
2583 rc2 = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE,
2584 &pStream->Out.Dbg.pFileStreamWrite);
2585 if (RT_SUCCESS(rc2))
2586 rc2 = DrvAudioHlpFileOpen(pStream->Out.Dbg.pFileStreamWrite, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS,
2587 &pStream->Host.Cfg.Props);
2588 }
2589 }
2590 }
2591
2592 if (pThis->Out.cStreamsFree)
2593 pThis->Out.cStreamsFree--;
2594 }
2595
2596#ifdef VBOX_WITH_STATISTICS
2597 STAM_COUNTER_ADD(&pThis->Stats.TotalStreamsCreated, 1);
2598#endif
2599 *ppStream = pStream;
2600 }
2601
2602 int rc2 = RTCritSectLeave(&pThis->CritSect);
2603 if (RT_SUCCESS(rc))
2604 rc = rc2;
2605
2606 LogFlowFuncLeaveRC(rc);
2607 return rc;
2608}
2609
2610/**
2611 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnEnable}
2612 */
2613static DECLCALLBACK(int) drvAudioEnable(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir, bool fEnable)
2614{
2615 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2616
2617 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2618
2619 int rc = RTCritSectEnter(&pThis->CritSect);
2620 if (RT_FAILURE(rc))
2621 return rc;
2622
2623 bool *pfEnabled;
2624 if (enmDir == PDMAUDIODIR_IN)
2625 pfEnabled = &pThis->In.fEnabled;
2626 else if (enmDir == PDMAUDIODIR_OUT)
2627 pfEnabled = &pThis->Out.fEnabled;
2628 else
2629 AssertFailedReturn(VERR_INVALID_PARAMETER);
2630
2631 if (fEnable != *pfEnabled)
2632 {
2633 LogRel(("Audio: %s %s\n",
2634 fEnable ? "Enabling " : "Disabling ", enmDir == PDMAUDIODIR_IN ? "input" : "output"));
2635
2636 PPDMAUDIOSTREAM pStream;
2637 RTListForEach(&pThis->lstStreams, pStream, PDMAUDIOSTREAM, Node)
2638 {
2639 if (pStream->enmDir != enmDir) /* Skip unwanted streams. */
2640 continue;
2641
2642 int rc2 = drvAudioStreamControlInternal(pThis, pStream,
2643 fEnable ? PDMAUDIOSTREAMCMD_ENABLE : PDMAUDIOSTREAMCMD_DISABLE);
2644 if (RT_FAILURE(rc2))
2645 LogRel(("Audio: Failed to %s %s stream '%s', rc=%Rrc\n",
2646 fEnable ? "enable" : "disable", enmDir == PDMAUDIODIR_IN ? "input" : "output", pStream->szName, rc2));
2647
2648 if (RT_SUCCESS(rc))
2649 rc = rc2;
2650
2651 /* Keep going. */
2652 }
2653
2654 *pfEnabled = fEnable;
2655 }
2656
2657 int rc3 = RTCritSectLeave(&pThis->CritSect);
2658 if (RT_SUCCESS(rc))
2659 rc = rc3;
2660
2661 LogFlowFuncLeaveRC(rc);
2662 return rc;
2663}
2664
2665/**
2666 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnIsEnabled}
2667 */
2668static DECLCALLBACK(bool) drvAudioIsEnabled(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
2669{
2670 AssertPtrReturn(pInterface, false);
2671
2672 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2673
2674 int rc2 = RTCritSectEnter(&pThis->CritSect);
2675 if (RT_FAILURE(rc2))
2676 return false;
2677
2678 bool *pfEnabled;
2679 if (enmDir == PDMAUDIODIR_IN)
2680 pfEnabled = &pThis->In.fEnabled;
2681 else if (enmDir == PDMAUDIODIR_OUT)
2682 pfEnabled = &pThis->Out.fEnabled;
2683 else
2684 AssertFailedReturn(false);
2685
2686 const bool fIsEnabled = *pfEnabled;
2687
2688 rc2 = RTCritSectLeave(&pThis->CritSect);
2689 AssertRC(rc2);
2690
2691 return fIsEnabled;
2692}
2693
2694/**
2695 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetConfig}
2696 */
2697static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
2698{
2699 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2700 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2701
2702 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2703
2704 int rc = RTCritSectEnter(&pThis->CritSect);
2705 if (RT_FAILURE(rc))
2706 return rc;
2707
2708 if (pThis->pHostDrvAudio)
2709 {
2710 if (pThis->pHostDrvAudio->pfnGetConfig)
2711 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, pCfg);
2712 else
2713 rc = VERR_NOT_SUPPORTED;
2714 }
2715 else
2716 AssertFailed();
2717
2718 int rc2 = RTCritSectLeave(&pThis->CritSect);
2719 if (RT_SUCCESS(rc))
2720 rc = rc2;
2721
2722 LogFlowFuncLeaveRC(rc);
2723 return rc;
2724}
2725
2726/**
2727 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetStatus}
2728 */
2729static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioGetStatus(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
2730{
2731 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
2732
2733 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2734
2735 PDMAUDIOBACKENDSTS backendSts = PDMAUDIOBACKENDSTS_UNKNOWN;
2736
2737 int rc = RTCritSectEnter(&pThis->CritSect);
2738 if (RT_SUCCESS(rc))
2739 {
2740 if (pThis->pHostDrvAudio)
2741 {
2742 if (pThis->pHostDrvAudio->pfnGetStatus)
2743 backendSts = pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, enmDir);
2744 }
2745 else
2746 backendSts = PDMAUDIOBACKENDSTS_NOT_ATTACHED;
2747
2748 int rc2 = RTCritSectLeave(&pThis->CritSect);
2749 if (RT_SUCCESS(rc))
2750 rc = rc2;
2751 }
2752
2753 LogFlowFuncLeaveRC(rc);
2754 return backendSts;
2755}
2756
2757/**
2758 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetReadable}
2759 */
2760static DECLCALLBACK(uint32_t) drvAudioStreamGetReadable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2761{
2762 AssertPtrReturn(pInterface, 0);
2763 AssertPtrReturn(pStream, 0);
2764
2765 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2766
2767 int rc2 = RTCritSectEnter(&pThis->CritSect);
2768 AssertRC(rc2);
2769
2770 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN, ("Can't read from a non-input stream\n"));
2771
2772 const uint32_t cfReadable = AudioMixBufLive(&pStream->Guest.MixBuf);
2773
2774 Log3Func(("[%s] cfReadable=%RU32 (%zu bytes)\n", pStream->szName, cfReadable,
2775 AUDIOMIXBUF_F2B(&pStream->Guest.MixBuf, cfReadable)));
2776
2777 uint32_t cbReadable = AUDIOMIXBUF_F2B(&pStream->Guest.MixBuf, cfReadable);
2778
2779 rc2 = RTCritSectLeave(&pThis->CritSect);
2780 AssertRC(rc2);
2781
2782 /* Return bytes instead of audio frames. */
2783 return cbReadable;
2784}
2785
2786/**
2787 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetWritable}
2788 */
2789static DECLCALLBACK(uint32_t) drvAudioStreamGetWritable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2790{
2791 AssertPtrReturn(pInterface, 0);
2792 AssertPtrReturn(pStream, 0);
2793
2794 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2795
2796 int rc2 = RTCritSectEnter(&pThis->CritSect);
2797 AssertRC(rc2);
2798
2799 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT, ("Can't write to a non-output stream\n"));
2800
2801 /* As the host side sets the overall pace, return the writable bytes from that side. */
2802 const uint64_t deltaLastReadWriteNs = RTTimeNanoTS() - pStream->tsLastReadWrittenNs;
2803
2804 uint32_t cbWritable = 0;
2805
2806 if (DrvAudioHlpStreamStatusCanWrite(pStream->fStatus))
2807 {
2808 cbWritable = DrvAudioHlpNanoToBytes(deltaLastReadWriteNs, &pStream->Host.Cfg.Props);
2809
2810 /* Make sure to align the writable size to the guest's frame size. */
2811 cbWritable = DrvAudioHlpBytesAlign(cbWritable, &pStream->Guest.Cfg.Props);
2812 }
2813
2814 Log3Func(("[%s] cbWritable=%RU32 (%RU64ms)\n", pStream->szName, cbWritable, deltaLastReadWriteNs / RT_NS_1MS_64));
2815
2816 rc2 = RTCritSectLeave(&pThis->CritSect);
2817 AssertRC(rc2);
2818
2819 return cbWritable;
2820}
2821
2822/**
2823 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetStatus}
2824 */
2825static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvAudioStreamGetStatus(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2826{
2827 AssertPtrReturn(pInterface, false);
2828
2829 if (!pStream)
2830 return PDMAUDIOSTREAMSTS_FLAG_NONE;
2831
2832 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2833
2834 int rc2 = RTCritSectEnter(&pThis->CritSect);
2835 AssertRC(rc2);
2836
2837 /* If the connector is ready to operate, also make sure to ask the backend. */
2838 PDMAUDIOSTREAMSTS stsStream = pStream->fStatus;
2839 if (DrvAudioHlpStreamStatusIsReady(stsStream))
2840 stsStream = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pStream->pvBackend);
2841
2842#ifdef LOG_ENABLED
2843 char *pszStreamSts = dbgAudioStreamStatusToStr(stsStream);
2844 Log3Func(("[%s] %s\n", pStream->szName, pszStreamSts));
2845 RTStrFree(pszStreamSts);
2846#endif
2847
2848 rc2 = RTCritSectLeave(&pThis->CritSect);
2849 AssertRC(rc2);
2850
2851 return stsStream;
2852}
2853
2854/**
2855 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamSetVolume}
2856 */
2857static DECLCALLBACK(int) drvAudioStreamSetVolume(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, PPDMAUDIOVOLUME pVol)
2858{
2859 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2860 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2861 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
2862
2863 LogFlowFunc(("[%s] volL=%RU32, volR=%RU32, fMute=%RTbool\n", pStream->szName, pVol->uLeft, pVol->uRight, pVol->fMuted));
2864
2865 AudioMixBufSetVolume(&pStream->Guest.MixBuf, pVol);
2866 AudioMixBufSetVolume(&pStream->Host.MixBuf, pVol);
2867
2868 return VINF_SUCCESS;
2869}
2870
2871/**
2872 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamDestroy}
2873 */
2874static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2875{
2876 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2877 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2878
2879 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2880
2881 int rc = RTCritSectEnter(&pThis->CritSect);
2882 AssertRC(rc);
2883
2884 LogRel2(("Audio: Destroying stream '%s'\n", pStream->szName));
2885
2886 LogFlowFunc(("[%s] cRefs=%RU32\n", pStream->szName, pStream->cRefs));
2887 if (pStream->cRefs > 1)
2888 rc = VERR_WRONG_ORDER;
2889
2890 if (RT_SUCCESS(rc))
2891 {
2892 rc = drvAudioStreamUninitInternal(pThis, pStream);
2893 if (RT_FAILURE(rc))
2894 LogRel(("Audio: Uninitializing stream '%s' failed with %Rrc\n", pStream->szName, rc));
2895 }
2896
2897 if (RT_SUCCESS(rc))
2898 {
2899 if (pStream->enmDir == PDMAUDIODIR_IN)
2900 {
2901 pThis->In.cStreamsFree++;
2902 }
2903 else /* Out */
2904 {
2905 pThis->Out.cStreamsFree++;
2906 }
2907
2908 RTListNodeRemove(&pStream->Node);
2909
2910 drvAudioStreamFree(pStream);
2911 pStream = NULL;
2912 }
2913
2914 int rc2 = RTCritSectLeave(&pThis->CritSect);
2915 if (RT_SUCCESS(rc))
2916 rc = rc2;
2917
2918 LogFlowFuncLeaveRC(rc);
2919 return rc;
2920}
2921
2922/**
2923 * Creates an audio stream on the backend side.
2924 *
2925 * @returns IPRT status code.
2926 * @param pThis Pointer to driver instance.
2927 * @param pStream Audio stream to create the backend side for.
2928 * @param pCfgReq Requested audio stream configuration to use for stream creation.
2929 * @param pCfgAcq Acquired audio stream configuration returned by the backend.
2930 */
2931static int drvAudioStreamCreateInternalBackend(PDRVAUDIO pThis,
2932 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
2933{
2934 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2935 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2936 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
2937 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
2938
2939 AssertMsg((pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED) == 0,
2940 ("Stream '%s' already initialized in backend\n", pStream->szName));
2941
2942 /* Get the right configuration for the stream to be created. */
2943 PDRVAUDIOCFG pDrvCfg = pCfgReq->enmDir == PDMAUDIODIR_IN ? &pThis->In.Cfg : &pThis->Out.Cfg;
2944
2945 /* Fill in the tweakable parameters into the requested host configuration.
2946 * All parameters in principle can be changed and returned by the backend via the acquired configuration. */
2947
2948 /*
2949 * Period size
2950 */
2951 if (pDrvCfg->uPeriodMs)
2952 pCfgReq->Backend.cfPeriod = DrvAudioHlpMilliToFrames(pDrvCfg->uPeriodMs, &pCfgReq->Props);
2953 else /* Set default period size. */
2954 pCfgReq->Backend.cfPeriod = DrvAudioHlpMilliToFrames(200 /* ms */, &pCfgReq->Props);
2955
2956 LogRel2(("Audio: Using %s period size (%RU64ms, %RU32 frames) for stream '%s'\n",
2957 pDrvCfg->uPeriodMs ? "custom" : "default", DrvAudioHlpFramesToMilli(pCfgReq->Backend.cfPeriod, &pCfgReq->Props),
2958 pCfgReq->Backend.cfPeriod, pStream->szName));
2959
2960 /*
2961 * Buffer size
2962 */
2963 if (pDrvCfg->uBufferSizeMs)
2964 pCfgReq->Backend.cfBufferSize = DrvAudioHlpMilliToFrames(pDrvCfg->uBufferSizeMs, &pCfgReq->Props);
2965 else /* Set default buffer size. */
2966 pCfgReq->Backend.cfBufferSize = DrvAudioHlpMilliToFrames(400 /* ms */, &pCfgReq->Props);
2967
2968 LogRel2(("Audio: Using %s buffer size (%RU64ms, %RU32 frames) for stream '%s'\n",
2969 pDrvCfg->uBufferSizeMs ? "custom" : "default", DrvAudioHlpFramesToMilli(pCfgReq->Backend.cfBufferSize, &pCfgReq->Props),
2970 pCfgReq->Backend.cfBufferSize, pStream->szName));
2971
2972 /*
2973 * Pre-buffering size
2974 */
2975 if (pDrvCfg->uPreBufMs != UINT32_MAX)
2976 {
2977 if (!pDrvCfg->uPreBufMs) /* Pre-buffering is set to disabled. */
2978 LogRel2(("Audio: Using custom pre-buffering (disabled) for stream '%s'\n", pStream->szName));
2979 pCfgReq->Backend.cfPreBuf = DrvAudioHlpMilliToFrames(pDrvCfg->uPreBufMs, &pCfgReq->Props);
2980 }
2981 else /* Set default pre-buffering size. */
2982 pCfgReq->Backend.cfPreBuf = pCfgReq->Backend.cfBufferSize;
2983
2984 LogRel2(("Audio: Using %s pre-buffering size (%RU64ms, %RU32 frames) for stream '%s'\n",
2985 pDrvCfg->uPreBufMs != UINT32_MAX ? "custom" : "default", DrvAudioHlpFramesToMilli(pCfgReq->Backend.cfPreBuf, &pCfgReq->Props),
2986 pCfgReq->Backend.cfPreBuf, pStream->szName));
2987
2988 /*
2989 * Validate input.
2990 */
2991 if (pCfgReq->Backend.cfBufferSize < pCfgReq->Backend.cfPeriod)
2992 {
2993 LogRel(("Audio: Error for stream '%s': Buffer size (%RU64ms) must not be smaller than the period size (%RU64ms)\n",
2994 pStream->szName, DrvAudioHlpFramesToMilli(pCfgReq->Backend.cfBufferSize, &pCfgReq->Props),
2995 DrvAudioHlpFramesToMilli(pCfgReq->Backend.cfPeriod, &pCfgReq->Props)));
2996 return VERR_INVALID_PARAMETER;
2997 }
2998
2999 if ( pCfgReq->Backend.cfPreBuf != UINT32_MAX /* Custom pre-buffering set? */
3000 && pCfgReq->Backend.cfPreBuf)
3001 {
3002 if (pCfgReq->Backend.cfBufferSize < pCfgReq->Backend.cfPreBuf)
3003 {
3004 LogRel(("Audio: Error for stream '%s': Pre-buffering size (%RU64ms) must not be bigger than the buffer size (%RU64ms)\n",
3005 pStream->szName, DrvAudioHlpFramesToMilli(pCfgReq->Backend.cfPreBuf, &pCfgReq->Props),
3006 DrvAudioHlpFramesToMilli(pCfgReq->Backend.cfBufferSize, &pCfgReq->Props)));
3007 return VERR_INVALID_PARAMETER;
3008 }
3009 }
3010
3011 /* Make the acquired host configuration the requested host configuration initially,
3012 * in case the backend does not report back an acquired configuration. */
3013 int rc = DrvAudioHlpStreamCfgCopy(pCfgAcq, pCfgReq);
3014 if (RT_FAILURE(rc))
3015 {
3016 LogRel(("Audio: Creating stream '%s' with an invalid backend configuration not possible, skipping\n",
3017 pStream->szName));
3018 return rc;
3019 }
3020
3021 rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pStream->pvBackend, pCfgReq, pCfgAcq);
3022 if (RT_FAILURE(rc))
3023 {
3024 if (rc == VERR_NOT_SUPPORTED)
3025 LogRel2(("Audio: Creating stream '%s' in backend not supported, skipping\n", pStream->szName));
3026 else
3027 LogRel(("Audio: Creating stream '%s' in backend failed with %Rrc\n", pStream->szName, rc));
3028
3029 return rc;
3030 }
3031
3032 /* Validate acquired configuration. */
3033 if (!DrvAudioHlpStreamCfgIsValid(pCfgAcq))
3034 {
3035 LogRel(("Audio: Creating stream '%s' returned an invalid backend configuration, skipping\n", pStream->szName));
3036 return VERR_INVALID_PARAMETER;
3037 }
3038
3039 /* Let the user know that the backend changed one of the user's custom values requested above. */
3040 if ( pDrvCfg->uBufferSizeMs
3041 && pCfgAcq->Backend.cfBufferSize != pCfgReq->Backend.cfBufferSize)
3042 {
3043 LogRel2(("Audio: Custom buffer size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
3044 pStream->szName, DrvAudioHlpFramesToMilli(pCfgAcq->Backend.cfBufferSize, &pCfgAcq->Props), pCfgAcq->Backend.cfBufferSize));
3045 }
3046
3047 if ( pDrvCfg->uPeriodMs
3048 && pCfgAcq->Backend.cfPeriod != pCfgReq->Backend.cfPeriod)
3049 {
3050 LogRel2(("Audio: Custom period size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
3051 pStream->szName, DrvAudioHlpFramesToMilli(pCfgAcq->Backend.cfPeriod, &pCfgAcq->Props), pCfgAcq->Backend.cfPeriod));
3052 }
3053
3054 if ( pDrvCfg->uPreBufMs != UINT32_MAX
3055 && pCfgAcq->Backend.cfPreBuf != pCfgReq->Backend.cfPreBuf)
3056 {
3057 LogRel2(("Audio: Custom pre-buffering size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
3058 pStream->szName, DrvAudioHlpFramesToMilli(pCfgAcq->Backend.cfPreBuf, &pCfgAcq->Props), pCfgAcq->Backend.cfPreBuf));
3059 }
3060
3061 pStream->fStatus |= PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
3062
3063 return VINF_SUCCESS;
3064}
3065
3066/**
3067 * Calls the backend to give it the chance to destroy its part of the audio stream.
3068 *
3069 * @returns IPRT status code.
3070 * @param pThis Pointer to driver instance.
3071 * @param pStream Audio stream destruct backend for.
3072 */
3073static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
3074{
3075 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
3076 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
3077
3078 int rc = VINF_SUCCESS;
3079
3080#ifdef LOG_ENABLED
3081 char *pszStreamSts = dbgAudioStreamStatusToStr(pStream->fStatus);
3082 LogFunc(("[%s] fStatus=%s\n", pStream->szName, pszStreamSts));
3083#endif
3084
3085 if (pStream->pvBackend)
3086 {
3087 /* Check if the pointer to the host audio driver is still valid.
3088 * It can be NULL if we were called in drvAudioDestruct, for example. */
3089 if (pThis->pHostDrvAudio)
3090 rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pStream->pvBackend);
3091
3092 pStream->pvBackend = NULL;
3093 pStream->cbBackend = 0;
3094 }
3095
3096#ifdef LOG_ENABLED
3097 RTStrFree(pszStreamSts);
3098#endif
3099
3100 LogFlowFunc(("[%s] Returning %Rrc\n", pStream->szName, rc));
3101 return rc;
3102}
3103
3104/**
3105 * Uninitializes an audio stream.
3106 *
3107 * @returns IPRT status code.
3108 * @param pThis Pointer to driver instance.
3109 * @param pStream Pointer to audio stream to uninitialize.
3110 */
3111static int drvAudioStreamUninitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
3112{
3113 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
3114 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
3115
3116 LogFlowFunc(("[%s] cRefs=%RU32\n", pStream->szName, pStream->cRefs));
3117
3118 if (pStream->cRefs > 1)
3119 return VERR_WRONG_ORDER;
3120
3121 int rc = drvAudioStreamControlInternal(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
3122 if (RT_SUCCESS(rc))
3123 rc = drvAudioStreamDestroyInternalBackend(pThis, pStream);
3124
3125 /* Destroy mixing buffers. */
3126 AudioMixBufDestroy(&pStream->Guest.MixBuf);
3127 AudioMixBufDestroy(&pStream->Host.MixBuf);
3128
3129 if (RT_SUCCESS(rc))
3130 {
3131 /* Reset status. */
3132 pStream->fStatus &= ~PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
3133
3134#ifdef VBOX_STRICT
3135 if (pStream->fStatus != PDMAUDIOSTREAMSTS_FLAG_NONE)
3136 {
3137 char *pszStreamSts = dbgAudioStreamStatusToStr(pStream->fStatus);
3138 LogFunc(("[%s] Warning: Still has %s set when uninitializing\n", pStream->szName, pszStreamSts));
3139 RTStrFree(pszStreamSts);
3140 }
3141#endif
3142 pStream->fStatus = PDMAUDIOSTREAMSTS_FLAG_NONE;
3143 }
3144
3145 if (pStream->enmDir == PDMAUDIODIR_IN)
3146 {
3147#ifdef VBOX_WITH_STATISTICS
3148 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->In.Stats.BytesElapsed);
3149 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->In.Stats.BytesTotalRead);
3150 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->In.Stats.FramesCaptured);
3151#endif
3152 if (pThis->In.Cfg.Dbg.fEnabled)
3153 {
3154 DrvAudioHlpFileDestroy(pStream->In.Dbg.pFileCaptureNonInterleaved);
3155 pStream->In.Dbg.pFileCaptureNonInterleaved = NULL;
3156
3157 DrvAudioHlpFileDestroy(pStream->In.Dbg.pFileStreamRead);
3158 pStream->In.Dbg.pFileStreamRead = NULL;
3159 }
3160 }
3161 else if (pStream->enmDir == PDMAUDIODIR_OUT)
3162 {
3163#ifdef VBOX_WITH_STATISTICS
3164 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->Out.Stats.BytesElapsed);
3165 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->Out.Stats.BytesTotalWritten);
3166 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->Out.Stats.FramesPlayed);
3167#endif
3168 if (pThis->Out.Cfg.Dbg.fEnabled)
3169 {
3170 DrvAudioHlpFileDestroy(pStream->Out.Dbg.pFilePlayNonInterleaved);
3171 pStream->Out.Dbg.pFilePlayNonInterleaved = NULL;
3172
3173 DrvAudioHlpFileDestroy(pStream->Out.Dbg.pFileStreamWrite);
3174 pStream->Out.Dbg.pFileStreamWrite = NULL;
3175 }
3176 }
3177 else
3178 AssertFailed();
3179
3180 LogFlowFunc(("Returning %Rrc\n", rc));
3181 return rc;
3182}
3183
3184/**
3185 * Does the actual backend driver attaching and queries the backend's interface.
3186 *
3187 * @return VBox status code.
3188 * @param pThis Pointer to driver instance.
3189 * @param fFlags Attach flags; see PDMDrvHlpAttach().
3190 */
3191static int drvAudioDoAttachInternal(PDRVAUDIO pThis, uint32_t fFlags)
3192{
3193 Assert(pThis->pHostDrvAudio == NULL); /* No nested attaching. */
3194
3195 /*
3196 * Attach driver below and query its connector interface.
3197 */
3198 PPDMIBASE pDownBase;
3199 int rc = PDMDrvHlpAttach(pThis->pDrvIns, fFlags, &pDownBase);
3200 if (RT_SUCCESS(rc))
3201 {
3202 pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
3203 if (!pThis->pHostDrvAudio)
3204 {
3205 LogRel(("Audio: Failed to query interface for underlying host driver\n"));
3206 rc = PDMDRV_SET_ERROR(pThis->pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
3207 N_("Host audio backend missing or invalid"));
3208 }
3209 }
3210
3211 if (RT_SUCCESS(rc))
3212 {
3213 /*
3214 * If everything went well, initialize the lower driver.
3215 */
3216 AssertPtr(pThis->pCFGMNode);
3217 rc = drvAudioHostInit(pThis, pThis->pCFGMNode);
3218 }
3219
3220 LogFunc(("[%s] rc=%Rrc\n", pThis->szName, rc));
3221 return rc;
3222}
3223
3224
3225/********************************************************************/
3226
3227/**
3228 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
3229 */
3230static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
3231{
3232 LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
3233
3234 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
3235 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3236
3237 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
3238 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
3239
3240 return NULL;
3241}
3242
3243/**
3244 * Power Off notification.
3245 *
3246 * @param pDrvIns The driver instance data.
3247 */
3248static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
3249{
3250 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3251
3252 LogFlowFuncEnter();
3253
3254 if (!pThis->pHostDrvAudio) /* If not lower driver is configured, bail out. */
3255 return;
3256
3257 /* Just destroy the host stream on the backend side.
3258 * The rest will either be destructed by the device emulation or
3259 * in drvAudioDestruct(). */
3260 PPDMAUDIOSTREAM pStream;
3261 RTListForEach(&pThis->lstStreams, pStream, PDMAUDIOSTREAM, Node)
3262 {
3263 drvAudioStreamControlInternalBackend(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
3264 drvAudioStreamDestroyInternalBackend(pThis, pStream);
3265 }
3266
3267 /*
3268 * Last call for the driver below us.
3269 * Let it know that we reached end of life.
3270 */
3271 if (pThis->pHostDrvAudio->pfnShutdown)
3272 pThis->pHostDrvAudio->pfnShutdown(pThis->pHostDrvAudio);
3273
3274 pThis->pHostDrvAudio = NULL;
3275
3276 LogFlowFuncLeave();
3277}
3278
3279/**
3280 * Constructs an audio driver instance.
3281 *
3282 * @copydoc FNPDMDRVCONSTRUCT
3283 */
3284static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
3285{
3286 LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfg, fFlags));
3287
3288 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3289 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
3290
3291 RTListInit(&pThis->lstStreams);
3292#ifdef VBOX_WITH_AUDIO_CALLBACKS
3293 RTListInit(&pThis->In.lstCB);
3294 RTListInit(&pThis->Out.lstCB);
3295#endif
3296
3297 /*
3298 * Init the static parts.
3299 */
3300 pThis->pDrvIns = pDrvIns;
3301 /* IBase. */
3302 pDrvIns->IBase.pfnQueryInterface = drvAudioQueryInterface;
3303 /* IAudioConnector. */
3304 pThis->IAudioConnector.pfnEnable = drvAudioEnable;
3305 pThis->IAudioConnector.pfnIsEnabled = drvAudioIsEnabled;
3306 pThis->IAudioConnector.pfnGetConfig = drvAudioGetConfig;
3307 pThis->IAudioConnector.pfnGetStatus = drvAudioGetStatus;
3308 pThis->IAudioConnector.pfnStreamCreate = drvAudioStreamCreate;
3309 pThis->IAudioConnector.pfnStreamDestroy = drvAudioStreamDestroy;
3310 pThis->IAudioConnector.pfnStreamRetain = drvAudioStreamRetain;
3311 pThis->IAudioConnector.pfnStreamRelease = drvAudioStreamRelease;
3312 pThis->IAudioConnector.pfnStreamControl = drvAudioStreamControl;
3313 pThis->IAudioConnector.pfnStreamRead = drvAudioStreamRead;
3314 pThis->IAudioConnector.pfnStreamWrite = drvAudioStreamWrite;
3315 pThis->IAudioConnector.pfnStreamIterate = drvAudioStreamIterate;
3316 pThis->IAudioConnector.pfnStreamGetReadable = drvAudioStreamGetReadable;
3317 pThis->IAudioConnector.pfnStreamGetWritable = drvAudioStreamGetWritable;
3318 pThis->IAudioConnector.pfnStreamGetStatus = drvAudioStreamGetStatus;
3319 pThis->IAudioConnector.pfnStreamSetVolume = drvAudioStreamSetVolume;
3320 pThis->IAudioConnector.pfnStreamPlay = drvAudioStreamPlay;
3321 pThis->IAudioConnector.pfnStreamCapture = drvAudioStreamCapture;
3322#ifdef VBOX_WITH_AUDIO_CALLBACKS
3323 pThis->IAudioConnector.pfnRegisterCallbacks = drvAudioRegisterCallbacks;
3324#endif
3325
3326 int rc = drvAudioInit(pDrvIns, pCfg);
3327 if (RT_SUCCESS(rc))
3328 {
3329#ifdef VBOX_WITH_STATISTICS
3330 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsActive, "TotalStreamsActive",
3331 STAMUNIT_COUNT, "Total active audio streams.");
3332 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsCreated, "TotalStreamsCreated",
3333 STAMUNIT_COUNT, "Total created audio streams.");
3334 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesRead, "TotalFramesRead",
3335 STAMUNIT_COUNT, "Total frames read by device emulation.");
3336 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesWritten, "TotalFramesWritten",
3337 STAMUNIT_COUNT, "Total frames written by device emulation ");
3338 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesMixedIn, "TotalFramesMixedIn",
3339 STAMUNIT_COUNT, "Total input frames mixed.");
3340 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesMixedOut, "TotalFramesMixedOut",
3341 STAMUNIT_COUNT, "Total output frames mixed.");
3342 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesLostIn, "TotalFramesLostIn",
3343 STAMUNIT_COUNT, "Total input frames lost.");
3344 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesLostOut, "TotalFramesLostOut",
3345 STAMUNIT_COUNT, "Total output frames lost.");
3346 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesOut, "TotalFramesOut",
3347 STAMUNIT_COUNT, "Total frames played by backend.");
3348 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesIn, "TotalFramesIn",
3349 STAMUNIT_COUNT, "Total frames captured by backend.");
3350 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesRead, "TotalBytesRead",
3351 STAMUNIT_BYTES, "Total bytes read.");
3352 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesWritten, "TotalBytesWritten",
3353 STAMUNIT_BYTES, "Total bytes written.");
3354
3355 PDMDrvHlpSTAMRegProfileAdvEx(pDrvIns, &pThis->Stats.DelayIn, "DelayIn",
3356 STAMUNIT_NS_PER_CALL, "Profiling of input data processing.");
3357 PDMDrvHlpSTAMRegProfileAdvEx(pDrvIns, &pThis->Stats.DelayOut, "DelayOut",
3358 STAMUNIT_NS_PER_CALL, "Profiling of output data processing.");
3359#endif
3360 }
3361
3362 rc = drvAudioDoAttachInternal(pThis, fFlags);
3363 if (RT_FAILURE(rc))
3364 {
3365 /* No lower attached driver (yet)? Not a failure, might get attached later at runtime, just skip. */
3366 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
3367 rc = VINF_SUCCESS;
3368 }
3369
3370 LogFlowFuncLeaveRC(rc);
3371 return rc;
3372}
3373
3374/**
3375 * Destructs an audio driver instance.
3376 *
3377 * @copydoc FNPDMDRVDESTRUCT
3378 */
3379static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
3380{
3381 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
3382 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3383
3384 LogFlowFuncEnter();
3385
3386 int rc2;
3387
3388 if (RTCritSectIsInitialized(&pThis->CritSect))
3389 {
3390 rc2 = RTCritSectEnter(&pThis->CritSect);
3391 AssertRC(rc2);
3392 }
3393
3394 /*
3395 * Note: No calls here to the driver below us anymore,
3396 * as PDM already has destroyed it.
3397 * If you need to call something from the host driver,
3398 * do this in drvAudioPowerOff() instead.
3399 */
3400
3401 /* Thus, NULL the pointer to the host audio driver first,
3402 * so that routines like drvAudioStreamDestroyInternal() don't call the driver(s) below us anymore. */
3403 pThis->pHostDrvAudio = NULL;
3404
3405 PPDMAUDIOSTREAM pStream, pStreamNext;
3406 RTListForEachSafe(&pThis->lstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
3407 {
3408 rc2 = drvAudioStreamUninitInternal(pThis, pStream);
3409 if (RT_SUCCESS(rc2))
3410 {
3411 RTListNodeRemove(&pStream->Node);
3412
3413 drvAudioStreamFree(pStream);
3414 pStream = NULL;
3415 }
3416 }
3417
3418 /* Sanity. */
3419 Assert(RTListIsEmpty(&pThis->lstStreams));
3420
3421#ifdef VBOX_WITH_AUDIO_CALLBACKS
3422 /*
3423 * Destroy callbacks, if any.
3424 */
3425 PPDMAUDIOCBRECORD pCB, pCBNext;
3426 RTListForEachSafe(&pThis->In.lstCB, pCB, pCBNext, PDMAUDIOCBRECORD, Node)
3427 drvAudioCallbackDestroy(pCB);
3428
3429 RTListForEachSafe(&pThis->Out.lstCB, pCB, pCBNext, PDMAUDIOCBRECORD, Node)
3430 drvAudioCallbackDestroy(pCB);
3431#endif
3432
3433 if (RTCritSectIsInitialized(&pThis->CritSect))
3434 {
3435 rc2 = RTCritSectLeave(&pThis->CritSect);
3436 AssertRC(rc2);
3437
3438 rc2 = RTCritSectDelete(&pThis->CritSect);
3439 AssertRC(rc2);
3440 }
3441
3442#ifdef VBOX_WITH_STATISTICS
3443 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalStreamsActive);
3444 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalStreamsCreated);
3445 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesRead);
3446 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesWritten);
3447 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesMixedIn);
3448 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesMixedOut);
3449 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesLostIn);
3450 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesLostOut);
3451 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesOut);
3452 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesIn);
3453 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalBytesRead);
3454 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalBytesWritten);
3455 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.DelayIn);
3456 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.DelayOut);
3457#endif
3458
3459 LogFlowFuncLeave();
3460}
3461
3462/**
3463 * Suspend notification.
3464 *
3465 * @param pDrvIns The driver instance data.
3466 */
3467static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
3468{
3469 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_PAUSE);
3470}
3471
3472/**
3473 * Resume notification.
3474 *
3475 * @param pDrvIns The driver instance data.
3476 */
3477static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
3478{
3479 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_RESUME);
3480}
3481
3482/**
3483 * Attach notification.
3484 *
3485 * @param pDrvIns The driver instance data.
3486 * @param fFlags Attach flags.
3487 */
3488static DECLCALLBACK(int) drvAudioAttach(PPDMDRVINS pDrvIns, uint32_t fFlags)
3489{
3490 RT_NOREF(fFlags);
3491
3492 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
3493 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3494
3495 int rc2 = RTCritSectEnter(&pThis->CritSect);
3496 AssertRC(rc2);
3497
3498 LogFunc(("%s\n", pThis->szName));
3499
3500 int rc = drvAudioDoAttachInternal(pThis, fFlags);
3501
3502 rc2 = RTCritSectLeave(&pThis->CritSect);
3503 if (RT_SUCCESS(rc))
3504 rc = rc2;
3505
3506 return rc;
3507}
3508
3509/**
3510 * Detach notification.
3511 *
3512 * @param pDrvIns The driver instance data.
3513 * @param fFlags Detach flags.
3514 */
3515static DECLCALLBACK(void) drvAudioDetach(PPDMDRVINS pDrvIns, uint32_t fFlags)
3516{
3517 RT_NOREF(fFlags);
3518
3519 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
3520 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3521
3522 int rc2 = RTCritSectEnter(&pThis->CritSect);
3523 AssertRC(rc2);
3524
3525 pThis->pHostDrvAudio = NULL;
3526
3527 LogFunc(("%s\n", pThis->szName));
3528
3529 rc2 = RTCritSectLeave(&pThis->CritSect);
3530 AssertRC(rc2);
3531}
3532
3533/**
3534 * Audio driver registration record.
3535 */
3536const PDMDRVREG g_DrvAUDIO =
3537{
3538 /* u32Version */
3539 PDM_DRVREG_VERSION,
3540 /* szName */
3541 "AUDIO",
3542 /* szRCMod */
3543 "",
3544 /* szR0Mod */
3545 "",
3546 /* pszDescription */
3547 "Audio connector driver",
3548 /* fFlags */
3549 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
3550 /* fClass */
3551 PDM_DRVREG_CLASS_AUDIO,
3552 /* cMaxInstances */
3553 UINT32_MAX,
3554 /* cbInstance */
3555 sizeof(DRVAUDIO),
3556 /* pfnConstruct */
3557 drvAudioConstruct,
3558 /* pfnDestruct */
3559 drvAudioDestruct,
3560 /* pfnRelocate */
3561 NULL,
3562 /* pfnIOCtl */
3563 NULL,
3564 /* pfnPowerOn */
3565 NULL,
3566 /* pfnReset */
3567 NULL,
3568 /* pfnSuspend */
3569 drvAudioSuspend,
3570 /* pfnResume */
3571 drvAudioResume,
3572 /* pfnAttach */
3573 drvAudioAttach,
3574 /* pfnDetach */
3575 drvAudioDetach,
3576 /* pfnPowerOff */
3577 drvAudioPowerOff,
3578 /* pfnSoftReset */
3579 NULL,
3580 /* u32EndVersion */
3581 PDM_DRVREG_VERSION
3582};
3583
Note: See TracBrowser for help on using the repository browser.

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