VirtualBox

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

Last change on this file since 82906 was 82579, checked in by vboxsync, 5 years ago

Audio: Make sure to only set a new recording source if the backend in question is ready for it; otherwise stay with current settings. bugref:9625.

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