VirtualBox

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

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

Audio/DrvAudio: Simplified drvAudioStreamControlInternalBackend() by not doing any state machine checking whatsoever -- state machine checking (and setting) is done in drvAudioStreamControlInternal() instead.

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

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