VirtualBox

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

Last change on this file since 76734 was 76727, checked in by vboxsync, 6 years ago

Audio/DrvAudio: More buffering defaults tweaking.

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