VirtualBox

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

Last change on this file since 65708 was 65699, checked in by vboxsync, 8 years ago

Audio: Bugfixes.

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

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