VirtualBox

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

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

Audio/DrvAudio: Added drvAudioStreamDropInternal() to distinguish from drvAudioStreamReset() better.

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