VirtualBox

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

Last change on this file since 73441 was 73432, checked in by vboxsync, 7 years ago

Audio: Use the DrvAudioHlpStreamStatusXXX() helpers where applicable.

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