VirtualBox

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

Last change on this file since 67446 was 67443, checked in by vboxsync, 8 years ago

Audio/DrvAudio.cpp: Docs.

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

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