VirtualBox

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

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

Audio/DrvAudio: Leave and re-enter the driver's critical section when calling drvAudioDevicesEnumerateInternal() via drvAudioStreamMaybeReInit() to avoid deadlocks with the IMMNotificationClient implementation callback. bugref:8658

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