VirtualBox

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

最後變更 在這個檔案從69956是 69956,由 vboxsync 提交於 7 年 前

Audio: Implemented support for dumping raw PCM data if debug mode is enabled.

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

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