VirtualBox

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

Last change on this file since 63681 was 63671, checked in by vboxsync, 8 years ago

Audio/DrvAudio: Renamed drvAudioStreamDestroyInternal() -> drvAudioStreamUninitInternal() and made the stream destruction independent of that function. Added a couple more (verbose) release logging / assertions.

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