VirtualBox

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

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

Audio/DrvAudio: Logging adjustments.

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

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