VirtualBox

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

Last change on this file since 63547 was 63537, checked in by vboxsync, 8 years ago

Audio/DrvAudio.cpp: Simplified drvAudioStreamInitInternal().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.1 KB
Line 
1/* $Id: DrvAudio.cpp 63537 2016-08-16 10:31:42Z 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-2016 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
43static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream);
44static int drvAudioStreamControlInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd);
45static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd);
46static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream);
47static int drvAudioStreamDestroyInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream);
48static int drvAudioStreamInitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest);
49static int drvAudioStreamIterateInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream);
50static int drvAudioStreamReInitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream);
51
52#ifndef VBOX_AUDIO_TESTCASE
53
54# if 0 /* unused */
55
56static PDMAUDIOFMT drvAudioGetConfFormat(PCFGMNODE pCfgHandle, const char *pszKey,
57 PDMAUDIOFMT enmDefault, bool *pfDefault)
58{
59 if ( pCfgHandle == NULL
60 || pszKey == NULL)
61 {
62 *pfDefault = true;
63 return enmDefault;
64 }
65
66 char *pszValue = NULL;
67 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
68 if (RT_FAILURE(rc))
69 {
70 *pfDefault = true;
71 return enmDefault;
72 }
73
74 PDMAUDIOFMT fmt = DrvAudioHlpStrToAudFmt(pszValue);
75 if (fmt == PDMAUDIOFMT_INVALID)
76 {
77 *pfDefault = true;
78 return enmDefault;
79 }
80
81 *pfDefault = false;
82 return fmt;
83}
84
85static int drvAudioGetConfInt(PCFGMNODE pCfgHandle, const char *pszKey,
86 int iDefault, bool *pfDefault)
87{
88
89 if ( pCfgHandle == NULL
90 || pszKey == NULL)
91 {
92 *pfDefault = true;
93 return iDefault;
94 }
95
96 uint64_t u64Data = 0;
97 int rc = CFGMR3QueryInteger(pCfgHandle, pszKey, &u64Data);
98 if (RT_FAILURE(rc))
99 {
100 *pfDefault = true;
101 return iDefault;
102
103 }
104
105 *pfDefault = false;
106 return u64Data;
107}
108
109static const char *drvAudioGetConfStr(PCFGMNODE pCfgHandle, const char *pszKey,
110 const char *pszDefault, bool *pfDefault)
111{
112 if ( pCfgHandle == NULL
113 || pszKey == NULL)
114 {
115 *pfDefault = true;
116 return pszDefault;
117 }
118
119 char *pszValue = NULL;
120 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
121 if (RT_FAILURE(rc))
122 {
123 *pfDefault = true;
124 return pszDefault;
125 }
126
127 *pfDefault = false;
128 return pszValue;
129}
130
131# endif /* unused */
132
133/**
134 * Returns the host stream part of an audio stream pair, or NULL
135 * if no host stream has been assigned / is not available.
136 *
137 * @returns IPRT status code.
138 * @param pStream Audio stream to retrieve host stream part for.
139 */
140DECLINLINE(PPDMAUDIOSTREAM) drvAudioGetHostStream(PPDMAUDIOSTREAM pStream)
141{
142 AssertPtrReturn(pStream, NULL);
143
144 if (!pStream)
145 return NULL;
146
147 PPDMAUDIOSTREAM pHstStream = pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST
148 ? pStream
149 : pStream->pPair;
150 if (pHstStream)
151 {
152 Assert(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
153 }
154 else
155 LogFlowFunc(("%s: Warning: Does not have a host stream (anymore)\n", pStream->szName));
156
157 return pHstStream;
158}
159
160# if 0 /* unused */
161static int drvAudioProcessOptions(PCFGMNODE pCfgHandle, const char *pszPrefix, audio_option *paOpts, size_t cOpts)
162{
163 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
164 AssertPtrReturn(pszPrefix, VERR_INVALID_POINTER);
165 /* oaOpts and cOpts are optional. */
166
167 PCFGMNODE pCfgChildHandle = NULL;
168 PCFGMNODE pCfgChildChildHandle = NULL;
169
170 /* If pCfgHandle is NULL, let NULL be passed to get int and get string functions..
171 * The getter function will return default values.
172 */
173 if (pCfgHandle != NULL)
174 {
175 /* If its audio general setting, need to traverse to one child node.
176 * /Devices/ichac97/0/LUN#0/Config/Audio
177 */
178 if(!strncmp(pszPrefix, "AUDIO", 5)) /** @todo Use a \#define */
179 {
180 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
181 if(pCfgChildHandle)
182 pCfgHandle = pCfgChildHandle;
183 }
184 else
185 {
186 /* If its driver specific configuration , then need to traverse two level deep child
187 * child nodes. for eg. in case of DirectSoundConfiguration item
188 * /Devices/ichac97/0/LUN#0/Config/Audio/DirectSoundConfig
189 */
190 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
191 if (pCfgChildHandle)
192 {
193 pCfgChildChildHandle = CFGMR3GetFirstChild(pCfgChildHandle);
194 if (pCfgChildChildHandle)
195 pCfgHandle = pCfgChildChildHandle;
196 }
197 }
198 }
199
200 for (size_t i = 0; i < cOpts; i++)
201 {
202 audio_option *pOpt = &paOpts[i];
203 if (!pOpt->valp)
204 {
205 LogFlowFunc(("Option value pointer for `%s' is not set\n", pOpt->name));
206 continue;
207 }
208
209 bool fUseDefault;
210
211 switch (pOpt->tag)
212 {
213 case AUD_OPT_BOOL:
214 case AUD_OPT_INT:
215 {
216 int *intp = (int *)pOpt->valp;
217 *intp = drvAudioGetConfInt(pCfgHandle, pOpt->name, *intp, &fUseDefault);
218
219 break;
220 }
221
222 case AUD_OPT_FMT:
223 {
224 PDMAUDIOFMT *fmtp = (PDMAUDIOFMT *)pOpt->valp;
225 *fmtp = drvAudioGetConfFormat(pCfgHandle, pOpt->name, *fmtp, &fUseDefault);
226
227 break;
228 }
229
230 case AUD_OPT_STR:
231 {
232 const char **strp = (const char **)pOpt->valp;
233 *strp = drvAudioGetConfStr(pCfgHandle, pOpt->name, *strp, &fUseDefault);
234
235 break;
236 }
237
238 default:
239 LogFlowFunc(("Bad value tag for option `%s' - %d\n", pOpt->name, pOpt->tag));
240 fUseDefault = false;
241 break;
242 }
243
244 if (!pOpt->overridenp)
245 pOpt->overridenp = &pOpt->overriden;
246
247 *pOpt->overridenp = !fUseDefault;
248 }
249
250 return VINF_SUCCESS;
251}
252# endif /* unused */
253#endif /* !VBOX_AUDIO_TESTCASE */
254
255/**
256 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamControl}
257 */
258static DECLCALLBACK(int) drvAudioStreamControl(PPDMIAUDIOCONNECTOR pInterface,
259 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
260{
261 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
262
263 if (!pStream)
264 return VINF_SUCCESS;
265
266 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
267
268 int rc = RTCritSectEnter(&pThis->CritSect);
269 if (RT_FAILURE(rc))
270 return rc;
271
272 LogFlowFunc(("[%s] enmStreamCmd=%RU32\n", pStream->szName, enmStreamCmd));
273
274 rc = drvAudioStreamControlInternal(pThis, pStream, enmStreamCmd);
275
276 int rc2 = RTCritSectLeave(&pThis->CritSect);
277 if (RT_SUCCESS(rc))
278 rc = rc2;
279
280 return rc;
281}
282
283/**
284 * Controls an audio stream.
285 *
286 * @returns IPRT status code.
287 * @param pThis Pointer to driver instance.
288 * @param pStream Stream to control.
289 * @param enmStreamCmd Control command.
290 */
291static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
292{
293 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
294 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
295
296 LogFunc(("[%s] enmStreamCmd=%RU32\n", pStream->szName, enmStreamCmd));
297
298 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
299 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
300 AssertPtr(pGstStream);
301
302 LogFlowFunc(("Status host=0x%x, guest=0x%x\n", pHstStream->fStatus, pGstStream->fStatus));
303
304 int rc = VINF_SUCCESS;
305
306 switch (enmStreamCmd)
307 {
308 case PDMAUDIOSTREAMCMD_ENABLE:
309 {
310 if (!(pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
311 {
312 if (pHstStream)
313 {
314 /* Is a pending disable outstanding? Then disable first. */
315 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
316 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
317
318 if (RT_SUCCESS(rc))
319 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_ENABLE);
320 }
321
322 pGstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_ENABLED;
323 }
324 break;
325 }
326
327 case PDMAUDIOSTREAMCMD_DISABLE:
328 case PDMAUDIOSTREAMCMD_PAUSE:
329 {
330 if (pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
331 {
332 /* Is the guest side stream still active?
333 * Mark the host stream as pending disable and bail out. */
334 if (pHstStream)
335 {
336 LogFunc(("[%s] Pending disable/pause\n", pHstStream->szName));
337 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
338 }
339
340 if (enmStreamCmd == PDMAUDIOSTREAMCMD_DISABLE)
341 {
342 pGstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_ENABLED;
343 }
344 else if (enmStreamCmd == PDMAUDIOSTREAMCMD_PAUSE)
345 pGstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PAUSED;
346 else
347 AssertFailedBreakStmt(rc = VERR_NOT_IMPLEMENTED);
348 }
349
350 if ( pHstStream
351 && !(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE))
352 {
353 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, enmStreamCmd);
354 if (RT_SUCCESS(rc))
355 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
356 }
357 break;
358 }
359
360 case PDMAUDIOSTREAMCMD_RESUME:
361 {
362 if (pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED)
363 {
364 if (pHstStream)
365 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_RESUME);
366
367 pGstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PAUSED;
368 }
369 break;
370 }
371
372 default:
373 AssertMsgFailed(("Command %RU32 not implemented\n", enmStreamCmd));
374 rc = VERR_NOT_IMPLEMENTED;
375 break;
376 }
377
378 if (RT_FAILURE(rc))
379 LogFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
380
381 return rc;
382}
383
384/**
385 * Controls a stream's backend.
386 * If the stream has no backend available, VERR_NOT_FOUND is returned.
387 *
388 * @returns IPRT status code.
389 * @param pThis Pointer to driver instance.
390 * @param pStream Stream to control.
391 * @param enmStreamCmd Control command.
392 */
393static int drvAudioStreamControlInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
394{
395 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
396 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
397
398 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
399 if (!pHstStream) /* Stream does not have a host backend? Bail out. */
400 return VERR_NOT_FOUND;
401
402 LogFlowFunc(("[%s] enmStreamCmd=%RU32, fStatus=0x%x\n", pHstStream->szName, enmStreamCmd, pHstStream->fStatus));
403
404 AssertPtr(pThis->pHostDrvAudio);
405
406 int rc = VINF_SUCCESS;
407
408 switch (enmStreamCmd)
409 {
410 case PDMAUDIOSTREAMCMD_ENABLE:
411 {
412 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
413 {
414 LogRel2(("Audio: Enabling stream '%s'\n", pHstStream->szName));
415 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_ENABLE);
416 if (RT_SUCCESS(rc))
417 {
418 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_ENABLED;
419 }
420 else
421 LogRel2(("Audio: Disabling stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
422 }
423 break;
424 }
425
426 case PDMAUDIOSTREAMCMD_DISABLE:
427 {
428 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
429 {
430 LogRel2(("Audio: Disabling stream '%s'\n", pHstStream->szName));
431 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
432 if (RT_SUCCESS(rc))
433 {
434 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_ENABLED;
435 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
436 AudioMixBufReset(&pHstStream->MixBuf);
437 }
438 else
439 LogRel2(("Audio: Disabling stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
440 }
441 break;
442 }
443
444 case PDMAUDIOSTREAMCMD_PAUSE:
445 {
446 /* Only pause if the stream is enabled. */
447 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
448 break;
449
450 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED))
451 {
452 LogRel2(("Audio: Pausing stream '%s'\n", pHstStream->szName));
453 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_PAUSE);
454 if (RT_SUCCESS(rc))
455 {
456 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PAUSED;
457 }
458 else
459 LogRel2(("Audio: Pausing stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
460 }
461 break;
462 }
463
464 case PDMAUDIOSTREAMCMD_RESUME:
465 {
466 /* Only need to resume if the stream is enabled. */
467 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
468 break;
469
470 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED)
471 {
472 LogRel2(("Audio: Resuming stream '%s'\n", pHstStream->szName));
473 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_RESUME);
474 if (RT_SUCCESS(rc))
475 {
476 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PAUSED;
477 }
478 else
479 LogRel2(("Audio: Resuming stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
480 }
481 break;
482 }
483
484 default:
485 {
486 AssertMsgFailed(("Command %RU32 not implemented\n", enmStreamCmd));
487 rc = VERR_NOT_IMPLEMENTED;
488 break;
489 }
490 }
491
492 if (RT_FAILURE(rc))
493 LogFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
494
495 return rc;
496}
497
498/**
499 * Initializes an audio stream with a given host and guest stream configuration.
500 *
501 * @returns IPRT status code.
502 * @param pThis Pointer to driver instance.
503 * @param pStream Stream to initialize.
504 * @param pCfgHost Stream configuration to use for the host side (backend).
505 * @param pCfgGuest Stream configuration to use for the guest side.
506 */
507static int drvAudioStreamInitInternal(PDRVAUDIO pThis,
508 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest)
509{
510 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
511 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
512
513 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
514 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
515 AssertPtr(pGstStream);
516
517 /*
518 * Init host stream.
519 */
520
521 /* Make the acquired host configuration the requested host configuration initially,
522 * in case the backend does not report back an acquired configuration. */
523 PDMAUDIOSTREAMCFG CfgHostAcq;
524 memcpy(&CfgHostAcq, pCfgHost, sizeof(PDMAUDIOSTREAMCFG));
525
526#ifdef DEBUG
527 LogFunc(("[%s] Requested host format:\n", pStream->szName));
528 DrvAudioHlpStreamCfgPrint(pCfgHost);
529#else
530 LogRel2(("Audio: Requested %s host format for '%s': %RU32Hz, %s, %RU8 %s\n",
531 pCfgGuest->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStream->szName,
532 pCfgHost->uHz, DrvAudioHlpAudFmtToStr(pCfgHost->enmFormat),
533 pCfgHost->cChannels, pCfgHost->cChannels == 0 ? "Channel" : "Channels"));
534#endif
535
536 int rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pHstStream,
537 pCfgHost /* pCfgReq */, &CfgHostAcq /* pCfgAcq */);
538 if (RT_FAILURE(rc))
539 {
540 LogFlowFunc(("[%s] Initializing stream in host backend failed with rc=%Rrc\n", pStream->szName, rc));
541 return rc;
542 }
543
544 /* Only set the host's stream to initialized if we were able create the stream
545 * in the host backend. This is necessary for trying to re-initialize the stream
546 * at some later point in time. */
547 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
548
549#ifdef DEBUG
550 LogFunc(("[%s] Acquired host format:\n", pStream->szName));
551 DrvAudioHlpStreamCfgPrint(&CfgHostAcq);
552#else
553 LogRel2(("Audio: Acquired %s host format for '%s': %RU32Hz, %s, %RU8 %s\n",
554 pCfgGuest->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStream->szName,
555 CfgHostAcq.uHz, DrvAudioHlpAudFmtToStr(CfgHostAcq.enmFormat),
556 CfgHostAcq.cChannels, CfgHostAcq.cChannels == 0 ? "Channel" : "Channels"));
557#endif
558
559 /* No sample buffer size hint given by the backend? Default to some sane value. */
560 if (!CfgHostAcq.cSampleBufferSize)
561 CfgHostAcq.cSampleBufferSize = _1K; /** @todo Make this configurable? */
562
563 PDMAUDIOPCMPROPS PCMProps;
564 int rc2 = DrvAudioHlpStreamCfgToProps(&CfgHostAcq, &PCMProps);
565 AssertRC(rc2);
566
567 /* Destroy any former mixing buffer. */
568 AudioMixBufDestroy(&pHstStream->MixBuf);
569
570 LogFlowFunc(("[%s] cSamples=%RU32\n", pHstStream->szName, CfgHostAcq.cSampleBufferSize * 4));
571
572 rc2 = AudioMixBufInit(&pHstStream->MixBuf, pHstStream->szName, &PCMProps, CfgHostAcq.cSampleBufferSize * 4);
573 AssertRC(rc2);
574
575 /* Make a copy of the host stream configuration. */
576 memcpy(&pHstStream->Cfg, pCfgHost, sizeof(PDMAUDIOSTREAMCFG));
577
578 /*
579 * Init guest stream.
580 */
581
582 RT_ZERO(PCMProps);
583 rc2 = DrvAudioHlpStreamCfgToProps(pCfgGuest, &PCMProps);
584 AssertRC(rc2);
585
586 /* Destroy any former mixing buffer. */
587 AudioMixBufDestroy(&pGstStream->MixBuf);
588
589 LogFlowFunc(("[%s] cSamples=%RU32\n", pGstStream->szName, CfgHostAcq.cSampleBufferSize * 2));
590
591 rc2 = AudioMixBufInit(&pGstStream->MixBuf, pGstStream->szName, &PCMProps, CfgHostAcq.cSampleBufferSize * 2);
592 AssertRC(rc2);
593
594#ifdef VBOX_WITH_STATISTICS
595 char szStatName[255];
596#endif
597
598 if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
599 {
600 /* Host (Parent) -> Guest (Child). */
601 rc2 = AudioMixBufLinkTo(&pHstStream->MixBuf, &pGstStream->MixBuf);
602 AssertRC(rc2);
603
604#ifdef VBOX_WITH_STATISTICS
605 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pGstStream->szName);
606 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStream->In.StatBytesElapsed,
607 szStatName, STAMUNIT_BYTES, "Elapsed bytes read.");
608
609 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesRead", pGstStream->szName);
610 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStream->In.StatBytesTotalRead,
611 szStatName, STAMUNIT_BYTES, "Total bytes read.");
612
613 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/SamplesCaptured", pHstStream->szName);
614 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pHstStream->In.StatSamplesCaptured,
615 szStatName, STAMUNIT_COUNT, "Total samples captured.");
616#endif
617 }
618 else
619 {
620 /* Guest (Parent) -> Host (Child). */
621 rc2 = AudioMixBufLinkTo(&pGstStream->MixBuf, &pHstStream->MixBuf);
622 AssertRC(rc2);
623
624#ifdef VBOX_WITH_STATISTICS
625 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pGstStream->szName);
626 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStream->Out.StatBytesElapsed,
627 szStatName, STAMUNIT_BYTES, "Elapsed bytes written.");
628
629 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesRead", pGstStream->szName);
630 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStream->Out.StatBytesTotalWritten,
631 szStatName, STAMUNIT_BYTES, "Total bytes written.");
632
633 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/SamplesPlayed", pHstStream->szName);
634 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pHstStream->Out.StatSamplesPlayed,
635 szStatName, STAMUNIT_COUNT, "Total samples played.");
636#endif
637 }
638
639 /* Make a copy of the host stream configuration. */
640 memcpy(&pGstStream->Cfg, pCfgGuest, sizeof(PDMAUDIOSTREAMCFG));
641
642 LogFlowFunc(("[%s] Returning %Rrc\n", pStream->szName, rc));
643 return rc;
644}
645
646/**
647 * Re-initializes an audio stream with its existing host and guest stream configuration.
648 *
649 * @returns IPRT status code.
650 * @param pThis Pointer to driver instance.
651 * @param pStream Stream to re-initialize.
652 */
653static int drvAudioStreamReInitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
654{
655 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
656 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
657
658 LogFlowFunc(("[%s]\n", pStream->szName));
659
660 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
661 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
662 AssertPtr(pGstStream);
663
664 int rc;
665
666 if (/* Stream initialized? */
667 (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
668 /* Not in pending re-init before? */
669 && !(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT))
670 {
671 /* Disable first. */
672 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
673 if (RT_FAILURE(rc))
674 {
675 LogFunc(("[%s] Error disabling stream, rc=%Rrc\n", pStream->szName, rc));
676 return rc;
677 }
678
679 /* Give the backend the chance to clean up the old context. */
680 rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pHstStream);
681 if (RT_FAILURE(rc))
682 {
683 LogFunc(("[%s] Error destroying stream in backend, rc=%Rrc\n", pStream->szName, rc));
684 return rc;
685 }
686
687 /* Set the pending re-init bit. */
688 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT;
689 }
690
691 LogFlowFunc(("[%s] Host status is 0x%x\n", pStream->szName, pHstStream->fStatus));
692
693 /* Try to re-initialize the stream. */
694 rc = drvAudioStreamInitInternal(pThis, pStream, &pHstStream->Cfg, &pGstStream->Cfg);
695 if (RT_SUCCESS(rc))
696 {
697 /* Try to restore the previous stream status, if possible. */
698 PDMAUDIOSTREAMCMD enmCmdRestore = PDMAUDIOSTREAMCMD_UNKNOWN;
699
700 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED) /* Stream was running before? */
701 {
702 LogFunc(("[%s] Re-enabling host stream ...\n", pStream->szName));
703 enmCmdRestore = PDMAUDIOSTREAMCMD_ENABLE;
704 }
705
706 if (enmCmdRestore != PDMAUDIOSTREAMCMD_UNKNOWN)
707 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_ENABLE);
708
709 /* Re-initialization successful, remove bit again. */
710 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT;
711 }
712
713 LogFunc(("[%s] Reinitialization returned %Rrc\n", pStream->szName, rc));
714 return rc;
715}
716
717/**
718 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamWrite}
719 */
720static DECLCALLBACK(int) drvAudioStreamWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
721 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
722{
723 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
724 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
725 /* pcbWritten is optional. */
726
727 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
728
729 if ( !pStream
730 || !cbBuf)
731 {
732 if (pcbWritten)
733 *pcbWritten = 0;
734 return VINF_SUCCESS;
735 }
736
737 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT,
738 ("Stream '%s' is not an output stream and therefore cannot be written to (direction is 0x%x)\n",
739 pStream->szName, pStream->enmDir));
740
741 uint32_t cbWritten = 0;
742
743 int rc = RTCritSectEnter(&pThis->CritSect);
744 if (RT_FAILURE(rc))
745 return rc;
746
747 do
748 {
749 if ( pThis->pHostDrvAudio
750 && pThis->pHostDrvAudio->pfnGetStatus
751 && pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_OUT) != PDMAUDIOBACKENDSTS_RUNNING)
752 {
753 rc = VERR_NOT_AVAILABLE;
754 break;
755 }
756
757 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
758 if (!pHstStream)
759 {
760 rc = VERR_NOT_AVAILABLE;
761 break;
762 }
763
764 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
765
766 AssertMsg(pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
767 ("Writing to disabled guest output stream \"%s\" not possible\n", pGstStream->szName));
768
769 pGstStream->Out.tsLastWriteMS = RTTimeMilliTS();
770
771 if (!AudioMixBufFreeBytes(&pGstStream->MixBuf))
772 {
773 LogRel2(("Audio: Guest stream '%s' full, expect stuttering audio output\n", pGstStream->szName));
774 break;
775 }
776
777 uint32_t cWritten = 0;
778 rc = AudioMixBufWriteCirc(&pGstStream->MixBuf, pvBuf, cbBuf, &cWritten);
779 if (rc == VINF_BUFFER_OVERFLOW)
780 {
781 LogRel2(("Audio: Lost audio samples from guest stream '%s', expect stuttering audio output\n", pGstStream->szName));
782 rc = VINF_SUCCESS;
783 break;
784 }
785
786#ifdef VBOX_WITH_STATISTICS
787 STAM_COUNTER_ADD(&pThis->Stats.TotalBytesWritten, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritten));
788 STAM_COUNTER_ADD(&pGstStream->Out.StatBytesTotalWritten, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritten));
789#endif
790 cbWritten = AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritten);
791
792 Log3Func(("[%s] cUsed=%RU32, cLive=%RU32\n",
793 pGstStream->szName, AudioMixBufUsed(&pGstStream->MixBuf), AudioMixBufLive(&pGstStream->MixBuf)));
794
795 } while (0);
796
797 int rc2 = RTCritSectLeave(&pThis->CritSect);
798 if (RT_SUCCESS(rc))
799 rc = rc2;
800
801 if (RT_SUCCESS(rc))
802 {
803 if (pcbWritten)
804 *pcbWritten = cbWritten;
805 }
806
807 return rc;
808}
809
810/**
811 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRetain}
812 */
813static DECLCALLBACK(uint32_t) drvAudioStreamRetain(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
814{
815 AssertPtrReturn(pInterface, UINT32_MAX);
816 AssertPtrReturn(pStream, UINT32_MAX);
817
818 NOREF(pInterface);
819
820 return ++pStream->cRefs;
821}
822
823/**
824 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRelease}
825 */
826static DECLCALLBACK(uint32_t) drvAudioStreamRelease(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
827{
828 AssertPtrReturn(pInterface, UINT32_MAX);
829 AssertPtrReturn(pStream, UINT32_MAX);
830
831 NOREF(pInterface);
832
833 if (pStream->cRefs > 1) /* 1 reference always is kept by this audio driver. */
834 pStream->cRefs--;
835
836 return pStream->cRefs;
837}
838
839/**
840 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamIterate}
841 */
842static DECLCALLBACK(int) drvAudioStreamIterate(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
843{
844 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
845 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
846 /* pcData is optional. */
847
848 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
849
850 int rc = RTCritSectEnter(&pThis->CritSect);
851 if (RT_FAILURE(rc))
852 return rc;
853
854 rc = drvAudioStreamIterateInternal(pThis, pStream);
855
856 int rc2 = RTCritSectLeave(&pThis->CritSect);
857 if (RT_SUCCESS(rc))
858 rc = rc2;
859
860 if (RT_FAILURE(rc))
861 LogFlowFuncLeaveRC(rc);
862
863 return rc;
864}
865
866/**
867 * Does one iteration of an audio stream.
868 * This function gives the backend the chance of iterating / altering data and
869 * does the actual mixing between the guest <-> host mixing buffers.
870 *
871 * @returns IPRT status code.
872 * @param pThis Pointer to driver instance.
873 * @param pStream Stream to iterate.
874 *
875 * @remark
876 */
877static int drvAudioStreamIterateInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
878{
879 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
880
881 if (!pStream)
882 return VINF_SUCCESS;
883
884 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
885 AssertPtr(pHstStream);
886 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
887 AssertPtr(pGstStream);
888
889 int rc;
890
891 /* Whether to try closing a pending to close stream. */
892 bool fTryClosePending = false;
893
894 do
895 {
896 uint32_t cSamplesMixed = 0;
897
898 rc = pThis->pHostDrvAudio->pfnStreamIterate(pThis->pHostDrvAudio, pHstStream);
899 if (RT_FAILURE(rc))
900 break;
901
902 if (pHstStream->enmDir == PDMAUDIODIR_IN)
903 {
904 /* Has the host captured any samples which were not mixed to the guest side yet? */
905 uint32_t cSamplesCaptured = AudioMixBufUsed(&pHstStream->MixBuf);
906
907 Log3Func(("[%s] %RU32 samples captured\n", pHstStream->szName, cSamplesCaptured));
908
909 if (cSamplesCaptured)
910 {
911 /* When capturing samples, the guest is the parent while the host is the child.
912 * So try mixing not yet mixed host-side samples to the guest-side buffer. */
913 rc = AudioMixBufMixToParent(&pHstStream->MixBuf, cSamplesCaptured, &cSamplesMixed);
914 if ( RT_SUCCESS(rc)
915 && cSamplesMixed)
916 {
917 Log3Func(("[%s] %RU32 captured samples mixed\n", pHstStream->szName, cSamplesMixed));
918 }
919 }
920 else
921 {
922 fTryClosePending = true;
923 }
924 }
925 else if (pHstStream->enmDir == PDMAUDIODIR_OUT)
926 {
927 /* When playing samples, the host is the parent while the guest is the child.
928 * So try mixing not yet mixed guest-side samples to the host-side buffer. */
929 rc = AudioMixBufMixToParent(&pGstStream->MixBuf, AudioMixBufUsed(&pGstStream->MixBuf), &cSamplesMixed);
930 if ( RT_SUCCESS(rc)
931 && cSamplesMixed)
932 {
933 Log3Func(("[%s] %RU32 samples mixed, guest has %RU32 samples left (%RU32 live)\n",
934 pHstStream->szName, cSamplesMixed,
935 AudioMixBufUsed(&pGstStream->MixBuf), AudioMixBufLive(&pGstStream->MixBuf)));
936 }
937
938 uint32_t cSamplesLeft = AudioMixBufUsed(&pGstStream->MixBuf);
939 if (!cSamplesLeft) /* No samples (anymore)? */
940 {
941 fTryClosePending = true;
942 }
943 }
944 else
945 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
946
947 if (fTryClosePending)
948 {
949 /* Has the host stream marked as disabled but there still were guest streams relying
950 * on it? Check if the stream now can be closed and do so, if possible. */
951 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
952 {
953 LogFunc(("[%s] Closing pending stream\n", pHstStream->szName));
954 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
955 if (RT_SUCCESS(rc))
956 {
957 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
958 }
959 else
960 LogFunc(("%s: Backend vetoed against closing output stream, rc=%Rrc\n", pHstStream->szName, rc));
961 }
962 }
963
964 } while (0);
965
966 /* Update timestamps. */
967 pHstStream->tsLastIterateMS = RTTimeMilliTS();
968 pGstStream->tsLastIterateMS = RTTimeMilliTS();
969
970 if (RT_FAILURE(rc))
971 LogFunc(("Failed with %Rrc\n", rc));
972
973 return rc;
974}
975
976/**
977 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamPlay}
978 */
979static DECLCALLBACK(int) drvAudioStreamPlay(PPDMIAUDIOCONNECTOR pInterface,
980 PPDMAUDIOSTREAM pStream, uint32_t *pcSamplesPlayed)
981{
982 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
983 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
984 /* pcSamplesPlayed is optional. */
985
986 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
987
988 int rc = RTCritSectEnter(&pThis->CritSect);
989 if (RT_FAILURE(rc))
990 return rc;
991
992 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT,
993 ("Stream '%s' is not an output stream and therefore cannot be played back (direction is 0x%x)\n",
994 pStream->szName, pStream->enmDir));
995
996 uint32_t cSamplesPlayed = 0;
997
998 do
999 {
1000 if (!pThis->pHostDrvAudio)
1001 {
1002 rc = VERR_NOT_AVAILABLE;
1003 break;
1004 }
1005
1006 /* Backend output (temporarily) disabled / unavailable? */
1007 if ( pThis->pHostDrvAudio->pfnGetStatus
1008 && pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_OUT) != PDMAUDIOBACKENDSTS_RUNNING)
1009 {
1010 /* Pull the new config from the backend and check again. */
1011 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
1012 AssertRC(rc);
1013
1014 if ( !pThis->BackendCfg.cSinks
1015 || !pThis->BackendCfg.cMaxStreamsOut)
1016 {
1017 rc = VERR_NOT_AVAILABLE;
1018 break;
1019 }
1020 }
1021
1022 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1023 AssertPtr(pHstStream);
1024 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1025 AssertPtr(pGstStream);
1026
1027 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetStatus);
1028 PDMAUDIOSTRMSTS strmSts = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream);
1029 if (!(strmSts & PDMAUDIOSTRMSTS_FLAG_INITIALIZED))
1030 {
1031 LogFunc(("[%s] Backend not initialized (anymore), re-initializing ...\n", pHstStream->szName));
1032 rc = drvAudioStreamReInitInternal(pThis, pStream);
1033 if (RT_FAILURE(rc))
1034 {
1035 LogFunc(("[%s] Failed to re-initialize backend, rc=%Rrc\n", pHstStream->szName, rc));
1036 break;
1037 }
1038 }
1039
1040 uint32_t cSamplesLive = AudioMixBufLive(&pGstStream->MixBuf);
1041 if (cSamplesLive)
1042 {
1043 if ( (strmSts & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
1044 && (strmSts & PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE))
1045 {
1046 AssertPtr(pThis->pHostDrvAudio->pfnStreamPlay);
1047 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pHstStream, NULL /* pvBuf */, 0 /* cbBuf */,
1048 &cSamplesPlayed);
1049 if (RT_FAILURE(rc))
1050 {
1051 int rc2 = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1052 AssertRC(rc2);
1053 }
1054 else
1055 {
1056#ifdef VBOX_WITH_STATISTICS
1057 STAM_COUNTER_ADD(&pThis->Stats.TotalSamplesPlayed, cSamplesPlayed);
1058 STAM_COUNTER_ADD(&pHstStream->Out.StatSamplesPlayed, cSamplesPlayed);
1059#endif
1060 cSamplesLive = AudioMixBufLive(&pGstStream->MixBuf);
1061 }
1062 }
1063 }
1064
1065 if (!cSamplesLive)
1066 {
1067 /* Has the host stream marked as disabled but there still were guest streams relying
1068 * on it? Check if the stream now can be closed and do so, if possible. */
1069 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
1070 {
1071 LogFunc(("[%s] Closing pending stream\n", pHstStream->szName));
1072 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1073 if (RT_SUCCESS(rc))
1074 {
1075 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
1076 }
1077 else
1078 LogFunc(("[%s] Backend vetoed against closing output stream, rc=%Rrc\n", pHstStream->szName, rc));
1079 }
1080 }
1081
1082 } while (0);
1083
1084 int rc2 = RTCritSectLeave(&pThis->CritSect);
1085 if (RT_SUCCESS(rc))
1086 rc = rc2;
1087
1088 if (RT_SUCCESS(rc))
1089 {
1090 if (pcSamplesPlayed)
1091 *pcSamplesPlayed = cSamplesPlayed;
1092 }
1093
1094 if (RT_FAILURE(rc))
1095 LogFlowFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
1096
1097 return rc;
1098}
1099
1100/**
1101 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCapture}
1102 */
1103static DECLCALLBACK(int) drvAudioStreamCapture(PPDMIAUDIOCONNECTOR pInterface,
1104 PPDMAUDIOSTREAM pStream, uint32_t *pcSamplesCaptured)
1105{
1106 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1107
1108 int rc = RTCritSectEnter(&pThis->CritSect);
1109 if (RT_FAILURE(rc))
1110 return rc;
1111
1112 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
1113 ("Stream '%s' is not an input stream and therefore cannot be captured (direction is 0x%x)\n",
1114 pStream->szName, pStream->enmDir));
1115
1116 Log3Func(("[%s]\n", pStream->szName));
1117
1118 uint32_t cSamplesCaptured = 0;
1119
1120 do
1121 {
1122 /* Backend input (temporarily) disabled / unavailable? */
1123 if (pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_IN) != PDMAUDIOBACKENDSTS_RUNNING)
1124 {
1125 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
1126 AssertRC(rc);
1127
1128 if ( !pThis->BackendCfg.cSources
1129 || !pThis->BackendCfg.cMaxStreamsIn)
1130 {
1131 rc = VERR_NOT_AVAILABLE;
1132 break;
1133 }
1134 }
1135
1136 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1137 AssertPtr(pHstStream);
1138 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1139 AssertPtr(pGstStream);
1140
1141 PDMAUDIOSTRMSTS strmSts = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream);
1142 if (!(strmSts & PDMAUDIOSTRMSTS_FLAG_INITIALIZED))
1143 {
1144 LogFunc(("[%s] Backend not initialized (anymore), re-initializing ...\n", pHstStream->szName));
1145 rc = drvAudioStreamReInitInternal(pThis, pStream);
1146 break;
1147 }
1148
1149 uint32_t cSamplesLive = AudioMixBufLive(&pGstStream->MixBuf);
1150 if (!cSamplesLive)
1151 {
1152 if ( (strmSts & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
1153 && (strmSts & PDMAUDIOSTRMSTS_FLAG_DATA_READABLE))
1154 {
1155 rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pHstStream, NULL /* pvBuf */, 0 /* cbBuf */,
1156 &cSamplesCaptured);
1157 if (RT_FAILURE(rc))
1158 {
1159 int rc2 = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1160 AssertRC(rc2);
1161 }
1162 else
1163 {
1164#ifdef VBOX_WITH_STATISTICS
1165 STAM_COUNTER_ADD(&pHstStream->In.StatSamplesCaptured, cSamplesCaptured);
1166#endif
1167 }
1168 }
1169
1170 Log3Func(("[%s] strmSts=0x%x, cSamplesCaptured=%RU32, rc=%Rrc\n", pHstStream->szName, strmSts, cSamplesCaptured, rc));
1171 }
1172
1173 } while (0);
1174
1175 if (RT_SUCCESS(rc))
1176 {
1177 if (pcSamplesCaptured)
1178 *pcSamplesCaptured = cSamplesCaptured;
1179 }
1180
1181 int rc2 = RTCritSectLeave(&pThis->CritSect);
1182 if (RT_SUCCESS(rc))
1183 rc = rc2;
1184
1185 if (RT_FAILURE(rc))
1186 LogFlowFuncLeaveRC(rc);
1187
1188 return rc;
1189}
1190
1191#ifdef VBOX_WITH_AUDIO_CALLBACKS
1192/**
1193 * Duplicates an audio callback.
1194 *
1195 * @returns Pointer to duplicated callback, or NULL on failure.
1196 * @param pCB Callback to duplicate.
1197 */
1198static PPDMAUDIOCALLBACK drvAudioCallbackDuplicate(PPDMAUDIOCALLBACK pCB)
1199{
1200 AssertPtrReturn(pCB, VERR_INVALID_POINTER);
1201
1202 PPDMAUDIOCALLBACK pCBCopy = (PPDMAUDIOCALLBACK)RTMemDup((void *)pCB, sizeof(PDMAUDIOCALLBACK));
1203 if (!pCBCopy)
1204 return NULL;
1205
1206 if (pCB->pvCtx)
1207 {
1208 pCBCopy->pvCtx = RTMemDup(pCB->pvCtx, pCB->cbCtx);
1209 if (!pCBCopy->pvCtx)
1210 {
1211 RTMemFree(pCBCopy);
1212 return NULL;
1213 }
1214
1215 pCBCopy->cbCtx = pCB->cbCtx;
1216 }
1217
1218 return pCBCopy;
1219}
1220
1221/**
1222 * Destroys a given callback.
1223 *
1224 * @param pCB Callback to destroy.
1225 */
1226static void drvAudioCallbackDestroy(PPDMAUDIOCALLBACK pCB)
1227{
1228 if (!pCB)
1229 return;
1230
1231 RTListNodeRemove(&pCB->Node);
1232 if (pCB->pvCtx)
1233 {
1234 Assert(pCB->cbCtx);
1235 RTMemFree(pCB->pvCtx);
1236 }
1237 RTMemFree(pCB);
1238}
1239
1240/**
1241 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnRegisterCallbacks}
1242 */
1243static DECLCALLBACK(int) drvAudioRegisterCallbacks(PPDMIAUDIOCONNECTOR pInterface,
1244 PPDMAUDIOCALLBACK paCallbacks, size_t cCallbacks)
1245{
1246 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1247 AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER);
1248 AssertReturn(cCallbacks, VERR_INVALID_PARAMETER);
1249
1250 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1251
1252 int rc = RTCritSectEnter(&pThis->CritSect);
1253 if (RT_FAILURE(rc))
1254 return rc;
1255
1256 for (size_t i = 0; i < cCallbacks; i++)
1257 {
1258 PPDMAUDIOCALLBACK pCB = drvAudioCallbackDuplicate(&paCallbacks[i]);
1259 if (!pCB)
1260 {
1261 rc = VERR_NO_MEMORY;
1262 break;
1263 }
1264
1265 switch (pCB->enmType)
1266 {
1267 case PDMAUDIOCALLBACKTYPE_INPUT:
1268 RTListAppend(&pThis->lstCBIn, &pCB->Node);
1269 break;
1270
1271 case PDMAUDIOCALLBACKTYPE_OUTPUT:
1272 RTListAppend(&pThis->lstCBOut, &pCB->Node);
1273 break;
1274
1275 default:
1276 AssertMsgFailed(("Not supported\n"));
1277 break;
1278 }
1279 }
1280
1281 /** @todo Undo allocations on error. */
1282
1283 int rc2 = RTCritSectLeave(&pThis->CritSect);
1284 if (RT_SUCCESS(rc))
1285 rc = rc2;
1286
1287 return rc;
1288}
1289
1290/**
1291 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnCallback}
1292 */
1293static DECLCALLBACK(int) drvAudioCallback(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIOCALLBACKTYPE enmType,
1294 void *pvUser, size_t cbUser)
1295{
1296 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1297 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
1298 AssertReturn(cbUser, VERR_INVALID_PARAMETER);
1299
1300 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1301 PRTLISTANCHOR pListAnchor = NULL;
1302
1303 switch (enmType)
1304 {
1305 case PDMAUDIOCALLBACKTYPE_INPUT:
1306 pListAnchor = &pThis->lstCBIn;
1307 break;
1308
1309 case PDMAUDIOCALLBACKTYPE_OUTPUT:
1310 pListAnchor = &pThis->lstCBOut;
1311 break;
1312
1313 default:
1314 AssertMsgFailed(("Not supported\n"));
1315 break;
1316 }
1317
1318 if (pListAnchor)
1319 {
1320 PPDMAUDIOCALLBACK pCB;
1321 RTListForEach(pListAnchor, pCB, PDMAUDIOCALLBACK, Node)
1322 {
1323 Assert(pCB->enmType == enmType);
1324 pCB->pfnCallback(enmType, pCB->pvCtx, pCB->cbCtx, pvUser, cbUser);
1325 }
1326 }
1327
1328 return VINF_SUCCESS;
1329}
1330#endif
1331
1332/**
1333 * Initializes the host backend and queries its initial configuration.
1334 * If the host backend fails, VERR_AUDIO_BACKEND_INIT_FAILED will be returned.
1335 *
1336 * Note: As this routine is called when attaching to the device LUN in the
1337 * device emulation, we either check for success or VERR_AUDIO_BACKEND_INIT_FAILED.
1338 * Everything else is considered as fatal and must be handled separately in
1339 * the device emulation!
1340 *
1341 * @return IPRT status code.
1342 * @param pThis Driver instance to be called.
1343 * @param pCfgHandle CFGM configuration handle to use for this driver.
1344 */
1345static int drvAudioHostInit(PDRVAUDIO pThis, PCFGMNODE pCfgHandle)
1346{
1347 /* pCfgHandle is optional. */
1348 NOREF(pCfgHandle);
1349 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1350
1351 LogFlowFuncEnter();
1352
1353 AssertPtr(pThis->pHostDrvAudio);
1354 int rc = pThis->pHostDrvAudio->pfnInit(pThis->pHostDrvAudio);
1355 if (RT_FAILURE(rc))
1356 {
1357 LogRel(("Audio: Initialization of host backend failed with %Rrc\n", rc));
1358 return VERR_AUDIO_BACKEND_INIT_FAILED;
1359 }
1360
1361 /* Get the configuration data from backend. */
1362 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
1363 if (RT_FAILURE(rc))
1364 {
1365 LogRel(("Audio: Getting host backend configuration failed with %Rrc\n", rc));
1366 return VERR_AUDIO_BACKEND_INIT_FAILED;
1367 }
1368
1369 pThis->cStreamsFreeIn = 0;
1370 pThis->cStreamsFreeOut = 0;
1371
1372 if (pThis->BackendCfg.cSinks)
1373 {
1374 Assert(pThis->BackendCfg.cbStreamOut);
1375
1376 pThis->cStreamsFreeOut = pThis->BackendCfg.cMaxStreamsOut;
1377 }
1378
1379 if (pThis->BackendCfg.cSources)
1380 {
1381 Assert(pThis->BackendCfg.cbStreamIn);
1382
1383 pThis->cStreamsFreeIn = pThis->BackendCfg.cMaxStreamsIn;
1384 }
1385
1386 LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->cStreamsFreeIn, pThis->cStreamsFreeOut));
1387
1388 LogRel2(("Audio: Host audio backend supports %RU32 input streams and %RU32 output streams at once\n",
1389 /* Clamp for logging. Unlimited streams are defined by UINT32_MAX. */
1390 RT_MIN(64, pThis->cStreamsFreeIn), RT_MIN(64, pThis->cStreamsFreeOut)));
1391
1392 LogFlowFuncLeave();
1393 return VINF_SUCCESS;
1394}
1395
1396/**
1397 * Handles state changes for all audio streams.
1398 *
1399 * @param pDrvIns Pointer to driver instance.
1400 * @param enmCmd Stream command to set for all streams.
1401 */
1402static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
1403{
1404 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1405 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1406
1407 LogFlowFunc(("enmCmd=%RU32\n", enmCmd));
1408
1409 if (!pThis->pHostDrvAudio)
1410 return;
1411
1412 PPDMAUDIOSTREAM pHstStream;
1413 RTListForEach(&pThis->lstHstStreams, pHstStream, PDMAUDIOSTREAM, Node)
1414 drvAudioStreamControlInternalBackend(pThis, pHstStream, enmCmd);
1415}
1416
1417/**
1418 * Intializes an audio driver instance.
1419 *
1420 * @returns IPRT status code.
1421 * @param pDrvIns Pointer to driver instance.
1422 * @param pCfgHandle CFGM handle to use for configuration.
1423 */
1424static int drvAudioInit(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
1425{
1426 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
1427 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
1428
1429 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1430 LogFlowFunc(("pThis=%p, pDrvIns=%p\n", pThis, pDrvIns));
1431
1432 int rc = RTCritSectInit(&pThis->CritSect);
1433
1434 /** @todo Add audio driver options. */
1435
1436 /*
1437 * If everything went well, initialize the lower driver.
1438 */
1439 if (RT_SUCCESS(rc))
1440 rc = drvAudioHostInit(pThis, pCfgHandle);
1441
1442 LogFlowFuncLeaveRC(rc);
1443 return rc;
1444}
1445
1446/**
1447 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRead}
1448 */
1449static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
1450 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1451{
1452 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1453 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1454
1455 if (!pStream)
1456 {
1457 if (pcbRead)
1458 *pcbRead = 0;
1459 return VINF_SUCCESS;
1460 }
1461
1462 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1463 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1464 /* pcbWritten is optional. */
1465
1466 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
1467 ("Stream '%s' is not an input stream and therefore cannot be read from (direction is 0x%x)\n",
1468 pStream->szName, pStream->enmDir));
1469
1470 uint32_t cbRead = 0;
1471
1472 int rc = RTCritSectEnter(&pThis->CritSect);
1473 if (RT_FAILURE(rc))
1474 return rc;
1475
1476 do
1477 {
1478 if ( pThis->pHostDrvAudio
1479 && pThis->pHostDrvAudio->pfnGetStatus
1480 && pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_IN) != PDMAUDIOBACKENDSTS_RUNNING)
1481 {
1482 rc = VERR_NOT_AVAILABLE;
1483 break;
1484 }
1485
1486 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1487 if (pHstStream)
1488 {
1489 rc = VERR_NOT_AVAILABLE;
1490 break;
1491 }
1492
1493 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1494
1495 AssertMsg(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
1496 ("Reading from disabled host input stream '%s' not possible\n", pHstStream->szName));
1497
1498 pGstStream->In.tsLastReadMS = RTTimeMilliTS();
1499
1500 Log3Func(("%s\n", pStream->szName));
1501
1502 /*
1503 * Read from the parent buffer (that is, the guest buffer) which
1504 * should have the audio data in the format the guest needs.
1505 */
1506 uint32_t cRead;
1507 rc = AudioMixBufReadCirc(&pGstStream->MixBuf, pvBuf, cbBuf, &cRead);
1508 if (RT_SUCCESS(rc))
1509 {
1510 if (cRead)
1511 {
1512#ifdef VBOX_WITH_STATISTICS
1513 STAM_COUNTER_ADD(&pThis->Stats.TotalBytesRead, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cRead));
1514 STAM_COUNTER_ADD(&pGstStream->In.StatBytesTotalRead, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cRead));
1515#endif
1516 AudioMixBufFinish(&pGstStream->MixBuf, cRead);
1517
1518 cbRead = AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cRead);
1519 }
1520 }
1521
1522 Log3Func(("cRead=%RU32 (%RU32 bytes), rc=%Rrc\n", cRead, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cRead), rc));
1523
1524 } while (0);
1525
1526 int rc2 = RTCritSectLeave(&pThis->CritSect);
1527 if (RT_SUCCESS(rc))
1528 rc = rc2;
1529
1530 if (RT_SUCCESS(rc))
1531 {
1532 if (pcbRead)
1533 *pcbRead = cbRead;
1534 }
1535
1536 return rc;
1537}
1538
1539/**
1540 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCreate}
1541 */
1542static DECLCALLBACK(int) drvAudioStreamCreate(PPDMIAUDIOCONNECTOR pInterface,
1543 PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest,
1544 PPDMAUDIOSTREAM *ppStream)
1545{
1546 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1547 AssertPtrReturn(pCfgHost, VERR_INVALID_POINTER);
1548 AssertPtrReturn(pCfgGuest, VERR_INVALID_POINTER);
1549 AssertPtrReturn(ppStream, VERR_INVALID_POINTER);
1550
1551 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1552
1553 int rc = RTCritSectEnter(&pThis->CritSect);
1554 if (RT_FAILURE(rc))
1555 return rc;
1556
1557 LogFlowFunc(("Host=%s, Guest=%s\n", pCfgHost->szName, pCfgGuest->szName));
1558#ifdef DEBUG
1559 DrvAudioHlpStreamCfgPrint(pCfgHost);
1560 DrvAudioHlpStreamCfgPrint(pCfgGuest);
1561#endif
1562
1563 /*
1564 * The guest stream always will get the audio stream configuration told
1565 * by the device emulation (which in turn was/could be set by the guest OS).
1566 */
1567 PPDMAUDIOSTREAM pGstStrm = NULL;
1568
1569 /** @todo Docs! */
1570 PPDMAUDIOSTREAM pHstStrm = NULL;
1571
1572#define RC_BREAK(x) { rc = x; break; }
1573
1574 do
1575 {
1576 if ( !DrvAudioHlpStreamCfgIsValid(pCfgHost)
1577 || !DrvAudioHlpStreamCfgIsValid(pCfgGuest))
1578 {
1579 RC_BREAK(VERR_INVALID_PARAMETER);
1580 }
1581
1582 /* Make sure that both configurations actually intend the same thing. */
1583 if (pCfgHost->enmDir != pCfgGuest->enmDir)
1584 {
1585 AssertMsgFailed(("Stream configuration directions do not match\n"));
1586 RC_BREAK(VERR_INVALID_PARAMETER);
1587 }
1588
1589 /* Note: cbHstStrm will contain sizeof(PDMAUDIOSTREAM) + additional data
1590 * which the host backend will need. */
1591 size_t cbHstStrm;
1592 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
1593 {
1594 if (!pThis->cStreamsFreeIn)
1595 LogFunc(("Warning: No more input streams free to use\n"));
1596
1597 /* Validate backend configuration. */
1598 if (!pThis->BackendCfg.cbStreamIn)
1599 {
1600 LogFunc(("Backend input configuration not valid, bailing out\n"));
1601 RC_BREAK(VERR_INVALID_PARAMETER);
1602 }
1603
1604 cbHstStrm = pThis->BackendCfg.cbStreamIn;
1605 }
1606 else /* Out */
1607 {
1608 if (!pThis->cStreamsFreeOut)
1609 {
1610 LogFlowFunc(("Maximum number of host output streams reached\n"));
1611 RC_BREAK(VERR_AUDIO_NO_FREE_OUTPUT_STREAMS);
1612 }
1613
1614 /* Validate backend configuration. */
1615 if (!pThis->BackendCfg.cbStreamOut)
1616 {
1617 LogFlowFunc(("Backend output configuration invalid, bailing out\n"));
1618 RC_BREAK(VERR_INVALID_PARAMETER);
1619 }
1620
1621 cbHstStrm = pThis->BackendCfg.cbStreamOut;
1622 }
1623
1624 pHstStrm = (PPDMAUDIOSTREAM)RTMemAllocZ(cbHstStrm);
1625 AssertPtrBreakStmt(pHstStrm, rc = VERR_NO_MEMORY);
1626
1627 pHstStrm->enmCtx = PDMAUDIOSTREAMCTX_HOST;
1628 pHstStrm->enmDir = pCfgHost->enmDir;
1629
1630 pGstStrm = (PPDMAUDIOSTREAM)RTMemAllocZ(sizeof(PDMAUDIOSTREAM));
1631 AssertPtrBreakStmt(pGstStrm, rc = VERR_NO_MEMORY);
1632
1633 pGstStrm->enmCtx = PDMAUDIOSTREAMCTX_GUEST;
1634 pGstStrm->enmDir = pCfgGuest->enmDir;
1635
1636 /*
1637 * Init host stream.
1638 */
1639 RTStrPrintf(pHstStrm->szName, RT_ELEMENTS(pHstStrm->szName), "%s (Host)",
1640 strlen(pCfgHost->szName) ? pCfgHost->szName : "<Untitled>");
1641
1642 pHstStrm->pPair = pGstStrm;
1643
1644 /*
1645 * Init guest stream.
1646 */
1647 RTStrPrintf(pGstStrm->szName, RT_ELEMENTS(pGstStrm->szName), "%s (Guest)",
1648 strlen(pCfgGuest->szName) ? pCfgGuest->szName : "<Untitled>");
1649
1650 pGstStrm->fStatus = pHstStrm->fStatus; /* Reflect the host stream's status. */
1651 pGstStrm->pPair = pHstStrm;
1652
1653 /*
1654 * Try to init the rest.
1655 */
1656 rc = drvAudioStreamInitInternal(pThis, pHstStrm, pCfgHost, pCfgGuest);
1657 if (RT_FAILURE(rc))
1658 {
1659 LogFlowFunc(("Stream not available (yet)\n"));
1660 rc = VINF_SUCCESS;
1661 }
1662
1663 } while (0);
1664
1665#undef RC_BREAK
1666
1667 if (RT_FAILURE(rc))
1668 {
1669 if (pGstStrm)
1670 {
1671 drvAudioStreamDestroyInternal(pThis, pGstStrm);
1672 pGstStrm = NULL;
1673 }
1674
1675 if (pHstStrm)
1676 {
1677 drvAudioStreamDestroyInternal(pThis, pHstStrm);
1678 pHstStrm = NULL;
1679 }
1680 }
1681 else
1682 {
1683 /* Set initial reference counts. */
1684 RTListAppend(&pThis->lstGstStreams, &pGstStrm->Node);
1685 pGstStrm->cRefs = 1;
1686
1687 RTListAppend(&pThis->lstHstStreams, &pHstStrm->Node);
1688 pHstStrm->cRefs = 1;
1689
1690 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
1691 {
1692 if (pThis->cStreamsFreeIn)
1693 pThis->cStreamsFreeIn--;
1694 }
1695 else /* Out */
1696 {
1697 if (pThis->cStreamsFreeOut)
1698 pThis->cStreamsFreeOut--;
1699 }
1700
1701#ifdef VBOX_WITH_STATISTICS
1702 STAM_COUNTER_ADD(&pThis->Stats.TotalStreamsCreated, 1);
1703#endif
1704 /* Always return the guest-side part to the device emulation. */
1705 *ppStream = pGstStrm;
1706 }
1707
1708 int rc2 = RTCritSectLeave(&pThis->CritSect);
1709 if (RT_SUCCESS(rc))
1710 rc = rc2;
1711
1712 LogFlowFuncLeaveRC(rc);
1713 return rc;
1714}
1715
1716/**
1717 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetConfig}
1718 */
1719static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
1720{
1721 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1722 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1723
1724 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1725
1726 int rc = RTCritSectEnter(&pThis->CritSect);
1727 if (RT_FAILURE(rc))
1728 return rc;
1729
1730 if (pThis->pHostDrvAudio)
1731 {
1732 if (pThis->pHostDrvAudio->pfnGetConfig)
1733 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, pCfg);
1734 else
1735 rc = VERR_NOT_SUPPORTED;
1736 }
1737 else
1738 AssertFailed();
1739
1740 int rc2 = RTCritSectLeave(&pThis->CritSect);
1741 if (RT_SUCCESS(rc))
1742 rc = rc2;
1743
1744 LogFlowFuncLeaveRC(rc);
1745 return rc;
1746}
1747
1748/**
1749 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetStatus}
1750 */
1751static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioGetStatus(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
1752{
1753 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
1754
1755 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1756
1757 PDMAUDIOBACKENDSTS backendSts = PDMAUDIOBACKENDSTS_UNKNOWN;
1758
1759 int rc = RTCritSectEnter(&pThis->CritSect);
1760 if (RT_SUCCESS(rc))
1761 {
1762 if ( pThis->pHostDrvAudio
1763 && pThis->pHostDrvAudio->pfnGetStatus)
1764 {
1765 backendSts = pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, enmDir);
1766 }
1767
1768 int rc2 = RTCritSectLeave(&pThis->CritSect);
1769 if (RT_SUCCESS(rc))
1770 rc = rc2;
1771 }
1772
1773 LogFlowFuncLeaveRC(rc);
1774 return backendSts;
1775}
1776
1777/**
1778 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetReadable}
1779 */
1780static DECLCALLBACK(uint32_t) drvAudioStreamGetReadable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1781{
1782 AssertPtrReturn(pInterface, 0);
1783 AssertPtrReturn(pStream, 0);
1784
1785 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1786
1787 int rc2 = RTCritSectEnter(&pThis->CritSect);
1788 AssertRC(rc2);
1789
1790 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN, ("Can't read from a non-input stream\n"));
1791
1792 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1793 if (!pHstStream) /* No host stream available? Bail out early. */
1794 {
1795 rc2 = RTCritSectLeave(&pThis->CritSect);
1796 AssertRC(rc2);
1797
1798 return 0;
1799 }
1800
1801 uint32_t cReadable = 0;
1802
1803 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1804 if (pGstStream)
1805 cReadable = AudioMixBufLive(&pGstStream->MixBuf);
1806
1807 Log3Func(("[%s] cbReadable=%RU32 (%zu bytes)\n", pHstStream->szName, cReadable,
1808 AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cReadable)));
1809
1810 rc2 = RTCritSectLeave(&pThis->CritSect);
1811 AssertRC(rc2);
1812
1813 /* Return bytes instead of audio samples. */
1814 return AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cReadable);
1815}
1816
1817/**
1818 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetWritable}
1819 */
1820static DECLCALLBACK(uint32_t) drvAudioStreamGetWritable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1821{
1822 AssertPtrReturn(pInterface, 0);
1823 AssertPtrReturn(pStream, 0);
1824
1825 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1826
1827 int rc2 = RTCritSectEnter(&pThis->CritSect);
1828 AssertRC(rc2);
1829
1830 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT, ("Can't write to a non-output stream\n"));
1831
1832 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1833 if (!pHstStream) /* No host stream available? Bail out early. */
1834 {
1835 rc2 = RTCritSectLeave(&pThis->CritSect);
1836 AssertRC(rc2);
1837
1838 AssertMsgFailed(("Guest stream '%s' does not have a host stream attached\n", pStream->szName));
1839 return 0;
1840 }
1841
1842 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1843 AssertPtr(pGstStream);
1844
1845 uint32_t cWritable = AudioMixBufFree(&pGstStream->MixBuf);
1846
1847 Log3Func(("[%s] cWritable=%RU32 (%zu bytes)\n", pHstStream->szName, cWritable,
1848 AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritable)));
1849
1850 rc2 = RTCritSectLeave(&pThis->CritSect);
1851 AssertRC(rc2);
1852
1853 /* Return bytes instead of audio samples. */
1854 return AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritable);
1855}
1856
1857/**
1858 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetStatus}
1859 */
1860static DECLCALLBACK(PDMAUDIOSTRMSTS) drvAudioStreamGetStatus(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1861{
1862 AssertPtrReturn(pInterface, false);
1863
1864 if (!pStream)
1865 return PDMAUDIOSTRMSTS_FLAG_NONE;
1866
1867 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1868
1869 int rc2 = RTCritSectEnter(&pThis->CritSect);
1870 AssertRC(rc2);
1871
1872 PDMAUDIOSTRMSTS strmSts = PDMAUDIOSTRMSTS_FLAG_NONE;
1873
1874 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1875 if (pHstStream)
1876 {
1877 strmSts = pHstStream->fStatus;
1878 Log3Func(("%s: strmSts=0x%x\n", pHstStream->szName, strmSts));
1879 }
1880
1881 rc2 = RTCritSectLeave(&pThis->CritSect);
1882 AssertRC(rc2);
1883
1884 return strmSts;
1885}
1886
1887/**
1888 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamSetVolume}
1889 */
1890static DECLCALLBACK(int) drvAudioStreamSetVolume(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, PPDMAUDIOVOLUME pVol)
1891{
1892 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1893 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1894 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1895
1896 LogFlowFunc(("%s: volL=%RU32, volR=%RU32, fMute=%RTbool\n", pStream->szName, pVol->uLeft, pVol->uRight, pVol->fMuted));
1897
1898 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1899 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
1900
1901 AudioMixBufSetVolume(&pHstStream->MixBuf, pVol);
1902 AudioMixBufSetVolume(&pGstStream->MixBuf, pVol);
1903 return VINF_SUCCESS;
1904}
1905
1906/**
1907 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamDestroy}
1908 */
1909static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1910{
1911 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1912 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1913
1914 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1915
1916 int rc = RTCritSectEnter(&pThis->CritSect);
1917 AssertRC(rc);
1918
1919 PDMAUDIODIR enmDir = pStream->enmDir;
1920
1921 LogFlowFunc(("%s: cRefs=%RU32\n", pStream->szName, pStream->cRefs));
1922 if (pStream->cRefs > 1)
1923 rc = VERR_WRONG_ORDER;
1924
1925 if (RT_SUCCESS(rc))
1926 {
1927 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1928 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
1929
1930 LogFlowFunc(("\tHost : %s\n", pHstStream ? pHstStream->szName : "<None>"));
1931 LogFlowFunc(("\tGuest: %s\n", pGstStream ? pGstStream->szName : "<None>"));
1932
1933 /* Should prevent double frees. */
1934 Assert(pHstStream != pGstStream);
1935
1936 if (pHstStream)
1937 {
1938 pHstStream->pPair = NULL;
1939 RTListNodeRemove(&pHstStream->Node);
1940 }
1941
1942 if (pGstStream)
1943 {
1944 pGstStream->pPair = NULL;
1945 RTListNodeRemove(&pGstStream->Node);
1946 }
1947
1948 if (pHstStream)
1949 {
1950 rc = drvAudioStreamDestroyInternal(pThis, pHstStream);
1951 AssertRC(rc);
1952
1953 pHstStream = NULL;
1954 }
1955
1956 if (pGstStream)
1957 {
1958 rc = drvAudioStreamDestroyInternal(pThis, pGstStream);
1959 AssertRC(rc);
1960
1961 pGstStream = NULL;
1962 }
1963 }
1964
1965 if (RT_SUCCESS(rc))
1966 {
1967 if (enmDir == PDMAUDIODIR_IN)
1968 {
1969 pThis->cStreamsFreeIn++;
1970 }
1971 else /* Out */
1972 {
1973 pThis->cStreamsFreeOut++;
1974 }
1975 }
1976
1977 int rc2 = RTCritSectLeave(&pThis->CritSect);
1978 if (RT_SUCCESS(rc))
1979 rc = rc2;
1980
1981 LogFlowFuncLeaveRC(rc);
1982 return rc;
1983}
1984
1985/**
1986 * Calls the backend to give it the chance to destroy its part of the audio stream.
1987 *
1988 * @returns IPRT status code.
1989 * @param pThis Pointer to driver instance.
1990 * @param pHstStream Host audio stream to call the backend destruction for.
1991 */
1992static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream)
1993{
1994 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1995 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
1996
1997 AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST,
1998 ("Stream '%s' is not a host stream and therefore has no backend\n", pHstStream->szName));
1999
2000 int rc = VINF_SUCCESS;
2001
2002 LogFlowFunc(("%s: fStatus=0x%x\n", pHstStream->szName, pHstStream->fStatus));
2003
2004 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
2005 {
2006 /* Check if the pointer to the host audio driver is still valid.
2007 * It can be NULL if we were called in drvAudioDestruct, for example. */
2008 if (pThis->pHostDrvAudio)
2009 rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pHstStream);
2010 if (RT_SUCCESS(rc))
2011 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
2012 }
2013
2014 LogFlowFunc(("%s: Returning %Rrc\n", pHstStream->szName, rc));
2015 return rc;
2016}
2017
2018/**
2019 * Destroys an audio stream.
2020 *
2021 * @returns IPRT status code.
2022 * @param pThis Pointer to driver instance.
2023 * @param pStream Pointer to audio stream to destroy.
2024 * That pointer will be invalid after successful destruction.
2025 */
2026static int drvAudioStreamDestroyInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
2027{
2028 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2029 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2030
2031 LogFlowFunc(("%s: cRefs=%RU32\n", pStream->szName, pStream->cRefs));
2032
2033 if (pStream->cRefs > 1)
2034 return VERR_WRONG_ORDER;
2035
2036 int rc = VINF_SUCCESS;
2037
2038 if (pStream->enmCtx == PDMAUDIOSTREAMCTX_GUEST)
2039 {
2040 if (pStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
2041 {
2042 rc = drvAudioStreamControlInternal(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
2043 if (RT_SUCCESS(rc))
2044 pStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
2045 }
2046
2047 if (pStream->enmDir == PDMAUDIODIR_IN)
2048 {
2049#ifdef VBOX_WITH_STATISTICS
2050 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->In.StatBytesElapsed);
2051 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->In.StatBytesTotalRead);
2052#endif
2053 }
2054 else if (pStream->enmDir == PDMAUDIODIR_OUT)
2055 {
2056#ifdef VBOX_WITH_STATISTICS
2057 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->Out.StatBytesElapsed);
2058 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->Out.StatBytesTotalWritten);
2059#endif
2060 }
2061 }
2062 else if (pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST)
2063 {
2064 rc = drvAudioStreamDestroyInternalBackend(pThis, pStream);
2065
2066 if (pStream->enmDir == PDMAUDIODIR_IN)
2067 {
2068#ifdef VBOX_WITH_STATISTICS
2069 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->In.StatSamplesCaptured);
2070#endif
2071 }
2072 else if (pStream->enmDir == PDMAUDIODIR_OUT)
2073 {
2074#ifdef VBOX_WITH_STATISTICS
2075 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->Out.StatSamplesPlayed);
2076#endif
2077 }
2078 }
2079 else
2080 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
2081
2082 if (RT_SUCCESS(rc))
2083 {
2084 /* Destroy mixing buffer. */
2085 AudioMixBufDestroy(&pStream->MixBuf);
2086
2087 if (pStream)
2088 {
2089 RTMemFree(pStream);
2090 pStream = NULL;
2091 }
2092 }
2093
2094 LogFlowFunc(("Returning %Rrc\n", rc));
2095 return rc;
2096}
2097
2098/********************************************************************/
2099
2100/**
2101 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2102 */
2103static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2104{
2105 LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
2106
2107 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2108 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2109
2110 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2111 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
2112
2113 return NULL;
2114}
2115
2116/**
2117 * Power Off notification.
2118 *
2119 * @param pDrvIns The driver instance data.
2120 */
2121static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
2122{
2123 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2124
2125 LogFlowFuncEnter();
2126
2127 /* Just destroy the host stream on the backend side.
2128 * The rest will either be destructed by the device emulation or
2129 * in drvAudioDestruct(). */
2130 PPDMAUDIOSTREAM pStream;
2131 RTListForEach(&pThis->lstHstStreams, pStream, PDMAUDIOSTREAM, Node)
2132 drvAudioStreamDestroyInternalBackend(pThis, pStream);
2133
2134 /*
2135 * Last call for the driver below us.
2136 * Let it know that we reached end of life.
2137 */
2138 if (pThis->pHostDrvAudio->pfnShutdown)
2139 pThis->pHostDrvAudio->pfnShutdown(pThis->pHostDrvAudio);
2140
2141 pThis->pHostDrvAudio = NULL;
2142
2143 LogFlowFuncLeave();
2144}
2145
2146/**
2147 * Constructs an audio driver instance.
2148 *
2149 * @copydoc FNPDMDRVCONSTRUCT
2150 */
2151static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
2152{
2153 LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfgHandle, fFlags));
2154
2155 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2156 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2157
2158 RTListInit(&pThis->lstHstStreams);
2159 RTListInit(&pThis->lstGstStreams);
2160#ifdef VBOX_WITH_AUDIO_CALLBACKS
2161 RTListInit(&pThis->lstCBIn);
2162 RTListInit(&pThis->lstCBOut);
2163#endif
2164
2165 /*
2166 * Init the static parts.
2167 */
2168 pThis->pDrvIns = pDrvIns;
2169 /* IBase. */
2170 pDrvIns->IBase.pfnQueryInterface = drvAudioQueryInterface;
2171 /* IAudioConnector. */
2172 pThis->IAudioConnector.pfnGetConfig = drvAudioGetConfig;
2173 pThis->IAudioConnector.pfnGetStatus = drvAudioGetStatus;
2174 pThis->IAudioConnector.pfnStreamCreate = drvAudioStreamCreate;
2175 pThis->IAudioConnector.pfnStreamDestroy = drvAudioStreamDestroy;
2176 pThis->IAudioConnector.pfnStreamRetain = drvAudioStreamRetain;
2177 pThis->IAudioConnector.pfnStreamRelease = drvAudioStreamRelease;
2178 pThis->IAudioConnector.pfnStreamControl = drvAudioStreamControl;
2179 pThis->IAudioConnector.pfnStreamRead = drvAudioStreamRead;
2180 pThis->IAudioConnector.pfnStreamWrite = drvAudioStreamWrite;
2181 pThis->IAudioConnector.pfnStreamIterate = drvAudioStreamIterate;
2182 pThis->IAudioConnector.pfnStreamGetReadable = drvAudioStreamGetReadable;
2183 pThis->IAudioConnector.pfnStreamGetWritable = drvAudioStreamGetWritable;
2184 pThis->IAudioConnector.pfnStreamGetStatus = drvAudioStreamGetStatus;
2185 pThis->IAudioConnector.pfnStreamSetVolume = drvAudioStreamSetVolume;
2186 pThis->IAudioConnector.pfnStreamPlay = drvAudioStreamPlay;
2187 pThis->IAudioConnector.pfnStreamCapture = drvAudioStreamCapture;
2188#ifdef VBOX_WITH_AUDIO_CALLBACKS
2189 pThis->IAudioConnector.pfnRegisterCallbacks = drvAudioRegisterCallbacks;
2190 pThis->IAudioConnector.pfnCallback = drvAudioCallback;
2191#endif
2192
2193 /*
2194 * Attach driver below and query its connector interface.
2195 */
2196 PPDMIBASE pDownBase;
2197 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
2198 if (RT_FAILURE(rc))
2199 {
2200 LogRel(("Audio: Failed to attach to driver %p below (flags=0x%x), rc=%Rrc\n",
2201 pDrvIns, fFlags, rc));
2202 return rc;
2203 }
2204
2205 pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
2206 if (!pThis->pHostDrvAudio)
2207 {
2208 LogRel(("Audio: Failed to query interface for underlying host driver\n"));
2209 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
2210 N_("Host audio backend missing or invalid"));
2211 }
2212
2213 rc = drvAudioInit(pDrvIns, pCfgHandle);
2214 if (RT_SUCCESS(rc))
2215 {
2216 pThis->fTerminate = false;
2217 pThis->pDrvIns = pDrvIns;
2218
2219#ifdef VBOX_WITH_STATISTICS
2220 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsActive, "TotalStreamsActive",
2221 STAMUNIT_COUNT, "Active input streams.");
2222 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsCreated, "TotalStreamsCreated",
2223 STAMUNIT_COUNT, "Total created input streams.");
2224 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalSamplesPlayed, "TotalSamplesPlayed",
2225 STAMUNIT_COUNT, "Total samples played.");
2226 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalSamplesCaptured, "TotalSamplesCaptured",
2227 STAMUNIT_COUNT, "Total samples captured.");
2228 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesRead, "TotalBytesRead",
2229 STAMUNIT_BYTES, "Total bytes read.");
2230 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesWritten, "TotalBytesWritten",
2231 STAMUNIT_BYTES, "Total bytes written.");
2232#endif
2233 }
2234
2235 LogFlowFuncLeaveRC(rc);
2236 return rc;
2237}
2238
2239/**
2240 * Destructs an audio driver instance.
2241 *
2242 * @copydoc FNPDMDRVDESTRUCT
2243 */
2244static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
2245{
2246 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2247 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2248
2249 LogFlowFuncEnter();
2250
2251 int rc2 = RTCritSectEnter(&pThis->CritSect);
2252 AssertRC(rc2);
2253
2254 /*
2255 * Note: No calls here to the driver below us anymore,
2256 * as PDM already has destroyed it.
2257 * If you need to call something from the host driver,
2258 * do this in drvAudioPowerOff() instead.
2259 */
2260
2261 /* Thus, NULL the pointer to the host audio driver first,
2262 * so that routines like drvAudioStreamDestroyInternal() don't call the driver(s) below us anymore. */
2263 pThis->pHostDrvAudio = NULL;
2264
2265 PPDMAUDIOSTREAM pStream, pStreamNext;
2266 RTListForEachSafe(&pThis->lstHstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
2267 drvAudioStreamDestroyInternal(pThis, pStream);
2268
2269 RTListForEachSafe(&pThis->lstGstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
2270 drvAudioStreamDestroyInternal(pThis, pStream);
2271
2272#ifdef VBOX_WITH_AUDIO_CALLBACKS
2273 /*
2274 * Destroy callbacks, if any.
2275 */
2276 PPDMAUDIOCALLBACK pCB, pCBNext;
2277 RTListForEachSafe(&pThis->lstCBIn, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
2278 drvAudioCallbackDestroy(pCB);
2279
2280 RTListForEachSafe(&pThis->lstCBOut, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
2281 drvAudioCallbackDestroy(pCB);
2282#endif
2283
2284 rc2 = RTCritSectLeave(&pThis->CritSect);
2285 AssertRC(rc2);
2286
2287 rc2 = RTCritSectDelete(&pThis->CritSect);
2288 AssertRC(rc2);
2289
2290 LogFlowFuncLeave();
2291}
2292
2293/**
2294 * Suspend notification.
2295 *
2296 * @param pDrvIns The driver instance data.
2297 */
2298static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
2299{
2300 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_PAUSE);
2301}
2302
2303/**
2304 * Resume notification.
2305 *
2306 * @param pDrvIns The driver instance data.
2307 */
2308static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
2309{
2310 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_RESUME);
2311}
2312
2313/**
2314 * Audio driver registration record.
2315 */
2316const PDMDRVREG g_DrvAUDIO =
2317{
2318 /* u32Version */
2319 PDM_DRVREG_VERSION,
2320 /* szName */
2321 "AUDIO",
2322 /* szRCMod */
2323 "",
2324 /* szR0Mod */
2325 "",
2326 /* pszDescription */
2327 "Audio connector driver",
2328 /* fFlags */
2329 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2330 /* fClass */
2331 PDM_DRVREG_CLASS_AUDIO,
2332 /* cMaxInstances */
2333 UINT32_MAX,
2334 /* cbInstance */
2335 sizeof(DRVAUDIO),
2336 /* pfnConstruct */
2337 drvAudioConstruct,
2338 /* pfnDestruct */
2339 drvAudioDestruct,
2340 /* pfnRelocate */
2341 NULL,
2342 /* pfnIOCtl */
2343 NULL,
2344 /* pfnPowerOn */
2345 NULL,
2346 /* pfnReset */
2347 NULL,
2348 /* pfnSuspend */
2349 drvAudioSuspend,
2350 /* pfnResume */
2351 drvAudioResume,
2352 /* pfnAttach */
2353 NULL,
2354 /* pfnDetach */
2355 NULL,
2356 /* pfnPowerOff */
2357 drvAudioPowerOff,
2358 /* pfnSoftReset */
2359 NULL,
2360 /* u32EndVersion */
2361 PDM_DRVREG_VERSION
2362};
2363
Note: See TracBrowser for help on using the repository browser.

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