VirtualBox

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

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

Audio/DrvAudio.cpp: Also log errors in non-verbose mode.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 121.1 KB
Line 
1/* $Id: DrvAudio.cpp 71045 2018-02-18 18:30:26Z vboxsync $ */
2/** @file
3 * Intermediate audio driver header.
4 *
5 * @remarks Intermediate audio driver for connecting the audio device emulation
6 * with the host backend.
7 */
8
9/*
10 * Copyright (C) 2006-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#ifdef LOG_ENABLED
1007 RTStrFree(pszHstSts);
1008 RTStrFree(pszGstSts);
1009#endif
1010
1011 int rc2 = RTCritSectLeave(&pThis->CritSect);
1012 if (RT_SUCCESS(rc))
1013 rc = rc2;
1014
1015 if (RT_SUCCESS(rc))
1016 {
1017 if (pcbWritten)
1018 *pcbWritten = cbWrittenTotal;
1019 }
1020
1021 return rc;
1022}
1023
1024/**
1025 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRetain}
1026 */
1027static DECLCALLBACK(uint32_t) drvAudioStreamRetain(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1028{
1029 AssertPtrReturn(pInterface, UINT32_MAX);
1030 AssertPtrReturn(pStream, UINT32_MAX);
1031
1032 NOREF(pInterface);
1033
1034 return ++pStream->cRefs;
1035}
1036
1037/**
1038 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRelease}
1039 */
1040static DECLCALLBACK(uint32_t) drvAudioStreamRelease(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1041{
1042 AssertPtrReturn(pInterface, UINT32_MAX);
1043 AssertPtrReturn(pStream, UINT32_MAX);
1044
1045 NOREF(pInterface);
1046
1047 if (pStream->cRefs > 1) /* 1 reference always is kept by this audio driver. */
1048 pStream->cRefs--;
1049
1050 return pStream->cRefs;
1051}
1052
1053/**
1054 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamIterate}
1055 */
1056static DECLCALLBACK(int) drvAudioStreamIterate(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1057{
1058 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1059 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1060
1061 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1062
1063 int rc = RTCritSectEnter(&pThis->CritSect);
1064 if (RT_FAILURE(rc))
1065 return rc;
1066
1067 rc = drvAudioStreamIterateInternal(pThis, pStream);
1068
1069 int rc2 = RTCritSectLeave(&pThis->CritSect);
1070 if (RT_SUCCESS(rc))
1071 rc = rc2;
1072
1073 if (RT_FAILURE(rc))
1074 LogFlowFuncLeaveRC(rc);
1075
1076 return rc;
1077}
1078
1079/**
1080 * Does one iteration of an audio stream.
1081 * This function gives the backend the chance of iterating / altering data and
1082 * does the actual mixing between the guest <-> host mixing buffers.
1083 *
1084 * @returns IPRT status code.
1085 * @param pThis Pointer to driver instance.
1086 * @param pStream Stream to iterate.
1087 */
1088static int drvAudioStreamIterateInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
1089{
1090 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1091
1092 if (!pThis->pHostDrvAudio)
1093 return VINF_SUCCESS;
1094
1095 if (!pStream)
1096 return VINF_SUCCESS;
1097
1098 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1099 AssertPtr(pHstStream);
1100 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : NULL;
1101 AssertPtr(pGstStream);
1102
1103 int rc;
1104
1105 /* Is the stream scheduled for re-initialization? Do so now. */
1106 if (pHstStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PENDING_REINIT)
1107 {
1108#ifdef VBOX_WITH_AUDIO_ENUM
1109 if (pThis->fEnumerateDevices)
1110 {
1111 /* Re-enumerate all host devices. */
1112 drvAudioDevicesEnumerateInternal(pThis, true /* fLog */, NULL /* pDevEnum */);
1113
1114 pThis->fEnumerateDevices = false;
1115 }
1116#endif /* VBOX_WITH_AUDIO_ENUM */
1117
1118 /* Remove the pending re-init flag in any case, regardless whether the actual re-initialization succeeded
1119 * or not. If it failed, the backend needs to notify us again to try again at some later point in time. */
1120 pHstStream->fStatus &= ~PDMAUDIOSTREAMSTS_FLAG_PENDING_REINIT;
1121
1122 rc = drvAudioStreamReInitInternal(pThis, pStream);
1123 if (RT_FAILURE(rc))
1124 return rc;
1125 }
1126
1127#ifdef LOG_ENABLED
1128 char *pszHstSts = dbgAudioStreamStatusToStr(pHstStream->fStatus);
1129 Log3Func(("[%s] fStatus=%s\n", pHstStream->szName, pszHstSts));
1130 RTStrFree(pszHstSts);
1131#endif /* LOG_ENABLED */
1132
1133 /* Not enabled or paused? Skip iteration. */
1134 if ( !(pHstStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_ENABLED)
1135 || (pHstStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PAUSED))
1136 {
1137 return VINF_SUCCESS;
1138 }
1139
1140 /* Whether to try closing a pending to close stream. */
1141 bool fTryClosePending = false;
1142
1143 do
1144 {
1145 uint32_t cfMixed = 0;
1146
1147 rc = pThis->pHostDrvAudio->pfnStreamIterate(pThis->pHostDrvAudio, pHstStream->pvBackend);
1148 if (RT_FAILURE(rc))
1149 break;
1150
1151 if (pHstStream->enmDir == PDMAUDIODIR_IN)
1152 {
1153 /* Has the host captured any frames which were not mixed to the guest side yet? */
1154 uint32_t cfCaptured = AudioMixBufUsed(&pHstStream->MixBuf);
1155 if (cfCaptured)
1156 {
1157 /* When capturing frames, the guest is the parent while the host is the child.
1158 * So try mixing not yet mixed host-side frames to the guest-side buffer. */
1159 rc = AudioMixBufMixToParent(&pHstStream->MixBuf, cfCaptured, &cfMixed);
1160 if (RT_FAILURE(rc))
1161 {
1162 if (rc == VERR_BUFFER_OVERFLOW)
1163 LogRel2(("Audio: Guest input stream '%s' full, expect stuttering audio capture\n", pGstStream->szName));
1164 else
1165 LogRel2(("Audio: Mixing to guest input stream '%s' failed: %Rrc\n", pGstStream->szName, rc));
1166#ifdef DEBUG_andy_disabled
1167 AssertFailed();
1168#endif
1169 }
1170
1171#ifdef VBOX_WITH_STATISTICS
1172 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesMixedIn, cfMixed);
1173 Assert(cfCaptured >= cfMixed);
1174 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesLostIn, cfCaptured - cfMixed);
1175#endif
1176 Log3Func(("[%s] %RU32/%RU32 input frames mixed, rc=%Rrc\n", pHstStream->szName, cfMixed, cfCaptured, rc));
1177 }
1178 else
1179 {
1180 /* No audio frames to transfer host to the guest (anymore)?
1181 * Then try closing this stream if marked so in the next block. */
1182 fTryClosePending = true;
1183 }
1184 }
1185 else if (pHstStream->enmDir == PDMAUDIODIR_OUT)
1186 {
1187 /* No audio frames to transfer from guest to host (anymore)?
1188 * Then try closing this stream if marked so in the next block. */
1189 fTryClosePending = AudioMixBufLive(&pHstStream->MixBuf) == 0;
1190 }
1191 else
1192 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1193
1194 /* Has the host stream marked as pending to disable?
1195 * Try disabling the stream then. */
1196 if ( pHstStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE
1197 && fTryClosePending)
1198 {
1199 if (pThis->pHostDrvAudio->pfnStreamGetPending) /* Optional to implement. */
1200 {
1201 const uint32_t cxPending = pThis->pHostDrvAudio->pfnStreamGetPending(pThis->pHostDrvAudio, pHstStream->pvBackend);
1202 Log3Func(("[%s] cxPending=%RU32\n", pHstStream->szName, cxPending));
1203
1204 /* Only try close pending if no audio data is pending on the backend-side anymore. */
1205 fTryClosePending = cxPending == 0;
1206 }
1207
1208 if (fTryClosePending)
1209 {
1210 LogFunc(("[%s] Closing pending stream\n", pHstStream->szName));
1211 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1212 if (RT_FAILURE(rc))
1213 LogFunc(("[%s] Backend vetoed against closing pending input stream, rc=%Rrc\n", pHstStream->szName, rc));
1214 }
1215 }
1216
1217 } while (0);
1218
1219 /* Update timestamps. */
1220 pHstStream->tsLastIterateMS = RTTimeMilliTS();
1221 pGstStream->tsLastIterateMS = RTTimeMilliTS();
1222
1223 if (RT_FAILURE(rc))
1224 LogFunc(("[%s] Failed with %Rrc\n", pHstStream->szName, rc));
1225
1226 return rc;
1227}
1228
1229/**
1230 * Links an audio stream to another audio stream (pair).
1231 *
1232 * @returns IPRT status code.
1233 * @param pStream Stream to handle linking for.
1234 * @param pPair Stream to link pStream to. Specify NULL to unlink pStream from a former linked stream.
1235 */
1236static int drvAudioStreamLinkToInternal(PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAM pPair)
1237{
1238 if (pPair) /* Link. */
1239 {
1240 pStream->pPair = pPair;
1241 pPair->pPair = pStream;
1242
1243 LogRel2(("Audio: Linked audio stream '%s' to '%s'\n", pStream->szName, pPair->szName));
1244 }
1245 else /* Unlink. */
1246 {
1247 if (pStream->pPair)
1248 {
1249 LogRel2(("Audio: Unlinked pair '%s' from stream '%s'\n", pStream->pPair->szName, pStream->szName));
1250
1251 AssertMsg(pStream->pPair->pPair == pStream,
1252 ("Pair '%s' is not linked to '%s' (linked to '%s')\n",
1253 pStream->pPair->szName, pStream->szName, pStream->pPair->pPair ? pStream->pPair->pPair->szName : "<NONE>"));
1254
1255 pStream->pPair->pPair = NULL; /* Also make sure to unlink the pair from pStream */
1256 pStream->pPair = NULL;
1257 }
1258 }
1259
1260 return VINF_SUCCESS;
1261}
1262
1263/**
1264 * Plays an audio host output stream which has been configured for non-interleaved (layout) data.
1265 *
1266 * @return IPRT status code.
1267 * @param pThis Pointer to driver instance.
1268 * @param pHstStream Host stream to play.
1269 * @param cfToPlay Number of audio frames to play.
1270 * @param pcfPlayed Returns number of audio frames played. Optional.
1271 */
1272static int drvAudioStreamPlayNonInterleaved(PDRVAUDIO pThis,
1273 PPDMAUDIOSTREAM pHstStream, uint32_t cfToPlay, uint32_t *pcfPlayed)
1274{
1275 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1276 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
1277 /* pcfPlayed is optional. */
1278
1279 /* Sanity. */
1280 Assert(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1281 Assert(pHstStream->enmDir == PDMAUDIODIR_OUT);
1282 Assert(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED);
1283
1284 if (!cfToPlay)
1285 {
1286 if (pcfPlayed)
1287 *pcfPlayed = 0;
1288 return VINF_SUCCESS;
1289 }
1290
1291 int rc = VINF_SUCCESS;
1292
1293 uint32_t cfPlayedTotal = 0;
1294
1295 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetWritable);
1296 uint32_t cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pHstStream->pvBackend);
1297 if (cbWritable)
1298 {
1299 if (cfToPlay > AUDIOMIXBUF_B2F(&pHstStream->MixBuf, cbWritable)) /* More frames available than we can write? Limit. */
1300 cfToPlay = AUDIOMIXBUF_B2F(&pHstStream->MixBuf, cbWritable);
1301
1302 if (cfToPlay)
1303 {
1304 uint8_t auBuf[256]; /** @todo Get rid of this here. */
1305
1306 uint32_t cbLeft = AUDIOMIXBUF_F2B(&pHstStream->MixBuf, cfToPlay);
1307 uint32_t cbChunk = sizeof(auBuf);
1308
1309 while (cbLeft)
1310 {
1311 uint32_t cfRead = 0;
1312 rc = AudioMixBufAcquireReadBlock(&pHstStream->MixBuf, auBuf, RT_MIN(cbChunk, cbLeft), &cfRead);
1313 if (RT_FAILURE(rc))
1314 break;
1315
1316 uint32_t cbRead = AUDIOMIXBUF_F2B(&pHstStream->MixBuf, cfRead);
1317 Assert(cbRead <= cbChunk);
1318
1319 uint32_t cfPlayed = 0;
1320 uint32_t cbPlayed = 0;
1321 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pHstStream->pvBackend,
1322 auBuf, cbRead, &cbPlayed);
1323 if ( RT_SUCCESS(rc)
1324 && cbPlayed)
1325 {
1326 if (pThis->Dbg.fEnabled)
1327 DrvAudioHlpFileWrite(pHstStream->Out.Dbg.pFilePlayNonInterleaved, auBuf, cbPlayed, 0 /* fFlags */);
1328
1329 if (cbRead != cbPlayed)
1330 LogRel2(("Audio: Host stream '%s' played wrong amount (%RU32 bytes read but played %RU32 (%RI32), writable was %RU32)\n",
1331 pHstStream->szName, cbRead, cbPlayed, cbRead - cbPlayed, cbWritable));
1332
1333 cfPlayed = AUDIOMIXBUF_B2F(&pHstStream->MixBuf, cbPlayed);
1334 cfPlayedTotal += cfPlayed;
1335
1336 Assert(cbLeft >= cbPlayed);
1337 cbLeft -= cbPlayed;
1338 }
1339
1340 AudioMixBufReleaseReadBlock(&pHstStream->MixBuf, cfPlayed);
1341
1342 if (RT_FAILURE(rc))
1343 break;
1344 }
1345 }
1346 }
1347
1348 Log3Func(("[%s] Played %RU32/%RU32 frames, rc=%Rrc\n", pHstStream->szName, cfPlayedTotal, cfToPlay, rc));
1349
1350 if (RT_SUCCESS(rc))
1351 {
1352 if (pcfPlayed)
1353 *pcfPlayed = cfPlayedTotal;
1354 }
1355
1356 return rc;
1357}
1358
1359/**
1360 * Plays an audio host output stream which has been configured for raw audio (layout) data.
1361 *
1362 * @return IPRT status code.
1363 * @param pThis Pointer to driver instance.
1364 * @param pHstStream Host stream to play.
1365 * @param cfToPlay Number of audio frames to play.
1366 * @param pcfPlayed Returns number of audio frames played. Optional.
1367 */
1368static int drvAudioStreamPlayRaw(PDRVAUDIO pThis,
1369 PPDMAUDIOSTREAM pHstStream, uint32_t cfToPlay, uint32_t *pcfPlayed)
1370{
1371 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1372 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
1373 /* pcfPlayed is optional. */
1374
1375 /* Sanity. */
1376 Assert(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1377 Assert(pHstStream->enmDir == PDMAUDIODIR_OUT);
1378 Assert(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
1379
1380 if (!cfToPlay)
1381 {
1382 if (pcfPlayed)
1383 *pcfPlayed = 0;
1384 return VINF_SUCCESS;
1385 }
1386
1387 int rc = VINF_SUCCESS;
1388
1389 uint32_t cfPlayedTotal = 0;
1390
1391 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetWritable);
1392 uint32_t cfWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pHstStream->pvBackend);
1393 if (cfWritable)
1394 {
1395 if (cfToPlay > cfWritable) /* More frames available than we can write? Limit. */
1396 cfToPlay = cfWritable;
1397
1398 PDMAUDIOFRAME aFrameBuf[_4K]; /** @todo Get rid of this here. */
1399
1400 uint32_t cfLeft = cfToPlay;
1401 while (cfLeft)
1402 {
1403 uint32_t cfRead = 0;
1404 rc = AudioMixBufPeek(&pHstStream->MixBuf, cfLeft, aFrameBuf,
1405 RT_MIN(cfLeft, RT_ELEMENTS(aFrameBuf)), &cfRead);
1406
1407 if (RT_SUCCESS(rc))
1408 {
1409 if (cfRead)
1410 {
1411 uint32_t cfPlayed;
1412
1413 /* Note: As the stream layout is RPDMAUDIOSTREAMLAYOUT_RAW, operate on audio frames
1414 * rather on bytes. */
1415 Assert(cfRead <= RT_ELEMENTS(aFrameBuf));
1416 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pHstStream->pvBackend,
1417 aFrameBuf, cfRead, &cfPlayed);
1418 if ( RT_FAILURE(rc)
1419 || !cfPlayed)
1420 {
1421 break;
1422 }
1423
1424 cfPlayedTotal += cfPlayed;
1425 Assert(cfPlayedTotal <= cfToPlay);
1426
1427 Assert(cfLeft >= cfRead);
1428 cfLeft -= cfRead;
1429 }
1430 else
1431 {
1432 if (rc == VINF_AUDIO_MORE_DATA_AVAILABLE) /* Do another peeking round if there is more data available. */
1433 continue;
1434
1435 break;
1436 }
1437 }
1438 else if (RT_FAILURE(rc))
1439 break;
1440 }
1441 }
1442
1443 Log3Func(("[%s] Played %RU32/%RU32 frames, rc=%Rrc\n", pHstStream->szName, cfPlayedTotal, cfToPlay, rc));
1444
1445 if (RT_SUCCESS(rc))
1446 {
1447 if (pcfPlayed)
1448 *pcfPlayed = cfPlayedTotal;
1449
1450 }
1451
1452 return rc;
1453}
1454
1455/**
1456 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamPlay}
1457 */
1458static DECLCALLBACK(int) drvAudioStreamPlay(PPDMIAUDIOCONNECTOR pInterface,
1459 PPDMAUDIOSTREAM pStream, uint32_t *pcFramesPlayed)
1460{
1461 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1462 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1463 /* pcFramesPlayed is optional. */
1464
1465 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1466
1467 int rc = RTCritSectEnter(&pThis->CritSect);
1468 if (RT_FAILURE(rc))
1469 return rc;
1470
1471 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT,
1472 ("Stream '%s' is not an output stream and therefore cannot be played back (direction is 0x%x)\n",
1473 pStream->szName, pStream->enmDir));
1474
1475 uint32_t cfPlayedTotal = 0;
1476
1477 do
1478 {
1479 if (!pThis->pHostDrvAudio)
1480 break;
1481
1482 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1483 AssertPtr(pHstStream);
1484 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : NULL;
1485 AssertPtr(pGstStream);
1486
1487 AssertReleaseMsgBreakStmt(pHstStream != NULL,
1488 ("[%s] Host stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1489 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1490 rc = VERR_NOT_AVAILABLE);
1491 AssertReleaseMsgBreakStmt(pGstStream != NULL,
1492 ("[%s] Guest stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1493 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1494 rc = VERR_NOT_AVAILABLE);
1495
1496 /*
1497 * Check if the backend is ready to operate.
1498 */
1499
1500 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetStatus);
1501 PDMAUDIOSTREAMSTS stsBackend = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream->pvBackend);
1502#ifdef LOG_ENABLED
1503 char *pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
1504 Log3Func(("[%s] Start: stsBackend=%s\n", pHstStream->szName, pszBackendSts));
1505 RTStrFree(pszBackendSts);
1506#endif /* LOG_ENABLED */
1507 if (!(stsBackend & PDMAUDIOSTREAMSTS_FLAG_ENABLED)) /* Backend disabled? Bail out. */
1508 break;
1509
1510 uint32_t cfToPlay = AudioMixBufLive(&pHstStream->MixBuf);
1511
1512 if (pThis->pHostDrvAudio->pfnStreamPlayBegin)
1513 pThis->pHostDrvAudio->pfnStreamPlayBegin(pThis->pHostDrvAudio, pHstStream->pvBackend);
1514
1515 if (RT_LIKELY(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED))
1516 {
1517 rc = drvAudioStreamPlayNonInterleaved(pThis, pHstStream, cfToPlay, &cfPlayedTotal);
1518 }
1519 else if (pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW)
1520 {
1521 rc = drvAudioStreamPlayRaw(pThis, pHstStream, cfToPlay, &cfPlayedTotal);
1522 }
1523 else
1524 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1525
1526 if (pThis->pHostDrvAudio->pfnStreamPlayEnd)
1527 pThis->pHostDrvAudio->pfnStreamPlayEnd(pThis->pHostDrvAudio, pHstStream->pvBackend);
1528
1529#ifdef LOG_ENABLED
1530 uint32_t cfLive = 0;
1531#endif
1532 if (RT_SUCCESS(rc))
1533 {
1534 AudioMixBufFinish(&pHstStream->MixBuf, cfPlayedTotal);
1535
1536#ifdef VBOX_WITH_STATISTICS
1537 STAM_COUNTER_ADD (&pThis->Stats.TotalFramesOut, cfPlayedTotal);
1538 STAM_PROFILE_ADV_STOP(&pThis->Stats.DelayOut, out);
1539 STAM_COUNTER_ADD (&pHstStream->Out.StatFramesPlayed, cfPlayedTotal);
1540#endif
1541
1542#ifdef LOG_ENABLED
1543 cfLive = AudioMixBufLive(&pHstStream->MixBuf);
1544#endif
1545 }
1546
1547#ifdef LOG_ENABLED
1548 pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
1549 Log3Func(("[%s] End: stsBackend=%s, cfLive=%RU32, cfPlayedTotal=%RU32, rc=%Rrc\n",
1550 pHstStream->szName, pszBackendSts, cfLive, cfPlayedTotal, rc));
1551 RTStrFree(pszBackendSts);
1552#endif /* LOG_ENABLED */
1553
1554 } while (0);
1555
1556 int rc2 = RTCritSectLeave(&pThis->CritSect);
1557 if (RT_SUCCESS(rc))
1558 rc = rc2;
1559
1560 if (RT_SUCCESS(rc))
1561 {
1562 if (pcFramesPlayed)
1563 *pcFramesPlayed = cfPlayedTotal;
1564 }
1565
1566 if (RT_FAILURE(rc))
1567 LogFlowFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
1568
1569 return rc;
1570}
1571
1572/**
1573 * Captures non-interleaved input from a host stream.
1574 *
1575 * @returns IPRT status code.
1576 * @param pThis Driver instance.
1577 * @param pHstStream Host stream to capture from.
1578 * @param pcfCaptured Number of (host) audio frames captured. Optional.
1579 */
1580static int drvAudioStreamCaptureNonInterleaved(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream, uint32_t *pcfCaptured)
1581{
1582 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1583 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
1584 /* pcfCaptured is optional. */
1585
1586 /* Sanity. */
1587 Assert(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1588 Assert(pHstStream->enmDir == PDMAUDIODIR_IN);
1589 Assert(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED);
1590
1591 int rc = VINF_SUCCESS;
1592
1593 uint32_t cfCapturedTotal = 0;
1594
1595 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
1596
1597 uint8_t auBuf[_1K]; /** @todo Get rid of this. */
1598 size_t cbBuf = sizeof(auBuf);
1599
1600 for (;;)
1601 {
1602 uint32_t cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pHstStream->pvBackend);
1603 if (!cbReadable)
1604 {
1605 Log2Func(("[%s] No readable data available, skipping\n", pHstStream->szName));
1606 break;
1607 }
1608
1609 uint32_t cbFree = AUDIOMIXBUF_F2B(&pHstStream->MixBuf, AudioMixBufFree(&pHstStream->MixBuf));
1610 if (!cbFree)
1611 {
1612 Log2Func(("[%s] Host buffer full, skipping\n", pHstStream->szName));
1613 break;
1614 }
1615
1616 if (cbFree < cbReadable) /* More data captured than we can read? */
1617 {
1618 /** @todo Warn? */
1619 }
1620
1621 if (cbFree > cbBuf) /* Limit to buffer size. */
1622 cbFree = (uint32_t)cbBuf;
1623
1624 uint32_t cbCaptured;
1625 rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pHstStream->pvBackend,
1626 auBuf, cbFree, &cbCaptured);
1627 if (RT_FAILURE(rc))
1628 {
1629 int rc2 = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1630 AssertRC(rc2);
1631 }
1632 else if (cbCaptured)
1633 {
1634 if (pThis->Dbg.fEnabled)
1635 DrvAudioHlpFileWrite(pHstStream->In.Dbg.pFileCaptureNonInterleaved, auBuf, cbCaptured, 0 /* fFlags */);
1636
1637 Assert(cbCaptured <= cbBuf);
1638 if (cbCaptured > cbBuf) /* Paranoia. */
1639 cbCaptured = (uint32_t)cbBuf;
1640
1641 uint32_t cfCaptured = 0;
1642 rc = AudioMixBufWriteCirc(&pHstStream->MixBuf, auBuf, cbCaptured, &cfCaptured);
1643 if (RT_SUCCESS(rc))
1644 cfCapturedTotal += cfCaptured;
1645 }
1646 else /* Nothing captured -- bail out. */
1647 break;
1648
1649 if (RT_FAILURE(rc))
1650 break;
1651 }
1652
1653 if (RT_SUCCESS(rc))
1654 {
1655 if (cfCapturedTotal)
1656 Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pHstStream->szName, cfCapturedTotal, rc));
1657 }
1658 else
1659 LogFunc(("[%s] Capturing failed with rc=%Rrc\n", pHstStream->szName, rc));
1660
1661 if (pcfCaptured)
1662 *pcfCaptured = cfCapturedTotal;
1663
1664 return rc;
1665}
1666
1667/**
1668 * Captures raw input from a host stream.
1669 * Raw input means that the backend directly operates on PDMAUDIOFRAME structs without
1670 * no data layout processing done in between.
1671 *
1672 * Needed for e.g. the VRDP audio backend (in Main).
1673 *
1674 * @returns IPRT status code.
1675 * @param pThis Driver instance.
1676 * @param pHstStream Host stream to capture from.
1677 * @param pcfCaptured Number of (host) audio frames captured. Optional.
1678 */
1679static int drvAudioStreamCaptureRaw(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream, uint32_t *pcfCaptured)
1680{
1681 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1682 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
1683 /* pcfCaptured is optional. */
1684
1685 /* Sanity. */
1686 Assert(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1687 Assert(pHstStream->enmDir == PDMAUDIODIR_IN);
1688 Assert(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
1689
1690 int rc = VINF_SUCCESS;
1691
1692 uint32_t cfCapturedTotal = 0;
1693
1694 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
1695
1696 for (;;)
1697 {
1698 uint32_t cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pHstStream->pvBackend);
1699 if (!cbReadable) /* Nothing to read on the backend side? Bail out. */
1700 break;
1701
1702 const uint32_t cbFree = AudioMixBufFreeBytes(&pHstStream->MixBuf);
1703 if (!cbFree) /* No space left in the host stream? */
1704 break;
1705
1706 if (cbReadable > cbFree) /* Don't capture more than the host stream currently can hold. */
1707 cbReadable = cbFree;
1708
1709 PPDMAUDIOFRAME paFrames;
1710 uint32_t cfWritable;
1711 rc = AudioMixBufPeekMutable(&pHstStream->MixBuf, AUDIOMIXBUF_B2F(&pHstStream->MixBuf, cbReadable),
1712 &paFrames, &cfWritable);
1713 if ( RT_FAILURE(rc)
1714 || !cfWritable)
1715 {
1716 break;
1717 }
1718
1719 uint32_t cfCaptured;
1720 rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pHstStream->pvBackend,
1721 paFrames, cfWritable, &cfCaptured);
1722 if (RT_FAILURE(rc))
1723 {
1724 int rc2 = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1725 AssertRC(rc2);
1726 }
1727 else if (cfCaptured)
1728 {
1729 Assert(cfCaptured <= cfWritable);
1730 if (cfCaptured > cfWritable) /* Paranoia. */
1731 cfCaptured = cfWritable;
1732
1733 cfCapturedTotal += cfCaptured;
1734 }
1735 else /* Nothing captured -- bail out. */
1736 break;
1737
1738 if (RT_FAILURE(rc))
1739 break;
1740 }
1741
1742 Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pHstStream->szName, cfCapturedTotal, rc));
1743
1744 if (pcfCaptured)
1745 *pcfCaptured = cfCapturedTotal;
1746
1747 return rc;
1748}
1749
1750/**
1751 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCapture}
1752 */
1753static DECLCALLBACK(int) drvAudioStreamCapture(PPDMIAUDIOCONNECTOR pInterface,
1754 PPDMAUDIOSTREAM pStream, uint32_t *pcFramesCaptured)
1755{
1756 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1757
1758 int rc = RTCritSectEnter(&pThis->CritSect);
1759 if (RT_FAILURE(rc))
1760 return rc;
1761
1762 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
1763 ("Stream '%s' is not an input stream and therefore cannot be captured (direction is 0x%x)\n",
1764 pStream->szName, pStream->enmDir));
1765
1766 uint32_t cfCaptured = 0;
1767
1768 do
1769 {
1770 if (!pThis->pHostDrvAudio)
1771 break;
1772
1773 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1774 AssertPtr(pHstStream);
1775 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : NULL;
1776 AssertPtr(pGstStream);
1777
1778 AssertReleaseMsgBreakStmt(pHstStream != NULL,
1779 ("[%s] Host stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1780 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1781 rc = VERR_NOT_AVAILABLE);
1782 AssertReleaseMsgBreakStmt(pGstStream != NULL,
1783 ("[%s] Guest stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1784 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1785 rc = VERR_NOT_AVAILABLE);
1786
1787 /*
1788 * Check if the backend is ready to operate.
1789 */
1790
1791 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetStatus);
1792 PDMAUDIOSTREAMSTS stsBackend = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream->pvBackend);
1793#ifdef LOG_ENABLED
1794 char *pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
1795 Log3Func(("[%s] Start: stsBackend=%s\n", pHstStream->szName, pszBackendSts));
1796 RTStrFree(pszBackendSts);
1797#endif /* LOG_ENABLED */
1798 if (!(stsBackend & PDMAUDIOSTREAMSTS_FLAG_ENABLED)) /* Backend disabled? Bail out. */
1799 break;
1800
1801 /*
1802 * Do the actual capturing.
1803 */
1804
1805 if (pThis->pHostDrvAudio->pfnStreamCaptureBegin)
1806 pThis->pHostDrvAudio->pfnStreamCaptureBegin(pThis->pHostDrvAudio, pHstStream->pvBackend);
1807
1808 if (RT_LIKELY(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED))
1809 {
1810 rc = drvAudioStreamCaptureNonInterleaved(pThis, pHstStream, &cfCaptured);
1811 }
1812 else if (pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW)
1813 {
1814 rc = drvAudioStreamCaptureRaw(pThis, pHstStream, &cfCaptured);
1815 }
1816 else
1817 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1818
1819 if (pThis->pHostDrvAudio->pfnStreamCaptureEnd)
1820 pThis->pHostDrvAudio->pfnStreamCaptureEnd(pThis->pHostDrvAudio, pHstStream->pvBackend);
1821
1822#ifdef LOG_ENABLED
1823 pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
1824 Log3Func(("[%s] End: stsBackend=%s, cfCaptured=%RU32, rc=%Rrc\n",
1825 pHstStream->szName, pszBackendSts, cfCaptured, rc));
1826 RTStrFree(pszBackendSts);
1827#endif /* LOG_ENABLED */
1828
1829 if (RT_SUCCESS(rc))
1830 {
1831 Log3Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pHstStream->szName, cfCaptured, rc));
1832
1833#ifdef VBOX_WITH_STATISTICS
1834 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesIn, cfCaptured);
1835 STAM_COUNTER_ADD(&pHstStream->In.StatFramesCaptured, cfCaptured);
1836#endif
1837 }
1838 else if (RT_UNLIKELY(RT_FAILURE(rc)))
1839 {
1840 LogRel(("Audio: Capturing stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
1841 }
1842
1843 } while (0);
1844
1845 if (pcFramesCaptured)
1846 *pcFramesCaptured = cfCaptured;
1847
1848 int rc2 = RTCritSectLeave(&pThis->CritSect);
1849 if (RT_SUCCESS(rc))
1850 rc = rc2;
1851
1852 if (RT_FAILURE(rc))
1853 LogFlowFuncLeaveRC(rc);
1854
1855 return rc;
1856}
1857
1858#ifdef VBOX_WITH_AUDIO_CALLBACKS
1859/**
1860 * Duplicates an audio callback.
1861 *
1862 * @returns Pointer to duplicated callback, or NULL on failure.
1863 * @param pCB Callback to duplicate.
1864 */
1865static PPDMAUDIOCBRECORD drvAudioCallbackDuplicate(PPDMAUDIOCBRECORD pCB)
1866{
1867 AssertPtrReturn(pCB, NULL);
1868
1869 PPDMAUDIOCBRECORD pCBCopy = (PPDMAUDIOCBRECORD)RTMemDup((void *)pCB, sizeof(PDMAUDIOCBRECORD));
1870 if (!pCBCopy)
1871 return NULL;
1872
1873 if (pCB->pvCtx)
1874 {
1875 pCBCopy->pvCtx = RTMemDup(pCB->pvCtx, pCB->cbCtx);
1876 if (!pCBCopy->pvCtx)
1877 {
1878 RTMemFree(pCBCopy);
1879 return NULL;
1880 }
1881
1882 pCBCopy->cbCtx = pCB->cbCtx;
1883 }
1884
1885 return pCBCopy;
1886}
1887
1888/**
1889 * Destroys a given callback.
1890 *
1891 * @param pCB Callback to destroy.
1892 */
1893static void drvAudioCallbackDestroy(PPDMAUDIOCBRECORD pCB)
1894{
1895 if (!pCB)
1896 return;
1897
1898 RTListNodeRemove(&pCB->Node);
1899 if (pCB->pvCtx)
1900 {
1901 Assert(pCB->cbCtx);
1902 RTMemFree(pCB->pvCtx);
1903 }
1904 RTMemFree(pCB);
1905}
1906
1907/**
1908 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnRegisterCallbacks}
1909 */
1910static DECLCALLBACK(int) drvAudioRegisterCallbacks(PPDMIAUDIOCONNECTOR pInterface,
1911 PPDMAUDIOCBRECORD paCallbacks, size_t cCallbacks)
1912{
1913 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1914 AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER);
1915 AssertReturn(cCallbacks, VERR_INVALID_PARAMETER);
1916
1917 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1918
1919 int rc = RTCritSectEnter(&pThis->CritSect);
1920 if (RT_FAILURE(rc))
1921 return rc;
1922
1923 for (size_t i = 0; i < cCallbacks; i++)
1924 {
1925 PPDMAUDIOCBRECORD pCB = drvAudioCallbackDuplicate(&paCallbacks[i]);
1926 if (!pCB)
1927 {
1928 rc = VERR_NO_MEMORY;
1929 break;
1930 }
1931
1932 switch (pCB->enmSource)
1933 {
1934 case PDMAUDIOCBSOURCE_DEVICE:
1935 {
1936 switch (pCB->Device.enmType)
1937 {
1938 case PDMAUDIODEVICECBTYPE_DATA_INPUT:
1939 RTListAppend(&pThis->In.lstCB, &pCB->Node);
1940 break;
1941
1942 case PDMAUDIODEVICECBTYPE_DATA_OUTPUT:
1943 RTListAppend(&pThis->Out.lstCB, &pCB->Node);
1944 break;
1945
1946 default:
1947 AssertMsgFailed(("Not supported\n"));
1948 break;
1949 }
1950
1951 break;
1952 }
1953
1954 default:
1955 AssertMsgFailed(("Not supported\n"));
1956 break;
1957 }
1958 }
1959
1960 /** @todo Undo allocations on error. */
1961
1962 int rc2 = RTCritSectLeave(&pThis->CritSect);
1963 if (RT_SUCCESS(rc))
1964 rc = rc2;
1965
1966 return rc;
1967}
1968#endif /* VBOX_WITH_AUDIO_CALLBACKS */
1969
1970#ifdef VBOX_WITH_AUDIO_CALLBACKS
1971/**
1972 * Backend callback implementation.
1973 *
1974 * Important: No calls back to the backend within this function, as the backend
1975 * might hold any locks / critical sections while executing this callback.
1976 * Will result in some ugly deadlocks (or at least locking order violations) then.
1977 *
1978 * @copydoc FNPDMHOSTAUDIOCALLBACK
1979 */
1980static DECLCALLBACK(int) drvAudioBackendCallback(PPDMDRVINS pDrvIns,
1981 PDMAUDIOBACKENDCBTYPE enmType, void *pvUser, size_t cbUser)
1982{
1983 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
1984 RT_NOREF(pvUser, cbUser);
1985 /* pvUser and cbUser are optional. */
1986
1987 /* Get the upper driver (PDMIAUDIOCONNECTOR). */
1988 AssertPtr(pDrvIns->pUpBase);
1989 PPDMIAUDIOCONNECTOR pInterface = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
1990 AssertPtr(pInterface);
1991 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1992
1993 int rc = RTCritSectEnter(&pThis->CritSect);
1994 AssertRCReturn(rc, rc);
1995
1996 LogFunc(("pThis=%p, enmType=%RU32, pvUser=%p, cbUser=%zu\n", pThis, enmType, pvUser, cbUser));
1997
1998 switch (enmType)
1999 {
2000 case PDMAUDIOBACKENDCBTYPE_DEVICES_CHANGED:
2001 LogRel(("Audio: Host audio device configuration has changed\n"));
2002 rc = drvAudioScheduleReInitInternal(pThis);
2003 break;
2004
2005 default:
2006 AssertMsgFailed(("Not supported\n"));
2007 break;
2008 }
2009
2010 int rc2 = RTCritSectLeave(&pThis->CritSect);
2011 if (RT_SUCCESS(rc))
2012 rc = rc2;
2013
2014 LogFlowFunc(("Returning %Rrc\n", rc));
2015 return rc;
2016}
2017#endif /* VBOX_WITH_AUDIO_CALLBACKS */
2018
2019#ifdef VBOX_WITH_AUDIO_ENUM
2020/**
2021 * Enumerates all host audio devices.
2022 * This functionality might not be implemented by all backends and will return VERR_NOT_SUPPORTED
2023 * if not being supported.
2024 *
2025 * @returns IPRT status code.
2026 * @param pThis Driver instance to be called.
2027 * @param fLog Whether to print the enumerated device to the release log or not.
2028 * @param pDevEnum Where to store the device enumeration.
2029 */
2030static int drvAudioDevicesEnumerateInternal(PDRVAUDIO pThis, bool fLog, PPDMAUDIODEVICEENUM pDevEnum)
2031{
2032 int rc;
2033
2034 /*
2035 * If the backend supports it, do a device enumeration.
2036 */
2037 if (pThis->pHostDrvAudio->pfnGetDevices)
2038 {
2039 PDMAUDIODEVICEENUM DevEnum;
2040 rc = pThis->pHostDrvAudio->pfnGetDevices(pThis->pHostDrvAudio, &DevEnum);
2041 if (RT_SUCCESS(rc))
2042 {
2043 if (fLog)
2044 LogRel(("Audio: Found %RU16 devices\n", DevEnum.cDevices));
2045
2046 PPDMAUDIODEVICE pDev;
2047 RTListForEach(&DevEnum.lstDevices, pDev, PDMAUDIODEVICE, Node)
2048 {
2049 if (fLog)
2050 {
2051 char *pszFlags = DrvAudioHlpAudDevFlagsToStrA(pDev->fFlags);
2052
2053 LogRel(("Audio: Device '%s':\n", pDev->szName));
2054 LogRel(("Audio: \tUsage = %s\n", DrvAudioHlpAudDirToStr(pDev->enmUsage)));
2055 LogRel(("Audio: \tFlags = %s\n", pszFlags ? pszFlags : "<NONE>"));
2056 LogRel(("Audio: \tInput channels = %RU8\n", pDev->cMaxInputChannels));
2057 LogRel(("Audio: \tOutput channels = %RU8\n", pDev->cMaxOutputChannels));
2058
2059 if (pszFlags)
2060 RTStrFree(pszFlags);
2061 }
2062 }
2063
2064 if (pDevEnum)
2065 rc = DrvAudioHlpDeviceEnumCopy(pDevEnum, &DevEnum);
2066
2067 DrvAudioHlpDeviceEnumFree(&DevEnum);
2068 }
2069 else
2070 {
2071 if (fLog)
2072 LogRel(("Audio: Device enumeration failed with %Rrc\n", rc));
2073 /* Not fatal. */
2074 }
2075 }
2076 else
2077 {
2078 rc = VERR_NOT_SUPPORTED;
2079
2080 if (fLog)
2081 LogRel3(("Audio: Host audio backend does not support audio device enumeration, skipping\n"));
2082 }
2083
2084 LogFunc(("Returning %Rrc\n", rc));
2085 return rc;
2086}
2087#endif /* VBOX_WITH_AUDIO_ENUM */
2088
2089/**
2090 * Initializes the host backend and queries its initial configuration.
2091 * If the host backend fails, VERR_AUDIO_BACKEND_INIT_FAILED will be returned.
2092 *
2093 * Note: As this routine is called when attaching to the device LUN in the
2094 * device emulation, we either check for success or VERR_AUDIO_BACKEND_INIT_FAILED.
2095 * Everything else is considered as fatal and must be handled separately in
2096 * the device emulation!
2097 *
2098 * @return IPRT status code.
2099 * @param pThis Driver instance to be called.
2100 * @param pCfgHandle CFGM configuration handle to use for this driver.
2101 */
2102static int drvAudioHostInit(PDRVAUDIO pThis, PCFGMNODE pCfgHandle)
2103{
2104 /* pCfgHandle is optional. */
2105 NOREF(pCfgHandle);
2106 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2107
2108 LogFlowFuncEnter();
2109
2110 AssertPtr(pThis->pHostDrvAudio);
2111 int rc = pThis->pHostDrvAudio->pfnInit(pThis->pHostDrvAudio);
2112 if (RT_FAILURE(rc))
2113 {
2114 LogRel(("Audio: Initialization of host backend failed with %Rrc\n", rc));
2115 return VERR_AUDIO_BACKEND_INIT_FAILED;
2116 }
2117
2118 /*
2119 * Get the backend configuration.
2120 */
2121 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
2122 if (RT_FAILURE(rc))
2123 {
2124 LogRel(("Audio: Getting host backend configuration failed with %Rrc\n", rc));
2125 return VERR_AUDIO_BACKEND_INIT_FAILED;
2126 }
2127
2128 pThis->In.cStreamsFree = pThis->BackendCfg.cMaxStreamsIn;
2129 pThis->Out.cStreamsFree = pThis->BackendCfg.cMaxStreamsOut;
2130
2131 LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->In.cStreamsFree, pThis->Out.cStreamsFree));
2132
2133 LogRel2(("Audio: Host audio backend supports %RU32 input streams and %RU32 output streams at once\n",
2134 /* Clamp for logging. Unlimited streams are defined by UINT32_MAX. */
2135 RT_MIN(64, pThis->In.cStreamsFree), RT_MIN(64, pThis->Out.cStreamsFree)));
2136
2137#ifdef VBOX_WITH_AUDIO_ENUM
2138 int rc2 = drvAudioDevicesEnumerateInternal(pThis, true /* fLog */, NULL /* pDevEnum */);
2139 if (rc2 != VERR_NOT_SUPPORTED) /* Some backends don't implement device enumeration. */
2140 AssertRC(rc2);
2141
2142 RT_NOREF(rc2);
2143 /* Ignore rc. */
2144#endif
2145
2146#ifdef VBOX_WITH_AUDIO_CALLBACKS
2147 /*
2148 * If the backend supports it, offer a callback to this connector.
2149 */
2150 if (pThis->pHostDrvAudio->pfnSetCallback)
2151 {
2152 rc2 = pThis->pHostDrvAudio->pfnSetCallback(pThis->pHostDrvAudio, drvAudioBackendCallback);
2153 if (RT_FAILURE(rc2))
2154 LogRel(("Audio: Error registering backend callback, rc=%Rrc\n", rc2));
2155 /* Not fatal. */
2156 }
2157#endif
2158
2159 LogFlowFuncLeave();
2160 return VINF_SUCCESS;
2161}
2162
2163/**
2164 * Handles state changes for all audio streams.
2165 *
2166 * @param pDrvIns Pointer to driver instance.
2167 * @param enmCmd Stream command to set for all streams.
2168 */
2169static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
2170{
2171 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2172 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2173
2174 LogFlowFunc(("enmCmd=%s\n", DrvAudioHlpStreamCmdToStr(enmCmd)));
2175
2176 int rc2 = RTCritSectEnter(&pThis->CritSect);
2177 AssertRC(rc2);
2178
2179 if (pThis->pHostDrvAudio)
2180 {
2181 PPDMAUDIOSTREAM pHstStream;
2182 RTListForEach(&pThis->lstHstStreams, pHstStream, PDMAUDIOSTREAM, Node)
2183 drvAudioStreamControlInternalBackend(pThis, pHstStream, enmCmd);
2184 }
2185
2186 rc2 = RTCritSectLeave(&pThis->CritSect);
2187 AssertRC(rc2);
2188}
2189
2190/**
2191 * Intializes an audio driver instance.
2192 *
2193 * @returns IPRT status code.
2194 * @param pDrvIns Pointer to driver instance.
2195 * @param pCfgHandle CFGM handle to use for configuration.
2196 */
2197static int drvAudioInit(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
2198{
2199 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
2200 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
2201
2202 LogRel2(("Audio: Verbose logging enabled\n"));
2203
2204 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2205
2206 int rc = RTCritSectInit(&pThis->CritSect);
2207 AssertRCReturn(rc, rc);
2208
2209 /*
2210 * Configure driver from CFGM stuff.
2211 */
2212#ifdef DEBUG
2213 CFGMR3Dump(pCfgHandle);
2214#endif
2215
2216 pThis->fTerminate = false;
2217 pThis->pCFGMNode = pCfgHandle;
2218
2219 int rc2 = CFGMR3QueryString(pThis->pCFGMNode, "DriverName", pThis->szName, sizeof(pThis->szName));
2220 if (RT_FAILURE(rc2))
2221 RTStrPrintf(pThis->szName, sizeof(pThis->szName), "Untitled");
2222
2223 /* By default we don't enable anything if wrongly / not set-up. */
2224 CFGMR3QueryBoolDef(pThis->pCFGMNode, "InputEnabled", &pThis->In.fEnabled, false);
2225 CFGMR3QueryBoolDef(pThis->pCFGMNode, "OutputEnabled", &pThis->Out.fEnabled, false);
2226
2227 CFGMR3QueryBoolDef(pThis->pCFGMNode, "DebugEnabled", &pThis->Dbg.fEnabled, false);
2228 rc2 = CFGMR3QueryString(pThis->pCFGMNode, "DebugPathOut", pThis->Dbg.szPathOut, sizeof(pThis->Dbg.szPathOut));
2229 if ( RT_FAILURE(rc2)
2230 || !strlen(pThis->Dbg.szPathOut))
2231 {
2232 RTStrPrintf(pThis->Dbg.szPathOut, sizeof(pThis->Dbg.szPathOut), VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH);
2233 }
2234
2235 if (pThis->Dbg.fEnabled)
2236 LogRel(("Audio: Debugging enabled (audio data written to '%s')\n", pThis->Dbg.szPathOut));
2237
2238 LogRel2(("Audio: Initial status for driver '%s': Input is %s, output is %s\n",
2239 pThis->szName, pThis->In.fEnabled ? "enabled" : "disabled", pThis->Out.fEnabled ? "enabled" : "disabled"));
2240
2241 LogFunc(("[%s] rc=%Rrc\n", pThis->szName, rc));
2242 return rc;
2243}
2244
2245/**
2246 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRead}
2247 */
2248static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
2249 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
2250{
2251 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2252 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2253
2254 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2255 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2256 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
2257 /* pcbRead is optional. */
2258
2259 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
2260 ("Stream '%s' is not an input stream and therefore cannot be read from (direction is 0x%x)\n",
2261 pStream->szName, pStream->enmDir));
2262
2263 uint32_t cbReadTotal = 0;
2264
2265 int rc = RTCritSectEnter(&pThis->CritSect);
2266 if (RT_FAILURE(rc))
2267 return rc;
2268
2269 do
2270 {
2271 if (!pThis->In.fEnabled)
2272 {
2273 RT_BZERO(pvBuf, cbBuf);
2274 cbReadTotal = cbBuf;
2275 break;
2276 }
2277
2278 if ( pThis->pHostDrvAudio
2279 && pThis->pHostDrvAudio->pfnGetStatus
2280 && pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_IN) != PDMAUDIOBACKENDSTS_RUNNING)
2281 {
2282 rc = VERR_NOT_AVAILABLE;
2283 break;
2284 }
2285
2286 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2287 if (!pHstStream)
2288 {
2289 rc = VERR_NOT_AVAILABLE;
2290 break;
2291 }
2292
2293 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
2294 AssertPtr(pGstStream);
2295
2296 /*
2297 * Read from the parent buffer (that is, the guest buffer) which
2298 * should have the audio data in the format the guest needs.
2299 */
2300 uint32_t cReadTotal = 0;
2301
2302 uint32_t cToRead = RT_MIN(AUDIOMIXBUF_B2F(&pGstStream->MixBuf, cbBuf), AudioMixBufUsed(&pGstStream->MixBuf));
2303 while (cToRead)
2304 {
2305 uint32_t cRead;
2306 rc = AudioMixBufAcquireReadBlock(&pGstStream->MixBuf, (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cReadTotal),
2307 AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cToRead), &cRead);
2308 if (RT_FAILURE(rc))
2309 break;
2310
2311#if defined (VBOX_WITH_STATISTICS) || defined (VBOX_AUDIO_DEBUG_DUMP_PCM_DATA)
2312 const uint32_t cbRead = AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cRead);
2313#endif
2314
2315#ifdef VBOX_WITH_STATISTICS
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] cbReadTotal=%RU32, rc=%Rrc\n", pStream->szName, 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 PPDMAUDIOSTREAM pStream;
2668 RTListForEach(&pThis->lstHstStreams, pStream, PDMAUDIOSTREAM, Node)
2669 {
2670 if (pStream->enmDir != enmDir) /* Skip unwanted streams. */
2671 continue;
2672
2673 int rc2 = drvAudioStreamControlInternal(pThis, pStream,
2674 fEnable ? PDMAUDIOSTREAMCMD_ENABLE : PDMAUDIOSTREAMCMD_DISABLE);
2675 if (RT_FAILURE(rc2))
2676 LogRel(("Audio: Failed to %s %s stream '%s', rc=%Rrc\n",
2677 fEnable ? "enable" : "disable", enmDir == PDMAUDIODIR_IN ? "input" : "output", pStream->szName, rc2));
2678
2679 if (RT_SUCCESS(rc))
2680 rc = rc2;
2681
2682 /* Keep going. */
2683 }
2684
2685 *pfEnabled = fEnable;
2686 }
2687
2688 int rc3 = RTCritSectLeave(&pThis->CritSect);
2689 if (RT_SUCCESS(rc))
2690 rc = rc3;
2691
2692 LogFlowFuncLeaveRC(rc);
2693 return rc;
2694}
2695
2696/**
2697 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnIsEnabled}
2698 */
2699static DECLCALLBACK(bool) drvAudioIsEnabled(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
2700{
2701 AssertPtrReturn(pInterface, false);
2702
2703 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2704
2705 int rc2 = RTCritSectEnter(&pThis->CritSect);
2706 if (RT_FAILURE(rc2))
2707 return false;
2708
2709 bool *pfEnabled;
2710 if (enmDir == PDMAUDIODIR_IN)
2711 pfEnabled = &pThis->In.fEnabled;
2712 else if (enmDir == PDMAUDIODIR_OUT)
2713 pfEnabled = &pThis->Out.fEnabled;
2714 else
2715 AssertFailedReturn(false);
2716
2717 const bool fIsEnabled = *pfEnabled;
2718
2719 rc2 = RTCritSectLeave(&pThis->CritSect);
2720 AssertRC(rc2);
2721
2722 return fIsEnabled;
2723}
2724
2725/**
2726 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetConfig}
2727 */
2728static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
2729{
2730 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2731 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2732
2733 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2734
2735 int rc = RTCritSectEnter(&pThis->CritSect);
2736 if (RT_FAILURE(rc))
2737 return rc;
2738
2739 if (pThis->pHostDrvAudio)
2740 {
2741 if (pThis->pHostDrvAudio->pfnGetConfig)
2742 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, pCfg);
2743 else
2744 rc = VERR_NOT_SUPPORTED;
2745 }
2746 else
2747 AssertFailed();
2748
2749 int rc2 = RTCritSectLeave(&pThis->CritSect);
2750 if (RT_SUCCESS(rc))
2751 rc = rc2;
2752
2753 LogFlowFuncLeaveRC(rc);
2754 return rc;
2755}
2756
2757/**
2758 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetStatus}
2759 */
2760static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioGetStatus(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
2761{
2762 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
2763
2764 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2765
2766 PDMAUDIOBACKENDSTS backendSts = PDMAUDIOBACKENDSTS_UNKNOWN;
2767
2768 int rc = RTCritSectEnter(&pThis->CritSect);
2769 if (RT_SUCCESS(rc))
2770 {
2771 if (pThis->pHostDrvAudio)
2772 {
2773 if (pThis->pHostDrvAudio->pfnGetStatus)
2774 backendSts = pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, enmDir);
2775 }
2776 else
2777 backendSts = PDMAUDIOBACKENDSTS_NOT_ATTACHED;
2778
2779 int rc2 = RTCritSectLeave(&pThis->CritSect);
2780 if (RT_SUCCESS(rc))
2781 rc = rc2;
2782 }
2783
2784 LogFlowFuncLeaveRC(rc);
2785 return backendSts;
2786}
2787
2788/**
2789 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetReadable}
2790 */
2791static DECLCALLBACK(uint32_t) drvAudioStreamGetReadable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2792{
2793 AssertPtrReturn(pInterface, 0);
2794 AssertPtrReturn(pStream, 0);
2795
2796 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2797
2798 int rc2 = RTCritSectEnter(&pThis->CritSect);
2799 AssertRC(rc2);
2800
2801 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN, ("Can't read from a non-input stream\n"));
2802
2803 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2804 if (!pHstStream) /* No host stream available? Bail out early. */
2805 {
2806 rc2 = RTCritSectLeave(&pThis->CritSect);
2807 AssertRC(rc2);
2808
2809 return 0;
2810 }
2811
2812 uint32_t cReadable = 0;
2813
2814 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
2815 if (pGstStream)
2816 cReadable = AudioMixBufLive(&pGstStream->MixBuf);
2817
2818 Log3Func(("[%s] cbReadable=%RU32 (%zu bytes)\n", pHstStream->szName, cReadable,
2819 AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cReadable)));
2820
2821 uint32_t cbReadable = AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cReadable);
2822
2823 rc2 = RTCritSectLeave(&pThis->CritSect);
2824 AssertRC(rc2);
2825
2826 /* Return bytes instead of audio frames. */
2827 return cbReadable;
2828}
2829
2830/**
2831 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetWritable}
2832 */
2833static DECLCALLBACK(uint32_t) drvAudioStreamGetWritable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2834{
2835 AssertPtrReturn(pInterface, 0);
2836 AssertPtrReturn(pStream, 0);
2837
2838 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2839
2840 int rc2 = RTCritSectEnter(&pThis->CritSect);
2841 AssertRC(rc2);
2842
2843 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT, ("Can't write to a non-output stream\n"));
2844
2845 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2846 if (!pHstStream) /* No host stream available? Bail out early. */
2847 {
2848 rc2 = RTCritSectLeave(&pThis->CritSect);
2849 AssertRC(rc2);
2850
2851 AssertMsgFailed(("Guest stream '%s' does not have a host stream attached\n", pStream->szName));
2852 return 0;
2853 }
2854
2855 /* As the host side sets the overall pace, return the writable bytes from that side. */
2856 uint32_t cbWritable = AudioMixBufFreeBytes(&pHstStream->MixBuf);
2857
2858 Log3Func(("[%s] cbWritable=%RU32\n", pHstStream->szName, cbWritable));
2859
2860 rc2 = RTCritSectLeave(&pThis->CritSect);
2861 AssertRC(rc2);
2862
2863 return cbWritable;
2864}
2865
2866/**
2867 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetStatus}
2868 */
2869static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvAudioStreamGetStatus(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2870{
2871 AssertPtrReturn(pInterface, false);
2872
2873 if (!pStream)
2874 return PDMAUDIOSTREAMSTS_FLAG_NONE;
2875
2876 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2877
2878 int rc2 = RTCritSectEnter(&pThis->CritSect);
2879 AssertRC(rc2);
2880
2881 PDMAUDIOSTREAMSTS strmSts = PDMAUDIOSTREAMSTS_FLAG_NONE;
2882
2883 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2884 if (pHstStream)
2885 {
2886 strmSts = pHstStream->fStatus;
2887#ifdef LOG_ENABLED
2888 char *pszHstSts = dbgAudioStreamStatusToStr(pHstStream->fStatus);
2889 Log3Func(("[%s] %s\n", pHstStream->szName, pszHstSts));
2890 RTStrFree(pszHstSts);
2891#endif
2892 }
2893
2894 rc2 = RTCritSectLeave(&pThis->CritSect);
2895 AssertRC(rc2);
2896
2897 return strmSts;
2898}
2899
2900/**
2901 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamSetVolume}
2902 */
2903static DECLCALLBACK(int) drvAudioStreamSetVolume(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, PPDMAUDIOVOLUME pVol)
2904{
2905 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2906 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2907 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
2908
2909 LogFlowFunc(("[%s] volL=%RU32, volR=%RU32, fMute=%RTbool\n", pStream->szName, pVol->uLeft, pVol->uRight, pVol->fMuted));
2910
2911 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2912 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
2913
2914 AudioMixBufSetVolume(&pHstStream->MixBuf, pVol);
2915 AudioMixBufSetVolume(&pGstStream->MixBuf, pVol);
2916 return VINF_SUCCESS;
2917}
2918
2919/**
2920 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamDestroy}
2921 */
2922static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2923{
2924 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2925 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2926
2927 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2928
2929 int rc = RTCritSectEnter(&pThis->CritSect);
2930 AssertRC(rc);
2931
2932 PDMAUDIODIR enmDir = pStream->enmDir;
2933
2934 LogFlowFunc(("[%s] cRefs=%RU32\n", pStream->szName, pStream->cRefs));
2935 if (pStream->cRefs > 1)
2936 rc = VERR_WRONG_ORDER;
2937
2938 if (RT_SUCCESS(rc))
2939 {
2940 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2941 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
2942
2943 LogRel2(("Audio: Destroying host stream '%s' (guest stream '%s')\n",
2944 pHstStream ? pHstStream->szName : "<None>",
2945 pGstStream ? pGstStream->szName : "<None>"));
2946
2947 /* Should prevent double frees. */
2948 Assert(pHstStream != pGstStream);
2949
2950 if (pHstStream)
2951 {
2952 rc = drvAudioStreamUninitInternal(pThis, pHstStream);
2953 if (RT_SUCCESS(rc))
2954 {
2955 if (pHstStream->enmDir == PDMAUDIODIR_IN)
2956 {
2957#ifdef VBOX_WITH_STATISTICS
2958 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pHstStream->In.StatFramesCaptured);
2959#endif
2960 if (pThis->Dbg.fEnabled)
2961 {
2962 DrvAudioHlpFileDestroy(pHstStream->In.Dbg.pFileCaptureNonInterleaved);
2963 DrvAudioHlpFileDestroy(pHstStream->In.Dbg.pFileStreamRead);
2964 }
2965 }
2966 else if (pHstStream->enmDir == PDMAUDIODIR_OUT)
2967 {
2968#ifdef VBOX_WITH_STATISTICS
2969 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pHstStream->Out.StatFramesPlayed);
2970#endif
2971 if (pThis->Dbg.fEnabled)
2972 {
2973 DrvAudioHlpFileDestroy(pHstStream->Out.Dbg.pFilePlayNonInterleaved);
2974 DrvAudioHlpFileDestroy(pHstStream->Out.Dbg.pFileStreamWrite);
2975 }
2976 }
2977 else
2978 AssertFailed();
2979
2980 RTListNodeRemove(&pHstStream->Node);
2981
2982 drvAudioStreamFree(pHstStream);
2983 pHstStream = NULL;
2984 }
2985 else
2986 LogRel(("Audio: Uninitializing host stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
2987 }
2988
2989 if ( RT_SUCCESS(rc)
2990 && pGstStream)
2991 {
2992 rc = drvAudioStreamUninitInternal(pThis, pGstStream);
2993 if (RT_SUCCESS(rc))
2994 {
2995#ifdef VBOX_WITH_STATISTICS
2996 if (pGstStream->enmDir == PDMAUDIODIR_IN)
2997 {
2998 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->In.StatBytesElapsed);
2999 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->In.StatBytesTotalRead);
3000 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->In.StatFramesCaptured);
3001 }
3002 else if (pGstStream->enmDir == PDMAUDIODIR_OUT)
3003 {
3004 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->Out.StatBytesElapsed);
3005 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->Out.StatBytesTotalWritten);
3006 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->Out.StatFramesPlayed);
3007 }
3008 else
3009 AssertFailed();
3010#endif
3011 RTListNodeRemove(&pGstStream->Node);
3012
3013 RTMemFree(pGstStream);
3014 pGstStream = NULL;
3015 }
3016 else
3017 LogRel(("Audio: Uninitializing guest stream '%s' failed with %Rrc\n", pGstStream->szName, rc));
3018 }
3019 }
3020
3021 if (RT_SUCCESS(rc))
3022 {
3023 if (enmDir == PDMAUDIODIR_IN)
3024 {
3025 pThis->In.cStreamsFree++;
3026 }
3027 else /* Out */
3028 {
3029 pThis->Out.cStreamsFree++;
3030 }
3031 }
3032
3033 int rc2 = RTCritSectLeave(&pThis->CritSect);
3034 if (RT_SUCCESS(rc))
3035 rc = rc2;
3036
3037 LogFlowFuncLeaveRC(rc);
3038 return rc;
3039}
3040
3041/**
3042 * Creates an audio stream on the backend side.
3043 *
3044 * @returns IPRT status code.
3045 * @param pThis Pointer to driver instance.
3046 * @param pHstStream (Host) audio stream to use for creating the stream on the backend side.
3047 * @param pCfgReq Requested audio stream configuration to use for stream creation.
3048 * @param pCfgAcq Acquired audio stream configuration returned by the backend. Optional, can be NULL.
3049 */
3050static int drvAudioStreamCreateInternalBackend(PDRVAUDIO pThis,
3051 PPDMAUDIOSTREAM pHstStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
3052{
3053 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
3054 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
3055 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
3056 /* pCfgAcq is optional. */
3057
3058 AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST,
3059 ("Stream '%s' is not a host stream and therefore has no backend\n", pHstStream->szName));
3060
3061 AssertMsg((pHstStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED) == 0,
3062 ("Stream '%s' already initialized in backend\n", pHstStream->szName));
3063
3064 /* Make the acquired host configuration the requested host configuration initially,
3065 * in case the backend does not report back an acquired configuration. */
3066 PDMAUDIOSTREAMCFG CfgAcq;
3067 int rc = DrvAudioHlpStreamCfgCopy(&CfgAcq, pCfgReq);
3068 if (RT_FAILURE(rc))
3069 {
3070 LogRel(("Audio: Creating stream '%s' with an invalid backend configuration not possible, skipping\n",
3071 pHstStream->szName));
3072 return rc;
3073 }
3074
3075 rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pHstStream->pvBackend, pCfgReq, &CfgAcq);
3076 if (RT_FAILURE(rc))
3077 {
3078 if (rc == VERR_NOT_SUPPORTED)
3079 LogRel2(("Audio: Creating stream '%s' in backend not supported, skipping\n", pHstStream->szName));
3080 else
3081 LogRel(("Audio: Creating stream '%s' in backend failed with %Rrc\n", pHstStream->szName, rc));
3082
3083 return rc;
3084 }
3085
3086 /* Validate acquired configuration. */
3087 if (!DrvAudioHlpStreamCfgIsValid(&CfgAcq))
3088 {
3089 LogRel(("Audio: Creating stream '%s' returned an invalid backend configuration, skipping\n", pHstStream->szName));
3090 return VERR_INVALID_PARAMETER;
3091 }
3092
3093 /* Only set the host's stream to initialized if we were able create the stream
3094 * in the host backend. This is necessary for trying to re-initialize the stream
3095 * at some later point in time. */
3096 pHstStream->fStatus |= PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
3097
3098 if (pCfgAcq)
3099 {
3100 int rc2 = DrvAudioHlpStreamCfgCopy(pCfgAcq, &CfgAcq);
3101 AssertRC(rc2);
3102 }
3103
3104 return VINF_SUCCESS;
3105}
3106
3107/**
3108 * Calls the backend to give it the chance to destroy its part of the audio stream.
3109 *
3110 * @returns IPRT status code.
3111 * @param pThis Pointer to driver instance.
3112 * @param pHstStream Host audio stream to call the backend destruction for.
3113 */
3114static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream)
3115{
3116 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
3117 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
3118
3119 AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST,
3120 ("Stream '%s' is not a host stream and therefore has no backend\n", pHstStream->szName));
3121
3122 int rc = VINF_SUCCESS;
3123
3124#ifdef LOG_ENABLED
3125 char *pszHstSts = dbgAudioStreamStatusToStr(pHstStream->fStatus);
3126 LogFunc(("[%s] fStatus=%s\n", pHstStream->szName, pszHstSts));
3127 RTStrFree(pszHstSts);
3128#endif
3129
3130 if (pHstStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED)
3131 {
3132 /* Check if the pointer to the host audio driver is still valid.
3133 * It can be NULL if we were called in drvAudioDestruct, for example. */
3134 if (pThis->pHostDrvAudio)
3135 rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pHstStream->pvBackend);
3136 if (RT_SUCCESS(rc))
3137 {
3138 pHstStream->fStatus &= ~PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
3139
3140#ifdef LOG_ENABLED
3141 /* This is not fatal, but log it anyway. */
3142 if (pHstStream->fStatus != PDMAUDIOSTREAMSTS_FLAG_NONE)
3143 LogFunc(("[%s] Warning: Stream still has %s set when destroying, must properly drain first\n",
3144 pHstStream->szName, pszHstSts));
3145#endif
3146 }
3147 }
3148
3149 LogFlowFunc(("[%s] Returning %Rrc\n", pHstStream->szName, rc));
3150 return rc;
3151}
3152
3153/**
3154 * Uninitializes an audio stream.
3155 *
3156 * @returns IPRT status code.
3157 * @param pThis Pointer to driver instance.
3158 * @param pStream Pointer to audio stream to uninitialize.
3159 */
3160static int drvAudioStreamUninitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
3161{
3162 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
3163 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
3164
3165 LogFlowFunc(("[%s] cRefs=%RU32\n", pStream->szName, pStream->cRefs));
3166
3167 if (pStream->cRefs > 1)
3168 return VERR_WRONG_ORDER;
3169
3170 int rc = VINF_SUCCESS;
3171
3172 if (pStream->enmCtx == PDMAUDIOSTREAMCTX_GUEST)
3173 {
3174 if (pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED)
3175 {
3176 rc = drvAudioStreamControlInternal(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
3177 if (RT_SUCCESS(rc))
3178 {
3179 pStream->fStatus &= ~PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
3180 Assert(pStream->fStatus == PDMAUDIOSTREAMSTS_FLAG_NONE);
3181 }
3182 }
3183 }
3184 else if (pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST)
3185 {
3186 rc = drvAudioStreamDestroyInternalBackend(pThis, pStream);
3187 }
3188 else
3189 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
3190
3191 if (RT_SUCCESS(rc))
3192 {
3193 /* Make sure that the pair (if any) knows that we're not valid anymore. */
3194 int rc2 = drvAudioStreamLinkToInternal(pStream, NULL);
3195 AssertRC(rc2);
3196
3197 /* Reset status. */
3198 pStream->fStatus = PDMAUDIOSTREAMSTS_FLAG_NONE;
3199
3200 /* Destroy mixing buffer. */
3201 AudioMixBufDestroy(&pStream->MixBuf);
3202 }
3203
3204 LogFlowFunc(("Returning %Rrc\n", rc));
3205 return rc;
3206}
3207
3208/**
3209 * Does the actual backend driver attaching and queries the backend's interface.
3210 *
3211 * @return VBox status code.
3212 * @param pThis Pointer to driver instance.
3213 * @param fFlags Attach flags; see PDMDrvHlpAttach().
3214 */
3215static int drvAudioDoAttachInternal(PDRVAUDIO pThis, uint32_t fFlags)
3216{
3217 Assert(pThis->pHostDrvAudio == NULL); /* No nested attaching. */
3218
3219 /*
3220 * Attach driver below and query its connector interface.
3221 */
3222 PPDMIBASE pDownBase;
3223 int rc = PDMDrvHlpAttach(pThis->pDrvIns, fFlags, &pDownBase);
3224 if (RT_SUCCESS(rc))
3225 {
3226 pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
3227 if (!pThis->pHostDrvAudio)
3228 {
3229 LogRel(("Audio: Failed to query interface for underlying host driver\n"));
3230 rc = PDMDRV_SET_ERROR(pThis->pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
3231 N_("Host audio backend missing or invalid"));
3232 }
3233 }
3234
3235 if (RT_SUCCESS(rc))
3236 {
3237 /*
3238 * If everything went well, initialize the lower driver.
3239 */
3240 AssertPtr(pThis->pCFGMNode);
3241 rc = drvAudioHostInit(pThis, pThis->pCFGMNode);
3242 }
3243
3244 LogFunc(("[%s] rc=%Rrc\n", pThis->szName, rc));
3245 return rc;
3246}
3247
3248
3249/********************************************************************/
3250
3251/**
3252 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
3253 */
3254static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
3255{
3256 LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
3257
3258 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
3259 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3260
3261 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
3262 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
3263
3264 return NULL;
3265}
3266
3267/**
3268 * Power Off notification.
3269 *
3270 * @param pDrvIns The driver instance data.
3271 */
3272static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
3273{
3274 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3275
3276 LogFlowFuncEnter();
3277
3278 if (!pThis->pHostDrvAudio) /* If not lower driver is configured, bail out. */
3279 return;
3280
3281 /* Just destroy the host stream on the backend side.
3282 * The rest will either be destructed by the device emulation or
3283 * in drvAudioDestruct(). */
3284 PPDMAUDIOSTREAM pStream;
3285 RTListForEach(&pThis->lstHstStreams, pStream, PDMAUDIOSTREAM, Node)
3286 {
3287 drvAudioStreamControlInternalBackend(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
3288 drvAudioStreamDestroyInternalBackend(pThis, pStream);
3289 }
3290
3291 /*
3292 * Last call for the driver below us.
3293 * Let it know that we reached end of life.
3294 */
3295 if (pThis->pHostDrvAudio->pfnShutdown)
3296 pThis->pHostDrvAudio->pfnShutdown(pThis->pHostDrvAudio);
3297
3298 pThis->pHostDrvAudio = NULL;
3299
3300 LogFlowFuncLeave();
3301}
3302
3303/**
3304 * Constructs an audio driver instance.
3305 *
3306 * @copydoc FNPDMDRVCONSTRUCT
3307 */
3308static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
3309{
3310 LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfg, fFlags));
3311
3312 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3313 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
3314
3315 RTListInit(&pThis->lstHstStreams);
3316 RTListInit(&pThis->lstGstStreams);
3317#ifdef VBOX_WITH_AUDIO_CALLBACKS
3318 RTListInit(&pThis->In.lstCB);
3319 RTListInit(&pThis->Out.lstCB);
3320#endif
3321
3322 /*
3323 * Init the static parts.
3324 */
3325 pThis->pDrvIns = pDrvIns;
3326 /* IBase. */
3327 pDrvIns->IBase.pfnQueryInterface = drvAudioQueryInterface;
3328 /* IAudioConnector. */
3329 pThis->IAudioConnector.pfnEnable = drvAudioEnable;
3330 pThis->IAudioConnector.pfnIsEnabled = drvAudioIsEnabled;
3331 pThis->IAudioConnector.pfnGetConfig = drvAudioGetConfig;
3332 pThis->IAudioConnector.pfnGetStatus = drvAudioGetStatus;
3333 pThis->IAudioConnector.pfnStreamCreate = drvAudioStreamCreate;
3334 pThis->IAudioConnector.pfnStreamDestroy = drvAudioStreamDestroy;
3335 pThis->IAudioConnector.pfnStreamRetain = drvAudioStreamRetain;
3336 pThis->IAudioConnector.pfnStreamRelease = drvAudioStreamRelease;
3337 pThis->IAudioConnector.pfnStreamControl = drvAudioStreamControl;
3338 pThis->IAudioConnector.pfnStreamRead = drvAudioStreamRead;
3339 pThis->IAudioConnector.pfnStreamWrite = drvAudioStreamWrite;
3340 pThis->IAudioConnector.pfnStreamIterate = drvAudioStreamIterate;
3341 pThis->IAudioConnector.pfnStreamGetReadable = drvAudioStreamGetReadable;
3342 pThis->IAudioConnector.pfnStreamGetWritable = drvAudioStreamGetWritable;
3343 pThis->IAudioConnector.pfnStreamGetStatus = drvAudioStreamGetStatus;
3344 pThis->IAudioConnector.pfnStreamSetVolume = drvAudioStreamSetVolume;
3345 pThis->IAudioConnector.pfnStreamPlay = drvAudioStreamPlay;
3346 pThis->IAudioConnector.pfnStreamCapture = drvAudioStreamCapture;
3347#ifdef VBOX_WITH_AUDIO_CALLBACKS
3348 pThis->IAudioConnector.pfnRegisterCallbacks = drvAudioRegisterCallbacks;
3349#endif
3350
3351 int rc = drvAudioInit(pDrvIns, pCfg);
3352 if (RT_SUCCESS(rc))
3353 {
3354#ifdef VBOX_WITH_STATISTICS
3355 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsActive, "TotalStreamsActive",
3356 STAMUNIT_COUNT, "Total active audio streams.");
3357 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsCreated, "TotalStreamsCreated",
3358 STAMUNIT_COUNT, "Total created audio streams.");
3359 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesRead, "TotalFramesRead",
3360 STAMUNIT_COUNT, "Total frames read by device emulation.");
3361 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesWritten, "TotalFramesWritten",
3362 STAMUNIT_COUNT, "Total frames written by device emulation ");
3363 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesMixedIn, "TotalFramesMixedIn",
3364 STAMUNIT_COUNT, "Total input frames mixed.");
3365 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesMixedOut, "TotalFramesMixedOut",
3366 STAMUNIT_COUNT, "Total output frames mixed.");
3367 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesLostIn, "TotalFramesLostIn",
3368 STAMUNIT_COUNT, "Total input frames lost.");
3369 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesLostOut, "TotalFramesLostOut",
3370 STAMUNIT_COUNT, "Total output frames lost.");
3371 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesOut, "TotalFramesPlayed",
3372 STAMUNIT_COUNT, "Total frames played by backend.");
3373 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesIn, "TotalFramesCaptured",
3374 STAMUNIT_COUNT, "Total frames captured by backend.");
3375 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesRead, "TotalBytesRead",
3376 STAMUNIT_BYTES, "Total bytes read.");
3377 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesWritten, "TotalBytesWritten",
3378 STAMUNIT_BYTES, "Total bytes written.");
3379
3380 PDMDrvHlpSTAMRegProfileAdvEx(pDrvIns, &pThis->Stats.DelayIn, "DelayIn",
3381 STAMUNIT_NS_PER_CALL, "Profiling of input data processing.");
3382 PDMDrvHlpSTAMRegProfileAdvEx(pDrvIns, &pThis->Stats.DelayOut, "DelayOut",
3383 STAMUNIT_NS_PER_CALL, "Profiling of output data processing.");
3384#endif
3385 }
3386
3387 rc = drvAudioDoAttachInternal(pThis, fFlags);
3388 if (RT_FAILURE(rc))
3389 {
3390 /* No lower attached driver (yet)? Not a failure, might get attached later at runtime, just skip. */
3391 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
3392 rc = VINF_SUCCESS;
3393 }
3394
3395 LogFlowFuncLeaveRC(rc);
3396 return rc;
3397}
3398
3399/**
3400 * Destructs an audio driver instance.
3401 *
3402 * @copydoc FNPDMDRVDESTRUCT
3403 */
3404static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
3405{
3406 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
3407 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3408
3409 LogFlowFuncEnter();
3410
3411 int rc2;
3412
3413 if (RTCritSectIsInitialized(&pThis->CritSect))
3414 {
3415 rc2 = RTCritSectEnter(&pThis->CritSect);
3416 AssertRC(rc2);
3417 }
3418
3419 /*
3420 * Note: No calls here to the driver below us anymore,
3421 * as PDM already has destroyed it.
3422 * If you need to call something from the host driver,
3423 * do this in drvAudioPowerOff() instead.
3424 */
3425
3426 /* Thus, NULL the pointer to the host audio driver first,
3427 * so that routines like drvAudioStreamDestroyInternal() don't call the driver(s) below us anymore. */
3428 pThis->pHostDrvAudio = NULL;
3429
3430 PPDMAUDIOSTREAM pStream, pStreamNext;
3431 RTListForEachSafe(&pThis->lstHstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
3432 {
3433 rc2 = drvAudioStreamUninitInternal(pThis, pStream);
3434 if (RT_SUCCESS(rc2))
3435 {
3436 RTListNodeRemove(&pStream->Node);
3437
3438 drvAudioStreamFree(pStream);
3439 pStream = NULL;
3440 }
3441 }
3442
3443 /* Sanity. */
3444 Assert(RTListIsEmpty(&pThis->lstHstStreams));
3445
3446 RTListForEachSafe(&pThis->lstGstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
3447 {
3448 rc2 = drvAudioStreamUninitInternal(pThis, pStream);
3449 if (RT_SUCCESS(rc2))
3450 {
3451 RTListNodeRemove(&pStream->Node);
3452
3453 RTMemFree(pStream);
3454 pStream = NULL;
3455 }
3456 }
3457
3458 /* Sanity. */
3459 Assert(RTListIsEmpty(&pThis->lstGstStreams));
3460
3461#ifdef VBOX_WITH_AUDIO_CALLBACKS
3462 /*
3463 * Destroy callbacks, if any.
3464 */
3465 PPDMAUDIOCBRECORD pCB, pCBNext;
3466 RTListForEachSafe(&pThis->In.lstCB, pCB, pCBNext, PDMAUDIOCBRECORD, Node)
3467 drvAudioCallbackDestroy(pCB);
3468
3469 RTListForEachSafe(&pThis->Out.lstCB, pCB, pCBNext, PDMAUDIOCBRECORD, Node)
3470 drvAudioCallbackDestroy(pCB);
3471#endif
3472
3473 if (RTCritSectIsInitialized(&pThis->CritSect))
3474 {
3475 rc2 = RTCritSectLeave(&pThis->CritSect);
3476 AssertRC(rc2);
3477
3478 rc2 = RTCritSectDelete(&pThis->CritSect);
3479 AssertRC(rc2);
3480 }
3481
3482#ifdef VBOX_WITH_STATISTICS
3483 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalStreamsActive);
3484 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalStreamsCreated);
3485 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesRead);
3486 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesWritten);
3487 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesMixedIn);
3488 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesMixedOut);
3489 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesLostIn);
3490 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesLostOut);
3491 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesOut);
3492 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesIn);
3493 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalBytesRead);
3494 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalBytesWritten);
3495 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.DelayIn);
3496 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.DelayOut);
3497#endif
3498
3499 LogFlowFuncLeave();
3500}
3501
3502/**
3503 * Suspend notification.
3504 *
3505 * @param pDrvIns The driver instance data.
3506 */
3507static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
3508{
3509 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_PAUSE);
3510}
3511
3512/**
3513 * Resume notification.
3514 *
3515 * @param pDrvIns The driver instance data.
3516 */
3517static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
3518{
3519 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_RESUME);
3520}
3521
3522/**
3523 * Attach notification.
3524 *
3525 * @param pDrvIns The driver instance data.
3526 * @param fFlags Attach flags.
3527 */
3528static DECLCALLBACK(int) drvAudioAttach(PPDMDRVINS pDrvIns, uint32_t fFlags)
3529{
3530 RT_NOREF(fFlags);
3531
3532 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
3533 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3534
3535 int rc2 = RTCritSectEnter(&pThis->CritSect);
3536 AssertRC(rc2);
3537
3538 LogFunc(("%s\n", pThis->szName));
3539
3540 int rc = drvAudioDoAttachInternal(pThis, fFlags);
3541
3542 rc2 = RTCritSectLeave(&pThis->CritSect);
3543 if (RT_SUCCESS(rc))
3544 rc = rc2;
3545
3546 return rc;
3547}
3548
3549/**
3550 * Detach notification.
3551 *
3552 * @param pDrvIns The driver instance data.
3553 * @param fFlags Detach flags.
3554 */
3555static DECLCALLBACK(void) drvAudioDetach(PPDMDRVINS pDrvIns, uint32_t fFlags)
3556{
3557 RT_NOREF(fFlags);
3558
3559 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
3560 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3561
3562 int rc2 = RTCritSectEnter(&pThis->CritSect);
3563 AssertRC(rc2);
3564
3565 pThis->pHostDrvAudio = NULL;
3566
3567 LogFunc(("%s\n", pThis->szName));
3568
3569 rc2 = RTCritSectLeave(&pThis->CritSect);
3570 AssertRC(rc2);
3571}
3572
3573/**
3574 * Audio driver registration record.
3575 */
3576const PDMDRVREG g_DrvAUDIO =
3577{
3578 /* u32Version */
3579 PDM_DRVREG_VERSION,
3580 /* szName */
3581 "AUDIO",
3582 /* szRCMod */
3583 "",
3584 /* szR0Mod */
3585 "",
3586 /* pszDescription */
3587 "Audio connector driver",
3588 /* fFlags */
3589 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
3590 /* fClass */
3591 PDM_DRVREG_CLASS_AUDIO,
3592 /* cMaxInstances */
3593 UINT32_MAX,
3594 /* cbInstance */
3595 sizeof(DRVAUDIO),
3596 /* pfnConstruct */
3597 drvAudioConstruct,
3598 /* pfnDestruct */
3599 drvAudioDestruct,
3600 /* pfnRelocate */
3601 NULL,
3602 /* pfnIOCtl */
3603 NULL,
3604 /* pfnPowerOn */
3605 NULL,
3606 /* pfnReset */
3607 NULL,
3608 /* pfnSuspend */
3609 drvAudioSuspend,
3610 /* pfnResume */
3611 drvAudioResume,
3612 /* pfnAttach */
3613 drvAudioAttach,
3614 /* pfnDetach */
3615 drvAudioDetach,
3616 /* pfnPowerOff */
3617 drvAudioPowerOff,
3618 /* pfnSoftReset */
3619 NULL,
3620 /* u32EndVersion */
3621 PDM_DRVREG_VERSION
3622};
3623
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