VirtualBox

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

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

Build fix.

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

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