VirtualBox

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

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

Audio: Renamed the (tweakable) per-driver CFGM options to make it easier remembering them.

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