VirtualBox

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

Last change on this file since 67751 was 67647, checked in by vboxsync, 8 years ago

Audio/DrvAudio.cpp: Bail out in drvAudioStreamPlayRaw() and drvAudioStreamPlayNonInterleaved() if the backend did not play anything.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 113.9 KB
Line 
1/* $Id: DrvAudio.cpp 67647 2017-06-27 16:38:49Z 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 VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
141static char *drvAudioDbgGetFileNameA(PDRVAUDIO pThis, const char *pszPath, const char *pszSuffix);
142static void drvAudioDbgPCMDelete(PDRVAUDIO pThis, const char *pszPath, const char *pszSuffix);
143static void drvAudioDbgPCMDump(PDRVAUDIO pThis, const char *pszPath, const char *pszSuffix, const void *pvData, size_t cbData);
144#endif
145
146#ifdef LOG_ENABLED
147/**
148 * Converts an audio stream status to a string.
149 *
150 * @returns Stringified stream status flags. Must be free'd with RTStrFree().
151 * "NONE" if no flags set.
152 * @param fFlags Stream status flags to convert.
153 */
154static char *dbgAudioStreamStatusToStr(PDMAUDIOSTRMSTS fStatus)
155{
156#define APPEND_FLAG_TO_STR(_aFlag) \
157 if (fStatus & PDMAUDIOSTRMSTS_FLAG_##_aFlag) \
158 { \
159 if (pszFlags) \
160 { \
161 rc2 = RTStrAAppend(&pszFlags, " "); \
162 if (RT_FAILURE(rc2)) \
163 break; \
164 } \
165 \
166 rc2 = RTStrAAppend(&pszFlags, #_aFlag); \
167 if (RT_FAILURE(rc2)) \
168 break; \
169 } \
170
171 char *pszFlags = NULL;
172 int rc2 = VINF_SUCCESS;
173
174 do
175 {
176 APPEND_FLAG_TO_STR(INITIALIZED );
177 APPEND_FLAG_TO_STR(ENABLED );
178 APPEND_FLAG_TO_STR(PAUSED );
179 APPEND_FLAG_TO_STR(PENDING_DISABLE);
180 APPEND_FLAG_TO_STR(PENDING_REINIT );
181 } while (0);
182
183 if (!pszFlags)
184 rc2 = RTStrAAppend(&pszFlags, "NONE");
185
186 if ( RT_FAILURE(rc2)
187 && pszFlags)
188 {
189 RTStrFree(pszFlags);
190 pszFlags = NULL;
191 }
192
193#undef APPEND_FLAG_TO_STR
194
195 return pszFlags;
196}
197#endif /* LOG_ENABLED */
198
199/**
200 * Returns the host stream part of an audio stream pair, or NULL
201 * if no host stream has been assigned / is not available.
202 *
203 * @returns IPRT status code.
204 * @param pStream Audio stream to retrieve host stream part for.
205 */
206DECLINLINE(PPDMAUDIOSTREAM) drvAudioGetHostStream(PPDMAUDIOSTREAM pStream)
207{
208 AssertPtrReturn(pStream, NULL);
209
210 if (!pStream)
211 return NULL;
212
213 PPDMAUDIOSTREAM pHstStream = pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST
214 ? pStream
215 : pStream->pPair;
216 if (pHstStream)
217 {
218 AssertReleaseMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST,
219 ("Stream '%s' resolved as host part is not marked as such (enmCtx=%RU32)\n",
220 pHstStream->szName, pHstStream->enmCtx));
221
222 AssertReleaseMsg(pHstStream->pPair != NULL,
223 ("Stream '%s' resolved as host part has no guest part (anymore)\n", pHstStream->szName));
224 }
225 else
226 LogRel(("Audio: Warning: Stream '%s' does not have a host stream (anymore)\n", pStream->szName));
227
228 return pHstStream;
229}
230
231# if 0 /* unused */
232static int drvAudioProcessOptions(PCFGMNODE pCfgHandle, const char *pszPrefix, audio_option *paOpts, size_t cOpts)
233{
234 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
235 AssertPtrReturn(pszPrefix, VERR_INVALID_POINTER);
236 /* oaOpts and cOpts are optional. */
237
238 PCFGMNODE pCfgChildHandle = NULL;
239 PCFGMNODE pCfgChildChildHandle = NULL;
240
241 /* If pCfgHandle is NULL, let NULL be passed to get int and get string functions..
242 * The getter function will return default values.
243 */
244 if (pCfgHandle != NULL)
245 {
246 /* If its audio general setting, need to traverse to one child node.
247 * /Devices/ichac97/0/LUN#0/Config/Audio
248 */
249 if(!strncmp(pszPrefix, "AUDIO", 5)) /** @todo Use a \#define */
250 {
251 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
252 if(pCfgChildHandle)
253 pCfgHandle = pCfgChildHandle;
254 }
255 else
256 {
257 /* If its driver specific configuration , then need to traverse two level deep child
258 * child nodes. for eg. in case of DirectSoundConfiguration item
259 * /Devices/ichac97/0/LUN#0/Config/Audio/DirectSoundConfig
260 */
261 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
262 if (pCfgChildHandle)
263 {
264 pCfgChildChildHandle = CFGMR3GetFirstChild(pCfgChildHandle);
265 if (pCfgChildChildHandle)
266 pCfgHandle = pCfgChildChildHandle;
267 }
268 }
269 }
270
271 for (size_t i = 0; i < cOpts; i++)
272 {
273 audio_option *pOpt = &paOpts[i];
274 if (!pOpt->valp)
275 {
276 LogFlowFunc(("Option value pointer for `%s' is not set\n", pOpt->name));
277 continue;
278 }
279
280 bool fUseDefault;
281
282 switch (pOpt->tag)
283 {
284 case AUD_OPT_BOOL:
285 case AUD_OPT_INT:
286 {
287 int *intp = (int *)pOpt->valp;
288 *intp = drvAudioGetConfInt(pCfgHandle, pOpt->name, *intp, &fUseDefault);
289
290 break;
291 }
292
293 case AUD_OPT_FMT:
294 {
295 PDMAUDIOFMT *fmtp = (PDMAUDIOFMT *)pOpt->valp;
296 *fmtp = drvAudioGetConfFormat(pCfgHandle, pOpt->name, *fmtp, &fUseDefault);
297
298 break;
299 }
300
301 case AUD_OPT_STR:
302 {
303 const char **strp = (const char **)pOpt->valp;
304 *strp = drvAudioGetConfStr(pCfgHandle, pOpt->name, *strp, &fUseDefault);
305
306 break;
307 }
308
309 default:
310 LogFlowFunc(("Bad value tag for option `%s' - %d\n", pOpt->name, pOpt->tag));
311 fUseDefault = false;
312 break;
313 }
314
315 if (!pOpt->overridenp)
316 pOpt->overridenp = &pOpt->overriden;
317
318 *pOpt->overridenp = !fUseDefault;
319 }
320
321 return VINF_SUCCESS;
322}
323# endif /* unused */
324#endif /* !VBOX_AUDIO_TESTCASE */
325
326/**
327 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamControl}
328 */
329static DECLCALLBACK(int) drvAudioStreamControl(PPDMIAUDIOCONNECTOR pInterface,
330 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
331{
332 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
333
334 if (!pStream)
335 return VINF_SUCCESS;
336
337 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
338
339 int rc = RTCritSectEnter(&pThis->CritSect);
340 if (RT_FAILURE(rc))
341 return rc;
342
343 LogFlowFunc(("[%s] enmStreamCmd=%s\n", pStream->szName, DrvAudioHlpStreamCmdToStr(enmStreamCmd)));
344
345 rc = drvAudioStreamControlInternal(pThis, pStream, enmStreamCmd);
346
347 int rc2 = RTCritSectLeave(&pThis->CritSect);
348 if (RT_SUCCESS(rc))
349 rc = rc2;
350
351 return rc;
352}
353
354/**
355 * Controls an audio stream.
356 *
357 * @returns IPRT status code.
358 * @param pThis Pointer to driver instance.
359 * @param pStream Stream to control.
360 * @param enmStreamCmd Control command.
361 */
362static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
363{
364 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
365 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
366
367 LogFunc(("[%s] enmStreamCmd=%s\n", pStream->szName, DrvAudioHlpStreamCmdToStr(enmStreamCmd)));
368
369 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
370 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
371 AssertPtr(pGstStream);
372
373#ifdef LOG_ENABLED
374 char *pszHstSts = dbgAudioStreamStatusToStr(pHstStream->fStatus);
375 char *pszGstSts = dbgAudioStreamStatusToStr(pHstStream->fStatus);
376 LogFlowFunc(("Status host=%s, guest=%s\n", pszHstSts, pszGstSts));
377 RTStrFree(pszGstSts);
378 RTStrFree(pszHstSts);
379#endif /* LOG_ENABLED */
380
381 int rc = VINF_SUCCESS;
382
383 switch (enmStreamCmd)
384 {
385 case PDMAUDIOSTREAMCMD_ENABLE:
386 {
387 if (!(pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
388 {
389 if (pHstStream)
390 {
391 /* Is a pending disable outstanding? Then disable first. */
392 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
393 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
394
395 if (RT_SUCCESS(rc))
396 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_ENABLE);
397 }
398
399 if (RT_SUCCESS(rc))
400 pGstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_ENABLED;
401 }
402 break;
403 }
404
405 case PDMAUDIOSTREAMCMD_DISABLE:
406 {
407 if (pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
408 {
409 if (pHstStream)
410 {
411 /*
412 * For playback (output) streams first mark the host stream as pending disable,
413 * so that the rest of the remaining audio data will be played first before
414 * closing the stream.
415 */
416 if (pHstStream->enmDir == PDMAUDIODIR_OUT)
417 {
418 LogFunc(("[%s] Pending disable/pause\n", pHstStream->szName));
419 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
420 }
421
422 /* Can we close the host stream as well (not in pending disable mode)? */
423 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE))
424 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
425 }
426
427 if (RT_SUCCESS(rc))
428 pGstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_ENABLED;
429 }
430 break;
431 }
432
433 case PDMAUDIOSTREAMCMD_PAUSE:
434 {
435 if (!(pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED))
436 {
437 if (pHstStream)
438 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_PAUSE);
439
440 if (RT_SUCCESS(rc))
441 pGstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PAUSED;
442 }
443 break;
444 }
445
446 case PDMAUDIOSTREAMCMD_RESUME:
447 {
448 if (pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED)
449 {
450 if (pHstStream)
451 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_RESUME);
452
453 if (RT_SUCCESS(rc))
454 pGstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PAUSED;
455 }
456 break;
457 }
458
459 default:
460 AssertMsgFailed(("Command %s (%RU32) not implemented\n", DrvAudioHlpStreamCmdToStr(enmStreamCmd), enmStreamCmd));
461 rc = VERR_NOT_IMPLEMENTED;
462 break;
463 }
464
465 if (RT_FAILURE(rc))
466 LogFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
467
468 return rc;
469}
470
471/**
472 * Controls a stream's backend.
473 * If the stream has no backend available, VERR_NOT_FOUND is returned.
474 *
475 * @returns IPRT status code.
476 * @param pThis Pointer to driver instance.
477 * @param pStream Stream to control.
478 * @param enmStreamCmd Control command.
479 */
480static int drvAudioStreamControlInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
481{
482 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
483 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
484
485 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
486 if (!pHstStream) /* Stream does not have a host backend? Bail out. */
487 return VERR_NOT_FOUND;
488
489#ifdef LOG_ENABLED
490 char *pszHstSts = dbgAudioStreamStatusToStr(pHstStream->fStatus);
491 LogFlowFunc(("[%s] enmStreamCmd=%s, fStatus=%s\n", pHstStream->szName, DrvAudioHlpStreamCmdToStr(enmStreamCmd), pszHstSts));
492 RTStrFree(pszHstSts);
493#endif /* LOG_ENABLED */
494
495 AssertPtr(pThis->pHostDrvAudio);
496
497 int rc = VINF_SUCCESS;
498
499 switch (enmStreamCmd)
500 {
501 case PDMAUDIOSTREAMCMD_ENABLE:
502 {
503 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
504 {
505 LogRel2(("Audio: Enabling stream '%s'\n", pHstStream->szName));
506 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream->pvBackend,
507 PDMAUDIOSTREAMCMD_ENABLE);
508 if (RT_SUCCESS(rc))
509 {
510 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_ENABLED;
511 }
512 else
513 LogRel2(("Audio: Disabling stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
514 }
515 break;
516 }
517
518 case PDMAUDIOSTREAMCMD_DISABLE:
519 {
520 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
521 {
522 LogRel2(("Audio: Disabling stream '%s'\n", pHstStream->szName));
523 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream->pvBackend,
524 PDMAUDIOSTREAMCMD_DISABLE);
525 if (RT_SUCCESS(rc))
526 {
527 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_ENABLED;
528 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
529 AudioMixBufReset(&pHstStream->MixBuf);
530 }
531 else
532 LogRel2(("Audio: Disabling stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
533 }
534 break;
535 }
536
537 case PDMAUDIOSTREAMCMD_PAUSE:
538 {
539 /* Only pause if the stream is enabled. */
540 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
541 break;
542
543 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED))
544 {
545 LogRel2(("Audio: Pausing stream '%s'\n", pHstStream->szName));
546 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream->pvBackend,
547 PDMAUDIOSTREAMCMD_PAUSE);
548 if (RT_SUCCESS(rc))
549 {
550 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PAUSED;
551 }
552 else
553 LogRel2(("Audio: Pausing stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
554 }
555 break;
556 }
557
558 case PDMAUDIOSTREAMCMD_RESUME:
559 {
560 /* Only need to resume if the stream is enabled. */
561 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
562 break;
563
564 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED)
565 {
566 LogRel2(("Audio: Resuming stream '%s'\n", pHstStream->szName));
567 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream->pvBackend,
568 PDMAUDIOSTREAMCMD_RESUME);
569 if (RT_SUCCESS(rc))
570 {
571 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PAUSED;
572 }
573 else
574 LogRel2(("Audio: Resuming stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
575 }
576 break;
577 }
578
579 default:
580 {
581 AssertMsgFailed(("Command %RU32 not implemented\n", enmStreamCmd));
582 rc = VERR_NOT_IMPLEMENTED;
583 break;
584 }
585 }
586
587 if (RT_FAILURE(rc))
588 LogFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
589
590 return rc;
591}
592
593/**
594 * Initializes an audio stream with a given host and guest stream configuration.
595 *
596 * @returns IPRT status code.
597 * @param pThis Pointer to driver instance.
598 * @param pStream Stream to initialize.
599 * @param pCfgHost Stream configuration to use for the host side (backend).
600 * @param pCfgGuest Stream configuration to use for the guest side.
601 */
602static int drvAudioStreamInitInternal(PDRVAUDIO pThis,
603 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest)
604{
605 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
606 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
607 AssertPtrReturn(pCfgHost, VERR_INVALID_POINTER);
608 AssertPtrReturn(pCfgGuest, VERR_INVALID_POINTER);
609
610 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
611 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
612 AssertPtr(pGstStream);
613
614 /*
615 * Init host stream.
616 */
617
618 /* Set the host's default audio data layout. */
619 pCfgHost->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
620
621#ifdef DEBUG
622 LogFunc(("[%s] Requested host format:\n", pStream->szName));
623 DrvAudioHlpStreamCfgPrint(pCfgHost);
624#else
625 LogRel2(("Audio: Creating stream '%s'\n", pStream->szName));
626 LogRel2(("Audio: Requested %s host format for '%s': %RU32Hz, %RU8%s, %RU8 %s\n",
627 pCfgGuest->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStream->szName,
628 pCfgHost->Props.uHz, pCfgHost->Props.cBits, pCfgHost->Props.fSigned ? "S" : "U",
629 pCfgHost->Props.cChannels, pCfgHost->Props.cChannels == 0 ? "Channel" : "Channels"));
630#endif
631
632 PDMAUDIOSTREAMCFG CfgHostAcq;
633 int rc = drvAudioStreamCreateInternalBackend(pThis, pHstStream, pCfgHost, &CfgHostAcq);
634 if (RT_FAILURE(rc))
635 return rc;
636
637#ifdef DEBUG
638 LogFunc(("[%s] Acquired host format:\n", pStream->szName));
639 DrvAudioHlpStreamCfgPrint(&CfgHostAcq);
640#else
641 LogRel2(("Audio: Acquired %s host format for '%s': %RU32Hz, %RU8%s, %RU8 %s\n",
642 CfgHostAcq.enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStream->szName,
643 CfgHostAcq.Props.uHz, CfgHostAcq.Props.cBits, CfgHostAcq.Props.fSigned ? "S" : "U",
644 CfgHostAcq.Props.cChannels, CfgHostAcq.Props.cChannels == 0 ? "Channel" : "Channels"));
645#endif
646
647 /* No sample buffer size hint given by the backend? Default to some sane value. */
648 if (!CfgHostAcq.cSampleBufferHint)
649 {
650 CfgHostAcq.cSampleBufferHint = _1K; /** @todo Make this configurable? */
651 }
652
653 /* Destroy any former mixing buffer. */
654 AudioMixBufDestroy(&pHstStream->MixBuf);
655
656 /* Make sure to (re-)set the host buffer's shift size. */
657 CfgHostAcq.Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(CfgHostAcq.Props.cBits, CfgHostAcq.Props.cChannels);
658
659 /* Set set host buffer size multiplicator. */
660 const unsigned cSampleBufferHostFactor = 2; /** @todo Make this configurable. */
661
662 LogFunc(("[%s] cSamples=%RU32 (x %u)\n", pHstStream->szName, CfgHostAcq.cSampleBufferHint, cSampleBufferHostFactor));
663
664 int rc2 = AudioMixBufInit(&pHstStream->MixBuf, pHstStream->szName, &CfgHostAcq.Props,
665 CfgHostAcq.cSampleBufferHint * cSampleBufferHostFactor);
666 AssertRC(rc2);
667
668 /* Make a copy of the acquired host stream configuration. */
669 rc2 = DrvAudioHlpStreamCfgCopy(&pHstStream->Cfg, &CfgHostAcq);
670 AssertRC(rc2);
671
672 /*
673 * Init guest stream.
674 */
675
676 /* Destroy any former mixing buffer. */
677 AudioMixBufDestroy(&pGstStream->MixBuf);
678
679 /* Set the guests's default audio data layout. */
680 pCfgHost->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
681
682 /* Make sure to (re-)set the guest buffer's shift size. */
683 pCfgGuest->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfgGuest->Props.cBits, pCfgGuest->Props.cChannels);
684
685 /* Set set guest buffer size multiplicator. */
686 const unsigned cSampleBufferGuestFactor = 10; /** @todo Make this configurable. */
687
688 LogFunc(("[%s] cSamples=%RU32 (x %u)\n", pGstStream->szName, CfgHostAcq.cSampleBufferHint, cSampleBufferGuestFactor));
689
690 rc2 = AudioMixBufInit(&pGstStream->MixBuf, pGstStream->szName, &pCfgGuest->Props,
691 CfgHostAcq.cSampleBufferHint * cSampleBufferGuestFactor);
692 AssertRC(rc2);
693
694 if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
695 {
696 /* Host (Parent) -> Guest (Child). */
697 rc2 = AudioMixBufLinkTo(&pHstStream->MixBuf, &pGstStream->MixBuf);
698 AssertRC(rc2);
699 }
700 else
701 {
702 /* Guest (Parent) -> Host (Child). */
703 rc2 = AudioMixBufLinkTo(&pGstStream->MixBuf, &pHstStream->MixBuf);
704 AssertRC(rc2);
705 }
706
707 /* Make a copy of the guest stream configuration. */
708 rc2 = DrvAudioHlpStreamCfgCopy(&pGstStream->Cfg, pCfgGuest);
709 AssertRC(rc2);
710
711 if (RT_FAILURE(rc))
712 LogRel2(("Audio: Creating stream '%s' failed with %Rrc\n", pStream->szName, rc));
713
714 LogFlowFunc(("[%s] Returning %Rrc\n", pStream->szName, rc));
715 return rc;
716}
717
718/**
719 * Frees an audio stream and its allocated resources.
720 *
721 * @param pStream Audio stream to free.
722 * After this call the pointer will not be valid anymore.
723 */
724static void drvAudioStreamFree(PPDMAUDIOSTREAM pStream)
725{
726 if (!pStream)
727 return;
728
729 if (pStream->pvBackend)
730 {
731 Assert(pStream->cbBackend);
732 RTMemFree(pStream->pvBackend);
733 pStream->pvBackend = NULL;
734 }
735
736 RTMemFree(pStream);
737 pStream = NULL;
738}
739
740#ifdef VBOX_WITH_AUDIO_CALLBACKS
741/**
742 * Schedules a re-initialization of all current audio streams.
743 * The actual re-initialization will happen at some later point in time.
744 *
745 * @returns IPRT status code.
746 * @param pThis Pointer to driver instance.
747 */
748static int drvAudioScheduleReInitInternal(PDRVAUDIO pThis)
749{
750 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
751
752 LogFunc(("\n"));
753
754 /* Mark all host streams to re-initialize. */
755 PPDMAUDIOSTREAM pHstStream;
756 RTListForEach(&pThis->lstHstStreams, pHstStream, PDMAUDIOSTREAM, Node)
757 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT;
758
759# ifdef VBOX_WITH_AUDIO_ENUM
760 /* Re-enumerate all host devices as soon as possible. */
761 pThis->fEnumerateDevices = true;
762# endif
763
764 return VINF_SUCCESS;
765}
766#endif /* VBOX_WITH_AUDIO_CALLBACKS */
767
768/**
769 * Re-initializes an audio stream with its existing host and guest stream configuration.
770 * This might be the case if the backend told us we need to re-initialize because something
771 * on the host side has changed.
772 *
773 * @returns IPRT status code.
774 * @param pThis Pointer to driver instance.
775 * @param pStream Stream to re-initialize.
776 */
777static int drvAudioStreamReInitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
778{
779 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
780 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
781
782 LogFlowFunc(("[%s]\n", pStream->szName));
783
784 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
785 AssertPtr(pHstStream);
786
787 /*
788 * Gather current stream status.
789 */
790 bool fIsEnabled = RT_BOOL(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED); /* Stream is enabled? */
791
792 /*
793 * Destroy and re-create stream on backend side.
794 */
795 int rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
796 if (RT_SUCCESS(rc))
797 {
798 rc = drvAudioStreamDestroyInternalBackend(pThis, pHstStream);
799 if (RT_SUCCESS(rc))
800 {
801 rc = drvAudioStreamCreateInternalBackend(pThis, pHstStream, &pHstStream->Cfg, NULL /* pCfgAcq */);
802 /** @todo Validate (re-)acquired configuration with pHstStream->Cfg? */
803 }
804 }
805
806 /*
807 * Restore previous stream state.
808 */
809 if (RT_SUCCESS(rc))
810 {
811 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
812
813 if (fIsEnabled)
814 {
815 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_ENABLE);
816 if (RT_SUCCESS(rc))
817 {
818 if (pGstStream)
819 {
820 /* Also reset the guest stream mixing buffer. */
821 AudioMixBufReset(&pGstStream->MixBuf);
822 }
823 }
824 }
825
826#ifdef VBOX_WITH_STATISTICS
827 /*
828 * Reset statistics.
829 */
830 if (RT_SUCCESS(rc))
831 {
832 if (pHstStream->enmDir == PDMAUDIODIR_IN)
833 {
834 STAM_COUNTER_RESET(&pHstStream->In.StatBytesElapsed);
835 STAM_COUNTER_RESET(&pHstStream->In.StatBytesTotalRead);
836 STAM_COUNTER_RESET(&pHstStream->In.StatSamplesCaptured);
837
838 if (pGstStream)
839 {
840 Assert(pGstStream->enmDir == pHstStream->enmDir);
841
842 STAM_COUNTER_RESET(&pGstStream->In.StatBytesElapsed);
843 STAM_COUNTER_RESET(&pGstStream->In.StatBytesTotalRead);
844 STAM_COUNTER_RESET(&pGstStream->In.StatSamplesCaptured);
845 }
846 }
847 else if (pHstStream->enmDir == PDMAUDIODIR_OUT)
848 {
849 STAM_COUNTER_RESET(&pHstStream->Out.StatBytesElapsed);
850 STAM_COUNTER_RESET(&pHstStream->Out.StatBytesTotalWritten);
851 STAM_COUNTER_RESET(&pHstStream->Out.StatSamplesPlayed);
852
853 if (pGstStream)
854 {
855 Assert(pGstStream->enmDir == pHstStream->enmDir);
856
857 STAM_COUNTER_RESET(&pGstStream->Out.StatBytesElapsed);
858 STAM_COUNTER_RESET(&pGstStream->Out.StatBytesTotalWritten);
859 STAM_COUNTER_RESET(&pGstStream->Out.StatSamplesPlayed);
860 }
861 }
862 else
863 AssertFailed();
864 }
865#endif
866 }
867
868 if (RT_FAILURE(rc))
869 LogRel2(("Audio: Re-initializing stream '%s' failed with %Rrc\n", pStream->szName, rc));
870
871 LogFunc(("[%s] Returning %Rrc\n", pStream->szName, rc));
872 return rc;
873}
874
875/**
876 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamWrite}
877 */
878static DECLCALLBACK(int) drvAudioStreamWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
879 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
880{
881 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
882 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
883 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
884 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
885 /* pcbWritten is optional. */
886
887 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
888
889 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT,
890 ("Stream '%s' is not an output stream and therefore cannot be written to (direction is 0x%x)\n",
891 pStream->szName, pStream->enmDir));
892
893 uint32_t cbWritten = 0;
894
895 int rc = RTCritSectEnter(&pThis->CritSect);
896 if (RT_FAILURE(rc))
897 return rc;
898
899#ifdef VBOX_WITH_STATISTICS
900 STAM_PROFILE_ADV_START(&pThis->Stats.DelayOut, out);
901#endif
902
903#ifdef LOG_ENABLED
904 char *pszGstSts = NULL;
905 char *pszHstSts = NULL;
906#endif
907
908 do
909 {
910 if ( pThis->pHostDrvAudio
911 && pThis->pHostDrvAudio->pfnGetStatus
912 && pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_OUT) != PDMAUDIOBACKENDSTS_RUNNING)
913 {
914 rc = VERR_NOT_AVAILABLE;
915 break;
916 }
917
918 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
919 if (!pHstStream)
920 {
921 rc = VERR_NOT_AVAILABLE;
922 break;
923 }
924
925#ifdef LOG_ENABLED
926 pszHstSts = dbgAudioStreamStatusToStr(pHstStream->fStatus);
927 AssertPtr(pszHstSts);
928#endif
929 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
930 AssertPtr(pGstStream);
931
932#ifdef LOG_ENABLED
933 pszGstSts = dbgAudioStreamStatusToStr(pGstStream->fStatus);
934 AssertPtr(pszGstSts);
935#endif
936
937#ifdef LOG_ENABLED
938 AssertMsg(pGstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
939 ("Writing to disabled guest output stream \"%s\" not possible (status is %s, host status %s)\n",
940 pGstStream->szName, pszGstSts, pszHstSts));
941#endif
942 const uint32_t cbFree = AudioMixBufFreeBytes(&pGstStream->MixBuf);
943 if (!cbFree) /* No free guest side buffer space, bail out. */
944 break;
945
946 /* Do not attempt to write more than the guest side currently can handle. */
947 if (cbBuf > cbFree)
948 cbBuf = cbFree;
949
950 uint32_t csWritten = 0;
951 rc = AudioMixBufWriteCirc(&pGstStream->MixBuf, pvBuf, cbBuf, &csWritten);
952 if (RT_FAILURE(rc))
953 {
954 LogRel2(("Audio: Lost audio samples due to full guest stream '%s', expect stuttering audio output\n",
955 pGstStream->szName));
956 rc = VINF_SUCCESS; /* Continue. */
957 }
958
959#ifdef DEBUG_andy
960 if ( RT_FAILURE(rc)
961 || !csWritten)
962 {
963 AssertMsgFailed(("[%s] Write failed (guest status %s, host status %s): cbBuf=%RU32, csWritten=%RU32, rc=%Rrc\n",
964 pGstStream->szName, pszGstSts, pszHstSts, cbBuf, csWritten, rc));
965 }
966#endif
967
968#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
969 drvAudioDbgPCMDump(pThis, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH, "StreamWrite.pcm", pvBuf, cbBuf);
970#endif
971
972#ifdef VBOX_WITH_STATISTICS
973 STAM_COUNTER_ADD(&pThis->Stats.TotalSamplesWritten, csWritten);
974#endif
975 uint32_t csMixed = 0;
976 if (csWritten)
977 {
978 int rc2 = AudioMixBufMixToParent(&pGstStream->MixBuf, csWritten, &csMixed);
979 if (RT_FAILURE(rc2))
980 {
981 LogRel2(("Audio: Lost audio samples (%RU32) due to full host stream '%s', expect stuttering audio output\n",
982 csWritten - csMixed, pHstStream->szName));
983 }
984
985#ifdef DEBUG_andy
986 if ( RT_FAILURE(rc2)
987 || csMixed < csWritten)
988 {
989 AssertMsgFailed(("[%s] Mixing failed (guest status %s, host status %s): cbBuf=%RU32, csWritten=%RU32, csMixed=%RU32, rc=%Rrc\n",
990 pGstStream->szName, pszGstSts, pszHstSts, cbBuf, csWritten, csMixed, rc2));
991 }
992#endif
993 if (RT_SUCCESS(rc))
994 rc = rc2;
995
996 cbWritten = AUDIOMIXBUF_S2B(&pGstStream->MixBuf, csWritten);
997
998#ifdef VBOX_WITH_STATISTICS
999 STAM_COUNTER_ADD(&pThis->Stats.TotalSamplesMixedOut, csMixed);
1000 Assert(csWritten >= csMixed);
1001 STAM_COUNTER_ADD(&pThis->Stats.TotalSamplesLostOut, csWritten - csMixed);
1002 STAM_COUNTER_ADD(&pThis->Stats.TotalBytesWritten, cbWritten);
1003 STAM_COUNTER_ADD(&pGstStream->Out.StatBytesTotalWritten, cbWritten);
1004#endif
1005 }
1006
1007 Log3Func(("[%s] cbBuf=%RU32, cUsed=%RU32, cLive=%RU32, cWritten=%RU32, cMixed=%RU32, rc=%Rrc\n",
1008 pGstStream->szName,
1009 cbBuf, AudioMixBufUsed(&pGstStream->MixBuf), AudioMixBufLive(&pGstStream->MixBuf), csWritten, csMixed, rc));
1010
1011 } while (0);
1012
1013#ifdef LOG_ENABLED
1014 RTStrFree(pszHstSts);
1015 RTStrFree(pszGstSts);
1016#endif
1017
1018 int rc2 = RTCritSectLeave(&pThis->CritSect);
1019 if (RT_SUCCESS(rc))
1020 rc = rc2;
1021
1022 if (RT_SUCCESS(rc))
1023 {
1024 if (pcbWritten)
1025 *pcbWritten = cbWritten;
1026 }
1027
1028 return rc;
1029}
1030
1031/**
1032 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRetain}
1033 */
1034static DECLCALLBACK(uint32_t) drvAudioStreamRetain(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1035{
1036 AssertPtrReturn(pInterface, UINT32_MAX);
1037 AssertPtrReturn(pStream, UINT32_MAX);
1038
1039 NOREF(pInterface);
1040
1041 return ++pStream->cRefs;
1042}
1043
1044/**
1045 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRelease}
1046 */
1047static DECLCALLBACK(uint32_t) drvAudioStreamRelease(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1048{
1049 AssertPtrReturn(pInterface, UINT32_MAX);
1050 AssertPtrReturn(pStream, UINT32_MAX);
1051
1052 NOREF(pInterface);
1053
1054 if (pStream->cRefs > 1) /* 1 reference always is kept by this audio driver. */
1055 pStream->cRefs--;
1056
1057 return pStream->cRefs;
1058}
1059
1060/**
1061 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamIterate}
1062 */
1063static DECLCALLBACK(int) drvAudioStreamIterate(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1064{
1065 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1066 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1067 /* pcData is optional. */
1068
1069 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1070
1071 int rc = RTCritSectEnter(&pThis->CritSect);
1072 if (RT_FAILURE(rc))
1073 return rc;
1074
1075 rc = drvAudioStreamIterateInternal(pThis, pStream);
1076
1077 int rc2 = RTCritSectLeave(&pThis->CritSect);
1078 if (RT_SUCCESS(rc))
1079 rc = rc2;
1080
1081 if (RT_FAILURE(rc))
1082 LogFlowFuncLeaveRC(rc);
1083
1084 return rc;
1085}
1086
1087/**
1088 * Does one iteration of an audio stream.
1089 * This function gives the backend the chance of iterating / altering data and
1090 * does the actual mixing between the guest <-> host mixing buffers.
1091 *
1092 * @returns IPRT status code.
1093 * @param pThis Pointer to driver instance.
1094 * @param pStream Stream to iterate.
1095 */
1096static int drvAudioStreamIterateInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
1097{
1098 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1099
1100 if (!pStream)
1101 return VINF_SUCCESS;
1102
1103 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1104 AssertPtr(pHstStream);
1105 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : NULL;
1106 AssertPtr(pGstStream);
1107
1108 int rc;
1109
1110 /* Is the stream scheduled for re-initialization? Do so now. */
1111 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT)
1112 {
1113#ifdef VBOX_WITH_AUDIO_ENUM
1114 if (pThis->fEnumerateDevices)
1115 {
1116 /* Re-enumerate all host devices. */
1117 drvAudioDevicesEnumerateInternal(pThis, true /* fLog */, NULL /* pDevEnum */);
1118
1119 pThis->fEnumerateDevices = false;
1120 }
1121#endif /* VBOX_WITH_AUDIO_ENUM */
1122
1123 /* Remove the pending re-init flag in any case, regardless whether the actual re-initialization succeeded
1124 * or not. If it failed, the backend needs to notify us again to try again at some later point in time. */
1125 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT;
1126
1127 rc = drvAudioStreamReInitInternal(pThis, pStream);
1128 if (RT_FAILURE(rc))
1129 return rc;
1130 }
1131
1132#ifdef LOG_ENABLED
1133 char *pszHstSts = dbgAudioStreamStatusToStr(pHstStream->fStatus);
1134 Log3Func(("[%s] fStatus=%s\n", pHstStream->szName, pszHstSts));
1135 RTStrFree(pszHstSts);
1136#endif /* LOG_ENABLED */
1137
1138 /* Not enabled or paused? Skip iteration. */
1139 if ( !(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
1140 || (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED))
1141 {
1142 return VINF_SUCCESS;
1143 }
1144
1145 /* Whether to try closing a pending to close stream. */
1146 bool fTryClosePending = false;
1147
1148 do
1149 {
1150 uint32_t csMixed = 0;
1151
1152 rc = pThis->pHostDrvAudio->pfnStreamIterate(pThis->pHostDrvAudio, pHstStream->pvBackend);
1153 if (RT_FAILURE(rc))
1154 break;
1155
1156 if (pHstStream->enmDir == PDMAUDIODIR_IN)
1157 {
1158 /* Has the host captured any samples which were not mixed to the guest side yet? */
1159 uint32_t csCaptured = AudioMixBufUsed(&pHstStream->MixBuf);
1160 if (csCaptured)
1161 {
1162 /* When capturing samples, the guest is the parent while the host is the child.
1163 * So try mixing not yet mixed host-side samples to the guest-side buffer. */
1164 rc = AudioMixBufMixToParent(&pHstStream->MixBuf, csCaptured, &csMixed);
1165 if (RT_FAILURE(rc))
1166 {
1167 if (rc == VERR_BUFFER_OVERFLOW)
1168 LogRel2(("Audio: Guest input stream '%s' full, expect stuttering audio capture\n", pGstStream->szName));
1169 else
1170 LogRel2(("Audio: Mixing to guest input stream '%s' failed: %Rrc\n", pGstStream->szName, rc));
1171#ifdef DEBUG_andy_disabled
1172 AssertFailed();
1173#endif
1174 }
1175
1176#ifdef VBOX_WITH_STATISTICS
1177 STAM_COUNTER_ADD(&pThis->Stats.TotalSamplesMixedIn, csMixed);
1178 Assert(csCaptured >= csMixed);
1179 STAM_COUNTER_ADD(&pThis->Stats.TotalSamplesLostIn, csCaptured - csMixed);
1180#endif
1181 Log3Func(("[%s] %RU32/%RU32 input samples mixed, rc=%Rrc\n", pHstStream->szName, csMixed, csCaptured, rc));
1182 }
1183 else
1184 {
1185 fTryClosePending = true;
1186 }
1187 }
1188 else if (pHstStream->enmDir == PDMAUDIODIR_OUT)
1189 {
1190 /* Nothing to do here (yet). */
1191 }
1192 else
1193 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1194
1195 if (fTryClosePending)
1196 {
1197 /* Has the host stream marked as disabled but there still were guest streams relying
1198 * on it? Check if the stream now can be closed and do so, if possible. */
1199 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
1200 {
1201 LogFunc(("[%s] Closing pending stream\n", pHstStream->szName));
1202 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1203 if (RT_SUCCESS(rc))
1204 {
1205 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
1206 }
1207 else
1208 LogFunc(("[%s] Backend vetoed against closing pending output stream, rc=%Rrc\n", pHstStream->szName, rc));
1209 }
1210 }
1211
1212 } while (0);
1213
1214 /* Update timestamps. */
1215 pHstStream->tsLastIterateMS = RTTimeMilliTS();
1216 pGstStream->tsLastIterateMS = RTTimeMilliTS();
1217
1218 if (RT_FAILURE(rc))
1219 LogFunc(("[%s] Failed with %Rrc\n", pHstStream->szName, rc));
1220
1221 return rc;
1222}
1223
1224/**
1225 * Links an audio stream to another audio stream (pair).
1226 *
1227 * @returns IPRT status code.
1228 * @param pStream Stream to handle linking for.
1229 * @param pPair Stream to link pStream to. Specify NULL to unlink pStream from a former linked stream.
1230 */
1231static int drvAudioStreamLinkToInternal(PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAM pPair)
1232{
1233 if (pPair) /* Link. */
1234 {
1235 pStream->pPair = pPair;
1236 pPair->pPair = pStream;
1237
1238 LogRel2(("Audio: Linked audio stream '%s' to '%s'\n", pStream->szName, pPair->szName));
1239 }
1240 else /* Unlink. */
1241 {
1242 if (pStream->pPair)
1243 {
1244 LogRel2(("Audio: Unlinked pair '%s' from stream '%s'\n", pStream->pPair->szName, pStream->szName));
1245
1246 AssertMsg(pStream->pPair->pPair == pStream,
1247 ("Pair '%s' is not linked to '%s' (linked to '%s')\n",
1248 pStream->pPair->szName, pStream->szName, pStream->pPair->pPair ? pStream->pPair->pPair->szName : "<NONE>"));
1249
1250 pStream->pPair->pPair = NULL; /* Also make sure to unlink the pair from pStream */
1251 pStream->pPair = NULL;
1252 }
1253 }
1254
1255 return VINF_SUCCESS;
1256}
1257
1258/**
1259 * Plays an audio host output stream which has been configured for non-interleaved (layout) data.
1260 *
1261 * @return IPRT status code.
1262 * @param pThis Pointer to driver instance.
1263 * @param pHstStream Host stream to play.
1264 * @param csToPlay Number of audio samples to play.
1265 * @param pcsPlayed Returns number of audio samples played. Optional.
1266 */
1267static int drvAudioStreamPlayNonInterleaved(PDRVAUDIO pThis,
1268 PPDMAUDIOSTREAM pHstStream, uint32_t csToPlay, uint32_t *pcsPlayed)
1269{
1270 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1271 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
1272 /* pcsPlayed is optional. */
1273
1274 /* Sanity. */
1275 Assert(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1276 Assert(pHstStream->enmDir == PDMAUDIODIR_OUT);
1277 Assert(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED);
1278
1279 if (!csToPlay)
1280 {
1281 if (pcsPlayed)
1282 *pcsPlayed = 0;
1283 return VINF_SUCCESS;
1284 }
1285
1286 int rc = VINF_SUCCESS;
1287
1288 uint32_t csPlayedTotal = 0;
1289
1290 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetWritable);
1291 uint32_t cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pHstStream->pvBackend);
1292 if (cbWritable)
1293 {
1294 if (csToPlay > AUDIOMIXBUF_B2S(&pHstStream->MixBuf, cbWritable)) /* More samples available than we can write? Limit. */
1295 csToPlay = AUDIOMIXBUF_B2S(&pHstStream->MixBuf, cbWritable);
1296
1297 if (csToPlay)
1298 {
1299 uint8_t auBuf[256]; /** @todo Get rid of this here. */
1300
1301 uint32_t cbLeft = AUDIOMIXBUF_S2B(&pHstStream->MixBuf, csToPlay);
1302 uint32_t cbChunk = sizeof(auBuf);
1303
1304 while (cbLeft)
1305 {
1306 uint32_t csRead = 0;
1307 rc = AudioMixBufReadCirc(&pHstStream->MixBuf, auBuf, RT_MIN(cbChunk, cbLeft), &csRead);
1308 if ( !csRead
1309 || RT_FAILURE(rc))
1310 {
1311 break;
1312 }
1313
1314 uint32_t cbRead = AUDIOMIXBUF_S2B(&pHstStream->MixBuf, csRead);
1315 Assert(cbRead <= cbChunk);
1316
1317 uint32_t cbPlayed = 0;
1318 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pHstStream->pvBackend,
1319 auBuf, cbRead, &cbPlayed);
1320 if ( RT_FAILURE(rc)
1321 || !cbPlayed)
1322 {
1323 break;
1324 }
1325
1326#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
1327 drvAudioDbgPCMDump(pThis, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH, "PlayNonInterleaved.pcm",
1328 auBuf, cbPlayed);
1329#endif
1330 AssertMsg(cbPlayed <= cbRead, ("Played more than available (%RU32 available but got %RU32)\n", cbRead, cbPlayed));
1331#if 0 /** @todo Also handle mono channels. Needs fixing */
1332 AssertMsg(cbPlayed % 2 == 0,
1333 ("Backend for stream '%s' returned uneven played bytes count (csRead=%RU32, cbPlayed=%RU32)\n",
1334 pHstStream->szName, csRead, cbPlayed));*/
1335#endif
1336 csPlayedTotal += AUDIOMIXBUF_B2S(&pHstStream->MixBuf, cbPlayed);
1337 Assert(cbLeft >= cbPlayed);
1338 cbLeft -= cbPlayed;
1339 }
1340 }
1341 }
1342
1343 Log3Func(("[%s] Played %RU32/%RU32 samples, rc=%Rrc\n", pHstStream->szName, csPlayedTotal, csToPlay, rc));
1344
1345 if (RT_SUCCESS(rc))
1346 {
1347 if (pcsPlayed)
1348 *pcsPlayed = csPlayedTotal;
1349 }
1350
1351 return rc;
1352}
1353
1354/**
1355 * Plays an audio host output stream which has been configured for raw audio (layout) data.
1356 *
1357 * @return IPRT status code.
1358 * @param pThis Pointer to driver instance.
1359 * @param pHstStream Host stream to play.
1360 * @param csToPlay Number of audio samples to play.
1361 * @param pcsPlayed Returns number of audio samples played. Optional.
1362 */
1363static int drvAudioStreamPlayRaw(PDRVAUDIO pThis,
1364 PPDMAUDIOSTREAM pHstStream, uint32_t csToPlay, uint32_t *pcsPlayed)
1365{
1366 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1367 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
1368 /* pcsPlayed is optional. */
1369
1370 /* Sanity. */
1371 Assert(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1372 Assert(pHstStream->enmDir == PDMAUDIODIR_OUT);
1373 Assert(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
1374
1375 if (!csToPlay)
1376 {
1377 if (pcsPlayed)
1378 *pcsPlayed = 0;
1379 return VINF_SUCCESS;
1380 }
1381
1382 int rc = VINF_SUCCESS;
1383
1384 uint32_t csPlayedTotal = 0;
1385
1386 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetWritable);
1387 uint32_t csWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pHstStream->pvBackend);
1388 if (csWritable)
1389 {
1390 if (csToPlay > csWritable) /* More samples available than we can write? Limit. */
1391 csToPlay = csWritable;
1392
1393 PDMAUDIOSAMPLE aSampleBuf[256]; /** @todo Get rid of this here. */
1394
1395 uint32_t csLeft = csToPlay;
1396 while (csLeft)
1397 {
1398 uint32_t csRead = 0;
1399 rc = AudioMixBufPeek(&pHstStream->MixBuf, csLeft, aSampleBuf,
1400 RT_MIN(csLeft, RT_ELEMENTS(aSampleBuf)), &csRead);
1401
1402 if ( RT_SUCCESS(rc)
1403 && csRead)
1404 {
1405 uint32_t csPlayed;
1406
1407 /* Note: As the stream layout is RPDMAUDIOSTREAMLAYOUT_RAW, operate on audio frames
1408 * rather on bytes. */
1409 Assert(csRead <= RT_ELEMENTS(aSampleBuf));
1410 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pHstStream->pvBackend,
1411 aSampleBuf, csRead, &csPlayed);
1412 if ( RT_FAILURE(rc)
1413 || !csPlayed)
1414 {
1415 break;
1416 }
1417
1418 csPlayedTotal += csPlayed;
1419 Assert(csPlayedTotal <= csToPlay);
1420
1421 Assert(csLeft >= csRead);
1422 csLeft -= csRead;
1423 }
1424 else if (RT_FAILURE(rc))
1425 break;
1426 }
1427 }
1428
1429 Log3Func(("[%s] Played %RU32/%RU32 samples, rc=%Rrc\n", pHstStream->szName, csPlayedTotal, csToPlay, rc));
1430
1431 if (RT_SUCCESS(rc))
1432 {
1433 if (pcsPlayed)
1434 *pcsPlayed = csPlayedTotal;
1435
1436 }
1437
1438 return rc;
1439}
1440
1441/**
1442 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamPlay}
1443 */
1444static DECLCALLBACK(int) drvAudioStreamPlay(PPDMIAUDIOCONNECTOR pInterface,
1445 PPDMAUDIOSTREAM pStream, uint32_t *pcSamplesPlayed)
1446{
1447 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1448 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1449 /* pcSamplesPlayed is optional. */
1450
1451 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1452
1453 int rc = RTCritSectEnter(&pThis->CritSect);
1454 if (RT_FAILURE(rc))
1455 return rc;
1456
1457 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT,
1458 ("Stream '%s' is not an output stream and therefore cannot be played back (direction is 0x%x)\n",
1459 pStream->szName, pStream->enmDir));
1460
1461 uint32_t csPlayedTotal = 0;
1462
1463 do
1464 {
1465 if (!pThis->pHostDrvAudio)
1466 {
1467 rc = VERR_NOT_AVAILABLE;
1468 break;
1469 }
1470
1471 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1472 AssertPtr(pHstStream);
1473 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : NULL;
1474 AssertPtr(pGstStream);
1475
1476 AssertReleaseMsgBreakStmt(pHstStream != NULL,
1477 ("[%s] Host stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1478 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1479 rc = VERR_NOT_AVAILABLE);
1480 AssertReleaseMsgBreakStmt(pGstStream != NULL,
1481 ("[%s] Guest stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1482 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1483 rc = VERR_NOT_AVAILABLE);
1484
1485 /*
1486 * Check if the backend is ready to operate.
1487 */
1488
1489 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetStatus);
1490 PDMAUDIOSTRMSTS stsBackend = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream->pvBackend);
1491#ifdef LOG_ENABLED
1492 char *pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
1493 Log3Func(("[%s] Start: stsBackend=%s\n", pHstStream->szName, pszBackendSts));
1494 RTStrFree(pszBackendSts);
1495#endif /* LOG_ENABLED */
1496 if (!(stsBackend & PDMAUDIOSTRMSTS_FLAG_ENABLED)) /* Backend disabled? Bail out. */
1497 break;
1498
1499 uint32_t csToPlay = AudioMixBufLive(&pHstStream->MixBuf);
1500
1501 if (RT_LIKELY(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED))
1502 {
1503 rc = drvAudioStreamPlayNonInterleaved(pThis, pHstStream, csToPlay, &csPlayedTotal);
1504 }
1505 else if (pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW)
1506 {
1507 rc = drvAudioStreamPlayRaw(pThis, pHstStream, csToPlay, &csPlayedTotal);
1508 }
1509 else
1510 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1511
1512 uint32_t csLive = 0;
1513
1514 if (RT_SUCCESS(rc))
1515 {
1516 AudioMixBufFinish(&pHstStream->MixBuf, csPlayedTotal);
1517
1518#ifdef VBOX_WITH_STATISTICS
1519 STAM_COUNTER_ADD(&pThis->Stats.TotalSamplesOut, csPlayedTotal);
1520 STAM_PROFILE_ADV_STOP(&pThis->Stats.DelayOut, out);
1521 STAM_COUNTER_ADD(&pHstStream->Out.StatSamplesPlayed, csPlayedTotal);
1522#endif
1523 csLive = AudioMixBufLive(&pHstStream->MixBuf);
1524 }
1525
1526#ifdef LOG_ENABLED
1527 pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
1528 Log3Func(("[%s] End: stsBackend=%s, csLive=%RU32, csPlayedTotal=%RU32, rc=%Rrc\n",
1529 pHstStream->szName, pszBackendSts, csLive, csPlayedTotal, rc));
1530 RTStrFree(pszBackendSts);
1531#endif /* LOG_ENABLED */
1532
1533 if (!csLive)
1534 {
1535 /* Has the host stream marked as disabled but there still were guest streams relying
1536 * on it? Check if the stream now can be closed and do so, if possible. */
1537 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
1538 {
1539 LogFunc(("[%s] Closing pending stream\n", pHstStream->szName));
1540 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1541 if (RT_SUCCESS(rc))
1542 {
1543 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
1544 }
1545 else
1546 LogFunc(("[%s] Backend vetoed against closing output stream, rc=%Rrc\n", pHstStream->szName, rc));
1547 }
1548 }
1549
1550 } while (0);
1551
1552 int rc2 = RTCritSectLeave(&pThis->CritSect);
1553 if (RT_SUCCESS(rc))
1554 rc = rc2;
1555
1556 if (RT_SUCCESS(rc))
1557 {
1558 if (pcSamplesPlayed)
1559 *pcSamplesPlayed = csPlayedTotal;
1560 }
1561
1562 if (RT_FAILURE(rc))
1563 LogFlowFunc(("[%s] Failed with %Rrc\n", pStream->szName, rc));
1564
1565 return rc;
1566}
1567
1568/**
1569 * Captures non-interleaved input from a host stream.
1570 *
1571 * @returns IPRT status code.
1572 * @param pThis Driver instance.
1573 * @param pHstStream Host stream to capture from.
1574 * @param pcsCaptured Number of (host) audio samples captured. Optional.
1575 */
1576static int drvAudioStreamCaptureNonInterleaved(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream, uint32_t *pcsCaptured)
1577{
1578 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1579 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
1580 /* pcsCaptured is optional. */
1581
1582 /* Sanity. */
1583 Assert(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1584 Assert(pHstStream->enmDir == PDMAUDIODIR_IN);
1585 Assert(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED);
1586
1587 int rc = VINF_SUCCESS;
1588
1589 uint32_t csCapturedTotal = 0;
1590
1591 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
1592
1593 uint8_t auBuf[_1K]; /** @todo Get rid of this. */
1594 size_t cbBuf = sizeof(auBuf);
1595
1596 for (;;)
1597 {
1598 uint32_t cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pHstStream->pvBackend);
1599 if (!cbReadable)
1600 break;
1601
1602 uint32_t cbFree = AUDIOMIXBUF_S2B(&pHstStream->MixBuf, AudioMixBufFree(&pHstStream->MixBuf));
1603 if (!cbFree)
1604 break;
1605
1606 if (cbFree < cbReadable) /* More data captured than we can read? */
1607 {
1608 /** @todo Warn? */
1609 }
1610
1611 if (cbFree > cbBuf) /* Limit to buffer size. */
1612 cbFree = (uint32_t)cbBuf;
1613
1614 uint32_t cbCaptured;
1615 rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pHstStream->pvBackend,
1616 auBuf, cbFree, &cbCaptured);
1617 if (RT_FAILURE(rc))
1618 {
1619 int rc2 = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1620 AssertRC(rc2);
1621 }
1622 else if (cbCaptured)
1623 {
1624#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
1625 drvAudioDbgPCMDump(pThis, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH, "CaptureNonInterleaved.pcm",
1626 auBuf, cbCaptured);
1627#endif
1628 Assert(cbCaptured <= cbBuf);
1629 if (cbCaptured > cbBuf) /* Paranoia. */
1630 cbCaptured = (uint32_t)cbBuf;
1631
1632 uint32_t csCaptured = 0;
1633 rc = AudioMixBufWriteCirc(&pHstStream->MixBuf, auBuf, cbCaptured, &csCaptured);
1634 if (RT_SUCCESS(rc))
1635 csCapturedTotal += csCaptured;
1636 }
1637 else /* Nothing captured -- bail out. */
1638 break;
1639
1640 if (RT_FAILURE(rc))
1641 break;
1642 }
1643
1644 Log2Func(("[%s] %RU32 samples captured, rc=%Rrc\n", pHstStream->szName, csCapturedTotal, rc));
1645
1646 if (pcsCaptured)
1647 *pcsCaptured = csCapturedTotal;
1648
1649 return rc;
1650}
1651
1652/**
1653 * Captures raw input from a host stream.
1654 * Raw input means that the backend directly operates on PDMAUDIOSAMPLE structs without
1655 * no data layout processing done in between.
1656 *
1657 * Needed for e.g. the VRDP audio backend (in Main).
1658 *
1659 * @returns IPRT status code.
1660 * @param pThis Driver instance.
1661 * @param pHstStream Host stream to capture from.
1662 * @param pcsCaptured Number of (host) audio samples captured. Optional.
1663 */
1664static int drvAudioStreamCaptureRaw(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream, uint32_t *pcsCaptured)
1665{
1666 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1667 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
1668 /* pcsCaptured is optional. */
1669
1670 /* Sanity. */
1671 Assert(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1672 Assert(pHstStream->enmDir == PDMAUDIODIR_IN);
1673 Assert(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
1674
1675 int rc = VINF_SUCCESS;
1676
1677 uint32_t csCapturedTotal = 0;
1678
1679 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
1680
1681 for (;;)
1682 {
1683 uint32_t cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pHstStream->pvBackend);
1684 if (!cbReadable) /* Nothing to read on the backend side? Bail out. */
1685 break;
1686
1687 const uint32_t cbFree = AudioMixBufFreeBytes(&pHstStream->MixBuf);
1688 if (!cbFree) /* No space left in the host stream? */
1689 break;
1690
1691 if (cbReadable > cbFree) /* Don't capture more than the host stream currently can hold. */
1692 cbReadable = cbFree;
1693
1694 PPDMAUDIOSAMPLE paSamples;
1695 uint32_t csWritable;
1696 rc = AudioMixBufPeekMutable(&pHstStream->MixBuf, AUDIOMIXBUF_B2S(&pHstStream->MixBuf, cbReadable),
1697 &paSamples, &csWritable);
1698 if ( RT_FAILURE(rc)
1699 || !csWritable)
1700 {
1701 break;
1702 }
1703
1704 uint32_t csCaptured;
1705 rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pHstStream->pvBackend,
1706 paSamples, csWritable, &csCaptured);
1707 if (RT_FAILURE(rc))
1708 {
1709 int rc2 = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1710 AssertRC(rc2);
1711 }
1712 else if (csCaptured)
1713 {
1714 Assert(csCaptured <= csWritable);
1715 if (csCaptured > csWritable) /* Paranoia. */
1716 csCaptured = csWritable;
1717
1718 csCapturedTotal += csCaptured;
1719 }
1720 else /* Nothing captured -- bail out. */
1721 break;
1722
1723 if (RT_FAILURE(rc))
1724 break;
1725 }
1726
1727 Log2Func(("[%s] %RU32 samples captured, rc=%Rrc\n", pHstStream->szName, csCapturedTotal, rc));
1728
1729 if (pcsCaptured)
1730 *pcsCaptured = csCapturedTotal;
1731
1732 return rc;
1733}
1734
1735/**
1736 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCapture}
1737 */
1738static DECLCALLBACK(int) drvAudioStreamCapture(PPDMIAUDIOCONNECTOR pInterface,
1739 PPDMAUDIOSTREAM pStream, uint32_t *pcSamplesCaptured)
1740{
1741 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1742
1743 int rc = RTCritSectEnter(&pThis->CritSect);
1744 if (RT_FAILURE(rc))
1745 return rc;
1746
1747 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
1748 ("Stream '%s' is not an input stream and therefore cannot be captured (direction is 0x%x)\n",
1749 pStream->szName, pStream->enmDir));
1750
1751 uint32_t csCaptured = 0;
1752
1753 do
1754 {
1755 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1756 AssertPtr(pHstStream);
1757 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : NULL;
1758 AssertPtr(pGstStream);
1759
1760 AssertReleaseMsgBreakStmt(pHstStream != NULL,
1761 ("[%s] Host stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1762 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1763 rc = VERR_NOT_AVAILABLE);
1764 AssertReleaseMsgBreakStmt(pGstStream != NULL,
1765 ("[%s] Guest stream is NULL (cRefs=%RU32, fStatus=%x, enmCtx=%ld)\n",
1766 pStream->szName, pStream->cRefs, pStream->fStatus, pStream->enmCtx),
1767 rc = VERR_NOT_AVAILABLE);
1768
1769 /*
1770 * Check if the backend is ready to operate.
1771 */
1772
1773 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetStatus);
1774 PDMAUDIOSTRMSTS stsBackend = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream->pvBackend);
1775#ifdef LOG_ENABLED
1776 char *pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
1777 Log3Func(("[%s] Start: stsBackend=%s\n", pHstStream->szName, pszBackendSts));
1778 RTStrFree(pszBackendSts);
1779#endif /* LOG_ENABLED */
1780 if (!(stsBackend & PDMAUDIOSTRMSTS_FLAG_ENABLED)) /* Backend disabled? Bail out. */
1781 break;
1782
1783 /*
1784 * Do the actual capturing.
1785 */
1786
1787 if (RT_LIKELY(pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED))
1788 {
1789 rc = drvAudioStreamCaptureNonInterleaved(pThis, pHstStream, &csCaptured);
1790 }
1791 else if (pHstStream->Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW)
1792 {
1793 rc = drvAudioStreamCaptureRaw(pThis, pHstStream, &csCaptured);
1794 }
1795 else
1796 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1797
1798#ifdef LOG_ENABLED
1799 pszBackendSts = dbgAudioStreamStatusToStr(stsBackend);
1800 Log3Func(("[%s] End: stsBackend=%s, csCaptured=%RU32, rc=%Rrc\n",
1801 pHstStream->szName, pszBackendSts, csCaptured, rc));
1802 RTStrFree(pszBackendSts);
1803#endif /* LOG_ENABLED */
1804
1805 if (RT_SUCCESS(rc))
1806 {
1807 Log3Func(("[%s] %RU32 samples captured, rc=%Rrc\n", pHstStream->szName, csCaptured, rc));
1808
1809#ifdef VBOX_WITH_STATISTICS
1810 STAM_COUNTER_ADD(&pThis->Stats.TotalSamplesIn, csCaptured);
1811 STAM_COUNTER_ADD(&pHstStream->In.StatSamplesCaptured, csCaptured);
1812#endif
1813 }
1814 else if (RT_UNLIKELY(RT_FAILURE(rc)))
1815 {
1816#ifdef DEBUG_andy
1817 AssertFailed();
1818#endif
1819 LogRel2(("Audio: Capturing stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
1820 }
1821
1822 } while (0);
1823
1824 if (pcSamplesCaptured)
1825 *pcSamplesCaptured = csCaptured;
1826
1827 int rc2 = RTCritSectLeave(&pThis->CritSect);
1828 if (RT_SUCCESS(rc))
1829 rc = rc2;
1830
1831 if (RT_FAILURE(rc))
1832 LogFlowFuncLeaveRC(rc);
1833
1834 return rc;
1835}
1836
1837#ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS
1838/**
1839 * Duplicates an audio callback.
1840 *
1841 * @returns Pointer to duplicated callback, or NULL on failure.
1842 * @param pCB Callback to duplicate.
1843 */
1844static PPDMAUDIOCALLBACK drvAudioCallbackDuplicate(PPDMAUDIOCALLBACK pCB)
1845{
1846 AssertPtrReturn(pCB, NULL);
1847
1848 PPDMAUDIOCALLBACK pCBCopy = (PPDMAUDIOCALLBACK)RTMemDup((void *)pCB, sizeof(PDMAUDIOCALLBACK));
1849 if (!pCBCopy)
1850 return NULL;
1851
1852 if (pCB->pvCtx)
1853 {
1854 pCBCopy->pvCtx = RTMemDup(pCB->pvCtx, pCB->cbCtx);
1855 if (!pCBCopy->pvCtx)
1856 {
1857 RTMemFree(pCBCopy);
1858 return NULL;
1859 }
1860
1861 pCBCopy->cbCtx = pCB->cbCtx;
1862 }
1863
1864 return pCBCopy;
1865}
1866
1867/**
1868 * Destroys a given callback.
1869 *
1870 * @param pCB Callback to destroy.
1871 */
1872static void drvAudioCallbackDestroy(PPDMAUDIOCALLBACK pCB)
1873{
1874 if (!pCB)
1875 return;
1876
1877 RTListNodeRemove(&pCB->Node);
1878 if (pCB->pvCtx)
1879 {
1880 Assert(pCB->cbCtx);
1881 RTMemFree(pCB->pvCtx);
1882 }
1883 RTMemFree(pCB);
1884}
1885
1886/**
1887 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnRegisterCallbacks}
1888 */
1889static DECLCALLBACK(int) drvAudioRegisterCallbacks(PPDMIAUDIOCONNECTOR pInterface,
1890 PPDMAUDIOCALLBACK paCallbacks, size_t cCallbacks)
1891{
1892 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1893 AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER);
1894 AssertReturn(cCallbacks, VERR_INVALID_PARAMETER);
1895
1896 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1897
1898 int rc = RTCritSectEnter(&pThis->CritSect);
1899 if (RT_FAILURE(rc))
1900 return rc;
1901
1902 for (size_t i = 0; i < cCallbacks; i++)
1903 {
1904 PPDMAUDIOCALLBACK pCB = drvAudioCallbackDuplicate(&paCallbacks[i]);
1905 if (!pCB)
1906 {
1907 rc = VERR_NO_MEMORY;
1908 break;
1909 }
1910
1911 switch (pCB->enmType)
1912 {
1913 case PDMAUDIOCBTYPE_DATA_INPUT:
1914 RTListAppend(&pThis->lstCBIn, &pCB->Node);
1915 break;
1916
1917 case PDMAUDIOCBTYPE_DATA_OUTPUT:
1918 RTListAppend(&pThis->lstCBOut, &pCB->Node);
1919 break;
1920
1921 default:
1922 AssertMsgFailed(("Not supported\n"));
1923 break;
1924 }
1925 }
1926
1927 /** @todo Undo allocations on error. */
1928
1929 int rc2 = RTCritSectLeave(&pThis->CritSect);
1930 if (RT_SUCCESS(rc))
1931 rc = rc2;
1932
1933 return rc;
1934}
1935
1936/**
1937 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnCallback}
1938 */
1939static DECLCALLBACK(int) drvAudioCallback(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIOCBTYPE enmType,
1940 void *pvUser, size_t cbUser)
1941{
1942 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1943 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
1944 AssertReturn(cbUser, VERR_INVALID_PARAMETER);
1945
1946 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1947 PRTLISTANCHOR pListAnchor = NULL;
1948
1949 switch (enmType)
1950 {
1951 case PDMAUDIOCBTYPE_DATA_INPUT:
1952 pListAnchor = &pThis->lstCBIn;
1953 break;
1954
1955 case PDMAUDIOCBTYPE_DATA_OUTPUT:
1956 pListAnchor = &pThis->lstCBOut;
1957 break;
1958
1959 default:
1960 AssertMsgFailed(("Not supported\n"));
1961 break;
1962 }
1963
1964 if (pListAnchor)
1965 {
1966 PPDMAUDIOCALLBACK pCB;
1967 RTListForEach(pListAnchor, pCB, PDMAUDIOCALLBACK, Node)
1968 {
1969 Assert(pCB->enmType == enmType);
1970 int rc2 = pCB->pfnCallback(enmType, pCB->pvCtx, pCB->cbCtx, pvUser, cbUser);
1971 if (RT_FAILURE(rc2))
1972 LogFunc(("Failed with %Rrc\n", rc2));
1973 }
1974
1975 return VINF_SUCCESS;
1976 }
1977
1978 return VERR_NOT_SUPPORTED;
1979}
1980#endif /* VBOX_WITH_AUDIO_DEVICE_CALLBACKS */
1981
1982#ifdef VBOX_WITH_AUDIO_CALLBACKS
1983/**
1984 * Backend callback implementation.
1985 *
1986 * Important: No calls back to the backend within this function, as the backend
1987 * might hold any locks / critical sections while executing this callback.
1988 * Will result in some ugly deadlocks (or at least locking order violations) then.
1989 *
1990 * @copydoc FNPDMHOSTAUDIOCALLBACK
1991 */
1992static DECLCALLBACK(int) drvAudioBackendCallback(PPDMDRVINS pDrvIns,
1993 PDMAUDIOCBTYPE enmType, void *pvUser, size_t cbUser)
1994{
1995 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
1996 RT_NOREF(pvUser, cbUser);
1997 /* pvUser and cbUser are optional. */
1998
1999 /* Get the upper driver (PDMIAUDIOCONNECTOR). */
2000 AssertPtr(pDrvIns->pUpBase);
2001 PPDMIAUDIOCONNECTOR pInterface = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
2002 AssertPtr(pInterface);
2003 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2004
2005 int rc = RTCritSectEnter(&pThis->CritSect);
2006 AssertRCReturn(rc, rc);
2007
2008 LogFunc(("pThis=%p, enmType=%RU32, pvUser=%p, cbUser=%zu\n", pThis, enmType, pvUser, cbUser));
2009
2010 switch (enmType)
2011 {
2012 case PDMAUDIOCBTYPE_DEVICES_CHANGED:
2013 LogRel(("Audio: Host audio device configuration has changed\n"));
2014 rc = drvAudioScheduleReInitInternal(pThis);
2015 break;
2016
2017 default:
2018 AssertMsgFailed(("Not supported\n"));
2019 break;
2020 }
2021
2022 int rc2 = RTCritSectLeave(&pThis->CritSect);
2023 if (RT_SUCCESS(rc))
2024 rc = rc2;
2025
2026 LogFlowFunc(("Returning %Rrc\n", rc));
2027 return rc;
2028}
2029#endif /* VBOX_WITH_AUDIO_CALLBACKS */
2030
2031#ifdef VBOX_WITH_AUDIO_ENUM
2032/**
2033 * Enumerates all host audio devices.
2034 * This functionality might not be implemented by all backends and will return VERR_NOT_SUPPORTED
2035 * if not being supported.
2036 *
2037 * @returns IPRT status code.
2038 * @param pThis Driver instance to be called.
2039 * @param fLog Whether to print the enumerated device to the release log or not.
2040 * @param pDevEnum Where to store the device enumeration.
2041 */
2042static int drvAudioDevicesEnumerateInternal(PDRVAUDIO pThis, bool fLog, PPDMAUDIODEVICEENUM pDevEnum)
2043{
2044 int rc;
2045
2046 /*
2047 * If the backend supports it, do a device enumeration.
2048 */
2049 if (pThis->pHostDrvAudio->pfnGetDevices)
2050 {
2051 PDMAUDIODEVICEENUM DevEnum;
2052 rc = pThis->pHostDrvAudio->pfnGetDevices(pThis->pHostDrvAudio, &DevEnum);
2053 if (RT_SUCCESS(rc))
2054 {
2055 if (fLog)
2056 LogRel(("Audio: Found %RU16 devices\n", DevEnum.cDevices));
2057
2058 PPDMAUDIODEVICE pDev;
2059 RTListForEach(&DevEnum.lstDevices, pDev, PDMAUDIODEVICE, Node)
2060 {
2061 if (fLog)
2062 {
2063 char *pszFlags = DrvAudioHlpAudDevFlagsToStrA(pDev->fFlags);
2064
2065 LogRel(("Audio: Device '%s':\n", pDev->szName));
2066 LogRel(("Audio: \tUsage = %s\n", DrvAudioHlpAudDirToStr(pDev->enmUsage)));
2067 LogRel(("Audio: \tFlags = %s\n", pszFlags ? pszFlags : "<NONE>"));
2068 LogRel(("Audio: \tInput channels = %RU8\n", pDev->cMaxInputChannels));
2069 LogRel(("Audio: \tOutput channels = %RU8\n", pDev->cMaxOutputChannels));
2070
2071 if (pszFlags)
2072 RTStrFree(pszFlags);
2073 }
2074 }
2075
2076 if (pDevEnum)
2077 rc = DrvAudioHlpDeviceEnumCopy(pDevEnum, &DevEnum);
2078
2079 DrvAudioHlpDeviceEnumFree(&DevEnum);
2080 }
2081 else
2082 {
2083 if (fLog)
2084 LogRel(("Audio: Device enumeration failed with %Rrc\n", rc));
2085 /* Not fatal. */
2086 }
2087 }
2088 else
2089 {
2090 rc = VERR_NOT_SUPPORTED;
2091
2092 if (fLog)
2093 LogRel3(("Audio: Host audio backend does not support audio device enumeration, skipping\n"));
2094 }
2095
2096 LogFunc(("Returning %Rrc\n", rc));
2097 return rc;
2098}
2099#endif /* VBOX_WITH_AUDIO_ENUM */
2100
2101#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
2102/**
2103 * Returns an unique file name for this given audio connector instance.
2104 *
2105 * @return Allocated file name. Must be free'd using RTStrFree().
2106 * @param pThis Driver instance.
2107 * @param pszPath Path name of the file to delete. The path must exist.
2108 * @param pszSuffix File name suffix to use.
2109 */
2110static char *drvAudioDbgGetFileNameA(PDRVAUDIO pThis, const char *pszPath, const char *pszSuffix)
2111{
2112 char szFileName[64];
2113 RTStrPrintf(szFileName, sizeof(szFileName), "drvAudio%RU32-%s", pThis->pDrvIns->iInstance, pszSuffix);
2114
2115 char szFilePath[RTPATH_MAX];
2116 int rc2 = RTStrCopy(szFilePath, sizeof(szFilePath), pszPath);
2117 AssertRC(rc2);
2118 rc2 = RTPathAppend(szFilePath, sizeof(szFilePath), szFileName);
2119 AssertRC(rc2);
2120
2121 return RTStrDup(szFilePath);
2122}
2123
2124/**
2125 * Deletes a PCM audio dump file.
2126 *
2127 * @param pThis Driver instance.
2128 * @param pszPath Path name of the file to delete. The path must exist.
2129 * @param pszSuffix File name suffix to use.
2130 */
2131static void drvAudioDbgPCMDelete(PDRVAUDIO pThis, const char *pszPath, const char *pszSuffix)
2132{
2133 char *pszFileName = drvAudioDbgGetFileNameA(pThis, pszPath, pszSuffix);
2134 AssertPtr(pszFileName);
2135
2136 RTFileDelete(pszFileName);
2137
2138 RTStrFree(pszFileName);
2139}
2140
2141/**
2142 * Dumps (raw) PCM audio data into a file by appending.
2143 * Every driver instance will have an own prefix to not introduce writing races.
2144 *
2145 * @param pThis Driver instance.
2146 * @param pszPath Path name to save file to. The path must exist.
2147 * @param pszSuffix File name suffix to use.
2148 * @param pvData Pointer to PCM data to dump.
2149 * @param cbData Size (in bytes) of PCM data to dump.
2150 */
2151static void drvAudioDbgPCMDump(PDRVAUDIO pThis,
2152 const char *pszPath, const char *pszSuffix, const void *pvData, size_t cbData)
2153{
2154 if (!pvData || !cbData)
2155 return;
2156
2157 AssertPtr(pThis->pDrvIns);
2158
2159 char *pszFileName = drvAudioDbgGetFileNameA(pThis, pszPath, pszSuffix);
2160 AssertPtr(pszFileName);
2161
2162 RTFILE fh;
2163 int rc2 = RTFileOpen(&fh, pszFileName, RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
2164 AssertRC(rc2);
2165 if (RT_SUCCESS(rc2))
2166 {
2167 RTFileWrite(fh, pvData, cbData, NULL);
2168 RTFileClose(fh);
2169 }
2170
2171 RTStrFree(pszFileName);
2172}
2173#endif /* VBOX_AUDIO_DEBUG_DUMP_PCM_DATA */
2174
2175/**
2176 * Initializes the host backend and queries its initial configuration.
2177 * If the host backend fails, VERR_AUDIO_BACKEND_INIT_FAILED will be returned.
2178 *
2179 * Note: As this routine is called when attaching to the device LUN in the
2180 * device emulation, we either check for success or VERR_AUDIO_BACKEND_INIT_FAILED.
2181 * Everything else is considered as fatal and must be handled separately in
2182 * the device emulation!
2183 *
2184 * @return IPRT status code.
2185 * @param pThis Driver instance to be called.
2186 * @param pCfgHandle CFGM configuration handle to use for this driver.
2187 */
2188static int drvAudioHostInit(PDRVAUDIO pThis, PCFGMNODE pCfgHandle)
2189{
2190 /* pCfgHandle is optional. */
2191 NOREF(pCfgHandle);
2192 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2193
2194 LogFlowFuncEnter();
2195
2196 AssertPtr(pThis->pHostDrvAudio);
2197 int rc = pThis->pHostDrvAudio->pfnInit(pThis->pHostDrvAudio);
2198 if (RT_FAILURE(rc))
2199 {
2200 LogRel(("Audio: Initialization of host backend failed with %Rrc\n", rc));
2201 return VERR_AUDIO_BACKEND_INIT_FAILED;
2202 }
2203
2204 /*
2205 * Get the backend configuration.
2206 */
2207 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
2208 if (RT_FAILURE(rc))
2209 {
2210 LogRel(("Audio: Getting host backend configuration failed with %Rrc\n", rc));
2211 return VERR_AUDIO_BACKEND_INIT_FAILED;
2212 }
2213
2214 pThis->cStreamsFreeIn = pThis->BackendCfg.cMaxStreamsIn;
2215 pThis->cStreamsFreeOut = pThis->BackendCfg.cMaxStreamsOut;
2216
2217 LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->cStreamsFreeIn, pThis->cStreamsFreeOut));
2218
2219 LogRel2(("Audio: Host audio backend supports %RU32 input streams and %RU32 output streams at once\n",
2220 /* Clamp for logging. Unlimited streams are defined by UINT32_MAX. */
2221 RT_MIN(64, pThis->cStreamsFreeIn), RT_MIN(64, pThis->cStreamsFreeOut)));
2222
2223#ifdef VBOX_WITH_AUDIO_ENUM
2224 int rc2 = drvAudioDevicesEnumerateInternal(pThis, true /* fLog */, NULL /* pDevEnum */);
2225 if (rc2 != VERR_NOT_SUPPORTED) /* Some backends don't implement device enumeration. */
2226 AssertRC(rc2);
2227
2228 RT_NOREF(rc2);
2229 /* Ignore rc. */
2230#endif
2231
2232#ifdef VBOX_WITH_AUDIO_CALLBACKS
2233 /*
2234 * If the backend supports it, offer a callback to this connector.
2235 */
2236 if (pThis->pHostDrvAudio->pfnSetCallback)
2237 {
2238 int rc2 = pThis->pHostDrvAudio->pfnSetCallback(pThis->pHostDrvAudio, drvAudioBackendCallback);
2239 if (RT_FAILURE(rc2))
2240 LogRel(("Audio: Error registering backend callback, rc=%Rrc\n", rc2));
2241 /* Not fatal. */
2242 }
2243#endif
2244
2245 LogFlowFuncLeave();
2246 return VINF_SUCCESS;
2247}
2248
2249/**
2250 * Handles state changes for all audio streams.
2251 *
2252 * @param pDrvIns Pointer to driver instance.
2253 * @param enmCmd Stream command to set for all streams.
2254 */
2255static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
2256{
2257 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2258 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2259
2260 LogFlowFunc(("enmCmd=%s\n", DrvAudioHlpStreamCmdToStr(enmCmd)));
2261
2262 if (!pThis->pHostDrvAudio)
2263 return;
2264
2265 PPDMAUDIOSTREAM pHstStream;
2266 RTListForEach(&pThis->lstHstStreams, pHstStream, PDMAUDIOSTREAM, Node)
2267 drvAudioStreamControlInternalBackend(pThis, pHstStream, enmCmd);
2268}
2269
2270/**
2271 * Intializes an audio driver instance.
2272 *
2273 * @returns IPRT status code.
2274 * @param pDrvIns Pointer to driver instance.
2275 * @param pCfgHandle CFGM handle to use for configuration.
2276 */
2277static int drvAudioInit(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
2278{
2279 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
2280 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
2281
2282 LogRel2(("Audio: Verbose logging enabled\n"));
2283
2284 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
2285 LogFlowFunc(("pThis=%p, pDrvIns=%p\n", pThis, pDrvIns));
2286
2287 int rc = RTCritSectInit(&pThis->CritSect);
2288 AssertRCReturn(rc, rc);
2289
2290 /** @todo Add audio driver options. */
2291
2292 /*
2293 * If everything went well, initialize the lower driver.
2294 */
2295 rc = drvAudioHostInit(pThis, pCfgHandle);
2296
2297 LogFlowFuncLeaveRC(rc);
2298 return rc;
2299}
2300
2301/**
2302 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRead}
2303 */
2304static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
2305 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
2306{
2307 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2308 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2309
2310 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2311 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2312 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
2313 /* pcbRead is optional. */
2314
2315 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN,
2316 ("Stream '%s' is not an input stream and therefore cannot be read from (direction is 0x%x)\n",
2317 pStream->szName, pStream->enmDir));
2318
2319 uint32_t cbReadTotal = 0;
2320
2321 int rc = RTCritSectEnter(&pThis->CritSect);
2322 if (RT_FAILURE(rc))
2323 return rc;
2324
2325 do
2326 {
2327 if ( pThis->pHostDrvAudio
2328 && pThis->pHostDrvAudio->pfnGetStatus
2329 && pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_IN) != PDMAUDIOBACKENDSTS_RUNNING)
2330 {
2331 rc = VERR_NOT_AVAILABLE;
2332 break;
2333 }
2334
2335 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2336 if (!pHstStream)
2337 {
2338 rc = VERR_NOT_AVAILABLE;
2339 break;
2340 }
2341
2342 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
2343 AssertPtr(pGstStream);
2344
2345 /*
2346 * Read from the parent buffer (that is, the guest buffer) which
2347 * should have the audio data in the format the guest needs.
2348 */
2349 uint32_t cReadTotal = 0;
2350
2351 uint32_t cToRead = RT_MIN(AUDIOMIXBUF_B2S(&pGstStream->MixBuf, cbBuf), AudioMixBufUsed(&pGstStream->MixBuf));
2352 while (cToRead)
2353 {
2354 uint32_t cRead;
2355 rc = AudioMixBufReadCirc(&pGstStream->MixBuf, (uint8_t *)pvBuf + AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cReadTotal),
2356 AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cToRead), &cRead);
2357 if (RT_FAILURE(rc))
2358 break;
2359
2360#if defined (VBOX_WITH_STATISTICS) || defined (VBOX_AUDIO_DEBUG_DUMP_PCM_DATA)
2361 const uint32_t cbRead = AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cRead);
2362#endif
2363
2364#ifdef VBOX_WITH_STATISTICS
2365 STAM_COUNTER_ADD(&pThis->Stats.TotalBytesRead, cbRead);
2366 STAM_COUNTER_ADD(&pGstStream->In.StatBytesTotalRead, cbRead);
2367#endif
2368 Assert(cToRead >= cRead);
2369 cToRead -= cRead;
2370
2371 cReadTotal += cRead;
2372 }
2373
2374 if (cReadTotal)
2375 {
2376#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
2377 drvAudioDbgPCMDump(pThis, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH, "StreamRead.pcm",
2378 pvBuf, AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cReadTotal));
2379#endif
2380 AudioMixBufFinish(&pGstStream->MixBuf, cReadTotal);
2381
2382 pGstStream->In.tsLastReadMS = RTTimeMilliTS();
2383
2384 cbReadTotal = AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cReadTotal);
2385 }
2386
2387 } while (0);
2388
2389 Log3Func(("[%s] cbReadTotal=%RU32, rc=%Rrc\n", pStream->szName, cbReadTotal, rc));
2390
2391 int rc2 = RTCritSectLeave(&pThis->CritSect);
2392 if (RT_SUCCESS(rc))
2393 rc = rc2;
2394
2395 if (RT_SUCCESS(rc))
2396 {
2397 if (pcbRead)
2398 *pcbRead = cbReadTotal;
2399 }
2400
2401 return rc;
2402}
2403
2404/**
2405 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCreate}
2406 */
2407static DECLCALLBACK(int) drvAudioStreamCreate(PPDMIAUDIOCONNECTOR pInterface,
2408 PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest,
2409 PPDMAUDIOSTREAM *ppStream)
2410{
2411 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2412 AssertPtrReturn(pCfgHost, VERR_INVALID_POINTER);
2413 AssertPtrReturn(pCfgGuest, VERR_INVALID_POINTER);
2414 AssertPtrReturn(ppStream, VERR_INVALID_POINTER);
2415
2416 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2417
2418 int rc = RTCritSectEnter(&pThis->CritSect);
2419 if (RT_FAILURE(rc))
2420 return rc;
2421
2422 LogFlowFunc(("Host=%s, Guest=%s\n", pCfgHost->szName, pCfgGuest->szName));
2423#ifdef DEBUG
2424 DrvAudioHlpStreamCfgPrint(pCfgHost);
2425 DrvAudioHlpStreamCfgPrint(pCfgGuest);
2426#endif
2427
2428 /*
2429 * The guest stream always will get the audio stream configuration told
2430 * by the device emulation (which in turn was/could be set by the guest OS).
2431 */
2432 PPDMAUDIOSTREAM pGstStrm = NULL;
2433
2434 /** @todo Docs! */
2435 PPDMAUDIOSTREAM pHstStrm = NULL;
2436
2437#define RC_BREAK(x) { rc = x; break; }
2438
2439 do
2440 {
2441 if ( !DrvAudioHlpStreamCfgIsValid(pCfgHost)
2442 || !DrvAudioHlpStreamCfgIsValid(pCfgGuest))
2443 {
2444 RC_BREAK(VERR_INVALID_PARAMETER);
2445 }
2446
2447 /* Make sure that both configurations actually intend the same thing. */
2448 if (pCfgHost->enmDir != pCfgGuest->enmDir)
2449 {
2450 AssertMsgFailed(("Stream configuration directions do not match\n"));
2451 RC_BREAK(VERR_INVALID_PARAMETER);
2452 }
2453
2454 /* Note: cbHstStrm will contain the size of the data the backend needs to operate on. */
2455 size_t cbHstStrm = 0;
2456 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
2457 {
2458 if (!pThis->cStreamsFreeIn)
2459 LogFunc(("Warning: No more input streams free to use\n"));
2460
2461 cbHstStrm = pThis->BackendCfg.cbStreamIn;
2462 }
2463 else /* Out */
2464 {
2465 if (!pThis->cStreamsFreeOut)
2466 {
2467 LogFlowFunc(("Maximum number of host output streams reached\n"));
2468 RC_BREAK(VERR_AUDIO_NO_FREE_OUTPUT_STREAMS);
2469 }
2470
2471 cbHstStrm = pThis->BackendCfg.cbStreamOut;
2472 }
2473
2474 pHstStrm = (PPDMAUDIOSTREAM)RTMemAllocZ(sizeof(PDMAUDIOSTREAM));
2475 AssertPtrBreakStmt(pHstStrm, rc = VERR_NO_MEMORY);
2476
2477 if (cbHstStrm) /* High unlikely that backends do not have an own space for data, but better check. */
2478 {
2479 pHstStrm->pvBackend = RTMemAllocZ(cbHstStrm);
2480 AssertPtrBreakStmt(pHstStrm->pvBackend, rc = VERR_NO_MEMORY);
2481
2482 pHstStrm->cbBackend = cbHstStrm;
2483 }
2484
2485 pHstStrm->enmCtx = PDMAUDIOSTREAMCTX_HOST;
2486 pHstStrm->enmDir = pCfgHost->enmDir;
2487
2488 pGstStrm = (PPDMAUDIOSTREAM)RTMemAllocZ(sizeof(PDMAUDIOSTREAM));
2489 AssertPtrBreakStmt(pGstStrm, rc = VERR_NO_MEMORY);
2490
2491 pGstStrm->enmCtx = PDMAUDIOSTREAMCTX_GUEST;
2492 pGstStrm->enmDir = pCfgGuest->enmDir;
2493
2494 /*
2495 * Init host stream.
2496 */
2497 RTStrPrintf(pHstStrm->szName, RT_ELEMENTS(pHstStrm->szName), "%s (Host)",
2498 strlen(pCfgHost->szName) ? pCfgHost->szName : "<Untitled>");
2499
2500 rc = drvAudioStreamLinkToInternal(pHstStrm, pGstStrm);
2501 AssertRCBreak(rc);
2502
2503 /*
2504 * Init guest stream.
2505 */
2506 RTStrPrintf(pGstStrm->szName, RT_ELEMENTS(pGstStrm->szName), "%s (Guest)",
2507 strlen(pCfgGuest->szName) ? pCfgGuest->szName : "<Untitled>");
2508
2509 pGstStrm->fStatus = pHstStrm->fStatus; /* Reflect the host stream's status. */
2510
2511 rc = drvAudioStreamLinkToInternal(pGstStrm, pHstStrm);
2512 AssertRCBreak(rc);
2513
2514 /*
2515 * Try to init the rest.
2516 */
2517 rc = drvAudioStreamInitInternal(pThis, pHstStrm, pCfgHost, pCfgGuest);
2518 if (RT_FAILURE(rc))
2519 break;
2520
2521#ifdef VBOX_WITH_STATISTICS
2522 char szStatName[255];
2523
2524 if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
2525 {
2526 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pGstStrm->szName);
2527 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->In.StatBytesElapsed,
2528 szStatName, STAMUNIT_BYTES, "Elapsed bytes read.");
2529
2530 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesRead", pGstStrm->szName);
2531 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->In.StatBytesTotalRead,
2532 szStatName, STAMUNIT_BYTES, "Total bytes read.");
2533
2534 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/SamplesCaptured", pHstStrm->szName);
2535 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pHstStrm->In.StatSamplesCaptured,
2536 szStatName, STAMUNIT_COUNT, "Total samples captured.");
2537 }
2538 else if (pCfgGuest->enmDir == PDMAUDIODIR_OUT)
2539 {
2540 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pGstStrm->szName);
2541 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->Out.StatBytesElapsed,
2542 szStatName, STAMUNIT_BYTES, "Elapsed bytes written.");
2543
2544 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesWritten", pGstStrm->szName);
2545 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->Out.StatBytesTotalWritten,
2546 szStatName, STAMUNIT_BYTES, "Total bytes written.");
2547
2548 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/SamplesPlayed", pHstStrm->szName);
2549 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pHstStrm->Out.StatSamplesPlayed,
2550 szStatName, STAMUNIT_COUNT, "Total samples played.");
2551 }
2552 else
2553 AssertFailed();
2554#endif
2555
2556 } while (0);
2557
2558#undef RC_BREAK
2559
2560 if (RT_FAILURE(rc))
2561 {
2562 if (pGstStrm)
2563 {
2564 int rc2 = drvAudioStreamUninitInternal(pThis, pGstStrm);
2565 if (RT_SUCCESS(rc2))
2566 {
2567 RTMemFree(pGstStrm);
2568 pGstStrm = NULL;
2569 }
2570 }
2571
2572 if (pHstStrm)
2573 {
2574 int rc2 = drvAudioStreamUninitInternal(pThis, pHstStrm);
2575 if (RT_SUCCESS(rc2))
2576 {
2577 drvAudioStreamFree(pHstStrm);
2578 pHstStrm = NULL;
2579 }
2580 }
2581 }
2582 else
2583 {
2584 /* Set initial reference counts. */
2585 RTListAppend(&pThis->lstGstStreams, &pGstStrm->Node);
2586 pGstStrm->cRefs = 1;
2587
2588 RTListAppend(&pThis->lstHstStreams, &pHstStrm->Node);
2589 pHstStrm->cRefs = 1;
2590
2591 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
2592 {
2593 if (pThis->cStreamsFreeIn)
2594 pThis->cStreamsFreeIn--;
2595 }
2596 else /* Out */
2597 {
2598 if (pThis->cStreamsFreeOut)
2599 pThis->cStreamsFreeOut--;
2600 }
2601
2602#ifdef VBOX_WITH_STATISTICS
2603 STAM_COUNTER_ADD(&pThis->Stats.TotalStreamsCreated, 1);
2604#endif
2605 /* Always return the guest-side part to the device emulation. */
2606 *ppStream = pGstStrm;
2607 }
2608
2609 int rc2 = RTCritSectLeave(&pThis->CritSect);
2610 if (RT_SUCCESS(rc))
2611 rc = rc2;
2612
2613 LogFlowFuncLeaveRC(rc);
2614 return rc;
2615}
2616
2617/**
2618 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetConfig}
2619 */
2620static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
2621{
2622 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2623 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2624
2625 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2626
2627 int rc = RTCritSectEnter(&pThis->CritSect);
2628 if (RT_FAILURE(rc))
2629 return rc;
2630
2631 if (pThis->pHostDrvAudio)
2632 {
2633 if (pThis->pHostDrvAudio->pfnGetConfig)
2634 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, pCfg);
2635 else
2636 rc = VERR_NOT_SUPPORTED;
2637 }
2638 else
2639 AssertFailed();
2640
2641 int rc2 = RTCritSectLeave(&pThis->CritSect);
2642 if (RT_SUCCESS(rc))
2643 rc = rc2;
2644
2645 LogFlowFuncLeaveRC(rc);
2646 return rc;
2647}
2648
2649/**
2650 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetStatus}
2651 */
2652static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioGetStatus(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
2653{
2654 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
2655
2656 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2657
2658 PDMAUDIOBACKENDSTS backendSts = PDMAUDIOBACKENDSTS_UNKNOWN;
2659
2660 int rc = RTCritSectEnter(&pThis->CritSect);
2661 if (RT_SUCCESS(rc))
2662 {
2663 if ( pThis->pHostDrvAudio
2664 && pThis->pHostDrvAudio->pfnGetStatus)
2665 {
2666 backendSts = pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, enmDir);
2667 }
2668
2669 int rc2 = RTCritSectLeave(&pThis->CritSect);
2670 if (RT_SUCCESS(rc))
2671 rc = rc2;
2672 }
2673
2674 LogFlowFuncLeaveRC(rc);
2675 return backendSts;
2676}
2677
2678/**
2679 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetReadable}
2680 */
2681static DECLCALLBACK(uint32_t) drvAudioStreamGetReadable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2682{
2683 AssertPtrReturn(pInterface, 0);
2684 AssertPtrReturn(pStream, 0);
2685
2686 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2687
2688 int rc2 = RTCritSectEnter(&pThis->CritSect);
2689 AssertRC(rc2);
2690
2691 AssertMsg(pStream->enmDir == PDMAUDIODIR_IN, ("Can't read from a non-input stream\n"));
2692
2693 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2694 if (!pHstStream) /* No host stream available? Bail out early. */
2695 {
2696 rc2 = RTCritSectLeave(&pThis->CritSect);
2697 AssertRC(rc2);
2698
2699 return 0;
2700 }
2701
2702 uint32_t cReadable = 0;
2703
2704 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
2705 if (pGstStream)
2706 cReadable = AudioMixBufLive(&pGstStream->MixBuf);
2707
2708 Log3Func(("[%s] cbReadable=%RU32 (%zu bytes)\n", pHstStream->szName, cReadable,
2709 AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cReadable)));
2710
2711 uint32_t cbReadable = AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cReadable);
2712
2713 rc2 = RTCritSectLeave(&pThis->CritSect);
2714 AssertRC(rc2);
2715
2716 /* Return bytes instead of audio samples. */
2717 return cbReadable;
2718}
2719
2720/**
2721 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetWritable}
2722 */
2723static DECLCALLBACK(uint32_t) drvAudioStreamGetWritable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2724{
2725 AssertPtrReturn(pInterface, 0);
2726 AssertPtrReturn(pStream, 0);
2727
2728 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2729
2730 int rc2 = RTCritSectEnter(&pThis->CritSect);
2731 AssertRC(rc2);
2732
2733 AssertMsg(pStream->enmDir == PDMAUDIODIR_OUT, ("Can't write to a non-output stream\n"));
2734
2735 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2736 if (!pHstStream) /* No host stream available? Bail out early. */
2737 {
2738 rc2 = RTCritSectLeave(&pThis->CritSect);
2739 AssertRC(rc2);
2740
2741 AssertMsgFailed(("Guest stream '%s' does not have a host stream attached\n", pStream->szName));
2742 return 0;
2743 }
2744
2745 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
2746 AssertPtr(pGstStream);
2747
2748 uint32_t cWritable = AudioMixBufFree(&pGstStream->MixBuf);
2749
2750 Log3Func(("[%s] cWritable=%RU32 (%zu bytes)\n", pHstStream->szName, cWritable,
2751 AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritable)));
2752
2753 uint32_t cbWritable = AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritable);
2754
2755 rc2 = RTCritSectLeave(&pThis->CritSect);
2756 AssertRC(rc2);
2757
2758 /* Return bytes instead of audio samples. */
2759 return cbWritable;
2760}
2761
2762/**
2763 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetStatus}
2764 */
2765static DECLCALLBACK(PDMAUDIOSTRMSTS) drvAudioStreamGetStatus(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2766{
2767 AssertPtrReturn(pInterface, false);
2768
2769 if (!pStream)
2770 return PDMAUDIOSTRMSTS_FLAG_NONE;
2771
2772 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2773
2774 int rc2 = RTCritSectEnter(&pThis->CritSect);
2775 AssertRC(rc2);
2776
2777 PDMAUDIOSTRMSTS strmSts = PDMAUDIOSTRMSTS_FLAG_NONE;
2778
2779 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2780 if (pHstStream)
2781 {
2782 strmSts = pHstStream->fStatus;
2783#ifdef LOG_ENABLED
2784 char *pszHstSts = dbgAudioStreamStatusToStr(pHstStream->fStatus);
2785 Log3Func(("[%s] %s\n", pHstStream->szName, pszHstSts));
2786 RTStrFree(pszHstSts);
2787#endif
2788 }
2789
2790 rc2 = RTCritSectLeave(&pThis->CritSect);
2791 AssertRC(rc2);
2792
2793 return strmSts;
2794}
2795
2796/**
2797 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamSetVolume}
2798 */
2799static DECLCALLBACK(int) drvAudioStreamSetVolume(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, PPDMAUDIOVOLUME pVol)
2800{
2801 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2802 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2803 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
2804
2805 LogFlowFunc(("[%s] volL=%RU32, volR=%RU32, fMute=%RTbool\n", pStream->szName, pVol->uLeft, pVol->uRight, pVol->fMuted));
2806
2807 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2808 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
2809
2810 AudioMixBufSetVolume(&pHstStream->MixBuf, pVol);
2811 AudioMixBufSetVolume(&pGstStream->MixBuf, pVol);
2812 return VINF_SUCCESS;
2813}
2814
2815/**
2816 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamDestroy}
2817 */
2818static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
2819{
2820 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2821 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2822
2823 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
2824
2825 int rc = RTCritSectEnter(&pThis->CritSect);
2826 AssertRC(rc);
2827
2828 PDMAUDIODIR enmDir = pStream->enmDir;
2829
2830 LogFlowFunc(("[%s] cRefs=%RU32\n", pStream->szName, pStream->cRefs));
2831 if (pStream->cRefs > 1)
2832 rc = VERR_WRONG_ORDER;
2833
2834 if (RT_SUCCESS(rc))
2835 {
2836 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
2837 PPDMAUDIOSTREAM pGstStream = pHstStream ? pHstStream->pPair : pStream;
2838
2839 LogRel2(("Audio: Destroying host stream '%s' (guest stream '%s')\n",
2840 pHstStream ? pHstStream->szName : "<None>",
2841 pGstStream ? pGstStream->szName : "<None>"));
2842
2843 /* Should prevent double frees. */
2844 Assert(pHstStream != pGstStream);
2845
2846 if (pHstStream)
2847 {
2848 rc = drvAudioStreamUninitInternal(pThis, pHstStream);
2849 if (RT_SUCCESS(rc))
2850 {
2851#ifdef VBOX_WITH_STATISTICS
2852 if (pHstStream->enmDir == PDMAUDIODIR_IN)
2853 {
2854 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pHstStream->In.StatSamplesCaptured);
2855 }
2856 else if (pHstStream->enmDir == PDMAUDIODIR_OUT)
2857 {
2858 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pHstStream->Out.StatSamplesPlayed);
2859 }
2860 else
2861 AssertFailed();
2862#endif
2863 RTListNodeRemove(&pHstStream->Node);
2864
2865 drvAudioStreamFree(pHstStream);
2866 pHstStream = NULL;
2867 }
2868 else
2869 LogRel2(("Audio: Uninitializing host stream '%s' failed with %Rrc\n", pHstStream->szName, rc));
2870 }
2871
2872 if ( RT_SUCCESS(rc)
2873 && pGstStream)
2874 {
2875 rc = drvAudioStreamUninitInternal(pThis, pGstStream);
2876 if (RT_SUCCESS(rc))
2877 {
2878#ifdef VBOX_WITH_STATISTICS
2879 if (pGstStream->enmDir == PDMAUDIODIR_IN)
2880 {
2881 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->In.StatBytesElapsed);
2882 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->In.StatBytesTotalRead);
2883 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->In.StatSamplesCaptured);
2884 }
2885 else if (pGstStream->enmDir == PDMAUDIODIR_OUT)
2886 {
2887 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->Out.StatBytesElapsed);
2888 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->Out.StatBytesTotalWritten);
2889 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->Out.StatSamplesPlayed);
2890 }
2891 else
2892 AssertFailed();
2893#endif
2894 RTListNodeRemove(&pGstStream->Node);
2895
2896 RTMemFree(pGstStream);
2897 pGstStream = NULL;
2898 }
2899 else
2900 LogRel2(("Audio: Uninitializing guest stream '%s' failed with %Rrc\n", pGstStream->szName, rc));
2901 }
2902 }
2903
2904 if (RT_SUCCESS(rc))
2905 {
2906 if (enmDir == PDMAUDIODIR_IN)
2907 {
2908 pThis->cStreamsFreeIn++;
2909 }
2910 else /* Out */
2911 {
2912 pThis->cStreamsFreeOut++;
2913 }
2914 }
2915
2916 int rc2 = RTCritSectLeave(&pThis->CritSect);
2917 if (RT_SUCCESS(rc))
2918 rc = rc2;
2919
2920 LogFlowFuncLeaveRC(rc);
2921 return rc;
2922}
2923
2924/**
2925 * Creates an audio stream on the backend side.
2926 *
2927 * @returns IPRT status code.
2928 * @param pThis Pointer to driver instance.
2929 * @param pHstStream (Host) audio stream to use for creating the stream on the backend side.
2930 * @param pCfgReq Requested audio stream configuration to use for stream creation.
2931 * @param pCfgAcq Acquired audio stream configuration returned by the backend. Optional, can be NULL.
2932 */
2933static int drvAudioStreamCreateInternalBackend(PDRVAUDIO pThis,
2934 PPDMAUDIOSTREAM pHstStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
2935{
2936 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2937 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
2938 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
2939 /* pCfgAcq is optional. */
2940
2941 AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST,
2942 ("Stream '%s' is not a host stream and therefore has no backend\n", pHstStream->szName));
2943
2944 AssertMsg((pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED) == 0,
2945 ("Stream '%s' already initialized in backend\n", pHstStream->szName));
2946
2947 /* Make the acquired host configuration the requested host configuration initially,
2948 * in case the backend does not report back an acquired configuration. */
2949 PDMAUDIOSTREAMCFG CfgAcq;
2950 int rc = DrvAudioHlpStreamCfgCopy(&CfgAcq, pCfgReq);
2951 if (RT_FAILURE(rc))
2952 {
2953 LogRel(("Audio: Creating stream '%s' with an invalid backend configuration not possible, skipping\n",
2954 pHstStream->szName));
2955 return rc;
2956 }
2957
2958 rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pHstStream->pvBackend, pCfgReq, &CfgAcq);
2959 if (RT_FAILURE(rc))
2960 {
2961 LogRel(("Audio: Creating stream '%s' in backend failed with %Rrc\n", pHstStream->szName, rc));
2962 return rc;
2963 }
2964
2965 /* Validate acquired configuration. */
2966 if (!DrvAudioHlpStreamCfgIsValid(&CfgAcq))
2967 {
2968 LogRel(("Audio: Creating stream '%s' returned an invalid backend configuration, skipping\n", pHstStream->szName));
2969 return VERR_INVALID_PARAMETER;
2970 }
2971
2972 /* Only set the host's stream to initialized if we were able create the stream
2973 * in the host backend. This is necessary for trying to re-initialize the stream
2974 * at some later point in time. */
2975 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
2976
2977 if (pCfgAcq)
2978 {
2979 int rc2 = DrvAudioHlpStreamCfgCopy(pCfgAcq, &CfgAcq);
2980 AssertRC(rc2);
2981 }
2982
2983 return VINF_SUCCESS;
2984}
2985
2986/**
2987 * Calls the backend to give it the chance to destroy its part of the audio stream.
2988 *
2989 * @returns IPRT status code.
2990 * @param pThis Pointer to driver instance.
2991 * @param pHstStream Host audio stream to call the backend destruction for.
2992 */
2993static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream)
2994{
2995 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2996 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
2997
2998 AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST,
2999 ("Stream '%s' is not a host stream and therefore has no backend\n", pHstStream->szName));
3000
3001 int rc = VINF_SUCCESS;
3002
3003#ifdef LOG_ENABLED
3004 char *pszHstSts = dbgAudioStreamStatusToStr(pHstStream->fStatus);
3005 LogFunc(("[%s] fStatus=%s\n", pHstStream->szName, pszHstSts));
3006 RTStrFree(pszHstSts);
3007#endif /* LOG_ENABLED */
3008
3009 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
3010 {
3011 /* Check if the pointer to the host audio driver is still valid.
3012 * It can be NULL if we were called in drvAudioDestruct, for example. */
3013 if (pThis->pHostDrvAudio)
3014 rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pHstStream->pvBackend);
3015 if (RT_SUCCESS(rc))
3016 {
3017 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
3018#if 0 /** @todo r=andy Disabled for now -- need to test this on Windows hosts. */
3019 Assert(pHstStream->fStatus == PDMAUDIOSTRMSTS_FLAG_NONE);
3020#endif
3021 }
3022 }
3023
3024 LogFlowFunc(("[%s] Returning %Rrc\n", pHstStream->szName, rc));
3025 return rc;
3026}
3027
3028/**
3029 * Uninitializes an audio stream.
3030 *
3031 * @returns IPRT status code.
3032 * @param pThis Pointer to driver instance.
3033 * @param pStream Pointer to audio stream to uninitialize.
3034 */
3035static int drvAudioStreamUninitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
3036{
3037 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
3038 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
3039
3040 LogFlowFunc(("[%s] cRefs=%RU32\n", pStream->szName, pStream->cRefs));
3041
3042 if (pStream->cRefs > 1)
3043 return VERR_WRONG_ORDER;
3044
3045 int rc = VINF_SUCCESS;
3046
3047 if (pStream->enmCtx == PDMAUDIOSTREAMCTX_GUEST)
3048 {
3049 if (pStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
3050 {
3051 rc = drvAudioStreamControlInternal(pThis, pStream, PDMAUDIOSTREAMCMD_DISABLE);
3052 if (RT_SUCCESS(rc))
3053 {
3054 pStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
3055 Assert(pStream->fStatus == PDMAUDIOSTRMSTS_FLAG_NONE);
3056 }
3057 }
3058 }
3059 else if (pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST)
3060 {
3061 rc = drvAudioStreamDestroyInternalBackend(pThis, pStream);
3062 }
3063 else
3064 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
3065
3066 if (RT_SUCCESS(rc))
3067 {
3068 /* Make sure that the pair (if any) knows that we're not valid anymore. */
3069 int rc2 = drvAudioStreamLinkToInternal(pStream, NULL);
3070 AssertRC(rc2);
3071
3072 /* Reset status. */
3073 pStream->fStatus = PDMAUDIOSTRMSTS_FLAG_NONE;
3074
3075 /* Destroy mixing buffer. */
3076 AudioMixBufDestroy(&pStream->MixBuf);
3077 }
3078
3079 LogFlowFunc(("Returning %Rrc\n", rc));
3080 return rc;
3081}
3082
3083/********************************************************************/
3084
3085/**
3086 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
3087 */
3088static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
3089{
3090 LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
3091
3092 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
3093 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3094
3095 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
3096 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
3097
3098 return NULL;
3099}
3100
3101/**
3102 * Power Off notification.
3103 *
3104 * @param pDrvIns The driver instance data.
3105 */
3106static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
3107{
3108 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3109
3110 LogFlowFuncEnter();
3111
3112 /* Just destroy the host stream on the backend side.
3113 * The rest will either be destructed by the device emulation or
3114 * in drvAudioDestruct(). */
3115 PPDMAUDIOSTREAM pStream;
3116 RTListForEach(&pThis->lstHstStreams, pStream, PDMAUDIOSTREAM, Node)
3117 drvAudioStreamDestroyInternalBackend(pThis, pStream);
3118
3119 /*
3120 * Last call for the driver below us.
3121 * Let it know that we reached end of life.
3122 */
3123 if (pThis->pHostDrvAudio->pfnShutdown)
3124 pThis->pHostDrvAudio->pfnShutdown(pThis->pHostDrvAudio);
3125
3126 pThis->pHostDrvAudio = NULL;
3127
3128 LogFlowFuncLeave();
3129}
3130
3131/**
3132 * Constructs an audio driver instance.
3133 *
3134 * @copydoc FNPDMDRVCONSTRUCT
3135 */
3136static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
3137{
3138 LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfg, fFlags));
3139
3140 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3141 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
3142
3143 RTListInit(&pThis->lstHstStreams);
3144 RTListInit(&pThis->lstGstStreams);
3145#ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS
3146 RTListInit(&pThis->lstCBIn);
3147 RTListInit(&pThis->lstCBOut);
3148#endif
3149
3150 /*
3151 * Init the static parts.
3152 */
3153 pThis->pDrvIns = pDrvIns;
3154 /* IBase. */
3155 pDrvIns->IBase.pfnQueryInterface = drvAudioQueryInterface;
3156 /* IAudioConnector. */
3157 pThis->IAudioConnector.pfnGetConfig = drvAudioGetConfig;
3158 pThis->IAudioConnector.pfnGetStatus = drvAudioGetStatus;
3159 pThis->IAudioConnector.pfnStreamCreate = drvAudioStreamCreate;
3160 pThis->IAudioConnector.pfnStreamDestroy = drvAudioStreamDestroy;
3161 pThis->IAudioConnector.pfnStreamRetain = drvAudioStreamRetain;
3162 pThis->IAudioConnector.pfnStreamRelease = drvAudioStreamRelease;
3163 pThis->IAudioConnector.pfnStreamControl = drvAudioStreamControl;
3164 pThis->IAudioConnector.pfnStreamRead = drvAudioStreamRead;
3165 pThis->IAudioConnector.pfnStreamWrite = drvAudioStreamWrite;
3166 pThis->IAudioConnector.pfnStreamIterate = drvAudioStreamIterate;
3167 pThis->IAudioConnector.pfnStreamGetReadable = drvAudioStreamGetReadable;
3168 pThis->IAudioConnector.pfnStreamGetWritable = drvAudioStreamGetWritable;
3169 pThis->IAudioConnector.pfnStreamGetStatus = drvAudioStreamGetStatus;
3170 pThis->IAudioConnector.pfnStreamSetVolume = drvAudioStreamSetVolume;
3171 pThis->IAudioConnector.pfnStreamPlay = drvAudioStreamPlay;
3172 pThis->IAudioConnector.pfnStreamCapture = drvAudioStreamCapture;
3173#ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS
3174 pThis->IAudioConnector.pfnRegisterCallbacks = drvAudioRegisterCallbacks;
3175 pThis->IAudioConnector.pfnCallback = drvAudioCallback;
3176#endif
3177
3178 /*
3179 * Attach driver below and query its connector interface.
3180 */
3181 PPDMIBASE pDownBase;
3182 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
3183 if (RT_FAILURE(rc))
3184 {
3185 LogRel(("Audio: Failed to attach to driver %p below (flags=0x%x), rc=%Rrc\n",
3186 pDrvIns, fFlags, rc));
3187 return rc;
3188 }
3189
3190 pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
3191 if (!pThis->pHostDrvAudio)
3192 {
3193 LogRel(("Audio: Failed to query interface for underlying host driver\n"));
3194 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
3195 N_("Host audio backend missing or invalid"));
3196 }
3197
3198 rc = drvAudioInit(pDrvIns, pCfg);
3199 if (RT_SUCCESS(rc))
3200 {
3201 pThis->fTerminate = false;
3202 pThis->pDrvIns = pDrvIns;
3203
3204#ifdef VBOX_WITH_STATISTICS
3205 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsActive, "TotalStreamsActive",
3206 STAMUNIT_COUNT, "Total active audio streams.");
3207 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalStreamsCreated, "TotalStreamsCreated",
3208 STAMUNIT_COUNT, "Total created audio streams.");
3209 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalSamplesRead, "TotalSamplesRead",
3210 STAMUNIT_COUNT, "Total samples read by device emulation.");
3211 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalSamplesWritten, "TotalSamplesWritten",
3212 STAMUNIT_COUNT, "Total samples written by device emulation ");
3213 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalSamplesMixedIn, "TotalSamplesMixedIn",
3214 STAMUNIT_COUNT, "Total input samples mixed.");
3215 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalSamplesMixedOut, "TotalSamplesMixedOut",
3216 STAMUNIT_COUNT, "Total output samples mixed.");
3217 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalSamplesLostIn, "TotalSamplesLostIn",
3218 STAMUNIT_COUNT, "Total input samples lost.");
3219 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalSamplesLostOut, "TotalSamplesLostOut",
3220 STAMUNIT_COUNT, "Total output samples lost.");
3221 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalSamplesOut, "TotalSamplesPlayed",
3222 STAMUNIT_COUNT, "Total samples played by backend.");
3223 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalSamplesIn, "TotalSamplesCaptured",
3224 STAMUNIT_COUNT, "Total samples captured by backend.");
3225 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesRead, "TotalBytesRead",
3226 STAMUNIT_BYTES, "Total bytes read.");
3227 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->Stats.TotalBytesWritten, "TotalBytesWritten",
3228 STAMUNIT_BYTES, "Total bytes written.");
3229
3230 PDMDrvHlpSTAMRegProfileAdvEx(pDrvIns, &pThis->Stats.DelayIn, "DelayIn",
3231 STAMUNIT_NS_PER_CALL, "Profiling of input data processing.");
3232 PDMDrvHlpSTAMRegProfileAdvEx(pDrvIns, &pThis->Stats.DelayOut, "DelayOut",
3233 STAMUNIT_NS_PER_CALL, "Profiling of output data processing.");
3234#endif
3235
3236#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
3237 drvAudioDbgPCMDelete(pThis, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH, "StreamRead.pcm");
3238 drvAudioDbgPCMDelete(pThis, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH, "StreamWrite.pcm");
3239 drvAudioDbgPCMDelete(pThis, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH, "PlayNonInterleaved.pcm");
3240 drvAudioDbgPCMDelete(pThis, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH, "CaptureNonInterleaved.pcm");
3241#endif
3242 }
3243
3244 LogFlowFuncLeaveRC(rc);
3245 return rc;
3246}
3247
3248/**
3249 * Destructs an audio driver instance.
3250 *
3251 * @copydoc FNPDMDRVDESTRUCT
3252 */
3253static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
3254{
3255 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
3256 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
3257
3258 LogFlowFuncEnter();
3259
3260 int rc2 = RTCritSectEnter(&pThis->CritSect);
3261 AssertRC(rc2);
3262
3263 /*
3264 * Note: No calls here to the driver below us anymore,
3265 * as PDM already has destroyed it.
3266 * If you need to call something from the host driver,
3267 * do this in drvAudioPowerOff() instead.
3268 */
3269
3270 /* Thus, NULL the pointer to the host audio driver first,
3271 * so that routines like drvAudioStreamDestroyInternal() don't call the driver(s) below us anymore. */
3272 pThis->pHostDrvAudio = NULL;
3273
3274 PPDMAUDIOSTREAM pStream, pStreamNext;
3275 RTListForEachSafe(&pThis->lstHstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
3276 {
3277 rc2 = drvAudioStreamUninitInternal(pThis, pStream);
3278 if (RT_SUCCESS(rc2))
3279 {
3280 RTListNodeRemove(&pStream->Node);
3281
3282 drvAudioStreamFree(pStream);
3283 pStream = NULL;
3284 }
3285 }
3286
3287 /* Sanity. */
3288 Assert(RTListIsEmpty(&pThis->lstHstStreams));
3289
3290 RTListForEachSafe(&pThis->lstGstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
3291 {
3292 rc2 = drvAudioStreamUninitInternal(pThis, pStream);
3293 if (RT_SUCCESS(rc2))
3294 {
3295 RTListNodeRemove(&pStream->Node);
3296
3297 RTMemFree(pStream);
3298 pStream = NULL;
3299 }
3300 }
3301
3302 /* Sanity. */
3303 Assert(RTListIsEmpty(&pThis->lstGstStreams));
3304
3305#ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS
3306 /*
3307 * Destroy device callbacks, if any.
3308 */
3309 PPDMAUDIOCALLBACK pCB, pCBNext;
3310 RTListForEachSafe(&pThis->lstCBIn, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
3311 drvAudioCallbackDestroy(pCB);
3312
3313 RTListForEachSafe(&pThis->lstCBOut, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
3314 drvAudioCallbackDestroy(pCB);
3315#endif
3316
3317 rc2 = RTCritSectLeave(&pThis->CritSect);
3318 AssertRC(rc2);
3319
3320 rc2 = RTCritSectDelete(&pThis->CritSect);
3321 AssertRC(rc2);
3322
3323#ifdef VBOX_WITH_STATISTICS
3324 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalStreamsActive);
3325 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalStreamsCreated);
3326 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalSamplesRead);
3327 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalSamplesWritten);
3328 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalSamplesMixedIn);
3329 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalSamplesMixedOut);
3330 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalSamplesLostIn);
3331 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalSamplesLostOut);
3332 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalSamplesOut);
3333 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalSamplesIn);
3334 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalBytesRead);
3335 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.TotalBytesWritten);
3336 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.DelayIn);
3337 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pThis->Stats.DelayOut);
3338#endif
3339
3340 LogFlowFuncLeave();
3341}
3342
3343/**
3344 * Suspend notification.
3345 *
3346 * @param pDrvIns The driver instance data.
3347 */
3348static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
3349{
3350 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_PAUSE);
3351}
3352
3353/**
3354 * Resume notification.
3355 *
3356 * @param pDrvIns The driver instance data.
3357 */
3358static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
3359{
3360 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_RESUME);
3361}
3362
3363/**
3364 * Audio driver registration record.
3365 */
3366const PDMDRVREG g_DrvAUDIO =
3367{
3368 /* u32Version */
3369 PDM_DRVREG_VERSION,
3370 /* szName */
3371 "AUDIO",
3372 /* szRCMod */
3373 "",
3374 /* szR0Mod */
3375 "",
3376 /* pszDescription */
3377 "Audio connector driver",
3378 /* fFlags */
3379 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
3380 /* fClass */
3381 PDM_DRVREG_CLASS_AUDIO,
3382 /* cMaxInstances */
3383 UINT32_MAX,
3384 /* cbInstance */
3385 sizeof(DRVAUDIO),
3386 /* pfnConstruct */
3387 drvAudioConstruct,
3388 /* pfnDestruct */
3389 drvAudioDestruct,
3390 /* pfnRelocate */
3391 NULL,
3392 /* pfnIOCtl */
3393 NULL,
3394 /* pfnPowerOn */
3395 NULL,
3396 /* pfnReset */
3397 NULL,
3398 /* pfnSuspend */
3399 drvAudioSuspend,
3400 /* pfnResume */
3401 drvAudioResume,
3402 /* pfnAttach */
3403 NULL,
3404 /* pfnDetach */
3405 NULL,
3406 /* pfnPowerOff */
3407 drvAudioPowerOff,
3408 /* pfnSoftReset */
3409 NULL,
3410 /* u32EndVersion */
3411 PDM_DRVREG_VERSION
3412};
3413
Note: See TracBrowser for help on using the repository browser.

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