VirtualBox

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

Last change on this file since 86494 was 86494, checked in by vboxsync, 4 years ago

Devices/Audio: Re-initialize the stream in drvAudioStreamGetStatus() when scheduled or it will never be initialized due to the audio mixer skipping everything when the returned status doesn't indicate a ready stream, ticketref:18594, bugref:8658 comment 11

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

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