VirtualBox

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

Last change on this file since 63654 was 63624, checked in by vboxsync, 8 years ago

Audio/DrvAudio.cpp: Fixed capturing input data.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 76.3 KB
Line 
1/* $Id: DrvAudio.cpp 63624 2016-08-24 08:32:51Z 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 AssertReleaseMsgBreakStmt(pHstStream != NULL,
1028 ("%s: Host stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1029 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1030 rc = VERR_NOT_AVAILABLE);
1031 AssertReleaseMsgBreakStmt(pGstStream != NULL,
1032 ("%s: Guest stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1033 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1034 rc = VERR_NOT_AVAILABLE);
1035
1036 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetStatus);
1037 PDMAUDIOSTRMSTS strmSts = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream);
1038 if (!(strmSts & PDMAUDIOSTRMSTS_FLAG_INITIALIZED))
1039 {
1040 LogFunc(("[%s] Backend not initialized (anymore), re-initializing ...\n", pHstStream->szName));
1041 rc = drvAudioStreamReInitInternal(pThis, pStream);
1042 if (RT_FAILURE(rc))
1043 {
1044 LogFunc(("[%s] Failed to re-initialize backend, rc=%Rrc\n", pHstStream->szName, rc));
1045 break;
1046 }
1047 }
1048
1049 uint32_t cSamplesLive = AudioMixBufLive(&pGstStream->MixBuf);
1050 if (cSamplesLive)
1051 {
1052 if ( (strmSts & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
1053 && (strmSts & PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE))
1054 {
1055 AssertPtr(pThis->pHostDrvAudio->pfnStreamPlay);
1056 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pHstStream, NULL /* pvBuf */, 0 /* cbBuf */,
1057 &cSamplesPlayed);
1058 if (RT_FAILURE(rc))
1059 {
1060 int rc2 = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1061 AssertRC(rc2);
1062 }
1063 else
1064 {
1065#ifdef VBOX_WITH_STATISTICS
1066 STAM_COUNTER_ADD(&pThis->Stats.TotalSamplesPlayed, cSamplesPlayed);
1067 STAM_COUNTER_ADD(&pHstStream->Out.StatSamplesPlayed, cSamplesPlayed);
1068#endif
1069 cSamplesLive = AudioMixBufLive(&pGstStream->MixBuf);
1070 }
1071 }
1072 }
1073
1074 if (!cSamplesLive)
1075 {
1076 /* Has the host stream marked as disabled but there still were guest streams relying
1077 * on it? Check if the stream now can be closed and do so, if possible. */
1078 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
1079 {
1080 LogFunc(("[%s] Closing pending stream\n", pHstStream->szName));
1081 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1082 if (RT_SUCCESS(rc))
1083 {
1084 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
1085 }
1086 else
1087 LogFunc(("[%s] Backend vetoed against closing output stream, rc=%Rrc\n", pHstStream->szName, rc));
1088 }
1089 }
1090
1091 } while (0);
1092
1093 int rc2 = RTCritSectLeave(&pThis->CritSect);
1094 if (RT_SUCCESS(rc))
1095 rc = rc2;
1096
1097 if (RT_SUCCESS(rc))
1098 {
1099 if (pcSamplesPlayed)
1100 *pcSamplesPlayed = cSamplesPlayed;
1101 }
1102
1103 if (RT_FAILURE(rc))
1104 LogFlowFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
1105
1106 return rc;
1107}
1108
1109/**
1110 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCapture}
1111 */
1112static DECLCALLBACK(int) drvAudioStreamCapture(PPDMIAUDIOCONNECTOR pInterface,
1113 PPDMAUDIOSTREAM pStream, uint32_t *pcSamplesCaptured)
1114{
1115 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1116
1117 int rc = RTCritSectEnter(&pThis->CritSect);
1118 if (RT_FAILURE(rc))
1119 return rc;
1120
1121 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
1122 ("Stream '%s' is not an input stream and therefore cannot be captured (direction is 0x%x)\n",
1123 pStream->szName, pStream->enmDir));
1124
1125 Log3Func(("[%s]\n", pStream->szName));
1126
1127 uint32_t cSamplesCaptured = 0;
1128
1129 do
1130 {
1131 /* Backend input (temporarily) disabled / unavailable? */
1132 if (pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_IN) != PDMAUDIOBACKENDSTS_RUNNING)
1133 {
1134 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
1135 AssertRC(rc);
1136
1137 if ( !pThis->BackendCfg.cSources
1138 || !pThis->BackendCfg.cMaxStreamsIn)
1139 {
1140 rc = VERR_NOT_AVAILABLE;
1141 break;
1142 }
1143 }
1144
1145 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1146 AssertPtr(pHstStream);
1147 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1148 AssertPtr(pGstStream);
1149
1150 AssertReleaseMsgBreakStmt(pHstStream != NULL,
1151 ("%s: Host stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1152 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1153 rc = VERR_NOT_AVAILABLE);
1154 AssertReleaseMsgBreakStmt(pGstStream != NULL,
1155 ("%s: Guest stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1156 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1157 rc = VERR_NOT_AVAILABLE);
1158
1159 PDMAUDIOSTRMSTS strmSts = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream);
1160 if (!(strmSts & PDMAUDIOSTRMSTS_FLAG_INITIALIZED))
1161 {
1162 LogFunc(("[%s] Backend not initialized (anymore), re-initializing ...\n", pHstStream->szName));
1163 rc = drvAudioStreamReInitInternal(pThis, pStream);
1164 break;
1165 }
1166
1167 uint32_t cSamplesLive = AudioMixBufLive(&pGstStream->MixBuf);
1168 if (!cSamplesLive)
1169 {
1170 if ( (strmSts & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
1171 && (strmSts & PDMAUDIOSTRMSTS_FLAG_DATA_READABLE))
1172 {
1173 rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pHstStream, NULL /* pvBuf */, 0 /* cbBuf */,
1174 &cSamplesCaptured);
1175 if (RT_FAILURE(rc))
1176 {
1177 int rc2 = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1178 AssertRC(rc2);
1179 }
1180 else
1181 {
1182#ifdef VBOX_WITH_STATISTICS
1183 STAM_COUNTER_ADD(&pHstStream->In.StatSamplesCaptured, cSamplesCaptured);
1184#endif
1185 }
1186 }
1187
1188 Log3Func(("[%s] strmSts=0x%x, cSamplesCaptured=%RU32, rc=%Rrc\n", pHstStream->szName, strmSts, cSamplesCaptured, rc));
1189 }
1190
1191 } while (0);
1192
1193 if (RT_SUCCESS(rc))
1194 {
1195 if (pcSamplesCaptured)
1196 *pcSamplesCaptured = cSamplesCaptured;
1197 }
1198
1199 int rc2 = RTCritSectLeave(&pThis->CritSect);
1200 if (RT_SUCCESS(rc))
1201 rc = rc2;
1202
1203 if (RT_FAILURE(rc))
1204 LogFlowFuncLeaveRC(rc);
1205
1206 return rc;
1207}
1208
1209#ifdef VBOX_WITH_AUDIO_CALLBACKS
1210/**
1211 * Duplicates an audio callback.
1212 *
1213 * @returns Pointer to duplicated callback, or NULL on failure.
1214 * @param pCB Callback to duplicate.
1215 */
1216static PPDMAUDIOCALLBACK drvAudioCallbackDuplicate(PPDMAUDIOCALLBACK pCB)
1217{
1218 AssertPtrReturn(pCB, VERR_INVALID_POINTER);
1219
1220 PPDMAUDIOCALLBACK pCBCopy = (PPDMAUDIOCALLBACK)RTMemDup((void *)pCB, sizeof(PDMAUDIOCALLBACK));
1221 if (!pCBCopy)
1222 return NULL;
1223
1224 if (pCB->pvCtx)
1225 {
1226 pCBCopy->pvCtx = RTMemDup(pCB->pvCtx, pCB->cbCtx);
1227 if (!pCBCopy->pvCtx)
1228 {
1229 RTMemFree(pCBCopy);
1230 return NULL;
1231 }
1232
1233 pCBCopy->cbCtx = pCB->cbCtx;
1234 }
1235
1236 return pCBCopy;
1237}
1238
1239/**
1240 * Destroys a given callback.
1241 *
1242 * @param pCB Callback to destroy.
1243 */
1244static void drvAudioCallbackDestroy(PPDMAUDIOCALLBACK pCB)
1245{
1246 if (!pCB)
1247 return;
1248
1249 RTListNodeRemove(&pCB->Node);
1250 if (pCB->pvCtx)
1251 {
1252 Assert(pCB->cbCtx);
1253 RTMemFree(pCB->pvCtx);
1254 }
1255 RTMemFree(pCB);
1256}
1257
1258/**
1259 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnRegisterCallbacks}
1260 */
1261static DECLCALLBACK(int) drvAudioRegisterCallbacks(PPDMIAUDIOCONNECTOR pInterface,
1262 PPDMAUDIOCALLBACK paCallbacks, size_t cCallbacks)
1263{
1264 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1265 AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER);
1266 AssertReturn(cCallbacks, VERR_INVALID_PARAMETER);
1267
1268 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1269
1270 int rc = RTCritSectEnter(&pThis->CritSect);
1271 if (RT_FAILURE(rc))
1272 return rc;
1273
1274 for (size_t i = 0; i < cCallbacks; i++)
1275 {
1276 PPDMAUDIOCALLBACK pCB = drvAudioCallbackDuplicate(&paCallbacks[i]);
1277 if (!pCB)
1278 {
1279 rc = VERR_NO_MEMORY;
1280 break;
1281 }
1282
1283 switch (pCB->enmType)
1284 {
1285 case PDMAUDIOCALLBACKTYPE_INPUT:
1286 RTListAppend(&pThis->lstCBIn, &pCB->Node);
1287 break;
1288
1289 case PDMAUDIOCALLBACKTYPE_OUTPUT:
1290 RTListAppend(&pThis->lstCBOut, &pCB->Node);
1291 break;
1292
1293 default:
1294 AssertMsgFailed(("Not supported\n"));
1295 break;
1296 }
1297 }
1298
1299 /** @todo Undo allocations on error. */
1300
1301 int rc2 = RTCritSectLeave(&pThis->CritSect);
1302 if (RT_SUCCESS(rc))
1303 rc = rc2;
1304
1305 return rc;
1306}
1307
1308/**
1309 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnCallback}
1310 */
1311static DECLCALLBACK(int) drvAudioCallback(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIOCALLBACKTYPE enmType,
1312 void *pvUser, size_t cbUser)
1313{
1314 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1315 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
1316 AssertReturn(cbUser, VERR_INVALID_PARAMETER);
1317
1318 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1319 PRTLISTANCHOR pListAnchor = NULL;
1320
1321 switch (enmType)
1322 {
1323 case PDMAUDIOCALLBACKTYPE_INPUT:
1324 pListAnchor = &pThis->lstCBIn;
1325 break;
1326
1327 case PDMAUDIOCALLBACKTYPE_OUTPUT:
1328 pListAnchor = &pThis->lstCBOut;
1329 break;
1330
1331 default:
1332 AssertMsgFailed(("Not supported\n"));
1333 break;
1334 }
1335
1336 if (pListAnchor)
1337 {
1338 PPDMAUDIOCALLBACK pCB;
1339 RTListForEach(pListAnchor, pCB, PDMAUDIOCALLBACK, Node)
1340 {
1341 Assert(pCB->enmType == enmType);
1342 pCB->pfnCallback(enmType, pCB->pvCtx, pCB->cbCtx, pvUser, cbUser);
1343 }
1344 }
1345
1346 return VINF_SUCCESS;
1347}
1348#endif
1349
1350/**
1351 * Initializes the host backend and queries its initial configuration.
1352 * If the host backend fails, VERR_AUDIO_BACKEND_INIT_FAILED will be returned.
1353 *
1354 * Note: As this routine is called when attaching to the device LUN in the
1355 * device emulation, we either check for success or VERR_AUDIO_BACKEND_INIT_FAILED.
1356 * Everything else is considered as fatal and must be handled separately in
1357 * the device emulation!
1358 *
1359 * @return IPRT status code.
1360 * @param pThis Driver instance to be called.
1361 * @param pCfgHandle CFGM configuration handle to use for this driver.
1362 */
1363static int drvAudioHostInit(PDRVAUDIO pThis, PCFGMNODE pCfgHandle)
1364{
1365 /* pCfgHandle is optional. */
1366 NOREF(pCfgHandle);
1367 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1368
1369 LogFlowFuncEnter();
1370
1371 AssertPtr(pThis->pHostDrvAudio);
1372 int rc = pThis->pHostDrvAudio->pfnInit(pThis->pHostDrvAudio);
1373 if (RT_FAILURE(rc))
1374 {
1375 LogRel(("Audio: Initialization of host backend failed with %Rrc\n", rc));
1376 return VERR_AUDIO_BACKEND_INIT_FAILED;
1377 }
1378
1379 /* Get the configuration data from backend. */
1380 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
1381 if (RT_FAILURE(rc))
1382 {
1383 LogRel(("Audio: Getting host backend configuration failed with %Rrc\n", rc));
1384 return VERR_AUDIO_BACKEND_INIT_FAILED;
1385 }
1386
1387 pThis->cStreamsFreeIn = 0;
1388 pThis->cStreamsFreeOut = 0;
1389
1390 if (pThis->BackendCfg.cSinks)
1391 {
1392 Assert(pThis->BackendCfg.cbStreamOut);
1393
1394 pThis->cStreamsFreeOut = pThis->BackendCfg.cMaxStreamsOut;
1395 }
1396
1397 if (pThis->BackendCfg.cSources)
1398 {
1399 Assert(pThis->BackendCfg.cbStreamIn);
1400
1401 pThis->cStreamsFreeIn = pThis->BackendCfg.cMaxStreamsIn;
1402 }
1403
1404 LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->cStreamsFreeIn, pThis->cStreamsFreeOut));
1405
1406 LogRel2(("Audio: Host audio backend supports %RU32 input streams and %RU32 output streams at once\n",
1407 /* Clamp for logging. Unlimited streams are defined by UINT32_MAX. */
1408 RT_MIN(64, pThis->cStreamsFreeIn), RT_MIN(64, pThis->cStreamsFreeOut)));
1409
1410 LogFlowFuncLeave();
1411 return VINF_SUCCESS;
1412}
1413
1414/**
1415 * Handles state changes for all audio streams.
1416 *
1417 * @param pDrvIns Pointer to driver instance.
1418 * @param enmCmd Stream command to set for all streams.
1419 */
1420static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
1421{
1422 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1423 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1424
1425 LogFlowFunc(("enmCmd=%RU32\n", enmCmd));
1426
1427 if (!pThis->pHostDrvAudio)
1428 return;
1429
1430 PPDMAUDIOSTREAM pHstStream;
1431 RTListForEach(&pThis->lstHstStreams, pHstStream, PDMAUDIOSTREAM, Node)
1432 drvAudioStreamControlInternalBackend(pThis, pHstStream, enmCmd);
1433}
1434
1435/**
1436 * Intializes an audio driver instance.
1437 *
1438 * @returns IPRT status code.
1439 * @param pDrvIns Pointer to driver instance.
1440 * @param pCfgHandle CFGM handle to use for configuration.
1441 */
1442static int drvAudioInit(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
1443{
1444 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
1445 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
1446
1447 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1448 LogFlowFunc(("pThis=%p, pDrvIns=%p\n", pThis, pDrvIns));
1449
1450 int rc = RTCritSectInit(&pThis->CritSect);
1451
1452 /** @todo Add audio driver options. */
1453
1454 /*
1455 * If everything went well, initialize the lower driver.
1456 */
1457 if (RT_SUCCESS(rc))
1458 rc = drvAudioHostInit(pThis, pCfgHandle);
1459
1460 LogFlowFuncLeaveRC(rc);
1461 return rc;
1462}
1463
1464/**
1465 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRead}
1466 */
1467static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
1468 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1469{
1470 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1471 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1472
1473 if (!pStream)
1474 {
1475 if (pcbRead)
1476 *pcbRead = 0;
1477 return VINF_SUCCESS;
1478 }
1479
1480 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1481 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1482 /* pcbWritten is optional. */
1483
1484 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
1485 ("Stream '%s' is not an input stream and therefore cannot be read from (direction is 0x%x)\n",
1486 pStream->szName, pStream->enmDir));
1487
1488 uint32_t cbRead = 0;
1489
1490 int rc = RTCritSectEnter(&pThis->CritSect);
1491 if (RT_FAILURE(rc))
1492 return rc;
1493
1494 do
1495 {
1496 if ( pThis->pHostDrvAudio
1497 && pThis->pHostDrvAudio->pfnGetStatus
1498 && pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_IN) != PDMAUDIOBACKENDSTS_RUNNING)
1499 {
1500 rc = VERR_NOT_AVAILABLE;
1501 break;
1502 }
1503
1504 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1505 if (!pHstStream)
1506 {
1507 rc = VERR_NOT_AVAILABLE;
1508 break;
1509 }
1510
1511 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1512
1513 AssertMsg(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
1514 ("Reading from disabled host input stream '%s' not possible\n", pHstStream->szName));
1515
1516 pGstStream->In.tsLastReadMS = RTTimeMilliTS();
1517
1518 Log3Func(("%s\n", pStream->szName));
1519
1520 /*
1521 * Read from the parent buffer (that is, the guest buffer) which
1522 * should have the audio data in the format the guest needs.
1523 */
1524 uint32_t cRead;
1525 rc = AudioMixBufReadCirc(&pGstStream->MixBuf, pvBuf, cbBuf, &cRead);
1526 if (RT_SUCCESS(rc))
1527 {
1528 if (cRead)
1529 {
1530#ifdef VBOX_WITH_STATISTICS
1531 STAM_COUNTER_ADD(&pThis->Stats.TotalBytesRead, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cRead));
1532 STAM_COUNTER_ADD(&pGstStream->In.StatBytesTotalRead, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cRead));
1533#endif
1534 AudioMixBufFinish(&pGstStream->MixBuf, cRead);
1535
1536 cbRead = AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cRead);
1537 }
1538 }
1539
1540 Log3Func(("cRead=%RU32 (%RU32 bytes), rc=%Rrc\n", cRead, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cRead), rc));
1541
1542 } while (0);
1543
1544 int rc2 = RTCritSectLeave(&pThis->CritSect);
1545 if (RT_SUCCESS(rc))
1546 rc = rc2;
1547
1548 if (RT_SUCCESS(rc))
1549 {
1550 if (pcbRead)
1551 *pcbRead = cbRead;
1552 }
1553
1554 return rc;
1555}
1556
1557/**
1558 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCreate}
1559 */
1560static DECLCALLBACK(int) drvAudioStreamCreate(PPDMIAUDIOCONNECTOR pInterface,
1561 PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest,
1562 PPDMAUDIOSTREAM *ppStream)
1563{
1564 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1565 AssertPtrReturn(pCfgHost, VERR_INVALID_POINTER);
1566 AssertPtrReturn(pCfgGuest, VERR_INVALID_POINTER);
1567 AssertPtrReturn(ppStream, VERR_INVALID_POINTER);
1568
1569 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1570
1571 int rc = RTCritSectEnter(&pThis->CritSect);
1572 if (RT_FAILURE(rc))
1573 return rc;
1574
1575 LogFlowFunc(("Host=%s, Guest=%s\n", pCfgHost->szName, pCfgGuest->szName));
1576#ifdef DEBUG
1577 DrvAudioHlpStreamCfgPrint(pCfgHost);
1578 DrvAudioHlpStreamCfgPrint(pCfgGuest);
1579#endif
1580
1581 /*
1582 * The guest stream always will get the audio stream configuration told
1583 * by the device emulation (which in turn was/could be set by the guest OS).
1584 */
1585 PPDMAUDIOSTREAM pGstStrm = NULL;
1586
1587 /** @todo Docs! */
1588 PPDMAUDIOSTREAM pHstStrm = NULL;
1589
1590#define RC_BREAK(x) { rc = x; break; }
1591
1592 do
1593 {
1594 if ( !DrvAudioHlpStreamCfgIsValid(pCfgHost)
1595 || !DrvAudioHlpStreamCfgIsValid(pCfgGuest))
1596 {
1597 RC_BREAK(VERR_INVALID_PARAMETER);
1598 }
1599
1600 /* Make sure that both configurations actually intend the same thing. */
1601 if (pCfgHost->enmDir != pCfgGuest->enmDir)
1602 {
1603 AssertMsgFailed(("Stream configuration directions do not match\n"));
1604 RC_BREAK(VERR_INVALID_PARAMETER);
1605 }
1606
1607 /* Note: cbHstStrm will contain sizeof(PDMAUDIOSTREAM) + additional data
1608 * which the host backend will need. */
1609 size_t cbHstStrm;
1610 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
1611 {
1612 if (!pThis->cStreamsFreeIn)
1613 LogFunc(("Warning: No more input streams free to use\n"));
1614
1615 /* Validate backend configuration. */
1616 if (!pThis->BackendCfg.cbStreamIn)
1617 {
1618 LogFunc(("Backend input configuration not valid, bailing out\n"));
1619 RC_BREAK(VERR_INVALID_PARAMETER);
1620 }
1621
1622 cbHstStrm = pThis->BackendCfg.cbStreamIn;
1623 }
1624 else /* Out */
1625 {
1626 if (!pThis->cStreamsFreeOut)
1627 {
1628 LogFlowFunc(("Maximum number of host output streams reached\n"));
1629 RC_BREAK(VERR_AUDIO_NO_FREE_OUTPUT_STREAMS);
1630 }
1631
1632 /* Validate backend configuration. */
1633 if (!pThis->BackendCfg.cbStreamOut)
1634 {
1635 LogFlowFunc(("Backend output configuration invalid, bailing out\n"));
1636 RC_BREAK(VERR_INVALID_PARAMETER);
1637 }
1638
1639 cbHstStrm = pThis->BackendCfg.cbStreamOut;
1640 }
1641
1642 pHstStrm = (PPDMAUDIOSTREAM)RTMemAllocZ(cbHstStrm);
1643 AssertPtrBreakStmt(pHstStrm, rc = VERR_NO_MEMORY);
1644
1645 pHstStrm->enmCtx = PDMAUDIOSTREAMCTX_HOST;
1646 pHstStrm->enmDir = pCfgHost->enmDir;
1647
1648 pGstStrm = (PPDMAUDIOSTREAM)RTMemAllocZ(sizeof(PDMAUDIOSTREAM));
1649 AssertPtrBreakStmt(pGstStrm, rc = VERR_NO_MEMORY);
1650
1651 pGstStrm->enmCtx = PDMAUDIOSTREAMCTX_GUEST;
1652 pGstStrm->enmDir = pCfgGuest->enmDir;
1653
1654 /*
1655 * Init host stream.
1656 */
1657 RTStrPrintf(pHstStrm->szName, RT_ELEMENTS(pHstStrm->szName), "%s (Host)",
1658 strlen(pCfgHost->szName) ? pCfgHost->szName : "<Untitled>");
1659
1660 pHstStrm->pPair = pGstStrm;
1661
1662 /*
1663 * Init guest stream.
1664 */
1665 RTStrPrintf(pGstStrm->szName, RT_ELEMENTS(pGstStrm->szName), "%s (Guest)",
1666 strlen(pCfgGuest->szName) ? pCfgGuest->szName : "<Untitled>");
1667
1668 pGstStrm->fStatus = pHstStrm->fStatus; /* Reflect the host stream's status. */
1669 pGstStrm->pPair = pHstStrm;
1670
1671 /*
1672 * Try to init the rest.
1673 */
1674 rc = drvAudioStreamInitInternal(pThis, pHstStrm, pCfgHost, pCfgGuest);
1675 if (RT_FAILURE(rc))
1676 {
1677 LogFlowFunc(("Stream not available (yet)\n"));
1678 rc = VINF_SUCCESS;
1679 }
1680
1681 } while (0);
1682
1683#undef RC_BREAK
1684
1685 if (RT_FAILURE(rc))
1686 {
1687 if (pGstStrm)
1688 {
1689 drvAudioStreamDestroyInternal(pThis, pGstStrm);
1690 pGstStrm = NULL;
1691 }
1692
1693 if (pHstStrm)
1694 {
1695 drvAudioStreamDestroyInternal(pThis, pHstStrm);
1696 pHstStrm = NULL;
1697 }
1698 }
1699 else
1700 {
1701 /* Set initial reference counts. */
1702 RTListAppend(&pThis->lstGstStreams, &pGstStrm->Node);
1703 pGstStrm->cRefs = 1;
1704
1705 RTListAppend(&pThis->lstHstStreams, &pHstStrm->Node);
1706 pHstStrm->cRefs = 1;
1707
1708 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
1709 {
1710 if (pThis->cStreamsFreeIn)
1711 pThis->cStreamsFreeIn--;
1712 }
1713 else /* Out */
1714 {
1715 if (pThis->cStreamsFreeOut)
1716 pThis->cStreamsFreeOut--;
1717 }
1718
1719#ifdef VBOX_WITH_STATISTICS
1720 STAM_COUNTER_ADD(&pThis->Stats.TotalStreamsCreated, 1);
1721#endif
1722 /* Always return the guest-side part to the device emulation. */
1723 *ppStream = pGstStrm;
1724 }
1725
1726 int rc2 = RTCritSectLeave(&pThis->CritSect);
1727 if (RT_SUCCESS(rc))
1728 rc = rc2;
1729
1730 LogFlowFuncLeaveRC(rc);
1731 return rc;
1732}
1733
1734/**
1735 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetConfig}
1736 */
1737static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
1738{
1739 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1740 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1741
1742 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1743
1744 int rc = RTCritSectEnter(&pThis->CritSect);
1745 if (RT_FAILURE(rc))
1746 return rc;
1747
1748 if (pThis->pHostDrvAudio)
1749 {
1750 if (pThis->pHostDrvAudio->pfnGetConfig)
1751 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, pCfg);
1752 else
1753 rc = VERR_NOT_SUPPORTED;
1754 }
1755 else
1756 AssertFailed();
1757
1758 int rc2 = RTCritSectLeave(&pThis->CritSect);
1759 if (RT_SUCCESS(rc))
1760 rc = rc2;
1761
1762 LogFlowFuncLeaveRC(rc);
1763 return rc;
1764}
1765
1766/**
1767 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetStatus}
1768 */
1769static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioGetStatus(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
1770{
1771 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
1772
1773 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1774
1775 PDMAUDIOBACKENDSTS backendSts = PDMAUDIOBACKENDSTS_UNKNOWN;
1776
1777 int rc = RTCritSectEnter(&pThis->CritSect);
1778 if (RT_SUCCESS(rc))
1779 {
1780 if ( pThis->pHostDrvAudio
1781 && pThis->pHostDrvAudio->pfnGetStatus)
1782 {
1783 backendSts = pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, enmDir);
1784 }
1785
1786 int rc2 = RTCritSectLeave(&pThis->CritSect);
1787 if (RT_SUCCESS(rc))
1788 rc = rc2;
1789 }
1790
1791 LogFlowFuncLeaveRC(rc);
1792 return backendSts;
1793}
1794
1795/**
1796 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetReadable}
1797 */
1798static DECLCALLBACK(uint32_t) drvAudioStreamGetReadable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1799{
1800 AssertPtrReturn(pInterface, 0);
1801 AssertPtrReturn(pStream, 0);
1802
1803 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1804
1805 int rc2 = RTCritSectEnter(&pThis->CritSect);
1806 AssertRC(rc2);
1807
1808 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN, ("Can't read from a non-input stream\n"));
1809
1810 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1811 if (!pHstStream) /* No host stream available? Bail out early. */
1812 {
1813 rc2 = RTCritSectLeave(&pThis->CritSect);
1814 AssertRC(rc2);
1815
1816 return 0;
1817 }
1818
1819 uint32_t cReadable = 0;
1820
1821 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1822 if (pGstStream)
1823 cReadable = AudioMixBufLive(&pGstStream->MixBuf);
1824
1825 Log3Func(("[%s] cbReadable=%RU32 (%zu bytes)\n", pHstStream->szName, cReadable,
1826 AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cReadable)));
1827
1828 rc2 = RTCritSectLeave(&pThis->CritSect);
1829 AssertRC(rc2);
1830
1831 /* Return bytes instead of audio samples. */
1832 return AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cReadable);
1833}
1834
1835/**
1836 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetWritable}
1837 */
1838static DECLCALLBACK(uint32_t) drvAudioStreamGetWritable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1839{
1840 AssertPtrReturn(pInterface, 0);
1841 AssertPtrReturn(pStream, 0);
1842
1843 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1844
1845 int rc2 = RTCritSectEnter(&pThis->CritSect);
1846 AssertRC(rc2);
1847
1848 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT, ("Can't write to a non-output stream\n"));
1849
1850 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1851 if (!pHstStream) /* No host stream available? Bail out early. */
1852 {
1853 rc2 = RTCritSectLeave(&pThis->CritSect);
1854 AssertRC(rc2);
1855
1856 AssertMsgFailed(("Guest stream '%s' does not have a host stream attached\n", pStream->szName));
1857 return 0;
1858 }
1859
1860 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
1861 AssertPtr(pGstStream);
1862
1863 uint32_t cWritable = AudioMixBufFree(&pGstStream->MixBuf);
1864
1865 Log3Func(("[%s] cWritable=%RU32 (%zu bytes)\n", pHstStream->szName, cWritable,
1866 AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritable)));
1867
1868 rc2 = RTCritSectLeave(&pThis->CritSect);
1869 AssertRC(rc2);
1870
1871 /* Return bytes instead of audio samples. */
1872 return AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritable);
1873}
1874
1875/**
1876 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetStatus}
1877 */
1878static DECLCALLBACK(PDMAUDIOSTRMSTS) drvAudioStreamGetStatus(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1879{
1880 AssertPtrReturn(pInterface, false);
1881
1882 if (!pStream)
1883 return PDMAUDIOSTRMSTS_FLAG_NONE;
1884
1885 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1886
1887 int rc2 = RTCritSectEnter(&pThis->CritSect);
1888 AssertRC(rc2);
1889
1890 PDMAUDIOSTRMSTS strmSts = PDMAUDIOSTRMSTS_FLAG_NONE;
1891
1892 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1893 if (pHstStream)
1894 {
1895 strmSts = pHstStream->fStatus;
1896 Log3Func(("%s: strmSts=0x%x\n", pHstStream->szName, strmSts));
1897 }
1898
1899 rc2 = RTCritSectLeave(&pThis->CritSect);
1900 AssertRC(rc2);
1901
1902 return strmSts;
1903}
1904
1905/**
1906 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamSetVolume}
1907 */
1908static DECLCALLBACK(int) drvAudioStreamSetVolume(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, PPDMAUDIOVOLUME pVol)
1909{
1910 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1911 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1912 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1913
1914 LogFlowFunc(("%s: volL=%RU32, volR=%RU32, fMute=%RTbool\n", pStream->szName, pVol->uLeft, pVol->uRight, pVol->fMuted));
1915
1916 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1917 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
1918
1919 AudioMixBufSetVolume(&pHstStream->MixBuf, pVol);
1920 AudioMixBufSetVolume(&pGstStream->MixBuf, pVol);
1921 return VINF_SUCCESS;
1922}
1923
1924/**
1925 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamDestroy}
1926 */
1927static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1928{
1929 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1930 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1931
1932 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1933
1934 int rc = RTCritSectEnter(&pThis->CritSect);
1935 AssertRC(rc);
1936
1937 PDMAUDIODIR enmDir = pStream->enmDir;
1938
1939 LogFlowFunc(("%s: cRefs=%RU32\n", pStream->szName, pStream->cRefs));
1940 if (pStream->cRefs > 1)
1941 rc = VERR_WRONG_ORDER;
1942
1943 if (RT_SUCCESS(rc))
1944 {
1945 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1946 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
1947
1948 LogFlowFunc(("\tHost : %s\n", pHstStream ? pHstStream->szName : "<None>"));
1949 LogFlowFunc(("\tGuest: %s\n", pGstStream ? pGstStream->szName : "<None>"));
1950
1951 /* Should prevent double frees. */
1952 Assert(pHstStream != pGstStream);
1953
1954 if (pHstStream)
1955 {
1956 pHstStream->pPair = NULL;
1957 RTListNodeRemove(&pHstStream->Node);
1958 }
1959
1960 if (pGstStream)
1961 {
1962 pGstStream->pPair = NULL;
1963 RTListNodeRemove(&pGstStream->Node);
1964 }
1965
1966 if (pHstStream)
1967 {
1968 rc = drvAudioStreamDestroyInternal(pThis, pHstStream);
1969 AssertRC(rc);
1970
1971 pHstStream = NULL;
1972 }
1973
1974 if (pGstStream)
1975 {
1976 rc = drvAudioStreamDestroyInternal(pThis, pGstStream);
1977 AssertRC(rc);
1978
1979 pGstStream = NULL;
1980 }
1981 }
1982
1983 if (RT_SUCCESS(rc))
1984 {
1985 if (enmDir == PDMAUDIODIR_IN)
1986 {
1987 pThis->cStreamsFreeIn++;
1988 }
1989 else /* Out */
1990 {
1991 pThis->cStreamsFreeOut++;
1992 }
1993 }
1994
1995 int rc2 = RTCritSectLeave(&pThis->CritSect);
1996 if (RT_SUCCESS(rc))
1997 rc = rc2;
1998
1999 LogFlowFuncLeaveRC(rc);
2000 return rc;
2001}
2002
2003/**
2004 * Calls the backend to give it the chance to destroy its part of the audio stream.
2005 *
2006 * @returns IPRT status code.
2007 * @param pThis Pointer to driver instance.
2008 * @param pHstStream Host audio stream to call the backend destruction for.
2009 */
2010static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream)
2011{
2012 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2013 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
2014
2015 AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST,
2016 ("Stream '%s' is not a host stream and therefore has no backend\n", pHstStream->szName));
2017
2018 int rc = VINF_SUCCESS;
2019
2020 LogFlowFunc(("%s: fStatus=0x%x\n", pHstStream->szName, pHstStream->fStatus));
2021
2022 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
2023 {
2024 /* Check if the pointer to the host audio driver is still valid.
2025 * It can be NULL if we were called in drvAudioDestruct, for example. */
2026 if (pThis->pHostDrvAudio)
2027 rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pHstStream);
2028 if (RT_SUCCESS(rc))
2029 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
2030 }
2031
2032 LogFlowFunc(("%s: Returning %Rrc\n", pHstStream->szName, rc));
2033 return rc;
2034}
2035
2036/**
2037 * Destroys an audio stream.
2038 *
2039 * @returns IPRT status code.
2040 * @param pThis Pointer to driver instance.
2041 * @param pStream Pointer to audio stream to destroy.
2042 * That pointer will be invalid after successful destruction.
2043 */
2044static int drvAudioStreamDestroyInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
2045{
2046 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2047 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2048
2049 LogFlowFunc(("%s: cRefs=%RU32\n", pStream->szName, pStream->cRefs));
2050
2051 if (pStream->cRefs > 1)
2052 return VERR_WRONG_ORDER;
2053
2054 int rc = VINF_SUCCESS;
2055
2056 if (pStream->enmCtx == PDMAUDIOSTREAMCTX_GUEST)
2057 {
2058 if (pStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
2059 {
2060 rc = drvAudioStreamControlInternal(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
2061 if (RT_SUCCESS(rc))
2062 pStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
2063 }
2064
2065 if (pStream->enmDir == PDMAUDIODIR_IN)
2066 {
2067#ifdef VBOX_WITH_STATISTICS
2068 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->In.StatBytesElapsed);
2069 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->In.StatBytesTotalRead);
2070#endif
2071 }
2072 else if (pStream->enmDir == PDMAUDIODIR_OUT)
2073 {
2074#ifdef VBOX_WITH_STATISTICS
2075 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->Out.StatBytesElapsed);
2076 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->Out.StatBytesTotalWritten);
2077#endif
2078 }
2079 }
2080 else if (pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST)
2081 {
2082 rc = drvAudioStreamDestroyInternalBackend(pThis, pStream);
2083
2084 if (pStream->enmDir == PDMAUDIODIR_IN)
2085 {
2086#ifdef VBOX_WITH_STATISTICS
2087 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->In.StatSamplesCaptured);
2088#endif
2089 }
2090 else if (pStream->enmDir == PDMAUDIODIR_OUT)
2091 {
2092#ifdef VBOX_WITH_STATISTICS
2093 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->Out.StatSamplesPlayed);
2094#endif
2095 }
2096 }
2097 else
2098 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
2099
2100 if (RT_SUCCESS(rc))
2101 {
2102 /* Destroy mixing buffer. */
2103 AudioMixBufDestroy(&pStream->MixBuf);
2104
2105 if (pStream)
2106 {
2107 RTMemFree(pStream);
2108 pStream = NULL;
2109 }
2110 }
2111
2112 LogFlowFunc(("Returning %Rrc\n", rc));
2113 return rc;
2114}
2115
2116/********************************************************************/
2117
2118/**
2119 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2120 */
2121static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2122{
2123 LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
2124
2125 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2126 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2127
2128 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2129 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
2130
2131 return NULL;
2132}
2133
2134/**
2135 * Power Off notification.
2136 *
2137 * @param pDrvIns The driver instance data.
2138 */
2139static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
2140{
2141 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2142
2143 LogFlowFuncEnter();
2144
2145 /* Just destroy the host stream on the backend side.
2146 * The rest will either be destructed by the device emulation or
2147 * in drvAudioDestruct(). */
2148 PPDMAUDIOSTREAM pStream;
2149 RTListForEach(&pThis->lstHstStreams, pStream, PDMAUDIOSTREAM, Node)
2150 drvAudioStreamDestroyInternalBackend(pThis, pStream);
2151
2152 /*
2153 * Last call for the driver below us.
2154 * Let it know that we reached end of life.
2155 */
2156 if (pThis->pHostDrvAudio->pfnShutdown)
2157 pThis->pHostDrvAudio->pfnShutdown(pThis->pHostDrvAudio);
2158
2159 pThis->pHostDrvAudio = NULL;
2160
2161 LogFlowFuncLeave();
2162}
2163
2164/**
2165 * Constructs an audio driver instance.
2166 *
2167 * @copydoc FNPDMDRVCONSTRUCT
2168 */
2169static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
2170{
2171 LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfgHandle, fFlags));
2172
2173 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2174 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2175
2176 RTListInit(&pThis->lstHstStreams);
2177 RTListInit(&pThis->lstGstStreams);
2178#ifdef VBOX_WITH_AUDIO_CALLBACKS
2179 RTListInit(&pThis->lstCBIn);
2180 RTListInit(&pThis->lstCBOut);
2181#endif
2182
2183 /*
2184 * Init the static parts.
2185 */
2186 pThis->pDrvIns = pDrvIns;
2187 /* IBase. */
2188 pDrvIns->IBase.pfnQueryInterface = drvAudioQueryInterface;
2189 /* IAudioConnector. */
2190 pThis->IAudioConnector.pfnGetConfig = drvAudioGetConfig;
2191 pThis->IAudioConnector.pfnGetStatus = drvAudioGetStatus;
2192 pThis->IAudioConnector.pfnStreamCreate = drvAudioStreamCreate;
2193 pThis->IAudioConnector.pfnStreamDestroy = drvAudioStreamDestroy;
2194 pThis->IAudioConnector.pfnStreamRetain = drvAudioStreamRetain;
2195 pThis->IAudioConnector.pfnStreamRelease = drvAudioStreamRelease;
2196 pThis->IAudioConnector.pfnStreamControl = drvAudioStreamControl;
2197 pThis->IAudioConnector.pfnStreamRead = drvAudioStreamRead;
2198 pThis->IAudioConnector.pfnStreamWrite = drvAudioStreamWrite;
2199 pThis->IAudioConnector.pfnStreamIterate = drvAudioStreamIterate;
2200 pThis->IAudioConnector.pfnStreamGetReadable = drvAudioStreamGetReadable;
2201 pThis->IAudioConnector.pfnStreamGetWritable = drvAudioStreamGetWritable;
2202 pThis->IAudioConnector.pfnStreamGetStatus = drvAudioStreamGetStatus;
2203 pThis->IAudioConnector.pfnStreamSetVolume = drvAudioStreamSetVolume;
2204 pThis->IAudioConnector.pfnStreamPlay = drvAudioStreamPlay;
2205 pThis->IAudioConnector.pfnStreamCapture = drvAudioStreamCapture;
2206#ifdef VBOX_WITH_AUDIO_CALLBACKS
2207 pThis->IAudioConnector.pfnRegisterCallbacks = drvAudioRegisterCallbacks;
2208 pThis->IAudioConnector.pfnCallback = drvAudioCallback;
2209#endif
2210
2211 /*
2212 * Attach driver below and query its connector interface.
2213 */
2214 PPDMIBASE pDownBase;
2215 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
2216 if (RT_FAILURE(rc))
2217 {
2218 LogRel(("Audio: Failed to attach to driver %p below (flags=0x%x), rc=%Rrc\n",
2219 pDrvIns, fFlags, rc));
2220 return rc;
2221 }
2222
2223 pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
2224 if (!pThis->pHostDrvAudio)
2225 {
2226 LogRel(("Audio: Failed to query interface for underlying host driver\n"));
2227 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
2228 N_("Host audio backend missing or invalid"));
2229 }
2230
2231 rc = drvAudioInit(pDrvIns, pCfgHandle);
2232 if (RT_SUCCESS(rc))
2233 {
2234 pThis->fTerminate = false;
2235 pThis->pDrvIns = pDrvIns;
2236
2237#ifdef VBOX_WITH_STATISTICS
2238 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsActive, "TotalStreamsActive",
2239 STAMUNIT_COUNT, "Active input streams.");
2240 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsCreated, "TotalStreamsCreated",
2241 STAMUNIT_COUNT, "Total created input streams.");
2242 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalSamplesPlayed, "TotalSamplesPlayed",
2243 STAMUNIT_COUNT, "Total samples played.");
2244 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalSamplesCaptured, "TotalSamplesCaptured",
2245 STAMUNIT_COUNT, "Total samples captured.");
2246 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesRead, "TotalBytesRead",
2247 STAMUNIT_BYTES, "Total bytes read.");
2248 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesWritten, "TotalBytesWritten",
2249 STAMUNIT_BYTES, "Total bytes written.");
2250#endif
2251 }
2252
2253 LogFlowFuncLeaveRC(rc);
2254 return rc;
2255}
2256
2257/**
2258 * Destructs an audio driver instance.
2259 *
2260 * @copydoc FNPDMDRVDESTRUCT
2261 */
2262static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
2263{
2264 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2265 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2266
2267 LogFlowFuncEnter();
2268
2269 int rc2 = RTCritSectEnter(&pThis->CritSect);
2270 AssertRC(rc2);
2271
2272 /*
2273 * Note: No calls here to the driver below us anymore,
2274 * as PDM already has destroyed it.
2275 * If you need to call something from the host driver,
2276 * do this in drvAudioPowerOff() instead.
2277 */
2278
2279 /* Thus, NULL the pointer to the host audio driver first,
2280 * so that routines like drvAudioStreamDestroyInternal() don't call the driver(s) below us anymore. */
2281 pThis->pHostDrvAudio = NULL;
2282
2283 PPDMAUDIOSTREAM pStream, pStreamNext;
2284 RTListForEachSafe(&pThis->lstHstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
2285 drvAudioStreamDestroyInternal(pThis, pStream);
2286
2287 RTListForEachSafe(&pThis->lstGstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
2288 drvAudioStreamDestroyInternal(pThis, pStream);
2289
2290#ifdef VBOX_WITH_AUDIO_CALLBACKS
2291 /*
2292 * Destroy callbacks, if any.
2293 */
2294 PPDMAUDIOCALLBACK pCB, pCBNext;
2295 RTListForEachSafe(&pThis->lstCBIn, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
2296 drvAudioCallbackDestroy(pCB);
2297
2298 RTListForEachSafe(&pThis->lstCBOut, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
2299 drvAudioCallbackDestroy(pCB);
2300#endif
2301
2302 rc2 = RTCritSectLeave(&pThis->CritSect);
2303 AssertRC(rc2);
2304
2305 rc2 = RTCritSectDelete(&pThis->CritSect);
2306 AssertRC(rc2);
2307
2308 LogFlowFuncLeave();
2309}
2310
2311/**
2312 * Suspend notification.
2313 *
2314 * @param pDrvIns The driver instance data.
2315 */
2316static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
2317{
2318 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_PAUSE);
2319}
2320
2321/**
2322 * Resume notification.
2323 *
2324 * @param pDrvIns The driver instance data.
2325 */
2326static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
2327{
2328 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_RESUME);
2329}
2330
2331/**
2332 * Audio driver registration record.
2333 */
2334const PDMDRVREG g_DrvAUDIO =
2335{
2336 /* u32Version */
2337 PDM_DRVREG_VERSION,
2338 /* szName */
2339 "AUDIO",
2340 /* szRCMod */
2341 "",
2342 /* szR0Mod */
2343 "",
2344 /* pszDescription */
2345 "Audio connector driver",
2346 /* fFlags */
2347 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2348 /* fClass */
2349 PDM_DRVREG_CLASS_AUDIO,
2350 /* cMaxInstances */
2351 UINT32_MAX,
2352 /* cbInstance */
2353 sizeof(DRVAUDIO),
2354 /* pfnConstruct */
2355 drvAudioConstruct,
2356 /* pfnDestruct */
2357 drvAudioDestruct,
2358 /* pfnRelocate */
2359 NULL,
2360 /* pfnIOCtl */
2361 NULL,
2362 /* pfnPowerOn */
2363 NULL,
2364 /* pfnReset */
2365 NULL,
2366 /* pfnSuspend */
2367 drvAudioSuspend,
2368 /* pfnResume */
2369 drvAudioResume,
2370 /* pfnAttach */
2371 NULL,
2372 /* pfnDetach */
2373 NULL,
2374 /* pfnPowerOff */
2375 drvAudioPowerOff,
2376 /* pfnSoftReset */
2377 NULL,
2378 /* u32EndVersion */
2379 PDM_DRVREG_VERSION
2380};
2381
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