VirtualBox

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

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

Audio/DrvAudio: Added another (release) log warning when stuff is misconfigured.

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