VirtualBox

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

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

Audio/HDA: Added PDMAUDIOFILE_DEFAULT_OPEN_FLAGS for DrvAudioHlpFileOpen().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 119.7 KB
Line 
1/* $Id: DrvAudio.cpp 70073 2017-12-12 09:14:37Z vboxsync $ */
2/** @file
3 * Intermediate audio driver header.
4 *
5 * @remarks Intermediate audio driver for connecting the audio device emulation
6 * with the host backend.
7 */
8
9/*
10 * Copyright (C) 2006-2017 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 */
20
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 fFlags 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 /* 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_ENABLED | PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE);
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 LogRel2(("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 LogRel2(("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 LogRel2(("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[_4K]; /** @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 = AudioMixBufReadCirc(&pHstStream->MixBuf, auBuf, RT_MIN(cbChunk, cbLeft), &cfRead);
1313 if ( !cfRead
1314 || RT_FAILURE(rc))
1315 {
1316 break;
1317 }
1318
1319 uint32_t cbRead = AUDIOMIXBUF_F2B(&pHstStream->MixBuf, cfRead);
1320 Assert(cbRead <= cbChunk);
1321
1322 uint32_t cbPlayed = 0;
1323 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pHstStream->pvBackend,
1324 auBuf, cbRead, &cbPlayed);
1325 if ( RT_FAILURE(rc)
1326 || !cbPlayed)
1327 {
1328 break;
1329 }
1330
1331 if (pThis->Dbg.fEnabled)
1332 DrvAudioHlpFileWrite(pHstStream->Out.Dbg.pFilePlayNonInterleaved, auBuf, cbPlayed, 0 /* fFlags */);
1333
1334 AssertMsg(cbPlayed <= cbRead, ("Played more than available (%RU32 available but got %RU32)\n", cbRead, cbPlayed));
1335#if 0 /** @todo Also handle mono channels. Needs fixing */
1336 AssertMsg(cbPlayed % 2 == 0,
1337 ("Backend for stream '%s' returned uneven played bytes count (cfRead=%RU32, cbPlayed=%RU32)\n",
1338 pHstStream->szName, cfRead, cbPlayed));*/
1339#endif
1340 cfPlayedTotal += AUDIOMIXBUF_B2F(&pHstStream->MixBuf, cbPlayed);
1341 Assert(cbLeft >= cbPlayed);
1342 cbLeft -= cbPlayed;
1343 }
1344 }
1345 }
1346
1347 Log3Func(("[%s] Played %RU32/%RU32 frames, rc=%Rrc\n", pHstStream->szName, cfPlayedTotal, cfToPlay, rc));
1348
1349 if (RT_SUCCESS(rc))
1350 {
1351 if (pcfPlayed)
1352 *pcfPlayed = cfPlayedTotal;
1353 }
1354
1355 return rc;
1356}
1357
1358/**
1359 * Plays an audio host output stream which has been configured for raw audio (layout) data.
1360 *
1361 * @return IPRT status code.
1362 * @param pThis Pointer to driver instance.
1363 * @param pHstStream Host stream to play.
1364 * @param cfToPlay Number of audio frames to play.
1365 * @param pcfPlayed Returns number of audio frames played. Optional.
1366 */
1367static int drvAudioStreamPlayRaw(PDRVAUDIO pThis,
1368 PPDMAUDIOSTREAM pHstStream, uint32_t cfToPlay, uint32_t *pcfPlayed)
1369{
1370 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1371 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
1372 /* pcfPlayed is optional. */
1373
1374 /* Sanity. */
1375 Assert(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1376 Assert(pHstStream->enmDir == PDMAUDIODIR_OUT);
1377 Assert(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
1378
1379 if (!cfToPlay)
1380 {
1381 if (pcfPlayed)
1382 *pcfPlayed = 0;
1383 return VINF_SUCCESS;
1384 }
1385
1386 int rc = VINF_SUCCESS;
1387
1388 uint32_t cfPlayedTotal = 0;
1389
1390 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetWritable);
1391 uint32_t cfWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pHstStream->pvBackend);
1392 if (cfWritable)
1393 {
1394 if (cfToPlay > cfWritable) /* More frames available than we can write? Limit. */
1395 cfToPlay = cfWritable;
1396
1397 PDMAUDIOFRAME aFrameBuf[_4K]; /** @todo Get rid of this here. */
1398
1399 uint32_t cfLeft = cfToPlay;
1400 while (cfLeft)
1401 {
1402 uint32_t cfRead = 0;
1403 rc = AudioMixBufPeek(&pHstStream->MixBuf, cfLeft, aFrameBuf,
1404 RT_MIN(cfLeft, RT_ELEMENTS(aFrameBuf)), &cfRead);
1405
1406 if (RT_SUCCESS(rc))
1407 {
1408 if (cfRead)
1409 {
1410 uint32_t cfPlayed;
1411
1412 /* Note: As the stream layout is RPDMAUDIOSTREAMLAYOUT_RAW, operate on audio frames
1413 * rather on bytes. */
1414 Assert(cfRead <= RT_ELEMENTS(aFrameBuf));
1415 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pHstStream->pvBackend,
1416 aFrameBuf, cfRead, &cfPlayed);
1417 if ( RT_FAILURE(rc)
1418 || !cfPlayed)
1419 {
1420 break;
1421 }
1422
1423 cfPlayedTotal += cfPlayed;
1424 Assert(cfPlayedTotal <= cfToPlay);
1425
1426 Assert(cfLeft >= cfRead);
1427 cfLeft -= cfRead;
1428 }
1429 else
1430 {
1431 if (rc == VINF_AUDIO_MORE_DATA_AVAILABLE) /* Do another peeking round if there is more data available. */
1432 continue;
1433
1434 break;
1435 }
1436 }
1437 else if (RT_FAILURE(rc))
1438 break;
1439 }
1440 }
1441
1442 Log3Func(("[%s] Played %RU32/%RU32 frames, rc=%Rrc\n", pHstStream->szName, cfPlayedTotal, cfToPlay, rc));
1443
1444 if (RT_SUCCESS(rc))
1445 {
1446 if (pcfPlayed)
1447 *pcfPlayed = cfPlayedTotal;
1448
1449 }
1450
1451 return rc;
1452}
1453
1454/**
1455 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamPlay}
1456 */
1457static DECLCALLBACK(int) drvAudioStreamPlay(PPDMIAUDIOCONNECTOR pInterface,
1458 PPDMAUDIOSTREAM pStream, uint32_t *pcFramesPlayed)
1459{
1460 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1461 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1462 /* pcFramesPlayed is optional. */
1463
1464 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1465
1466 int rc = RTCritSectEnter(&pThis->CritSect);
1467 if (RT_FAILURE(rc))
1468 return rc;
1469
1470 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT,
1471 ("Stream '%s' is not an output stream and therefore cannot be played back (direction is 0x%x)\n",
1472 pStream->szName, pStream->enmDir));
1473
1474 uint32_t cfPlayedTotal = 0;
1475
1476 do
1477 {
1478 if (!pThis->pHostDrvAudio)
1479 break;
1480
1481 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1482 AssertPtr(pHstStream);
1483 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : NULL;
1484 AssertPtr(pGstStream);
1485
1486 AssertReleaseMsgBreakStmt(pHstStream != NULL,
1487 ("[%s] Host stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1488 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1489 rc = VERR_NOT_AVAILABLE);
1490 AssertReleaseMsgBreakStmt(pGstStream != NULL,
1491 ("[%s] Guest stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1492 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1493 rc = VERR_NOT_AVAILABLE);
1494
1495 /*
1496 * Check if the backend is ready to operate.
1497 */
1498
1499 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetStatus);
1500 PDMAUDIOSTREAMSTS stsBackend = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream->pvBackend);
1501#ifdef LOG_ENABLED
1502 char *pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
1503 Log3Func(("[%s] Start: stsBackend=%s\n", pHstStream->szName, pszBackendSts));
1504 RTStrFree(pszBackendSts);
1505#endif /* LOG_ENABLED */
1506 if (!(stsBackend & PDMAUDIOSTREAMSTS_FLAG_ENABLED)) /* Backend disabled? Bail out. */
1507 break;
1508
1509 uint32_t cfToPlay = AudioMixBufLive(&pHstStream->MixBuf);
1510
1511 if (pThis->pHostDrvAudio->pfnStreamPlayBegin)
1512 pThis->pHostDrvAudio->pfnStreamPlayBegin(pThis->pHostDrvAudio, pHstStream->pvBackend);
1513
1514 if (RT_LIKELY(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED))
1515 {
1516 rc = drvAudioStreamPlayNonInterleaved(pThis, pHstStream, cfToPlay, &cfPlayedTotal);
1517 }
1518 else if (pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW)
1519 {
1520 rc = drvAudioStreamPlayRaw(pThis, pHstStream, cfToPlay, &cfPlayedTotal);
1521 }
1522 else
1523 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1524
1525 if (pThis->pHostDrvAudio->pfnStreamPlayEnd)
1526 pThis->pHostDrvAudio->pfnStreamPlayEnd(pThis->pHostDrvAudio, pHstStream->pvBackend);
1527
1528#ifdef LOG_ENABLED
1529 uint32_t cfLive = 0;
1530#endif
1531 if (RT_SUCCESS(rc))
1532 {
1533 AudioMixBufFinish(&pHstStream->MixBuf, cfPlayedTotal);
1534
1535#ifdef VBOX_WITH_STATISTICS
1536 STAM_COUNTER_ADD (&pThis->Stats.TotalFramesOut, cfPlayedTotal);
1537 STAM_PROFILE_ADV_STOP(&pThis->Stats.DelayOut, out);
1538 STAM_COUNTER_ADD (&pHstStream->Out.StatFramesPlayed, cfPlayedTotal);
1539#endif
1540
1541#ifdef LOG_ENABLED
1542 cfLive = AudioMixBufLive(&pHstStream->MixBuf);
1543#endif
1544 }
1545
1546#ifdef LOG_ENABLED
1547 pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
1548 Log3Func(("[%s] End: stsBackend=%s, cfLive=%RU32, cfPlayedTotal=%RU32, rc=%Rrc\n",
1549 pHstStream->szName, pszBackendSts, cfLive, cfPlayedTotal, rc));
1550 RTStrFree(pszBackendSts);
1551#endif /* LOG_ENABLED */
1552
1553 } while (0);
1554
1555 int rc2 = RTCritSectLeave(&pThis->CritSect);
1556 if (RT_SUCCESS(rc))
1557 rc = rc2;
1558
1559 if (RT_SUCCESS(rc))
1560 {
1561 if (pcFramesPlayed)
1562 *pcFramesPlayed = cfPlayedTotal;
1563 }
1564
1565 if (RT_FAILURE(rc))
1566 LogFlowFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
1567
1568 return rc;
1569}
1570
1571/**
1572 * Captures non-interleaved input from a host stream.
1573 *
1574 * @returns IPRT status code.
1575 * @param pThis Driver instance.
1576 * @param pHstStream Host stream to capture from.
1577 * @param pcfCaptured Number of (host) audio frames captured. Optional.
1578 */
1579static int drvAudioStreamCaptureNonInterleaved(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream, uint32_t *pcfCaptured)
1580{
1581 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1582 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
1583 /* pcfCaptured is optional. */
1584
1585 /* Sanity. */
1586 Assert(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1587 Assert(pHstStream->enmDir == PDMAUDIODIR_IN);
1588 Assert(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED);
1589
1590 int rc = VINF_SUCCESS;
1591
1592 uint32_t cfCapturedTotal = 0;
1593
1594 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
1595
1596 uint8_t auBuf[_1K]; /** @todo Get rid of this. */
1597 size_t cbBuf = sizeof(auBuf);
1598
1599 for (;;)
1600 {
1601 uint32_t cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pHstStream->pvBackend);
1602 if (!cbReadable)
1603 {
1604 Log2Func(("[%s] No readable data available, skipping\n", pHstStream->szName));
1605 break;
1606 }
1607
1608 uint32_t cbFree = AUDIOMIXBUF_F2B(&pHstStream->MixBuf, AudioMixBufFree(&pHstStream->MixBuf));
1609 if (!cbFree)
1610 {
1611 Log2Func(("[%s] Host buffer full, skipping\n", pHstStream->szName));
1612 break;
1613 }
1614
1615 if (cbFree < cbReadable) /* More data captured than we can read? */
1616 {
1617 /** @todo Warn? */
1618 }
1619
1620 if (cbFree > cbBuf) /* Limit to buffer size. */
1621 cbFree = (uint32_t)cbBuf;
1622
1623 uint32_t cbCaptured;
1624 rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pHstStream->pvBackend,
1625 auBuf, cbFree, &cbCaptured);
1626 if (RT_FAILURE(rc))
1627 {
1628 int rc2 = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1629 AssertRC(rc2);
1630 }
1631 else if (cbCaptured)
1632 {
1633 if (pThis->Dbg.fEnabled)
1634 DrvAudioHlpFileWrite(pHstStream->In.Dbg.pFileCaptureNonInterleaved, auBuf, cbCaptured, 0 /* fFlags */);
1635
1636 Assert(cbCaptured <= cbBuf);
1637 if (cbCaptured > cbBuf) /* Paranoia. */
1638 cbCaptured = (uint32_t)cbBuf;
1639
1640 uint32_t cfCaptured = 0;
1641 rc = AudioMixBufWriteCirc(&pHstStream->MixBuf, auBuf, cbCaptured, &cfCaptured);
1642 if (RT_SUCCESS(rc))
1643 cfCapturedTotal += cfCaptured;
1644 }
1645 else /* Nothing captured -- bail out. */
1646 break;
1647
1648 if (RT_FAILURE(rc))
1649 break;
1650 }
1651
1652 if (RT_SUCCESS(rc))
1653 {
1654 if (cfCapturedTotal)
1655 Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pHstStream->szName, cfCapturedTotal, rc));
1656 }
1657 else
1658 LogFunc(("[%s] Capturing failed with rc=%Rrc\n", pHstStream->szName, rc));
1659
1660 if (pcfCaptured)
1661 *pcfCaptured = cfCapturedTotal;
1662
1663 return rc;
1664}
1665
1666/**
1667 * Captures raw input from a host stream.
1668 * Raw input means that the backend directly operates on PDMAUDIOFRAME structs without
1669 * no data layout processing done in between.
1670 *
1671 * Needed for e.g. the VRDP audio backend (in Main).
1672 *
1673 * @returns IPRT status code.
1674 * @param pThis Driver instance.
1675 * @param pHstStream Host stream to capture from.
1676 * @param pcfCaptured Number of (host) audio frames captured. Optional.
1677 */
1678static int drvAudioStreamCaptureRaw(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream, uint32_t *pcfCaptured)
1679{
1680 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1681 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
1682 /* pcfCaptured is optional. */
1683
1684 /* Sanity. */
1685 Assert(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1686 Assert(pHstStream->enmDir == PDMAUDIODIR_IN);
1687 Assert(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
1688
1689 int rc = VINF_SUCCESS;
1690
1691 uint32_t cfCapturedTotal = 0;
1692
1693 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
1694
1695 for (;;)
1696 {
1697 uint32_t cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pHstStream->pvBackend);
1698 if (!cbReadable) /* Nothing to read on the backend side? Bail out. */
1699 break;
1700
1701 const uint32_t cbFree = AudioMixBufFreeBytes(&pHstStream->MixBuf);
1702 if (!cbFree) /* No space left in the host stream? */
1703 break;
1704
1705 if (cbReadable > cbFree) /* Don't capture more than the host stream currently can hold. */
1706 cbReadable = cbFree;
1707
1708 PPDMAUDIOFRAME paFrames;
1709 uint32_t cfWritable;
1710 rc = AudioMixBufPeekMutable(&pHstStream->MixBuf, AUDIOMIXBUF_B2F(&pHstStream->MixBuf, cbReadable),
1711 &paFrames, &cfWritable);
1712 if ( RT_FAILURE(rc)
1713 || !cfWritable)
1714 {
1715 break;
1716 }
1717
1718 uint32_t cfCaptured;
1719 rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pHstStream->pvBackend,
1720 paFrames, cfWritable, &cfCaptured);
1721 if (RT_FAILURE(rc))
1722 {
1723 int rc2 = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1724 AssertRC(rc2);
1725 }
1726 else if (cfCaptured)
1727 {
1728 Assert(cfCaptured <= cfWritable);
1729 if (cfCaptured > cfWritable) /* Paranoia. */
1730 cfCaptured = cfWritable;
1731
1732 cfCapturedTotal += cfCaptured;
1733 }
1734 else /* Nothing captured -- bail out. */
1735 break;
1736
1737 if (RT_FAILURE(rc))
1738 break;
1739 }
1740
1741 Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pHstStream->szName, cfCapturedTotal, rc));
1742
1743 if (pcfCaptured)
1744 *pcfCaptured = cfCapturedTotal;
1745
1746 return rc;
1747}
1748
1749/**
1750 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCapture}
1751 */
1752static DECLCALLBACK(int) drvAudioStreamCapture(PPDMIAUDIOCONNECTOR pInterface,
1753 PPDMAUDIOSTREAM pStream, uint32_t *pcFramesCaptured)
1754{
1755 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1756
1757 int rc = RTCritSectEnter(&pThis->CritSect);
1758 if (RT_FAILURE(rc))
1759 return rc;
1760
1761 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
1762 ("Stream '%s' is not an input stream and therefore cannot be captured (direction is 0x%x)\n",
1763 pStream->szName, pStream->enmDir));
1764
1765 uint32_t cfCaptured = 0;
1766
1767 do
1768 {
1769 if (!pThis->pHostDrvAudio)
1770 break;
1771
1772 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1773 AssertPtr(pHstStream);
1774 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : NULL;
1775 AssertPtr(pGstStream);
1776
1777 AssertReleaseMsgBreakStmt(pHstStream != NULL,
1778 ("[%s] Host stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1779 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1780 rc = VERR_NOT_AVAILABLE);
1781 AssertReleaseMsgBreakStmt(pGstStream != NULL,
1782 ("[%s] Guest stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1783 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1784 rc = VERR_NOT_AVAILABLE);
1785
1786 /*
1787 * Check if the backend is ready to operate.
1788 */
1789
1790 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetStatus);
1791 PDMAUDIOSTREAMSTS stsBackend = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream->pvBackend);
1792#ifdef LOG_ENABLED
1793 char *pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
1794 Log3Func(("[%s] Start: stsBackend=%s\n", pHstStream->szName, pszBackendSts));
1795 RTStrFree(pszBackendSts);
1796#endif /* LOG_ENABLED */
1797 if (!(stsBackend & PDMAUDIOSTREAMSTS_FLAG_ENABLED)) /* Backend disabled? Bail out. */
1798 break;
1799
1800 /*
1801 * Do the actual capturing.
1802 */
1803
1804 if (pThis->pHostDrvAudio->pfnStreamCaptureBegin)
1805 pThis->pHostDrvAudio->pfnStreamCaptureBegin(pThis->pHostDrvAudio, pHstStream->pvBackend);
1806
1807 if (RT_LIKELY(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED))
1808 {
1809 rc = drvAudioStreamCaptureNonInterleaved(pThis, pHstStream, &cfCaptured);
1810 }
1811 else if (pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW)
1812 {
1813 rc = drvAudioStreamCaptureRaw(pThis, pHstStream, &cfCaptured);
1814 }
1815 else
1816 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1817
1818 if (pThis->pHostDrvAudio->pfnStreamCaptureEnd)
1819 pThis->pHostDrvAudio->pfnStreamCaptureEnd(pThis->pHostDrvAudio, pHstStream->pvBackend);
1820
1821#ifdef LOG_ENABLED
1822 pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
1823 Log3Func(("[%s] End: stsBackend=%s, cfCaptured=%RU32, rc=%Rrc\n",
1824 pHstStream->szName, pszBackendSts, cfCaptured, rc));
1825 RTStrFree(pszBackendSts);
1826#endif /* LOG_ENABLED */
1827
1828 if (RT_SUCCESS(rc))
1829 {
1830 Log3Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pHstStream->szName, cfCaptured, rc));
1831
1832#ifdef VBOX_WITH_STATISTICS
1833 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesIn, cfCaptured);
1834 STAM_COUNTER_ADD(&pHstStream->In.StatFramesCaptured, cfCaptured);
1835#endif
1836 }
1837 else if (RT_UNLIKELY(RT_FAILURE(rc)))
1838 {
1839 LogRel2(("Audio: Capturing stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
1840 }
1841
1842 } while (0);
1843
1844 if (pcFramesCaptured)
1845 *pcFramesCaptured = cfCaptured;
1846
1847 int rc2 = RTCritSectLeave(&pThis->CritSect);
1848 if (RT_SUCCESS(rc))
1849 rc = rc2;
1850
1851 if (RT_FAILURE(rc))
1852 LogFlowFuncLeaveRC(rc);
1853
1854 return rc;
1855}
1856
1857#ifdef VBOX_WITH_AUDIO_CALLBACKS
1858/**
1859 * Duplicates an audio callback.
1860 *
1861 * @returns Pointer to duplicated callback, or NULL on failure.
1862 * @param pCB Callback to duplicate.
1863 */
1864static PPDMAUDIOCBRECORD drvAudioCallbackDuplicate(PPDMAUDIOCBRECORD pCB)
1865{
1866 AssertPtrReturn(pCB, NULL);
1867
1868 PPDMAUDIOCBRECORD pCBCopy = (PPDMAUDIOCBRECORD)RTMemDup((void *)pCB, sizeof(PDMAUDIOCBRECORD));
1869 if (!pCBCopy)
1870 return NULL;
1871
1872 if (pCB->pvCtx)
1873 {
1874 pCBCopy->pvCtx = RTMemDup(pCB->pvCtx, pCB->cbCtx);
1875 if (!pCBCopy->pvCtx)
1876 {
1877 RTMemFree(pCBCopy);
1878 return NULL;
1879 }
1880
1881 pCBCopy->cbCtx = pCB->cbCtx;
1882 }
1883
1884 return pCBCopy;
1885}
1886
1887/**
1888 * Destroys a given callback.
1889 *
1890 * @param pCB Callback to destroy.
1891 */
1892static void drvAudioCallbackDestroy(PPDMAUDIOCBRECORD pCB)
1893{
1894 if (!pCB)
1895 return;
1896
1897 RTListNodeRemove(&pCB->Node);
1898 if (pCB->pvCtx)
1899 {
1900 Assert(pCB->cbCtx);
1901 RTMemFree(pCB->pvCtx);
1902 }
1903 RTMemFree(pCB);
1904}
1905
1906/**
1907 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnRegisterCallbacks}
1908 */
1909static DECLCALLBACK(int) drvAudioRegisterCallbacks(PPDMIAUDIOCONNECTOR pInterface,
1910 PPDMAUDIOCBRECORD paCallbacks, size_t cCallbacks)
1911{
1912 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1913 AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER);
1914 AssertReturn(cCallbacks, VERR_INVALID_PARAMETER);
1915
1916 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1917
1918 int rc = RTCritSectEnter(&pThis->CritSect);
1919 if (RT_FAILURE(rc))
1920 return rc;
1921
1922 for (size_t i = 0; i < cCallbacks; i++)
1923 {
1924 PPDMAUDIOCBRECORD pCB = drvAudioCallbackDuplicate(&paCallbacks[i]);
1925 if (!pCB)
1926 {
1927 rc = VERR_NO_MEMORY;
1928 break;
1929 }
1930
1931 switch (pCB->enmSource)
1932 {
1933 case PDMAUDIOCBSOURCE_DEVICE:
1934 {
1935 switch (pCB->Device.enmType)
1936 {
1937 case PDMAUDIODEVICECBTYPE_DATA_INPUT:
1938 RTListAppend(&pThis->In.lstCB, &pCB->Node);
1939 break;
1940
1941 case PDMAUDIODEVICECBTYPE_DATA_OUTPUT:
1942 RTListAppend(&pThis->Out.lstCB, &pCB->Node);
1943 break;
1944
1945 default:
1946 AssertMsgFailed(("Not supported\n"));
1947 break;
1948 }
1949
1950 break;
1951 }
1952
1953 default:
1954 AssertMsgFailed(("Not supported\n"));
1955 break;
1956 }
1957 }
1958
1959 /** @todo Undo allocations on error. */
1960
1961 int rc2 = RTCritSectLeave(&pThis->CritSect);
1962 if (RT_SUCCESS(rc))
1963 rc = rc2;
1964
1965 return rc;
1966}
1967#endif /* VBOX_WITH_AUDIO_CALLBACKS */
1968
1969#ifdef VBOX_WITH_AUDIO_CALLBACKS
1970/**
1971 * Backend callback implementation.
1972 *
1973 * Important: No calls back to the backend within this function, as the backend
1974 * might hold any locks / critical sections while executing this callback.
1975 * Will result in some ugly deadlocks (or at least locking order violations) then.
1976 *
1977 * @copydoc FNPDMHOSTAUDIOCALLBACK
1978 */
1979static DECLCALLBACK(int) drvAudioBackendCallback(PPDMDRVINS pDrvIns,
1980 PDMAUDIOBACKENDCBTYPE enmType, void *pvUser, size_t cbUser)
1981{
1982 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
1983 RT_NOREF(pvUser, cbUser);
1984 /* pvUser and cbUser are optional. */
1985
1986 /* Get the upper driver (PDMIAUDIOCONNECTOR). */
1987 AssertPtr(pDrvIns->pUpBase);
1988 PPDMIAUDIOCONNECTOR pInterface = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
1989 AssertPtr(pInterface);
1990 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1991
1992 int rc = RTCritSectEnter(&pThis->CritSect);
1993 AssertRCReturn(rc, rc);
1994
1995 LogFunc(("pThis=%p, enmType=%RU32, pvUser=%p, cbUser=%zu\n", pThis, enmType, pvUser, cbUser));
1996
1997 switch (enmType)
1998 {
1999 case PDMAUDIOBACKENDCBTYPE_DEVICES_CHANGED:
2000 LogRel(("Audio: Host audio device configuration has changed\n"));
2001 rc = drvAudioScheduleReInitInternal(pThis);
2002 break;
2003
2004 default:
2005 AssertMsgFailed(("Not supported\n"));
2006 break;
2007 }
2008
2009 int rc2 = RTCritSectLeave(&pThis->CritSect);
2010 if (RT_SUCCESS(rc))
2011 rc = rc2;
2012
2013 LogFlowFunc(("Returning %Rrc\n", rc));
2014 return rc;
2015}
2016#endif /* VBOX_WITH_AUDIO_CALLBACKS */
2017
2018#ifdef VBOX_WITH_AUDIO_ENUM
2019/**
2020 * Enumerates all host audio devices.
2021 * This functionality might not be implemented by all backends and will return VERR_NOT_SUPPORTED
2022 * if not being supported.
2023 *
2024 * @returns IPRT status code.
2025 * @param pThis Driver instance to be called.
2026 * @param fLog Whether to print the enumerated device to the release log or not.
2027 * @param pDevEnum Where to store the device enumeration.
2028 */
2029static int drvAudioDevicesEnumerateInternal(PDRVAUDIO pThis, bool fLog, PPDMAUDIODEVICEENUM pDevEnum)
2030{
2031 int rc;
2032
2033 /*
2034 * If the backend supports it, do a device enumeration.
2035 */
2036 if (pThis->pHostDrvAudio->pfnGetDevices)
2037 {
2038 PDMAUDIODEVICEENUM DevEnum;
2039 rc = pThis->pHostDrvAudio->pfnGetDevices(pThis->pHostDrvAudio, &DevEnum);
2040 if (RT_SUCCESS(rc))
2041 {
2042 if (fLog)
2043 LogRel(("Audio: Found %RU16 devices\n", DevEnum.cDevices));
2044
2045 PPDMAUDIODEVICE pDev;
2046 RTListForEach(&DevEnum.lstDevices, pDev, PDMAUDIODEVICE, Node)
2047 {
2048 if (fLog)
2049 {
2050 char *pszFlags = DrvAudioHlpAudDevFlagsToStrA(pDev->fFlags);
2051
2052 LogRel(("Audio: Device '%s':\n", pDev->szName));
2053 LogRel(("Audio: \tUsage = %s\n", DrvAudioHlpAudDirToStr(pDev->enmUsage)));
2054 LogRel(("Audio: \tFlags = %s\n", pszFlags ? pszFlags : "<NONE>"));
2055 LogRel(("Audio: \tInput channels = %RU8\n", pDev->cMaxInputChannels));
2056 LogRel(("Audio: \tOutput channels = %RU8\n", pDev->cMaxOutputChannels));
2057
2058 if (pszFlags)
2059 RTStrFree(pszFlags);
2060 }
2061 }
2062
2063 if (pDevEnum)
2064 rc = DrvAudioHlpDeviceEnumCopy(pDevEnum, &DevEnum);
2065
2066 DrvAudioHlpDeviceEnumFree(&DevEnum);
2067 }
2068 else
2069 {
2070 if (fLog)
2071 LogRel(("Audio: Device enumeration failed with %Rrc\n", rc));
2072 /* Not fatal. */
2073 }
2074 }
2075 else
2076 {
2077 rc = VERR_NOT_SUPPORTED;
2078
2079 if (fLog)
2080 LogRel3(("Audio: Host audio backend does not support audio device enumeration, skipping\n"));
2081 }
2082
2083 LogFunc(("Returning %Rrc\n", rc));
2084 return rc;
2085}
2086#endif /* VBOX_WITH_AUDIO_ENUM */
2087
2088/**
2089 * Initializes the host backend and queries its initial configuration.
2090 * If the host backend fails, VERR_AUDIO_BACKEND_INIT_FAILED will be returned.
2091 *
2092 * Note: As this routine is called when attaching to the device LUN in the
2093 * device emulation, we either check for success or VERR_AUDIO_BACKEND_INIT_FAILED.
2094 * Everything else is considered as fatal and must be handled separately in
2095 * the device emulation!
2096 *
2097 * @return IPRT status code.
2098 * @param pThis Driver instance to be called.
2099 * @param pCfgHandle CFGM configuration handle to use for this driver.
2100 */
2101static int drvAudioHostInit(PDRVAUDIO pThis, PCFGMNODE pCfgHandle)
2102{
2103 /* pCfgHandle is optional. */
2104 NOREF(pCfgHandle);
2105 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2106
2107 LogFlowFuncEnter();
2108
2109 AssertPtr(pThis->pHostDrvAudio);
2110 int rc = pThis->pHostDrvAudio->pfnInit(pThis->pHostDrvAudio);
2111 if (RT_FAILURE(rc))
2112 {
2113 LogRel(("Audio: Initialization of host backend failed with %Rrc\n", rc));
2114 return VERR_AUDIO_BACKEND_INIT_FAILED;
2115 }
2116
2117 /*
2118 * Get the backend configuration.
2119 */
2120 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
2121 if (RT_FAILURE(rc))
2122 {
2123 LogRel(("Audio: Getting host backend configuration failed with %Rrc\n", rc));
2124 return VERR_AUDIO_BACKEND_INIT_FAILED;
2125 }
2126
2127 pThis->In.cStreamsFree = pThis->BackendCfg.cMaxStreamsIn;
2128 pThis->Out.cStreamsFree = pThis->BackendCfg.cMaxStreamsOut;
2129
2130 LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->In.cStreamsFree, pThis->Out.cStreamsFree));
2131
2132 LogRel2(("Audio: Host audio backend supports %RU32 input streams and %RU32 output streams at once\n",
2133 /* Clamp for logging. Unlimited streams are defined by UINT32_MAX. */
2134 RT_MIN(64, pThis->In.cStreamsFree), RT_MIN(64, pThis->Out.cStreamsFree)));
2135
2136#ifdef VBOX_WITH_AUDIO_ENUM
2137 int rc2 = drvAudioDevicesEnumerateInternal(pThis, true /* fLog */, NULL /* pDevEnum */);
2138 if (rc2 != VERR_NOT_SUPPORTED) /* Some backends don't implement device enumeration. */
2139 AssertRC(rc2);
2140
2141 RT_NOREF(rc2);
2142 /* Ignore rc. */
2143#endif
2144
2145#ifdef VBOX_WITH_AUDIO_CALLBACKS
2146 /*
2147 * If the backend supports it, offer a callback to this connector.
2148 */
2149 if (pThis->pHostDrvAudio->pfnSetCallback)
2150 {
2151 rc2 = pThis->pHostDrvAudio->pfnSetCallback(pThis->pHostDrvAudio, drvAudioBackendCallback);
2152 if (RT_FAILURE(rc2))
2153 LogRel(("Audio: Error registering backend callback, rc=%Rrc\n", rc2));
2154 /* Not fatal. */
2155 }
2156#endif
2157
2158 LogFlowFuncLeave();
2159 return VINF_SUCCESS;
2160}
2161
2162/**
2163 * Handles state changes for all audio streams.
2164 *
2165 * @param pDrvIns Pointer to driver instance.
2166 * @param enmCmd Stream command to set for all streams.
2167 */
2168static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
2169{
2170 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2171 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2172
2173 LogFlowFunc(("enmCmd=%s\n", DrvAudioHlpStreamCmdToStr(enmCmd)));
2174
2175 int rc2 = RTCritSectEnter(&pThis->CritSect);
2176 AssertRC(rc2);
2177
2178 if (!pThis->pHostDrvAudio)
2179 return;
2180
2181 PPDMAUDIOSTREAM pHstStream;
2182 RTListForEach(&pThis->lstHstStreams, pHstStream, PDMAUDIOSTREAM, Node)
2183 drvAudioStreamControlInternalBackend(pThis, pHstStream, enmCmd);
2184
2185 rc2 = RTCritSectLeave(&pThis->CritSect);
2186 AssertRC(rc2);
2187}
2188
2189/**
2190 * Intializes an audio driver instance.
2191 *
2192 * @returns IPRT status code.
2193 * @param pDrvIns Pointer to driver instance.
2194 * @param pCfgHandle CFGM handle to use for configuration.
2195 */
2196static int drvAudioInit(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
2197{
2198 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
2199 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
2200
2201 LogRel2(("Audio: Verbose logging enabled\n"));
2202
2203 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2204
2205 int rc = RTCritSectInit(&pThis->CritSect);
2206 AssertRCReturn(rc, rc);
2207
2208 /*
2209 * Configure driver from CFGM stuff.
2210 */
2211#ifdef DEBUG
2212 CFGMR3Dump(pCfgHandle);
2213#endif
2214
2215 int rc2 = CFGMR3QueryString(pCfgHandle, "DriverName", pThis->szName, sizeof(pThis->szName));
2216 if (RT_FAILURE(rc2))
2217 RTStrPrintf(pThis->szName, sizeof(pThis->szName), "Untitled");
2218
2219 /* By default we don't enable anything if wrongly / not set-up. */
2220 CFGMR3QueryBoolDef(pCfgHandle, "InputEnabled", &pThis->In.fEnabled, false);
2221 CFGMR3QueryBoolDef(pCfgHandle, "OutputEnabled", &pThis->Out.fEnabled, false);
2222
2223 CFGMR3QueryBoolDef(pCfgHandle, "DebugEnabled", &pThis->Dbg.fEnabled, false);
2224 rc2 = CFGMR3QueryString(pCfgHandle, "DebugPathOut", pThis->Dbg.szPathOut, sizeof(pThis->Dbg.szPathOut));
2225 if ( RT_FAILURE(rc2)
2226 || !strlen(pThis->Dbg.szPathOut))
2227 {
2228 RTStrPrintf(pThis->Dbg.szPathOut, sizeof(pThis->Dbg.szPathOut), VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH);
2229 }
2230
2231 if (pThis->Dbg.fEnabled)
2232 LogRel(("Audio: Debugging enabled (audio data written to '%s')\n", pThis->Dbg.szPathOut));
2233
2234 LogRel2(("Audio: Initial status for driver '%s': Input is %s, output is %s\n",
2235 pThis->szName, pThis->In.fEnabled ? "enabled" : "disabled", pThis->Out.fEnabled ? "enabled" : "disabled"));
2236
2237 /*
2238 * If everything went well, initialize the lower driver.
2239 */
2240 rc = drvAudioHostInit(pThis, pCfgHandle);
2241
2242 LogFlowFuncLeaveRC(rc);
2243 return rc;
2244}
2245
2246/**
2247 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRead}
2248 */
2249static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
2250 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
2251{
2252 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2253 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2254
2255 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2256 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2257 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
2258 /* pcbRead is optional. */
2259
2260 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
2261 ("Stream '%s' is not an input stream and therefore cannot be read from (direction is 0x%x)\n",
2262 pStream->szName, pStream->enmDir));
2263
2264 uint32_t cbReadTotal = 0;
2265
2266 int rc = RTCritSectEnter(&pThis->CritSect);
2267 if (RT_FAILURE(rc))
2268 return rc;
2269
2270 do
2271 {
2272 if (!pThis->In.fEnabled)
2273 {
2274 RT_BZERO(pvBuf, cbBuf);
2275 cbReadTotal = cbBuf;
2276 break;
2277 }
2278
2279 if ( pThis->pHostDrvAudio
2280 && pThis->pHostDrvAudio->pfnGetStatus
2281 && pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_IN) != PDMAUDIOBACKENDSTS_RUNNING)
2282 {
2283 rc = VERR_NOT_AVAILABLE;
2284 break;
2285 }
2286
2287 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2288 if (!pHstStream)
2289 {
2290 rc = VERR_NOT_AVAILABLE;
2291 break;
2292 }
2293
2294 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
2295 AssertPtr(pGstStream);
2296
2297 /*
2298 * Read from the parent buffer (that is, the guest buffer) which
2299 * should have the audio data in the format the guest needs.
2300 */
2301 uint32_t cReadTotal = 0;
2302
2303 uint32_t cToRead = RT_MIN(AUDIOMIXBUF_B2F(&pGstStream->MixBuf, cbBuf), AudioMixBufUsed(&pGstStream->MixBuf));
2304 while (cToRead)
2305 {
2306 uint32_t cRead;
2307 rc = AudioMixBufReadCirc(&pGstStream->MixBuf, (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cReadTotal),
2308 AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cToRead), &cRead);
2309 if (RT_FAILURE(rc))
2310 break;
2311
2312#if defined (VBOX_WITH_STATISTICS) || defined (VBOX_AUDIO_DEBUG_DUMP_PCM_DATA)
2313 const uint32_t cbRead = AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cRead);
2314#endif
2315
2316#ifdef VBOX_WITH_STATISTICS
2317 STAM_COUNTER_ADD(&pThis->Stats.TotalBytesRead, cbRead);
2318 STAM_COUNTER_ADD(&pGstStream->In.StatBytesTotalRead, cbRead);
2319#endif
2320 Assert(cToRead >= cRead);
2321 cToRead -= cRead;
2322
2323 cReadTotal += cRead;
2324 }
2325
2326 if (cReadTotal)
2327 {
2328 if (pThis->Dbg.fEnabled)
2329 DrvAudioHlpFileWrite(pHstStream->In.Dbg.pFileStreamRead,
2330 pvBuf, AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cReadTotal), 0 /* fFlags */);
2331
2332 AudioMixBufFinish(&pGstStream->MixBuf, cReadTotal);
2333
2334 pGstStream->In.tsLastReadMS = RTTimeMilliTS();
2335
2336 cbReadTotal = AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cReadTotal);
2337 }
2338
2339 } while (0);
2340
2341 Log3Func(("[%s] cbReadTotal=%RU32, rc=%Rrc\n", pStream->szName, cbReadTotal, rc));
2342
2343 int rc2 = RTCritSectLeave(&pThis->CritSect);
2344 if (RT_SUCCESS(rc))
2345 rc = rc2;
2346
2347 if (RT_SUCCESS(rc))
2348 {
2349 if (pcbRead)
2350 *pcbRead = cbReadTotal;
2351 }
2352
2353 return rc;
2354}
2355
2356/**
2357 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCreate}
2358 */
2359static DECLCALLBACK(int) drvAudioStreamCreate(PPDMIAUDIOCONNECTOR pInterface,
2360 PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest,
2361 PPDMAUDIOSTREAM *ppStream)
2362{
2363 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2364 AssertPtrReturn(pCfgHost, VERR_INVALID_POINTER);
2365 AssertPtrReturn(pCfgGuest, VERR_INVALID_POINTER);
2366 AssertPtrReturn(ppStream, VERR_INVALID_POINTER);
2367
2368 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2369
2370 int rc = RTCritSectEnter(&pThis->CritSect);
2371 if (RT_FAILURE(rc))
2372 return rc;
2373
2374 LogFlowFunc(("Host=%s, Guest=%s\n", pCfgHost->szName, pCfgGuest->szName));
2375#ifdef DEBUG
2376 DrvAudioHlpStreamCfgPrint(pCfgHost);
2377 DrvAudioHlpStreamCfgPrint(pCfgGuest);
2378#endif
2379
2380 /*
2381 * The guest stream always will get the audio stream configuration told
2382 * by the device emulation (which in turn was/could be set by the guest OS).
2383 */
2384 PPDMAUDIOSTREAM pGstStrm = NULL;
2385
2386 /** @todo Docs! */
2387 PPDMAUDIOSTREAM pHstStrm = NULL;
2388
2389#define RC_BREAK(x) { rc = x; break; }
2390
2391 do
2392 {
2393 if ( !DrvAudioHlpStreamCfgIsValid(pCfgHost)
2394 || !DrvAudioHlpStreamCfgIsValid(pCfgGuest))
2395 {
2396 RC_BREAK(VERR_INVALID_PARAMETER);
2397 }
2398
2399 /* Make sure that both configurations actually intend the same thing. */
2400 if (pCfgHost->enmDir != pCfgGuest->enmDir)
2401 {
2402 AssertMsgFailed(("Stream configuration directions do not match\n"));
2403 RC_BREAK(VERR_INVALID_PARAMETER);
2404 }
2405
2406 /* Note: cbHstStrm will contain the size of the data the backend needs to operate on. */
2407 size_t cbHstStrm = 0;
2408 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
2409 {
2410 if (!pThis->In.cStreamsFree)
2411 LogFunc(("Warning: No more input streams free to use\n"));
2412
2413 cbHstStrm = pThis->BackendCfg.cbStreamIn;
2414 }
2415 else /* Out */
2416 {
2417 if (!pThis->Out.cStreamsFree)
2418 {
2419 LogFlowFunc(("Maximum number of host output streams reached\n"));
2420 RC_BREAK(VERR_AUDIO_NO_FREE_OUTPUT_STREAMS);
2421 }
2422
2423 cbHstStrm = pThis->BackendCfg.cbStreamOut;
2424 }
2425
2426 pHstStrm = (PPDMAUDIOSTREAM)RTMemAllocZ(sizeof(PDMAUDIOSTREAM));
2427 AssertPtrBreakStmt(pHstStrm, rc = VERR_NO_MEMORY);
2428
2429 if (cbHstStrm) /* High unlikely that backends do not have an own space for data, but better check. */
2430 {
2431 pHstStrm->pvBackend = RTMemAllocZ(cbHstStrm);
2432 AssertPtrBreakStmt(pHstStrm->pvBackend, rc = VERR_NO_MEMORY);
2433
2434 pHstStrm->cbBackend = cbHstStrm;
2435 }
2436
2437 pHstStrm->enmCtx = PDMAUDIOSTREAMCTX_HOST;
2438 pHstStrm->enmDir = pCfgHost->enmDir;
2439
2440 pGstStrm = (PPDMAUDIOSTREAM)RTMemAllocZ(sizeof(PDMAUDIOSTREAM));
2441 AssertPtrBreakStmt(pGstStrm, rc = VERR_NO_MEMORY);
2442
2443 pGstStrm->enmCtx = PDMAUDIOSTREAMCTX_GUEST;
2444 pGstStrm->enmDir = pCfgGuest->enmDir;
2445
2446 /* Retrieve host driver name for easier identification. */
2447 AssertPtr(pThis->pHostDrvAudio);
2448 PPDMDRVINS pDrvAudioInst = PDMIBASE_2_PDMDRV(pThis->pDrvIns->pDownBase);
2449 AssertPtr(pDrvAudioInst);
2450 AssertPtr(pDrvAudioInst->pReg);
2451
2452 char szDriver[64];
2453 RTStrPrintf(szDriver, RT_ELEMENTS(szDriver), "%s", pDrvAudioInst->pReg->szName);
2454 if (!strlen(szDriver))
2455 {
2456 RTStrPrintf(szDriver, RT_ELEMENTS(szDriver), "Untitled");
2457 AssertFailed(); /* Should never happen. */
2458 }
2459
2460 /*
2461 * Init host stream.
2462 */
2463 RTStrPrintf(pHstStrm->szName, RT_ELEMENTS(pHstStrm->szName), "[%s] %s (Host)",
2464 szDriver, strlen(pCfgHost->szName) ? pCfgHost->szName : "<Untitled>");
2465
2466 rc = drvAudioStreamLinkToInternal(pHstStrm, pGstStrm);
2467 AssertRCBreak(rc);
2468
2469 /*
2470 * Init guest stream.
2471 */
2472 RTStrPrintf(pGstStrm->szName, RT_ELEMENTS(pGstStrm->szName), "[%s] %s (Guest)",
2473 szDriver, strlen(pCfgGuest->szName) ? pCfgGuest->szName : "<Untitled>");
2474
2475 pGstStrm->fStatus = pHstStrm->fStatus; /* Reflect the host stream's status. */
2476
2477 rc = drvAudioStreamLinkToInternal(pGstStrm, pHstStrm);
2478 AssertRCBreak(rc);
2479
2480 /*
2481 * Try to init the rest.
2482 */
2483 rc = drvAudioStreamInitInternal(pThis, pHstStrm, pCfgHost, pCfgGuest);
2484 if (RT_FAILURE(rc))
2485 break;
2486
2487#ifdef VBOX_WITH_STATISTICS
2488 char szStatName[255];
2489
2490 if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
2491 {
2492 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pGstStrm->szName);
2493 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->In.StatBytesElapsed,
2494 szStatName, STAMUNIT_BYTES, "Elapsed bytes read.");
2495
2496 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesRead", pGstStrm->szName);
2497 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->In.StatBytesTotalRead,
2498 szStatName, STAMUNIT_BYTES, "Total bytes read.");
2499
2500 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/FramesCaptured", pHstStrm->szName);
2501 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pHstStrm->In.StatFramesCaptured,
2502 szStatName, STAMUNIT_COUNT, "Total frames captured.");
2503 }
2504 else if (pCfgGuest->enmDir == PDMAUDIODIR_OUT)
2505 {
2506 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pGstStrm->szName);
2507 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->Out.StatBytesElapsed,
2508 szStatName, STAMUNIT_BYTES, "Elapsed bytes written.");
2509
2510 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesWritten", pGstStrm->szName);
2511 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->Out.StatBytesTotalWritten,
2512 szStatName, STAMUNIT_BYTES, "Total bytes written.");
2513
2514 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/FramesPlayed", pHstStrm->szName);
2515 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pHstStrm->Out.StatFramesPlayed,
2516 szStatName, STAMUNIT_COUNT, "Total frames played.");
2517 }
2518 else
2519 AssertFailed();
2520#endif
2521
2522 } while (0);
2523
2524#undef RC_BREAK
2525
2526 if (RT_FAILURE(rc))
2527 {
2528 if (pGstStrm)
2529 {
2530 int rc2 = drvAudioStreamUninitInternal(pThis, pGstStrm);
2531 if (RT_SUCCESS(rc2))
2532 {
2533 RTMemFree(pGstStrm);
2534 pGstStrm = NULL;
2535 }
2536 }
2537
2538 if (pHstStrm)
2539 {
2540 int rc2 = drvAudioStreamUninitInternal(pThis, pHstStrm);
2541 if (RT_SUCCESS(rc2))
2542 {
2543 drvAudioStreamFree(pHstStrm);
2544 pHstStrm = NULL;
2545 }
2546 }
2547 }
2548 else
2549 {
2550 /* Set initial reference counts. */
2551 RTListAppend(&pThis->lstGstStreams, &pGstStrm->Node);
2552 pGstStrm->cRefs = 1;
2553
2554 RTListAppend(&pThis->lstHstStreams, &pHstStrm->Node);
2555 pHstStrm->cRefs = 1;
2556
2557 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
2558 {
2559 if (pThis->Dbg.fEnabled)
2560 {
2561 char szFile[RTPATH_MAX + 1];
2562
2563 int rc2 = DrvAudioHlpGetFileName(szFile, RT_ELEMENTS(szFile), pThis->Dbg.szPathOut, "CaptureNonInterleaved",
2564 pThis->pDrvIns->iInstance, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
2565 if (RT_SUCCESS(rc2))
2566 {
2567 rc2 = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE,
2568 &pHstStrm->In.Dbg.pFileCaptureNonInterleaved);
2569 if (RT_SUCCESS(rc2))
2570 rc2 = DrvAudioHlpFileOpen(pHstStrm->In.Dbg.pFileCaptureNonInterleaved, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS,
2571 &pHstStrm->Cfg.Props);
2572 }
2573
2574 if (RT_SUCCESS(rc2))
2575 {
2576 rc2 = DrvAudioHlpGetFileName(szFile, RT_ELEMENTS(szFile), pThis->Dbg.szPathOut, "StreamRead",
2577 pThis->pDrvIns->iInstance, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
2578 if (RT_SUCCESS(rc2))
2579 {
2580 rc2 = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE,
2581 &pHstStrm->In.Dbg.pFileStreamRead);
2582 if (RT_SUCCESS(rc2))
2583 rc2 = DrvAudioHlpFileOpen(pHstStrm->In.Dbg.pFileStreamRead, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS,
2584 &pHstStrm->Cfg.Props);
2585 }
2586 }
2587 }
2588
2589 if (pThis->In.cStreamsFree)
2590 pThis->In.cStreamsFree--;
2591 }
2592 else /* Out */
2593 {
2594 if (pThis->Dbg.fEnabled)
2595 {
2596 char szFile[RTPATH_MAX + 1];
2597
2598 int rc2 = DrvAudioHlpGetFileName(szFile, RT_ELEMENTS(szFile), pThis->Dbg.szPathOut, "PlayNonInterleaved",
2599 pThis->pDrvIns->iInstance, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
2600 if (RT_SUCCESS(rc2))
2601 {
2602 rc2 = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE,
2603 &pHstStrm->Out.Dbg.pFilePlayNonInterleaved);
2604 if (RT_SUCCESS(rc2))
2605 rc = DrvAudioHlpFileOpen(pHstStrm->Out.Dbg.pFilePlayNonInterleaved, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS,
2606 &pHstStrm->Cfg.Props);
2607 }
2608
2609 if (RT_SUCCESS(rc2))
2610 {
2611 rc2 = DrvAudioHlpGetFileName(szFile, RT_ELEMENTS(szFile), pThis->Dbg.szPathOut, "StreamWrite",
2612 pThis->pDrvIns->iInstance, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
2613 if (RT_SUCCESS(rc2))
2614 {
2615 rc2 = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE,
2616 &pHstStrm->Out.Dbg.pFileStreamWrite);
2617 if (RT_SUCCESS(rc2))
2618 rc2 = DrvAudioHlpFileOpen(pHstStrm->Out.Dbg.pFileStreamWrite, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS,
2619 &pHstStrm->Cfg.Props);
2620 }
2621 }
2622 }
2623
2624 if (pThis->Out.cStreamsFree)
2625 pThis->Out.cStreamsFree--;
2626 }
2627
2628#ifdef VBOX_WITH_STATISTICS
2629 STAM_COUNTER_ADD(&pThis->Stats.TotalStreamsCreated, 1);
2630#endif
2631 /* Always return the guest-side part to the device emulation. */
2632 *ppStream = pGstStrm;
2633 }
2634
2635 int rc2 = RTCritSectLeave(&pThis->CritSect);
2636 if (RT_SUCCESS(rc))
2637 rc = rc2;
2638
2639 LogFlowFuncLeaveRC(rc);
2640 return rc;
2641}
2642
2643/**
2644 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnEnable}
2645 */
2646static DECLCALLBACK(int) drvAudioEnable(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir, bool fEnable)
2647{
2648 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2649
2650 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2651
2652 int rc = RTCritSectEnter(&pThis->CritSect);
2653 if (RT_FAILURE(rc))
2654 return rc;
2655
2656 bool *pfEnabled;
2657 if (enmDir == PDMAUDIODIR_IN)
2658 pfEnabled = &pThis->In.fEnabled;
2659 else if (enmDir == PDMAUDIODIR_OUT)
2660 pfEnabled = &pThis->Out.fEnabled;
2661 else
2662 AssertFailedReturn(VERR_INVALID_PARAMETER);
2663
2664 if (fEnable != *pfEnabled)
2665 {
2666 PPDMAUDIOSTREAM pStream;
2667 RTListForEach(&pThis->lstHstStreams, pStream, PDMAUDIOSTREAM, Node)
2668 {
2669 if (pStream->enmDir != enmDir) /* Skip unwanted streams. */
2670 continue;
2671
2672 int rc2 = drvAudioStreamControlInternal(pThis, pStream,
2673 fEnable ? PDMAUDIOSTREAMCMD_ENABLE : PDMAUDIOSTREAMCMD_DISABLE);
2674 if (RT_FAILURE(rc2))
2675 LogRel(("Audio: Failed to %s %s stream '%s', rc=%Rrc\n",
2676 fEnable ? "enable" : "disable", enmDir == PDMAUDIODIR_IN ? "input" : "output", pStream->szName, rc2));
2677
2678 if (RT_SUCCESS(rc))
2679 rc = rc2;
2680
2681 /* Keep going. */
2682 }
2683
2684 *pfEnabled = fEnable;
2685 }
2686
2687 int rc3 = RTCritSectLeave(&pThis->CritSect);
2688 if (RT_SUCCESS(rc))
2689 rc = rc3;
2690
2691 LogFlowFuncLeaveRC(rc);
2692 return rc;
2693}
2694
2695/**
2696 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnIsEnabled}
2697 */
2698static DECLCALLBACK(bool) drvAudioIsEnabled(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
2699{
2700 AssertPtrReturn(pInterface, false);
2701
2702 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2703
2704 int rc2 = RTCritSectEnter(&pThis->CritSect);
2705 if (RT_FAILURE(rc2))
2706 return false;
2707
2708 bool *pfEnabled;
2709 if (enmDir == PDMAUDIODIR_IN)
2710 pfEnabled = &pThis->In.fEnabled;
2711 else if (enmDir == PDMAUDIODIR_OUT)
2712 pfEnabled = &pThis->Out.fEnabled;
2713 else
2714 AssertFailedReturn(false);
2715
2716 const bool fIsEnabled = *pfEnabled;
2717
2718 rc2 = RTCritSectLeave(&pThis->CritSect);
2719 AssertRC(rc2);
2720
2721 return fIsEnabled;
2722}
2723
2724/**
2725 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetConfig}
2726 */
2727static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
2728{
2729 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2730 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2731
2732 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2733
2734 int rc = RTCritSectEnter(&pThis->CritSect);
2735 if (RT_FAILURE(rc))
2736 return rc;
2737
2738 if (pThis->pHostDrvAudio)
2739 {
2740 if (pThis->pHostDrvAudio->pfnGetConfig)
2741 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, pCfg);
2742 else
2743 rc = VERR_NOT_SUPPORTED;
2744 }
2745 else
2746 AssertFailed();
2747
2748 int rc2 = RTCritSectLeave(&pThis->CritSect);
2749 if (RT_SUCCESS(rc))
2750 rc = rc2;
2751
2752 LogFlowFuncLeaveRC(rc);
2753 return rc;
2754}
2755
2756/**
2757 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetStatus}
2758 */
2759static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioGetStatus(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
2760{
2761 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
2762
2763 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2764
2765 PDMAUDIOBACKENDSTS backendSts = PDMAUDIOBACKENDSTS_UNKNOWN;
2766
2767 int rc = RTCritSectEnter(&pThis->CritSect);
2768 if (RT_SUCCESS(rc))
2769 {
2770 if ( pThis->pHostDrvAudio
2771 && pThis->pHostDrvAudio->pfnGetStatus)
2772 {
2773 backendSts = pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, enmDir);
2774 }
2775
2776 int rc2 = RTCritSectLeave(&pThis->CritSect);
2777 if (RT_SUCCESS(rc))
2778 rc = rc2;
2779 }
2780
2781 LogFlowFuncLeaveRC(rc);
2782 return backendSts;
2783}
2784
2785/**
2786 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetReadable}
2787 */
2788static DECLCALLBACK(uint32_t) drvAudioStreamGetReadable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2789{
2790 AssertPtrReturn(pInterface, 0);
2791 AssertPtrReturn(pStream, 0);
2792
2793 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2794
2795 int rc2 = RTCritSectEnter(&pThis->CritSect);
2796 AssertRC(rc2);
2797
2798 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN, ("Can't read from a non-input stream\n"));
2799
2800 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2801 if (!pHstStream) /* No host stream available? Bail out early. */
2802 {
2803 rc2 = RTCritSectLeave(&pThis->CritSect);
2804 AssertRC(rc2);
2805
2806 return 0;
2807 }
2808
2809 uint32_t cReadable = 0;
2810
2811 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
2812 if (pGstStream)
2813 cReadable = AudioMixBufLive(&pGstStream->MixBuf);
2814
2815 Log3Func(("[%s] cbReadable=%RU32 (%zu bytes)\n", pHstStream->szName, cReadable,
2816 AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cReadable)));
2817
2818 uint32_t cbReadable = AUDIOMIXBUF_F2B(&pGstStream->MixBuf, cReadable);
2819
2820 rc2 = RTCritSectLeave(&pThis->CritSect);
2821 AssertRC(rc2);
2822
2823 /* Return bytes instead of audio frames. */
2824 return cbReadable;
2825}
2826
2827/**
2828 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetWritable}
2829 */
2830static DECLCALLBACK(uint32_t) drvAudioStreamGetWritable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2831{
2832 AssertPtrReturn(pInterface, 0);
2833 AssertPtrReturn(pStream, 0);
2834
2835 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2836
2837 int rc2 = RTCritSectEnter(&pThis->CritSect);
2838 AssertRC(rc2);
2839
2840 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT, ("Can't write to a non-output stream\n"));
2841
2842 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2843 if (!pHstStream) /* No host stream available? Bail out early. */
2844 {
2845 rc2 = RTCritSectLeave(&pThis->CritSect);
2846 AssertRC(rc2);
2847
2848 AssertMsgFailed(("Guest stream '%s' does not have a host stream attached\n", pStream->szName));
2849 return 0;
2850 }
2851
2852 /* As the host side sets the overall pace, return the writable bytes from that side. */
2853 uint32_t cbWritable = AudioMixBufFreeBytes(&pHstStream->MixBuf);
2854
2855 Log3Func(("[%s] cbWritable=%RU32\n", pHstStream->szName, cbWritable));
2856
2857 rc2 = RTCritSectLeave(&pThis->CritSect);
2858 AssertRC(rc2);
2859
2860 return cbWritable;
2861}
2862
2863/**
2864 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetStatus}
2865 */
2866static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvAudioStreamGetStatus(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2867{
2868 AssertPtrReturn(pInterface, false);
2869
2870 if (!pStream)
2871 return PDMAUDIOSTREAMSTS_FLAG_NONE;
2872
2873 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2874
2875 int rc2 = RTCritSectEnter(&pThis->CritSect);
2876 AssertRC(rc2);
2877
2878 PDMAUDIOSTREAMSTS strmSts = PDMAUDIOSTREAMSTS_FLAG_NONE;
2879
2880 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2881 if (pHstStream)
2882 {
2883 strmSts = pHstStream->fStatus;
2884#ifdef LOG_ENABLED
2885 char *pszHstSts = dbgAudioStreamStatusToStr(pHstStream->fStatus);
2886 Log3Func(("[%s] %s\n", pHstStream->szName, pszHstSts));
2887 RTStrFree(pszHstSts);
2888#endif
2889 }
2890
2891 rc2 = RTCritSectLeave(&pThis->CritSect);
2892 AssertRC(rc2);
2893
2894 return strmSts;
2895}
2896
2897/**
2898 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamSetVolume}
2899 */
2900static DECLCALLBACK(int) drvAudioStreamSetVolume(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, PPDMAUDIOVOLUME pVol)
2901{
2902 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2903 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2904 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
2905
2906 LogFlowFunc(("[%s] volL=%RU32, volR=%RU32, fMute=%RTbool\n", pStream->szName, pVol->uLeft, pVol->uRight, pVol->fMuted));
2907
2908 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2909 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
2910
2911 AudioMixBufSetVolume(&pHstStream->MixBuf, pVol);
2912 AudioMixBufSetVolume(&pGstStream->MixBuf, pVol);
2913 return VINF_SUCCESS;
2914}
2915
2916/**
2917 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamDestroy}
2918 */
2919static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2920{
2921 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2922 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2923
2924 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2925
2926 int rc = RTCritSectEnter(&pThis->CritSect);
2927 AssertRC(rc);
2928
2929 PDMAUDIODIR enmDir = pStream->enmDir;
2930
2931 LogFlowFunc(("[%s] cRefs=%RU32\n", pStream->szName, pStream->cRefs));
2932 if (pStream->cRefs > 1)
2933 rc = VERR_WRONG_ORDER;
2934
2935 if (RT_SUCCESS(rc))
2936 {
2937 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2938 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
2939
2940 LogRel2(("Audio: Destroying host stream '%s' (guest stream '%s')\n",
2941 pHstStream ? pHstStream->szName : "<None>",
2942 pGstStream ? pGstStream->szName : "<None>"));
2943
2944 /* Should prevent double frees. */
2945 Assert(pHstStream != pGstStream);
2946
2947 if (pHstStream)
2948 {
2949 rc = drvAudioStreamUninitInternal(pThis, pHstStream);
2950 if (RT_SUCCESS(rc))
2951 {
2952 if (pHstStream->enmDir == PDMAUDIODIR_IN)
2953 {
2954#ifdef VBOX_WITH_STATISTICS
2955 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pHstStream->In.StatFramesCaptured);
2956#endif
2957 if (pThis->Dbg.fEnabled)
2958 {
2959 DrvAudioHlpFileDestroy(pHstStream->In.Dbg.pFileCaptureNonInterleaved);
2960 DrvAudioHlpFileDestroy(pHstStream->In.Dbg.pFileStreamRead);
2961 }
2962 }
2963 else if (pHstStream->enmDir == PDMAUDIODIR_OUT)
2964 {
2965#ifdef VBOX_WITH_STATISTICS
2966 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pHstStream->Out.StatFramesPlayed);
2967#endif
2968 if (pThis->Dbg.fEnabled)
2969 {
2970 DrvAudioHlpFileDestroy(pHstStream->Out.Dbg.pFilePlayNonInterleaved);
2971 DrvAudioHlpFileDestroy(pHstStream->Out.Dbg.pFileStreamWrite);
2972 }
2973 }
2974 else
2975 AssertFailed();
2976
2977 RTListNodeRemove(&pHstStream->Node);
2978
2979 drvAudioStreamFree(pHstStream);
2980 pHstStream = NULL;
2981 }
2982 else
2983 LogRel2(("Audio: Uninitializing host stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
2984 }
2985
2986 if ( RT_SUCCESS(rc)
2987 && pGstStream)
2988 {
2989 rc = drvAudioStreamUninitInternal(pThis, pGstStream);
2990 if (RT_SUCCESS(rc))
2991 {
2992#ifdef VBOX_WITH_STATISTICS
2993 if (pGstStream->enmDir == PDMAUDIODIR_IN)
2994 {
2995 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->In.StatBytesElapsed);
2996 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->In.StatBytesTotalRead);
2997 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->In.StatFramesCaptured);
2998 }
2999 else if (pGstStream->enmDir == PDMAUDIODIR_OUT)
3000 {
3001 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->Out.StatBytesElapsed);
3002 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->Out.StatBytesTotalWritten);
3003 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->Out.StatFramesPlayed);
3004 }
3005 else
3006 AssertFailed();
3007#endif
3008 RTListNodeRemove(&pGstStream->Node);
3009
3010 RTMemFree(pGstStream);
3011 pGstStream = NULL;
3012 }
3013 else
3014 LogRel2(("Audio: Uninitializing guest stream '%s' failed with %Rrc\n", pGstStream->szName, rc));
3015 }
3016 }
3017
3018 if (RT_SUCCESS(rc))
3019 {
3020 if (enmDir == PDMAUDIODIR_IN)
3021 {
3022 pThis->In.cStreamsFree++;
3023 }
3024 else /* Out */
3025 {
3026 pThis->Out.cStreamsFree++;
3027 }
3028 }
3029
3030 int rc2 = RTCritSectLeave(&pThis->CritSect);
3031 if (RT_SUCCESS(rc))
3032 rc = rc2;
3033
3034 LogFlowFuncLeaveRC(rc);
3035 return rc;
3036}
3037
3038/**
3039 * Creates an audio stream on the backend side.
3040 *
3041 * @returns IPRT status code.
3042 * @param pThis Pointer to driver instance.
3043 * @param pHstStream (Host) audio stream to use for creating the stream on the backend side.
3044 * @param pCfgReq Requested audio stream configuration to use for stream creation.
3045 * @param pCfgAcq Acquired audio stream configuration returned by the backend. Optional, can be NULL.
3046 */
3047static int drvAudioStreamCreateInternalBackend(PDRVAUDIO pThis,
3048 PPDMAUDIOSTREAM pHstStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
3049{
3050 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
3051 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
3052 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
3053 /* pCfgAcq is optional. */
3054
3055 AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST,
3056 ("Stream '%s' is not a host stream and therefore has no backend\n", pHstStream->szName));
3057
3058 AssertMsg((pHstStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED) == 0,
3059 ("Stream '%s' already initialized in backend\n", pHstStream->szName));
3060
3061 /* Make the acquired host configuration the requested host configuration initially,
3062 * in case the backend does not report back an acquired configuration. */
3063 PDMAUDIOSTREAMCFG CfgAcq;
3064 int rc = DrvAudioHlpStreamCfgCopy(&CfgAcq, pCfgReq);
3065 if (RT_FAILURE(rc))
3066 {
3067 LogRel(("Audio: Creating stream '%s' with an invalid backend configuration not possible, skipping\n",
3068 pHstStream->szName));
3069 return rc;
3070 }
3071
3072 rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pHstStream->pvBackend, pCfgReq, &CfgAcq);
3073 if (RT_FAILURE(rc))
3074 {
3075 if (rc == VERR_NOT_SUPPORTED)
3076 LogRel2(("Audio: Creating stream '%s' in backend not supported, skipping\n", pHstStream->szName));
3077 else
3078 LogRel2(("Audio: Creating stream '%s' in backend failed with %Rrc\n", pHstStream->szName, rc));
3079
3080 return rc;
3081 }
3082
3083 /* Validate acquired configuration. */
3084 if (!DrvAudioHlpStreamCfgIsValid(&CfgAcq))
3085 {
3086 LogRel(("Audio: Creating stream '%s' returned an invalid backend configuration, skipping\n", pHstStream->szName));
3087 return VERR_INVALID_PARAMETER;
3088 }
3089
3090 /* Only set the host's stream to initialized if we were able create the stream
3091 * in the host backend. This is necessary for trying to re-initialize the stream
3092 * at some later point in time. */
3093 pHstStream->fStatus |= PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
3094
3095 if (pCfgAcq)
3096 {
3097 int rc2 = DrvAudioHlpStreamCfgCopy(pCfgAcq, &CfgAcq);
3098 AssertRC(rc2);
3099 }
3100
3101 return VINF_SUCCESS;
3102}
3103
3104/**
3105 * Calls the backend to give it the chance to destroy its part of the audio stream.
3106 *
3107 * @returns IPRT status code.
3108 * @param pThis Pointer to driver instance.
3109 * @param pHstStream Host audio stream to call the backend destruction for.
3110 */
3111static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream)
3112{
3113 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
3114 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
3115
3116 AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST,
3117 ("Stream '%s' is not a host stream and therefore has no backend\n", pHstStream->szName));
3118
3119 int rc = VINF_SUCCESS;
3120
3121#ifdef LOG_ENABLED
3122 char *pszHstSts = dbgAudioStreamStatusToStr(pHstStream->fStatus);
3123 LogFunc(("[%s] fStatus=%s\n", pHstStream->szName, pszHstSts));
3124 RTStrFree(pszHstSts);
3125#endif /* LOG_ENABLED */
3126
3127 if (pHstStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED)
3128 {
3129 /* Check if the pointer to the host audio driver is still valid.
3130 * It can be NULL if we were called in drvAudioDestruct, for example. */
3131 if (pThis->pHostDrvAudio)
3132 rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pHstStream->pvBackend);
3133 if (RT_SUCCESS(rc))
3134 {
3135 pHstStream->fStatus &= ~PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
3136#if 0 /** @todo r=andy Disabled for now -- need to test this on Windows hosts. */
3137 Assert(pHstStream->fStatus == PDMAUDIOSTRMSTS_FLAG_NONE);
3138#endif
3139 }
3140 }
3141
3142 LogFlowFunc(("[%s] Returning %Rrc\n", pHstStream->szName, rc));
3143 return rc;
3144}
3145
3146/**
3147 * Uninitializes an audio stream.
3148 *
3149 * @returns IPRT status code.
3150 * @param pThis Pointer to driver instance.
3151 * @param pStream Pointer to audio stream to uninitialize.
3152 */
3153static int drvAudioStreamUninitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
3154{
3155 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
3156 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
3157
3158 LogFlowFunc(("[%s] cRefs=%RU32\n", pStream->szName, pStream->cRefs));
3159
3160 if (pStream->cRefs > 1)
3161 return VERR_WRONG_ORDER;
3162
3163 int rc = VINF_SUCCESS;
3164
3165 if (pStream->enmCtx == PDMAUDIOSTREAMCTX_GUEST)
3166 {
3167 if (pStream->fStatus & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED)
3168 {
3169 rc = drvAudioStreamControlInternal(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
3170 if (RT_SUCCESS(rc))
3171 {
3172 pStream->fStatus &= ~PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
3173 Assert(pStream->fStatus == PDMAUDIOSTREAMSTS_FLAG_NONE);
3174 }
3175 }
3176 }
3177 else if (pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST)
3178 {
3179 rc = drvAudioStreamDestroyInternalBackend(pThis, pStream);
3180 }
3181 else
3182 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
3183
3184 if (RT_SUCCESS(rc))
3185 {
3186 /* Make sure that the pair (if any) knows that we're not valid anymore. */
3187 int rc2 = drvAudioStreamLinkToInternal(pStream, NULL);
3188 AssertRC(rc2);
3189
3190 /* Reset status. */
3191 pStream->fStatus = PDMAUDIOSTREAMSTS_FLAG_NONE;
3192
3193 /* Destroy mixing buffer. */
3194 AudioMixBufDestroy(&pStream->MixBuf);
3195 }
3196
3197 LogFlowFunc(("Returning %Rrc\n", rc));
3198 return rc;
3199}
3200
3201/********************************************************************/
3202
3203/**
3204 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
3205 */
3206static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
3207{
3208 LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
3209
3210 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
3211 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3212
3213 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
3214 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
3215
3216 return NULL;
3217}
3218
3219/**
3220 * Power Off notification.
3221 *
3222 * @param pDrvIns The driver instance data.
3223 */
3224static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
3225{
3226 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3227
3228 LogFlowFuncEnter();
3229
3230 /* Just destroy the host stream on the backend side.
3231 * The rest will either be destructed by the device emulation or
3232 * in drvAudioDestruct(). */
3233 PPDMAUDIOSTREAM pStream;
3234 RTListForEach(&pThis->lstHstStreams, pStream, PDMAUDIOSTREAM, Node)
3235 drvAudioStreamDestroyInternalBackend(pThis, pStream);
3236
3237 /*
3238 * Last call for the driver below us.
3239 * Let it know that we reached end of life.
3240 */
3241 if (pThis->pHostDrvAudio->pfnShutdown)
3242 pThis->pHostDrvAudio->pfnShutdown(pThis->pHostDrvAudio);
3243
3244 pThis->pHostDrvAudio = NULL;
3245
3246 LogFlowFuncLeave();
3247}
3248
3249/**
3250 * Constructs an audio driver instance.
3251 *
3252 * @copydoc FNPDMDRVCONSTRUCT
3253 */
3254static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
3255{
3256 LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfg, fFlags));
3257
3258 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3259 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
3260
3261 RTListInit(&pThis->lstHstStreams);
3262 RTListInit(&pThis->lstGstStreams);
3263#ifdef VBOX_WITH_AUDIO_CALLBACKS
3264 RTListInit(&pThis->In.lstCB);
3265 RTListInit(&pThis->Out.lstCB);
3266#endif
3267
3268 /*
3269 * Init the static parts.
3270 */
3271 pThis->pDrvIns = pDrvIns;
3272 /* IBase. */
3273 pDrvIns->IBase.pfnQueryInterface = drvAudioQueryInterface;
3274 /* IAudioConnector. */
3275 pThis->IAudioConnector.pfnEnable = drvAudioEnable;
3276 pThis->IAudioConnector.pfnIsEnabled = drvAudioIsEnabled;
3277 pThis->IAudioConnector.pfnGetConfig = drvAudioGetConfig;
3278 pThis->IAudioConnector.pfnGetStatus = drvAudioGetStatus;
3279 pThis->IAudioConnector.pfnStreamCreate = drvAudioStreamCreate;
3280 pThis->IAudioConnector.pfnStreamDestroy = drvAudioStreamDestroy;
3281 pThis->IAudioConnector.pfnStreamRetain = drvAudioStreamRetain;
3282 pThis->IAudioConnector.pfnStreamRelease = drvAudioStreamRelease;
3283 pThis->IAudioConnector.pfnStreamControl = drvAudioStreamControl;
3284 pThis->IAudioConnector.pfnStreamRead = drvAudioStreamRead;
3285 pThis->IAudioConnector.pfnStreamWrite = drvAudioStreamWrite;
3286 pThis->IAudioConnector.pfnStreamIterate = drvAudioStreamIterate;
3287 pThis->IAudioConnector.pfnStreamGetReadable = drvAudioStreamGetReadable;
3288 pThis->IAudioConnector.pfnStreamGetWritable = drvAudioStreamGetWritable;
3289 pThis->IAudioConnector.pfnStreamGetStatus = drvAudioStreamGetStatus;
3290 pThis->IAudioConnector.pfnStreamSetVolume = drvAudioStreamSetVolume;
3291 pThis->IAudioConnector.pfnStreamPlay = drvAudioStreamPlay;
3292 pThis->IAudioConnector.pfnStreamCapture = drvAudioStreamCapture;
3293#ifdef VBOX_WITH_AUDIO_CALLBACKS
3294 pThis->IAudioConnector.pfnRegisterCallbacks = drvAudioRegisterCallbacks;
3295#endif
3296
3297 /*
3298 * Attach driver below and query its connector interface.
3299 */
3300 PPDMIBASE pDownBase;
3301 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
3302 if (RT_FAILURE(rc))
3303 {
3304 LogRel(("Audio: Failed to attach to driver %p below (flags=0x%x), rc=%Rrc\n",
3305 pDrvIns, fFlags, rc));
3306 return rc;
3307 }
3308
3309 pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
3310 if (!pThis->pHostDrvAudio)
3311 {
3312 LogRel(("Audio: Failed to query interface for underlying host driver\n"));
3313 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
3314 N_("Host audio backend missing or invalid"));
3315 }
3316
3317 rc = drvAudioInit(pDrvIns, pCfg);
3318 if (RT_SUCCESS(rc))
3319 {
3320 pThis->fTerminate = false;
3321 pThis->pDrvIns = pDrvIns;
3322
3323#ifdef VBOX_WITH_STATISTICS
3324 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsActive, "TotalStreamsActive",
3325 STAMUNIT_COUNT, "Total active audio streams.");
3326 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsCreated, "TotalStreamsCreated",
3327 STAMUNIT_COUNT, "Total created audio streams.");
3328 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesRead, "TotalFramesRead",
3329 STAMUNIT_COUNT, "Total frames read by device emulation.");
3330 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesWritten, "TotalFramesWritten",
3331 STAMUNIT_COUNT, "Total frames written by device emulation ");
3332 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesMixedIn, "TotalFramesMixedIn",
3333 STAMUNIT_COUNT, "Total input frames mixed.");
3334 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesMixedOut, "TotalFramesMixedOut",
3335 STAMUNIT_COUNT, "Total output frames mixed.");
3336 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesLostIn, "TotalFramesLostIn",
3337 STAMUNIT_COUNT, "Total input frames lost.");
3338 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesLostOut, "TotalFramesLostOut",
3339 STAMUNIT_COUNT, "Total output frames lost.");
3340 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesOut, "TotalFramesPlayed",
3341 STAMUNIT_COUNT, "Total frames played by backend.");
3342 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalFramesIn, "TotalFramesCaptured",
3343 STAMUNIT_COUNT, "Total frames captured by backend.");
3344 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesRead, "TotalBytesRead",
3345 STAMUNIT_BYTES, "Total bytes read.");
3346 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesWritten, "TotalBytesWritten",
3347 STAMUNIT_BYTES, "Total bytes written.");
3348
3349 PDMDrvHlpSTAMRegProfileAdvEx(pDrvIns, &pThis->Stats.DelayIn, "DelayIn",
3350 STAMUNIT_NS_PER_CALL, "Profiling of input data processing.");
3351 PDMDrvHlpSTAMRegProfileAdvEx(pDrvIns, &pThis->Stats.DelayOut, "DelayOut",
3352 STAMUNIT_NS_PER_CALL, "Profiling of output data processing.");
3353#endif
3354 }
3355
3356 LogFlowFuncLeaveRC(rc);
3357 return rc;
3358}
3359
3360/**
3361 * Destructs an audio driver instance.
3362 *
3363 * @copydoc FNPDMDRVDESTRUCT
3364 */
3365static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
3366{
3367 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
3368 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3369
3370 LogFlowFuncEnter();
3371
3372 int rc2 = RTCritSectEnter(&pThis->CritSect);
3373 AssertRC(rc2);
3374
3375 /*
3376 * Note: No calls here to the driver below us anymore,
3377 * as PDM already has destroyed it.
3378 * If you need to call something from the host driver,
3379 * do this in drvAudioPowerOff() instead.
3380 */
3381
3382 /* Thus, NULL the pointer to the host audio driver first,
3383 * so that routines like drvAudioStreamDestroyInternal() don't call the driver(s) below us anymore. */
3384 pThis->pHostDrvAudio = NULL;
3385
3386 PPDMAUDIOSTREAM pStream, pStreamNext;
3387 RTListForEachSafe(&pThis->lstHstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
3388 {
3389 rc2 = drvAudioStreamUninitInternal(pThis, pStream);
3390 if (RT_SUCCESS(rc2))
3391 {
3392 RTListNodeRemove(&pStream->Node);
3393
3394 drvAudioStreamFree(pStream);
3395 pStream = NULL;
3396 }
3397 }
3398
3399 /* Sanity. */
3400 Assert(RTListIsEmpty(&pThis->lstHstStreams));
3401
3402 RTListForEachSafe(&pThis->lstGstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
3403 {
3404 rc2 = drvAudioStreamUninitInternal(pThis, pStream);
3405 if (RT_SUCCESS(rc2))
3406 {
3407 RTListNodeRemove(&pStream->Node);
3408
3409 RTMemFree(pStream);
3410 pStream = NULL;
3411 }
3412 }
3413
3414 /* Sanity. */
3415 Assert(RTListIsEmpty(&pThis->lstGstStreams));
3416
3417#ifdef VBOX_WITH_AUDIO_CALLBACKS
3418 /*
3419 * Destroy callbacks, if any.
3420 */
3421 PPDMAUDIOCBRECORD pCB, pCBNext;
3422 RTListForEachSafe(&pThis->In.lstCB, pCB, pCBNext, PDMAUDIOCBRECORD, Node)
3423 drvAudioCallbackDestroy(pCB);
3424
3425 RTListForEachSafe(&pThis->Out.lstCB, pCB, pCBNext, PDMAUDIOCBRECORD, Node)
3426 drvAudioCallbackDestroy(pCB);
3427#endif
3428
3429 rc2 = RTCritSectLeave(&pThis->CritSect);
3430 AssertRC(rc2);
3431
3432 rc2 = RTCritSectDelete(&pThis->CritSect);
3433 AssertRC(rc2);
3434
3435#ifdef VBOX_WITH_STATISTICS
3436 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalStreamsActive);
3437 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalStreamsCreated);
3438 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesRead);
3439 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesWritten);
3440 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesMixedIn);
3441 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesMixedOut);
3442 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesLostIn);
3443 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesLostOut);
3444 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesOut);
3445 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalFramesIn);
3446 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalBytesRead);
3447 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalBytesWritten);
3448 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.DelayIn);
3449 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.DelayOut);
3450#endif
3451
3452 LogFlowFuncLeave();
3453}
3454
3455/**
3456 * Suspend notification.
3457 *
3458 * @param pDrvIns The driver instance data.
3459 */
3460static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
3461{
3462 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_PAUSE);
3463}
3464
3465/**
3466 * Resume notification.
3467 *
3468 * @param pDrvIns The driver instance data.
3469 */
3470static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
3471{
3472 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_RESUME);
3473}
3474
3475/**
3476 * Attach notification.
3477 *
3478 * @param pDrvIns The driver instance data.
3479 * @param fFlags Attach flags.
3480 */
3481static DECLCALLBACK(int) drvAudioAttach(PPDMDRVINS pDrvIns, uint32_t fFlags)
3482{
3483 RT_NOREF(fFlags);
3484
3485 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
3486 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3487
3488 int rc2 = RTCritSectEnter(&pThis->CritSect);
3489 AssertRC(rc2);
3490
3491 int rc = VINF_SUCCESS;
3492
3493 LogFunc(("%s\n", pThis->szName));
3494
3495 rc2 = RTCritSectLeave(&pThis->CritSect);
3496 if (RT_SUCCESS(rc))
3497 rc = rc2;
3498
3499 return rc;
3500}
3501
3502/**
3503 * Detach notification.
3504 *
3505 * @param pDrvIns The driver instance data.
3506 * @param fFlags Detach flags.
3507 */
3508static DECLCALLBACK(void) drvAudioDetach(PPDMDRVINS pDrvIns, uint32_t fFlags)
3509{
3510 RT_NOREF(fFlags);
3511
3512 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
3513 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3514
3515 int rc2 = RTCritSectEnter(&pThis->CritSect);
3516 AssertRC(rc2);
3517
3518 pThis->pHostDrvAudio = NULL;
3519
3520 LogFunc(("%s\n", pThis->szName));
3521
3522 rc2 = RTCritSectLeave(&pThis->CritSect);
3523 AssertRC(rc2);
3524}
3525
3526/**
3527 * Audio driver registration record.
3528 */
3529const PDMDRVREG g_DrvAUDIO =
3530{
3531 /* u32Version */
3532 PDM_DRVREG_VERSION,
3533 /* szName */
3534 "AUDIO",
3535 /* szRCMod */
3536 "",
3537 /* szR0Mod */
3538 "",
3539 /* pszDescription */
3540 "Audio connector driver",
3541 /* fFlags */
3542 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
3543 /* fClass */
3544 PDM_DRVREG_CLASS_AUDIO,
3545 /* cMaxInstances */
3546 UINT32_MAX,
3547 /* cbInstance */
3548 sizeof(DRVAUDIO),
3549 /* pfnConstruct */
3550 drvAudioConstruct,
3551 /* pfnDestruct */
3552 drvAudioDestruct,
3553 /* pfnRelocate */
3554 NULL,
3555 /* pfnIOCtl */
3556 NULL,
3557 /* pfnPowerOn */
3558 NULL,
3559 /* pfnReset */
3560 NULL,
3561 /* pfnSuspend */
3562 drvAudioSuspend,
3563 /* pfnResume */
3564 drvAudioResume,
3565 /* pfnAttach */
3566 drvAudioAttach,
3567 /* pfnDetach */
3568 drvAudioDetach,
3569 /* pfnPowerOff */
3570 drvAudioPowerOff,
3571 /* pfnSoftReset */
3572 NULL,
3573 /* u32EndVersion */
3574 PDM_DRVREG_VERSION
3575};
3576
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