VirtualBox

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

Last change on this file since 65282 was 65100, checked in by vboxsync, 8 years ago

Devices/Audio: doxygen fixes

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

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