VirtualBox

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

Last change on this file since 82450 was 82255, checked in by vboxsync, 5 years ago

vmm/pdmaudioifs.h: More of the same. bugref:9218

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