VirtualBox

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

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

Audio/DrvAudio.cpp: Logging: Retrieve host driver name for easier identification.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 115.6 KB
Line 
1/* $Id: DrvAudio.cpp 68387 2017-08-11 11:27:25Z 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 /* Retrieve host driver name for easier identification. */
2519 AssertPtr(pThis->pHostDrvAudio);
2520 PPDMDRVINS pDrvAudioInst = PDMIBASE_2_PDMDRV(pThis->pDrvIns->pDownBase);
2521 AssertPtr(pDrvAudioInst);
2522 AssertPtr(pDrvAudioInst->pReg);
2523
2524 char szDriver[64];
2525 RTStrPrintf(szDriver, RT_ELEMENTS(szDriver), "%s", pDrvAudioInst->pReg->szName);
2526 if (!strlen(szDriver))
2527 {
2528 RTStrPrintf(szDriver, RT_ELEMENTS(szDriver), "Untitled");
2529 AssertFailed(); /* Should never happen. */
2530 }
2531
2532 /*
2533 * Init host stream.
2534 */
2535 RTStrPrintf(pHstStrm->szName, RT_ELEMENTS(pHstStrm->szName), "[%s] %s (Host)",
2536 szDriver, strlen(pCfgHost->szName) ? pCfgHost->szName : "<Untitled>");
2537
2538 rc = drvAudioStreamLinkToInternal(pHstStrm, pGstStrm);
2539 AssertRCBreak(rc);
2540
2541 /*
2542 * Init guest stream.
2543 */
2544 RTStrPrintf(pGstStrm->szName, RT_ELEMENTS(pGstStrm->szName), "[%s] %s (Guest)",
2545 szDriver, strlen(pCfgGuest->szName) ? pCfgGuest->szName : "<Untitled>");
2546
2547 pGstStrm->fStatus = pHstStrm->fStatus; /* Reflect the host stream's status. */
2548
2549 rc = drvAudioStreamLinkToInternal(pGstStrm, pHstStrm);
2550 AssertRCBreak(rc);
2551
2552 /*
2553 * Try to init the rest.
2554 */
2555 rc = drvAudioStreamInitInternal(pThis, pHstStrm, pCfgHost, pCfgGuest);
2556 if (RT_FAILURE(rc))
2557 break;
2558
2559#ifdef VBOX_WITH_STATISTICS
2560 char szStatName[255];
2561
2562 if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
2563 {
2564 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pGstStrm->szName);
2565 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->In.StatBytesElapsed,
2566 szStatName, STAMUNIT_BYTES, "Elapsed bytes read.");
2567
2568 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesRead", pGstStrm->szName);
2569 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->In.StatBytesTotalRead,
2570 szStatName, STAMUNIT_BYTES, "Total bytes read.");
2571
2572 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/FramesCaptured", pHstStrm->szName);
2573 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pHstStrm->In.StatFramesCaptured,
2574 szStatName, STAMUNIT_COUNT, "Total frames captured.");
2575 }
2576 else if (pCfgGuest->enmDir == PDMAUDIODIR_OUT)
2577 {
2578 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pGstStrm->szName);
2579 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->Out.StatBytesElapsed,
2580 szStatName, STAMUNIT_BYTES, "Elapsed bytes written.");
2581
2582 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesWritten", pGstStrm->szName);
2583 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->Out.StatBytesTotalWritten,
2584 szStatName, STAMUNIT_BYTES, "Total bytes written.");
2585
2586 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/FramesPlayed", pHstStrm->szName);
2587 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pHstStrm->Out.StatFramesPlayed,
2588 szStatName, STAMUNIT_COUNT, "Total frames played.");
2589 }
2590 else
2591 AssertFailed();
2592#endif
2593
2594 } while (0);
2595
2596#undef RC_BREAK
2597
2598 if (RT_FAILURE(rc))
2599 {
2600 if (pGstStrm)
2601 {
2602 int rc2 = drvAudioStreamUninitInternal(pThis, pGstStrm);
2603 if (RT_SUCCESS(rc2))
2604 {
2605 RTMemFree(pGstStrm);
2606 pGstStrm = NULL;
2607 }
2608 }
2609
2610 if (pHstStrm)
2611 {
2612 int rc2 = drvAudioStreamUninitInternal(pThis, pHstStrm);
2613 if (RT_SUCCESS(rc2))
2614 {
2615 drvAudioStreamFree(pHstStrm);
2616 pHstStrm = NULL;
2617 }
2618 }
2619 }
2620 else
2621 {
2622 /* Set initial reference counts. */
2623 RTListAppend(&pThis->lstGstStreams, &pGstStrm->Node);
2624 pGstStrm->cRefs = 1;
2625
2626 RTListAppend(&pThis->lstHstStreams, &pHstStrm->Node);
2627 pHstStrm->cRefs = 1;
2628
2629 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
2630 {
2631 if (pThis->cStreamsFreeIn)
2632 pThis->cStreamsFreeIn--;
2633 }
2634 else /* Out */
2635 {
2636 if (pThis->cStreamsFreeOut)
2637 pThis->cStreamsFreeOut--;
2638 }
2639
2640#ifdef VBOX_WITH_STATISTICS
2641 STAM_COUNTER_ADD(&pThis->Stats.TotalStreamsCreated, 1);
2642#endif
2643 /* Always return the guest-side part to the device emulation. */
2644 *ppStream = pGstStrm;
2645 }
2646
2647 int rc2 = RTCritSectLeave(&pThis->CritSect);
2648 if (RT_SUCCESS(rc))
2649 rc = rc2;
2650
2651 LogFlowFuncLeaveRC(rc);
2652 return rc;
2653}
2654
2655/**
2656 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetConfig}
2657 */
2658static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
2659{
2660 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2661 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2662
2663 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2664
2665 int rc = RTCritSectEnter(&pThis->CritSect);
2666 if (RT_FAILURE(rc))
2667 return rc;
2668
2669 if (pThis->pHostDrvAudio)
2670 {
2671 if (pThis->pHostDrvAudio->pfnGetConfig)
2672 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, pCfg);
2673 else
2674 rc = VERR_NOT_SUPPORTED;
2675 }
2676 else
2677 AssertFailed();
2678
2679 int rc2 = RTCritSectLeave(&pThis->CritSect);
2680 if (RT_SUCCESS(rc))
2681 rc = rc2;
2682
2683 LogFlowFuncLeaveRC(rc);
2684 return rc;
2685}
2686
2687/**
2688 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetStatus}
2689 */
2690static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioGetStatus(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
2691{
2692 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
2693
2694 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2695
2696 PDMAUDIOBACKENDSTS backendSts = PDMAUDIOBACKENDSTS_UNKNOWN;
2697
2698 int rc = RTCritSectEnter(&pThis->CritSect);
2699 if (RT_SUCCESS(rc))
2700 {
2701 if ( pThis->pHostDrvAudio
2702 && pThis->pHostDrvAudio->pfnGetStatus)
2703 {
2704 backendSts = pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, enmDir);
2705 }
2706
2707 int rc2 = RTCritSectLeave(&pThis->CritSect);
2708 if (RT_SUCCESS(rc))
2709 rc = rc2;
2710 }
2711
2712 LogFlowFuncLeaveRC(rc);
2713 return backendSts;
2714}
2715
2716/**
2717 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetReadable}
2718 */
2719static DECLCALLBACK(uint32_t) drvAudioStreamGetReadable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2720{
2721 AssertPtrReturn(pInterface, 0);
2722 AssertPtrReturn(pStream, 0);
2723
2724 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2725
2726 int rc2 = RTCritSectEnter(&pThis->CritSect);
2727 AssertRC(rc2);
2728
2729 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN, ("Can't read from a non-input stream\n"));
2730
2731 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2732 if (!pHstStream) /* No host stream available? Bail out early. */
2733 {
2734 rc2 = RTCritSectLeave(&pThis->CritSect);
2735 AssertRC(rc2);
2736
2737 return 0;
2738 }
2739
2740 uint32_t cReadable = 0;
2741
2742 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
2743 if (pGstStream)
2744 cReadable = AudioMixBufLive(&pGstStream->MixBuf);
2745
2746 Log3Func(("[%s] cbReadable=%RU32 (%zu bytes)\n", pHstStream->szName, cReadable,
2747 AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cReadable)));
2748
2749 uint32_t cbReadable = AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cReadable);
2750
2751 rc2 = RTCritSectLeave(&pThis->CritSect);
2752 AssertRC(rc2);
2753
2754 /* Return bytes instead of audio frames. */
2755 return cbReadable;
2756}
2757
2758/**
2759 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetWritable}
2760 */
2761static DECLCALLBACK(uint32_t) drvAudioStreamGetWritable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2762{
2763 AssertPtrReturn(pInterface, 0);
2764 AssertPtrReturn(pStream, 0);
2765
2766 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2767
2768 int rc2 = RTCritSectEnter(&pThis->CritSect);
2769 AssertRC(rc2);
2770
2771 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT, ("Can't write to a non-output stream\n"));
2772
2773 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2774 if (!pHstStream) /* No host stream available? Bail out early. */
2775 {
2776 rc2 = RTCritSectLeave(&pThis->CritSect);
2777 AssertRC(rc2);
2778
2779 AssertMsgFailed(("Guest stream '%s' does not have a host stream attached\n", pStream->szName));
2780 return 0;
2781 }
2782
2783 /* As the host side sets the overall pace, return the writable bytes from that side. */
2784 uint32_t cbWritable = AudioMixBufFreeBytes(&pHstStream->MixBuf);
2785
2786 Log3Func(("[%s] cbWritable=%RU32\n", pHstStream->szName, cbWritable));
2787
2788 rc2 = RTCritSectLeave(&pThis->CritSect);
2789 AssertRC(rc2);
2790
2791 return cbWritable;
2792}
2793
2794/**
2795 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetStatus}
2796 */
2797static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvAudioStreamGetStatus(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2798{
2799 AssertPtrReturn(pInterface, false);
2800
2801 if (!pStream)
2802 return PDMAUDIOSTREAMSTS_FLAG_NONE;
2803
2804 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2805
2806 int rc2 = RTCritSectEnter(&pThis->CritSect);
2807 AssertRC(rc2);
2808
2809 PDMAUDIOSTREAMSTS strmSts = PDMAUDIOSTREAMSTS_FLAG_NONE;
2810
2811 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2812 if (pHstStream)
2813 {
2814 strmSts = pHstStream->fStatus;
2815#ifdef LOG_ENABLED
2816 char *pszHstSts = dbgAudioStreamStatusToStr(pHstStream->fStatus);
2817 Log3Func(("[%s] %s\n", pHstStream->szName, pszHstSts));
2818 RTStrFree(pszHstSts);
2819#endif
2820 }
2821
2822 rc2 = RTCritSectLeave(&pThis->CritSect);
2823 AssertRC(rc2);
2824
2825 return strmSts;
2826}
2827
2828/**
2829 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamSetVolume}
2830 */
2831static DECLCALLBACK(int) drvAudioStreamSetVolume(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, PPDMAUDIOVOLUME pVol)
2832{
2833 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2834 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2835 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
2836
2837 LogFlowFunc(("[%s] volL=%RU32, volR=%RU32, fMute=%RTbool\n", pStream->szName, pVol->uLeft, pVol->uRight, pVol->fMuted));
2838
2839 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2840 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
2841
2842 AudioMixBufSetVolume(&pHstStream->MixBuf, pVol);
2843 AudioMixBufSetVolume(&pGstStream->MixBuf, pVol);
2844 return VINF_SUCCESS;
2845}
2846
2847/**
2848 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamDestroy}
2849 */
2850static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2851{
2852 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2853 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2854
2855 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2856
2857 int rc = RTCritSectEnter(&pThis->CritSect);
2858 AssertRC(rc);
2859
2860 PDMAUDIODIR enmDir = pStream->enmDir;
2861
2862 LogFlowFunc(("[%s] cRefs=%RU32\n", pStream->szName, pStream->cRefs));
2863 if (pStream->cRefs > 1)
2864 rc = VERR_WRONG_ORDER;
2865
2866 if (RT_SUCCESS(rc))
2867 {
2868 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2869 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
2870
2871 LogRel2(("Audio: Destroying host stream '%s' (guest stream '%s')\n",
2872 pHstStream ? pHstStream->szName : "<None>",
2873 pGstStream ? pGstStream->szName : "<None>"));
2874
2875 /* Should prevent double frees. */
2876 Assert(pHstStream != pGstStream);
2877
2878 if (pHstStream)
2879 {
2880 rc = drvAudioStreamUninitInternal(pThis, pHstStream);
2881 if (RT_SUCCESS(rc))
2882 {
2883#ifdef VBOX_WITH_STATISTICS
2884 if (pHstStream->enmDir == PDMAUDIODIR_IN)
2885 {
2886 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pHstStream->In.StatFramesCaptured);
2887 }
2888 else if (pHstStream->enmDir == PDMAUDIODIR_OUT)
2889 {
2890 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pHstStream->Out.StatFramesPlayed);
2891 }
2892 else
2893 AssertFailed();
2894#endif
2895 RTListNodeRemove(&pHstStream->Node);
2896
2897 drvAudioStreamFree(pHstStream);
2898 pHstStream = NULL;
2899 }
2900 else
2901 LogRel2(("Audio: Uninitializing host stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
2902 }
2903
2904 if ( RT_SUCCESS(rc)
2905 && pGstStream)
2906 {
2907 rc = drvAudioStreamUninitInternal(pThis, pGstStream);
2908 if (RT_SUCCESS(rc))
2909 {
2910#ifdef VBOX_WITH_STATISTICS
2911 if (pGstStream->enmDir == PDMAUDIODIR_IN)
2912 {
2913 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->In.StatBytesElapsed);
2914 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->In.StatBytesTotalRead);
2915 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->In.StatFramesCaptured);
2916 }
2917 else if (pGstStream->enmDir == PDMAUDIODIR_OUT)
2918 {
2919 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->Out.StatBytesElapsed);
2920 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->Out.StatBytesTotalWritten);
2921 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->Out.StatFramesPlayed);
2922 }
2923 else
2924 AssertFailed();
2925#endif
2926 RTListNodeRemove(&pGstStream->Node);
2927
2928 RTMemFree(pGstStream);
2929 pGstStream = NULL;
2930 }
2931 else
2932 LogRel2(("Audio: Uninitializing guest stream '%s' failed with %Rrc\n", pGstStream->szName, rc));
2933 }
2934 }
2935
2936 if (RT_SUCCESS(rc))
2937 {
2938 if (enmDir == PDMAUDIODIR_IN)
2939 {
2940 pThis->cStreamsFreeIn++;
2941 }
2942 else /* Out */
2943 {
2944 pThis->cStreamsFreeOut++;
2945 }
2946 }
2947
2948 int rc2 = RTCritSectLeave(&pThis->CritSect);
2949 if (RT_SUCCESS(rc))
2950 rc = rc2;
2951
2952 LogFlowFuncLeaveRC(rc);
2953 return rc;
2954}
2955
2956/**
2957 * Creates an audio stream on the backend side.
2958 *
2959 * @returns IPRT status code.
2960 * @param pThis Pointer to driver instance.
2961 * @param pHstStream (Host) audio stream to use for creating the stream on the backend side.
2962 * @param pCfgReq Requested audio stream configuration to use for stream creation.
2963 * @param pCfgAcq Acquired audio stream configuration returned by the backend. Optional, can be NULL.
2964 */
2965static int drvAudioStreamCreateInternalBackend(PDRVAUDIO pThis,
2966 PPDMAUDIOSTREAM pHstStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
2967{
2968 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2969 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
2970 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
2971 /* pCfgAcq is optional. */
2972
2973 AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST,
2974 ("Stream '%s' is not a host stream and therefore has no backend\n", pHstStream->szName));
2975
2976 AssertMsg((pHstStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED) == 0,
2977 ("Stream '%s' already initialized in backend\n", pHstStream->szName));
2978
2979 /* Make the acquired host configuration the requested host configuration initially,
2980 * in case the backend does not report back an acquired configuration. */
2981 PDMAUDIOSTREAMCFG CfgAcq;
2982 int rc = DrvAudioHlpStreamCfgCopy(&CfgAcq, pCfgReq);
2983 if (RT_FAILURE(rc))
2984 {
2985 LogRel(("Audio: Creating stream '%s' with an invalid backend configuration not possible, skipping\n",
2986 pHstStream->szName));
2987 return rc;
2988 }
2989
2990 rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pHstStream->pvBackend, pCfgReq, &CfgAcq);
2991 if (RT_FAILURE(rc))
2992 {
2993 if (rc == VERR_NOT_SUPPORTED)
2994 LogRel2(("Audio: Creating stream '%s' in backend not supported, skipping\n", pHstStream->szName));
2995 else
2996 LogRel2(("Audio: Creating stream '%s' in backend failed with %Rrc\n", pHstStream->szName, rc));
2997
2998 return rc;
2999 }
3000
3001 /* Validate acquired configuration. */
3002 if (!DrvAudioHlpStreamCfgIsValid(&CfgAcq))
3003 {
3004 LogRel(("Audio: Creating stream '%s' returned an invalid backend configuration, skipping\n", pHstStream->szName));
3005 return VERR_INVALID_PARAMETER;
3006 }
3007
3008 /* Only set the host's stream to initialized if we were able create the stream
3009 * in the host backend. This is necessary for trying to re-initialize the stream
3010 * at some later point in time. */
3011 pHstStream->fStatus |= PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
3012
3013 if (pCfgAcq)
3014 {
3015 int rc2 = DrvAudioHlpStreamCfgCopy(pCfgAcq, &CfgAcq);
3016 AssertRC(rc2);
3017 }
3018
3019 return VINF_SUCCESS;
3020}
3021
3022/**
3023 * Calls the backend to give it the chance to destroy its part of the audio stream.
3024 *
3025 * @returns IPRT status code.
3026 * @param pThis Pointer to driver instance.
3027 * @param pHstStream Host audio stream to call the backend destruction for.
3028 */
3029static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream)
3030{
3031 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
3032 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
3033
3034 AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST,
3035 ("Stream '%s' is not a host stream and therefore has no backend\n", pHstStream->szName));
3036
3037 int rc = VINF_SUCCESS;
3038
3039#ifdef LOG_ENABLED
3040 char *pszHstSts = dbgAudioStreamStatusToStr(pHstStream->fStatus);
3041 LogFunc(("[%s] fStatus=%s\n", pHstStream->szName, pszHstSts));
3042 RTStrFree(pszHstSts);
3043#endif /* LOG_ENABLED */
3044
3045 if (pHstStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED)
3046 {
3047 /* Check if the pointer to the host audio driver is still valid.
3048 * It can be NULL if we were called in drvAudioDestruct, for example. */
3049 if (pThis->pHostDrvAudio)
3050 rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pHstStream->pvBackend);
3051 if (RT_SUCCESS(rc))
3052 {
3053 pHstStream->fStatus &= ~PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
3054#if 0 /** @todo r=andy Disabled for now -- need to test this on Windows hosts. */
3055 Assert(pHstStream->fStatus == PDMAUDIOSTRMSTS_FLAG_NONE);
3056#endif
3057 }
3058 }
3059
3060 LogFlowFunc(("[%s] Returning %Rrc\n", pHstStream->szName, rc));
3061 return rc;
3062}
3063
3064/**
3065 * Uninitializes an audio stream.
3066 *
3067 * @returns IPRT status code.
3068 * @param pThis Pointer to driver instance.
3069 * @param pStream Pointer to audio stream to uninitialize.
3070 */
3071static int drvAudioStreamUninitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
3072{
3073 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
3074 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
3075
3076 LogFlowFunc(("[%s] cRefs=%RU32\n", pStream->szName, pStream->cRefs));
3077
3078 if (pStream->cRefs > 1)
3079 return VERR_WRONG_ORDER;
3080
3081 int rc = VINF_SUCCESS;
3082
3083 if (pStream->enmCtx == PDMAUDIOSTREAMCTX_GUEST)
3084 {
3085 if (pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED)
3086 {
3087 rc = drvAudioStreamControlInternal(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
3088 if (RT_SUCCESS(rc))
3089 {
3090 pStream->fStatus &= ~PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
3091 Assert(pStream->fStatus == PDMAUDIOSTREAMSTS_FLAG_NONE);
3092 }
3093 }
3094 }
3095 else if (pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST)
3096 {
3097 rc = drvAudioStreamDestroyInternalBackend(pThis, pStream);
3098 }
3099 else
3100 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
3101
3102 if (RT_SUCCESS(rc))
3103 {
3104 /* Make sure that the pair (if any) knows that we're not valid anymore. */
3105 int rc2 = drvAudioStreamLinkToInternal(pStream, NULL);
3106 AssertRC(rc2);
3107
3108 /* Reset status. */
3109 pStream->fStatus = PDMAUDIOSTREAMSTS_FLAG_NONE;
3110
3111 /* Destroy mixing buffer. */
3112 AudioMixBufDestroy(&pStream->MixBuf);
3113 }
3114
3115 LogFlowFunc(("Returning %Rrc\n", rc));
3116 return rc;
3117}
3118
3119/********************************************************************/
3120
3121/**
3122 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
3123 */
3124static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
3125{
3126 LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
3127
3128 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
3129 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3130
3131 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
3132 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
3133
3134 return NULL;
3135}
3136
3137/**
3138 * Power Off notification.
3139 *
3140 * @param pDrvIns The driver instance data.
3141 */
3142static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
3143{
3144 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3145
3146 LogFlowFuncEnter();
3147
3148 /* Just destroy the host stream on the backend side.
3149 * The rest will either be destructed by the device emulation or
3150 * in drvAudioDestruct(). */
3151 PPDMAUDIOSTREAM pStream;
3152 RTListForEach(&pThis->lstHstStreams, pStream, PDMAUDIOSTREAM, Node)
3153 drvAudioStreamDestroyInternalBackend(pThis, pStream);
3154
3155 /*
3156 * Last call for the driver below us.
3157 * Let it know that we reached end of life.
3158 */
3159 if (pThis->pHostDrvAudio->pfnShutdown)
3160 pThis->pHostDrvAudio->pfnShutdown(pThis->pHostDrvAudio);
3161
3162 pThis->pHostDrvAudio = NULL;
3163
3164 LogFlowFuncLeave();
3165}
3166
3167/**
3168 * Constructs an audio driver instance.
3169 *
3170 * @copydoc FNPDMDRVCONSTRUCT
3171 */
3172static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
3173{
3174 LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfg, fFlags));
3175
3176 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3177 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
3178
3179 RTListInit(&pThis->lstHstStreams);
3180 RTListInit(&pThis->lstGstStreams);
3181#ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS
3182 RTListInit(&pThis->lstCBIn);
3183 RTListInit(&pThis->lstCBOut);
3184#endif
3185
3186 /*
3187 * Init the static parts.
3188 */
3189 pThis->pDrvIns = pDrvIns;
3190 /* IBase. */
3191 pDrvIns->IBase.pfnQueryInterface = drvAudioQueryInterface;
3192 /* IAudioConnector. */
3193 pThis->IAudioConnector.pfnGetConfig = drvAudioGetConfig;
3194 pThis->IAudioConnector.pfnGetStatus = drvAudioGetStatus;
3195 pThis->IAudioConnector.pfnStreamCreate = drvAudioStreamCreate;
3196 pThis->IAudioConnector.pfnStreamDestroy = drvAudioStreamDestroy;
3197 pThis->IAudioConnector.pfnStreamRetain = drvAudioStreamRetain;
3198 pThis->IAudioConnector.pfnStreamRelease = drvAudioStreamRelease;
3199 pThis->IAudioConnector.pfnStreamControl = drvAudioStreamControl;
3200 pThis->IAudioConnector.pfnStreamRead = drvAudioStreamRead;
3201 pThis->IAudioConnector.pfnStreamWrite = drvAudioStreamWrite;
3202 pThis->IAudioConnector.pfnStreamIterate = drvAudioStreamIterate;
3203 pThis->IAudioConnector.pfnStreamGetReadable = drvAudioStreamGetReadable;
3204 pThis->IAudioConnector.pfnStreamGetWritable = drvAudioStreamGetWritable;
3205 pThis->IAudioConnector.pfnStreamGetStatus = drvAudioStreamGetStatus;
3206 pThis->IAudioConnector.pfnStreamSetVolume = drvAudioStreamSetVolume;
3207 pThis->IAudioConnector.pfnStreamPlay = drvAudioStreamPlay;
3208 pThis->IAudioConnector.pfnStreamCapture = drvAudioStreamCapture;
3209#ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS
3210 pThis->IAudioConnector.pfnRegisterCallbacks = drvAudioRegisterCallbacks;
3211 pThis->IAudioConnector.pfnCallback = drvAudioCallback;
3212#endif
3213
3214 /*
3215 * Attach driver below and query its connector interface.
3216 */
3217 PPDMIBASE pDownBase;
3218 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
3219 if (RT_FAILURE(rc))
3220 {
3221 LogRel(("Audio: Failed to attach to driver %p below (flags=0x%x), rc=%Rrc\n",
3222 pDrvIns, fFlags, rc));
3223 return rc;
3224 }
3225
3226 pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
3227 if (!pThis->pHostDrvAudio)
3228 {
3229 LogRel(("Audio: Failed to query interface for underlying host driver\n"));
3230 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
3231 N_("Host audio backend missing or invalid"));
3232 }
3233
3234 rc = drvAudioInit(pDrvIns, pCfg);
3235 if (RT_SUCCESS(rc))
3236 {
3237 pThis->fTerminate = false;
3238 pThis->pDrvIns = pDrvIns;
3239
3240#ifdef VBOX_WITH_STATISTICS
3241 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsActive, "TotalStreamsActive",
3242 STAMUNIT_COUNT, "Total active audio streams.");
3243 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsCreated, "TotalStreamsCreated",
3244 STAMUNIT_COUNT, "Total created audio streams.");
3245 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesRead, "TotalFramesRead",
3246 STAMUNIT_COUNT, "Total frames read by device emulation.");
3247 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesWritten, "TotalFramesWritten",
3248 STAMUNIT_COUNT, "Total frames written by device emulation ");
3249 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesMixedIn, "TotalFramesMixedIn",
3250 STAMUNIT_COUNT, "Total input frames mixed.");
3251 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesMixedOut, "TotalFramesMixedOut",
3252 STAMUNIT_COUNT, "Total output frames mixed.");
3253 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesLostIn, "TotalFramesLostIn",
3254 STAMUNIT_COUNT, "Total input frames lost.");
3255 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesLostOut, "TotalFramesLostOut",
3256 STAMUNIT_COUNT, "Total output frames lost.");
3257 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesOut, "TotalFramesPlayed",
3258 STAMUNIT_COUNT, "Total frames played by backend.");
3259 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesIn, "TotalFramesCaptured",
3260 STAMUNIT_COUNT, "Total frames captured by backend.");
3261 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesRead, "TotalBytesRead",
3262 STAMUNIT_BYTES, "Total bytes read.");
3263 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesWritten, "TotalBytesWritten",
3264 STAMUNIT_BYTES, "Total bytes written.");
3265
3266 PDMDrvHlpSTAMRegProfileAdvEx(pDrvIns, &pThis->Stats.DelayIn, "DelayIn",
3267 STAMUNIT_NS_PER_CALL, "Profiling of input data processing.");
3268 PDMDrvHlpSTAMRegProfileAdvEx(pDrvIns, &pThis->Stats.DelayOut, "DelayOut",
3269 STAMUNIT_NS_PER_CALL, "Profiling of output data processing.");
3270#endif
3271
3272#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
3273 drvAudioDbgPCMDelete(pThis, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH, "StreamRead.pcm");
3274 drvAudioDbgPCMDelete(pThis, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH, "StreamWrite.pcm");
3275 drvAudioDbgPCMDelete(pThis, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH, "PlayNonInterleaved.pcm");
3276 drvAudioDbgPCMDelete(pThis, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH, "CaptureNonInterleaved.pcm");
3277#endif
3278 }
3279
3280 LogFlowFuncLeaveRC(rc);
3281 return rc;
3282}
3283
3284/**
3285 * Destructs an audio driver instance.
3286 *
3287 * @copydoc FNPDMDRVDESTRUCT
3288 */
3289static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
3290{
3291 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
3292 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3293
3294 LogFlowFuncEnter();
3295
3296 int rc2 = RTCritSectEnter(&pThis->CritSect);
3297 AssertRC(rc2);
3298
3299 /*
3300 * Note: No calls here to the driver below us anymore,
3301 * as PDM already has destroyed it.
3302 * If you need to call something from the host driver,
3303 * do this in drvAudioPowerOff() instead.
3304 */
3305
3306 /* Thus, NULL the pointer to the host audio driver first,
3307 * so that routines like drvAudioStreamDestroyInternal() don't call the driver(s) below us anymore. */
3308 pThis->pHostDrvAudio = NULL;
3309
3310 PPDMAUDIOSTREAM pStream, pStreamNext;
3311 RTListForEachSafe(&pThis->lstHstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
3312 {
3313 rc2 = drvAudioStreamUninitInternal(pThis, pStream);
3314 if (RT_SUCCESS(rc2))
3315 {
3316 RTListNodeRemove(&pStream->Node);
3317
3318 drvAudioStreamFree(pStream);
3319 pStream = NULL;
3320 }
3321 }
3322
3323 /* Sanity. */
3324 Assert(RTListIsEmpty(&pThis->lstHstStreams));
3325
3326 RTListForEachSafe(&pThis->lstGstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
3327 {
3328 rc2 = drvAudioStreamUninitInternal(pThis, pStream);
3329 if (RT_SUCCESS(rc2))
3330 {
3331 RTListNodeRemove(&pStream->Node);
3332
3333 RTMemFree(pStream);
3334 pStream = NULL;
3335 }
3336 }
3337
3338 /* Sanity. */
3339 Assert(RTListIsEmpty(&pThis->lstGstStreams));
3340
3341#ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS
3342 /*
3343 * Destroy device callbacks, if any.
3344 */
3345 PPDMAUDIOCALLBACK pCB, pCBNext;
3346 RTListForEachSafe(&pThis->lstCBIn, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
3347 drvAudioCallbackDestroy(pCB);
3348
3349 RTListForEachSafe(&pThis->lstCBOut, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
3350 drvAudioCallbackDestroy(pCB);
3351#endif
3352
3353 rc2 = RTCritSectLeave(&pThis->CritSect);
3354 AssertRC(rc2);
3355
3356 rc2 = RTCritSectDelete(&pThis->CritSect);
3357 AssertRC(rc2);
3358
3359#ifdef VBOX_WITH_STATISTICS
3360 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalStreamsActive);
3361 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalStreamsCreated);
3362 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesRead);
3363 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesWritten);
3364 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesMixedIn);
3365 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesMixedOut);
3366 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesLostIn);
3367 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesLostOut);
3368 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesOut);
3369 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesIn);
3370 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalBytesRead);
3371 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalBytesWritten);
3372 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.DelayIn);
3373 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.DelayOut);
3374#endif
3375
3376 LogFlowFuncLeave();
3377}
3378
3379/**
3380 * Suspend notification.
3381 *
3382 * @param pDrvIns The driver instance data.
3383 */
3384static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
3385{
3386 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_PAUSE);
3387}
3388
3389/**
3390 * Resume notification.
3391 *
3392 * @param pDrvIns The driver instance data.
3393 */
3394static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
3395{
3396 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_RESUME);
3397}
3398
3399/**
3400 * Audio driver registration record.
3401 */
3402const PDMDRVREG g_DrvAUDIO =
3403{
3404 /* u32Version */
3405 PDM_DRVREG_VERSION,
3406 /* szName */
3407 "AUDIO",
3408 /* szRCMod */
3409 "",
3410 /* szR0Mod */
3411 "",
3412 /* pszDescription */
3413 "Audio connector driver",
3414 /* fFlags */
3415 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
3416 /* fClass */
3417 PDM_DRVREG_CLASS_AUDIO,
3418 /* cMaxInstances */
3419 UINT32_MAX,
3420 /* cbInstance */
3421 sizeof(DRVAUDIO),
3422 /* pfnConstruct */
3423 drvAudioConstruct,
3424 /* pfnDestruct */
3425 drvAudioDestruct,
3426 /* pfnRelocate */
3427 NULL,
3428 /* pfnIOCtl */
3429 NULL,
3430 /* pfnPowerOn */
3431 NULL,
3432 /* pfnReset */
3433 NULL,
3434 /* pfnSuspend */
3435 drvAudioSuspend,
3436 /* pfnResume */
3437 drvAudioResume,
3438 /* pfnAttach */
3439 NULL,
3440 /* pfnDetach */
3441 NULL,
3442 /* pfnPowerOff */
3443 drvAudioPowerOff,
3444 /* pfnSoftReset */
3445 NULL,
3446 /* u32EndVersion */
3447 PDM_DRVREG_VERSION
3448};
3449
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