VirtualBox

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

Last change on this file since 61177 was 61177, checked in by vboxsync, 9 years ago

Audio: Bugfixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 61.4 KB
Line 
1/* $Id: DrvAudio.cpp 61177 2016-05-24 18:12:08Z 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-2016 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 * This code is based on: audio.c from QEMU AUDIO subsystem.
22 *
23 * QEMU Audio subsystem
24 *
25 * Copyright (c) 2003-2005 Vassili Karpov (malc)
26 *
27 * Permission is hereby granted, free of charge, to any person obtaining a copy
28 * of this software and associated documentation files (the "Software"), to deal
29 * in the Software without restriction, including without limitation the rights
30 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
31 * copies of the Software, and to permit persons to whom the Software is
32 * furnished to do so, subject to the following conditions:
33 *
34 * The above copyright notice and this permission notice shall be included in
35 * all copies or substantial portions of the Software.
36 *
37 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
40 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
43 * THE SOFTWARE.
44 */
45#define LOG_GROUP LOG_GROUP_DRV_AUDIO
46#include <VBox/log.h>
47#include <VBox/vmm/pdm.h>
48#include <VBox/err.h>
49#include <VBox/vmm/mm.h>
50#include <VBox/vmm/pdmaudioifs.h>
51
52#include <iprt/alloc.h>
53#include <iprt/asm-math.h>
54#include <iprt/assert.h>
55#include <iprt/circbuf.h>
56#include <iprt/string.h>
57#include <iprt/uuid.h>
58
59#include "VBoxDD.h"
60
61#include <ctype.h>
62#include <stdlib.h>
63
64#include "DrvAudio.h"
65#include "AudioMixBuffer.h"
66
67static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream);
68static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd);
69static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream);
70static int drvAudioStreamDestroyInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream);
71
72#ifndef VBOX_AUDIO_TESTCASE
73static PDMAUDIOFMT drvAudioGetConfFormat(PCFGMNODE pCfgHandle, const char *pszKey,
74 PDMAUDIOFMT enmDefault, bool *pfDefault)
75{
76 if ( pCfgHandle == NULL
77 || pszKey == NULL)
78 {
79 *pfDefault = true;
80 return enmDefault;
81 }
82
83 char *pszValue = NULL;
84 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
85 if (RT_FAILURE(rc))
86 {
87 *pfDefault = true;
88 return enmDefault;
89 }
90
91 PDMAUDIOFMT fmt = DrvAudioHlpStrToAudFmt(pszValue);
92 if (fmt == PDMAUDIOFMT_INVALID)
93 {
94 *pfDefault = true;
95 return enmDefault;
96 }
97
98 *pfDefault = false;
99 return fmt;
100}
101
102static int drvAudioGetConfInt(PCFGMNODE pCfgHandle, const char *pszKey,
103 int iDefault, bool *pfDefault)
104{
105
106 if ( pCfgHandle == NULL
107 || pszKey == NULL)
108 {
109 *pfDefault = true;
110 return iDefault;
111 }
112
113 uint64_t u64Data = 0;
114 int rc = CFGMR3QueryInteger(pCfgHandle, pszKey, &u64Data);
115 if (RT_FAILURE(rc))
116 {
117 *pfDefault = true;
118 return iDefault;
119
120 }
121
122 *pfDefault = false;
123 return u64Data;
124}
125
126static const char *drvAudioGetConfStr(PCFGMNODE pCfgHandle, const char *pszKey,
127 const char *pszDefault, bool *pfDefault)
128{
129 if ( pCfgHandle == NULL
130 || pszKey == NULL)
131 {
132 *pfDefault = true;
133 return pszDefault;
134 }
135
136 char *pszValue = NULL;
137 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
138 if (RT_FAILURE(rc))
139 {
140 *pfDefault = true;
141 return pszDefault;
142 }
143
144 *pfDefault = false;
145 return pszValue;
146}
147
148inline PPDMAUDIOSTREAM drvAudioGetHostStream(PPDMAUDIOSTREAM pStream)
149{
150 AssertPtrReturn(pStream, NULL);
151
152 PPDMAUDIOSTREAM pStreamHst = pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST
153 ? pStream
154 : pStream->pPair;
155 AssertPtr(pStreamHst);
156 return pStreamHst;
157}
158
159static int drvAudioProcessOptions(PCFGMNODE pCfgHandle, const char *pszPrefix, audio_option *paOpts, size_t cOpts)
160{
161 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
162 AssertPtrReturn(pszPrefix, VERR_INVALID_POINTER);
163 /* oaOpts and cOpts are optional. */
164
165 PCFGMNODE pCfgChildHandle = NULL;
166 PCFGMNODE pCfgChildChildHandle = NULL;
167
168 /* If pCfgHandle is NULL, let NULL be passed to get int and get string functions..
169 * The getter function will return default values.
170 */
171 if (pCfgHandle != NULL)
172 {
173 /* If its audio general setting, need to traverse to one child node.
174 * /Devices/ichac97/0/LUN#0/Config/Audio
175 */
176 if(!strncmp(pszPrefix, "AUDIO", 5)) /** @todo Use a \#define */
177 {
178 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
179 if(pCfgChildHandle)
180 pCfgHandle = pCfgChildHandle;
181 }
182 else
183 {
184 /* If its driver specific configuration , then need to traverse two level deep child
185 * child nodes. for eg. in case of DirectSoundConfiguration item
186 * /Devices/ichac97/0/LUN#0/Config/Audio/DirectSoundConfig
187 */
188 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
189 if (pCfgChildHandle)
190 {
191 pCfgChildChildHandle = CFGMR3GetFirstChild(pCfgChildHandle);
192 if (pCfgChildChildHandle)
193 pCfgHandle = pCfgChildChildHandle;
194 }
195 }
196 }
197
198 for (size_t i = 0; i < cOpts; i++)
199 {
200 audio_option *pOpt = &paOpts[i];
201 if (!pOpt->valp)
202 {
203 LogFlowFunc(("Option value pointer for `%s' is not set\n", pOpt->name));
204 continue;
205 }
206
207 bool fUseDefault;
208
209 switch (pOpt->tag)
210 {
211 case AUD_OPT_BOOL:
212 case AUD_OPT_INT:
213 {
214 int *intp = (int *)pOpt->valp;
215 *intp = drvAudioGetConfInt(pCfgHandle, pOpt->name, *intp, &fUseDefault);
216
217 break;
218 }
219
220 case AUD_OPT_FMT:
221 {
222 PDMAUDIOFMT *fmtp = (PDMAUDIOFMT *)pOpt->valp;
223 *fmtp = drvAudioGetConfFormat(pCfgHandle, pOpt->name, *fmtp, &fUseDefault);
224
225 break;
226 }
227
228 case AUD_OPT_STR:
229 {
230 const char **strp = (const char **)pOpt->valp;
231 *strp = drvAudioGetConfStr(pCfgHandle, pOpt->name, *strp, &fUseDefault);
232
233 break;
234 }
235
236 default:
237 LogFlowFunc(("Bad value tag for option `%s' - %d\n", pOpt->name, pOpt->tag));
238 fUseDefault = false;
239 break;
240 }
241
242 if (!pOpt->overridenp)
243 pOpt->overridenp = &pOpt->overriden;
244
245 *pOpt->overridenp = !fUseDefault;
246 }
247
248 return VINF_SUCCESS;
249}
250#endif /* !VBOX_AUDIO_TESTCASE */
251
252static DECLCALLBACK(int) drvAudioStreamControl(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
253{
254 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
255
256 return drvAudioStreamControlInternal(pThis, pStream, enmStreamCmd);
257}
258
259static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
260{
261 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
262 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
263
264 int rc = RTCritSectEnter(&pThis->CritSect);
265 if (RT_FAILURE(rc))
266 return rc;
267
268 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
269
270 LogFlowFunc(("%s: enmStreamCmd=%ld\n", pHstStream->szName, enmStreamCmd));
271
272 switch (enmStreamCmd)
273 {
274 case PDMAUDIOSTREAMCMD_ENABLE:
275 {
276 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
277 {
278 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_ENABLE);
279 if (RT_SUCCESS(rc))
280 {
281 Assert(!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE));
282 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_ENABLED;
283 }
284 }
285 else
286 rc = VINF_SUCCESS;
287
288 break;
289 }
290
291 case PDMAUDIOSTREAMCMD_DISABLE:
292 {
293 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
294 {
295 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
296 if (RT_SUCCESS(rc))
297 {
298 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_ENABLED;
299 AudioMixBufClear(&pHstStream->MixBuf);
300 }
301 }
302 else
303 rc = VINF_SUCCESS;
304
305 break;
306 }
307
308 case PDMAUDIOSTREAMCMD_PAUSE:
309 {
310 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED))
311 {
312 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_PAUSE);
313 if (RT_SUCCESS(rc))
314 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PAUSED;
315 }
316 else
317 rc = VINF_SUCCESS;
318
319 break;
320 }
321
322 case PDMAUDIOSTREAMCMD_RESUME:
323 {
324 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED)
325 {
326 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_RESUME);
327 if (RT_SUCCESS(rc))
328 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PAUSED;
329 }
330 else
331 rc = VINF_SUCCESS;
332
333 break;
334 }
335
336 default:
337 AssertMsgFailed(("Command %ld not implemented\n", enmStreamCmd));
338 rc = VERR_NOT_IMPLEMENTED;
339 break;
340 }
341
342 if (RT_FAILURE(rc))
343 LogFunc(("%s: Failed with %Rrc\n", pHstStream->szName, rc));
344
345 int rc2 = RTCritSectLeave(&pThis->CritSect);
346 if (RT_SUCCESS(rc))
347 rc = rc2;
348
349 return rc;
350}
351
352#if 1
353/**
354 * Writes VM audio output data from the guest stream into the host stream.
355 * The attached host driver backend then will play out the audio in a
356 * later step then.
357 *
358 * @return IPRT status code.
359 * @return int
360 * @param pThis
361 * @param pGstStrmOut
362 * @param pvBuf
363 * @param cbBuf
364 * @param pcbWritten
365 */
366static DECLCALLBACK(int) drvAudioStreamWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
367 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
368{
369 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
370 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
371 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
372 /* pcbWritten is optional. */
373
374 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
375
376 if (!cbBuf)
377 {
378 if (pcbWritten)
379 *pcbWritten = 0;
380 return VINF_SUCCESS;
381 }
382
383 int rc = RTCritSectEnter(&pThis->CritSect);
384 if (RT_FAILURE(rc))
385 return rc;
386
387 if (!pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_OUT))
388 {
389 rc = RTCritSectLeave(&pThis->CritSect);
390 AssertRC(rc);
391
392 return VERR_NOT_AVAILABLE;
393 }
394
395 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
396 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
397
398 AssertMsg(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
399 ("Writing to disabled host output stream \"%s\" not possible\n", pHstStream->szName));
400
401 if (!AudioMixBufFreeBytes(&pHstStream->MixBuf))
402 {
403 if (pcbWritten)
404 *pcbWritten = 0;
405
406 return RTCritSectLeave(&pThis->CritSect);
407 }
408
409 /*
410 * First, write data from the device emulation into our
411 * guest mixing buffer.
412 */
413 uint32_t cWritten;
414 rc = AudioMixBufWriteCirc(&pGstStream->MixBuf, pvBuf, cbBuf, &cWritten);
415 if (rc == VINF_BUFFER_OVERFLOW)
416 LogRelMax(32, ("Audio: Lost audio samples from guest, expect stuttering audio output\n"));
417
418 /* Host stream currently has no samples to play? */
419 if (AudioMixBufAvail(&pHstStream->MixBuf) == 0)
420 {
421 /* Mix just written guest stream samples to the host immediately. */
422 uint32_t cMixed;
423 rc = AudioMixBufMixToParent(&pGstStream->MixBuf, cWritten, &cMixed);
424 LogFlowFunc(("cMixed=%RU32\n", cMixed));
425 }
426
427#ifdef DEBUG_andy
428 AssertRC(rc);
429#endif
430
431#if 0
432 /*
433 * Second, mix the guest mixing buffer with the host mixing
434 * buffer so that the host backend can play the data lateron.
435 */
436 uint32_t cMixed;
437 if ( RT_SUCCESS(rc)
438 && cWritten)
439 {
440 rc = AudioMixBufMixToParent(&pGstStream->MixBuf, cWritten, &cMixed);
441 }
442 else
443 cMixed = 0;
444
445 if (RT_SUCCESS(rc))
446 {
447 /*
448 * Return the number of samples which actually have been mixed
449 * down to the parent, regardless how much samples were written
450 * into the children buffer.
451 */
452 if (pcbWritten)
453 *pcbWritten = AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cMixed);
454 }
455#else
456 if (RT_SUCCESS(rc))
457 {
458 if (pcbWritten)
459 *pcbWritten = AUDIOMIXBUF_S2B(&pGstStream->MixBuf, cWritten);
460 }
461#endif
462
463 LogFlowFunc(("%s -> %s: cbBuf=%RU32, cWritten=%RU32 (%RU32 bytes), rc=%Rrc\n",
464 pGstStream->szName, pHstStream->szName, cbBuf,
465 cWritten, AUDIOMIXBUF_S2B(&pHstStream->MixBuf, cWritten), rc));
466
467 int rc2 = RTCritSectLeave(&pThis->CritSect);
468 if (RT_SUCCESS(rc))
469 rc = rc2;
470
471 return rc;
472}
473#else
474static DECLCALLBACK(int) drvAudioWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut,
475 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
476{
477 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
478 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
479
480 AssertPtrReturn(pGstStrmOut, VERR_INVALID_POINTER);
481 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
482 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
483 /* pcbWritten is optional. */
484
485 int rc = RTCritSectEnter(&pThis->CritSect);
486 if (RT_FAILURE(rc))
487 return rc;
488
489 if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_OUT))
490 {
491 rc = RTCritSectLeave(&pThis->CritSect);
492 AssertRC(rc);
493
494 return VERR_NOT_AVAILABLE;
495 }
496
497 PPDMAUDIOHSTSTRMOUT pHstStrmOut = pGstStrmOut->pHstStrmOut;
498 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
499
500 AssertMsg(pGstStrmOut->pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
501 ("Writing to disabled host output stream \"%s\" not possible\n",
502 pHstStrmOut->MixBuf.pszName));
503
504 if (!AudioMixBufFreeBytes(&pGstStrmOut->MixBuf))
505 {
506 if (pcbWritten)
507 *pcbWritten = 0;
508
509 return RTCritSectLeave(&pThis->CritSect);
510 }
511
512 /*
513 * First, write data from the device emulation into our
514 * guest mixing buffer.
515 */
516 uint32_t cWritten;
517 //rc = AudioMixBufWriteAt(&pGstStrmOut->MixBuf, 0 /* Offset in samples */, pvBuf, cbBuf, &cWritten);
518 rc = AudioMixBufWriteCirc(&pGstStrmOut->MixBuf, pvBuf, cbBuf, &cWritten);
519 if (rc == VINF_BUFFER_OVERFLOW)
520 LogRel(("Audio: Lost audio samples from guest, expect stuttering audio output\n"));
521
522 /*
523 * Second, mix the guest mixing buffer with the host mixing
524 * buffer so that the host backend can play the data lateron.
525 */
526 uint32_t cMixed;
527 if ( RT_SUCCESS(rc)
528 && cWritten)
529 {
530 rc = AudioMixBufMixToParent(&pGstStrmOut->MixBuf, cWritten, &cMixed);
531 }
532 else
533 cMixed = 0;
534
535 if (RT_SUCCESS(rc))
536 {
537 /*
538 * Return the number of samples which actually have been mixed
539 * down to the parent, regardless how much samples were written
540 * into the children buffer.
541 */
542 if (pcbWritten)
543 *pcbWritten = AUDIOMIXBUF_S2B(&pGstStrmOut->MixBuf, cMixed);
544 }
545
546 LogFlowFunc(("%s -> %s: cbBuf=%RU32, cWritten=%RU32 (%RU32 bytes), cMixed=%RU32, rc=%Rrc\n",
547 pGstStrmOut->MixBuf.pszName, pHstStrmOut->MixBuf.pszName, cbBuf, cWritten,
548 AUDIOMIXBUF_S2B(&pGstStrmOut->MixBuf, cWritten), cMixed, rc));
549
550 int rc2 = RTCritSectLeave(&pThis->CritSect);
551 if (RT_SUCCESS(rc))
552 rc = rc2;
553
554 return rc;
555}
556#endif
557
558static DECLCALLBACK(uint32_t) drvAudioStreamAddRef(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
559{
560 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
561 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
562
563 NOREF(pInterface);
564
565 return ++pStream->cRefs;
566}
567
568static DECLCALLBACK(uint32_t) drvAudioStreamRelease(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
569{
570 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
571 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
572
573 NOREF(pInterface);
574
575 Assert(pStream->cRefs);
576 if (pStream->cRefs)
577 pStream->cRefs--;
578
579 return pStream->cRefs;
580}
581
582#if 1
583static DECLCALLBACK(int) drvAudioStreamGetData(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, uint32_t *pcData)
584{
585 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
586 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
587 /* pcData is optional. */
588
589 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
590
591 int rc = RTCritSectEnter(&pThis->CritSect);
592 if (RT_FAILURE(rc))
593 return rc;
594
595 uint32_t cData = 0;
596
597 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
598 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
599
600 do
601 {
602 if (!(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
603 break;
604
605 if (pHstStream->enmDir == PDMAUDIODIR_IN)
606 {
607 /* Call the host backend to capture the audio input data. */
608 rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pHstStream, &cData);
609 if (RT_FAILURE(rc))
610 break;
611
612 //cbData = AUDIOMIXBUF_S2B(&pStream->MixBuf, AudioMixBufMixed(&pStream->MixBuf)));
613
614 }
615 else /* Out */
616 {
617 uint32_t cSamplesMixed = 0;
618 uint32_t cSamplesToMix = 0;
619
620 uint32_t cSamplesLive = AudioMixBufAvail(&pHstStream->MixBuf);
621 if (!cSamplesLive)
622 {
623
624 }
625
626 cSamplesToMix = AudioMixBufAvail(&pGstStream->MixBuf);
627 if (cSamplesToMix)
628 rc = AudioMixBufMixToParent(&pGstStream->MixBuf, cSamplesToMix, &cSamplesMixed);
629
630 /* Has this stream marked as disabled but there still were guest streams relying
631 * on it? CheckpHstStreamif this stream now can be closed and do so, if possible. */
632 if ( (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
633 && !cSamplesToMix)
634 {
635 /* Stop playing the current (pending) stream. */
636 int rc2 = drvAudioStreamControlInternal(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
637 if (RT_SUCCESS(rc2))
638 {
639 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
640
641 LogFunc(("%s: Disabling stream\n", pHstStream->szName));
642 }
643 else
644 LogFunc(("%s: Backend vetoed against closing output stream, rc=%Rrc\n", pHstStream->szName, rc2));
645
646 break;
647 }
648
649 LogFlowFunc(("%s: cSamplesLive=%RU32, cSamplesToMix=%RU32, cSamplesMixed=%RU32\n",
650 pHstStream->szName, cSamplesLive, cSamplesToMix, cSamplesMixed));
651
652 /* Return live samples to play. */
653 cData = cSamplesLive;
654 }
655
656 } while (0);
657
658#ifdef DEBUG_andy
659 LogFlowFunc(("%s: cData=%RU32\n", pHstStream->szName, cData));
660#endif
661
662 if (pcData)
663 *pcData = cData;
664
665 int rc2 = RTCritSectLeave(&pThis->CritSect);
666 if (RT_SUCCESS(rc))
667 rc = rc2;
668
669 if (RT_FAILURE(rc))
670 LogFlowFuncLeaveRC(rc);
671
672 return rc;
673}
674#else
675static DECLCALLBACK(int) drvAudioGetDataIn(PPDMIAUDIOCONNECTOR pInterface, uint32_t *pcbAvailIn)
676{
677 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
678 /* pcbAvailIn is optional. */
679
680 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
681
682 int rc = RTCritSectEnter(&pThis->CritSect);
683 if (RT_FAILURE(rc))
684 return rc;
685
686 uint32_t cbAvailIn = 0;
687
688 PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
689 while ((pHstStrmIn = drvAudioFindAnyHstIn(pThis, pHstStrmIn)))
690 {
691 /* Disabled? Skip it! */
692 if (!(pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
693 continue;
694
695 /* Call the host backend to capture the audio input data. */
696 uint32_t cSamplesCaptured;
697 int rc2 = pThis->pHostDrvAudio->pfnCaptureIn(pThis->pHostDrvAudio, pHstStrmIn,
698 &cSamplesCaptured);
699 if (RT_FAILURE(rc2))
700 continue;
701
702 PPDMAUDIOGSTSTRMIN pGstStrmIn = pHstStrmIn->pGstStrmIn;
703 AssertPtrBreak(pGstStrmIn);
704
705 if (pGstStrmIn->State.fActive)
706 {
707 cbAvailIn = RT_MAX(cbAvailIn, AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf,
708 AudioMixBufMixed(&pHstStrmIn->MixBuf)));
709#ifdef DEBUG_andy
710 LogFlowFunc(("\t[%s] cbAvailIn=%RU32\n", pHstStrmIn->MixBuf.pszName, cbAvailIn));
711#endif
712 }
713 }
714
715 if (RT_SUCCESS(rc))
716 {
717 if (pcbAvailIn)
718 *pcbAvailIn = cbAvailIn;
719 }
720
721 int rc2 = RTCritSectLeave(&pThis->CritSect);
722 if (RT_SUCCESS(rc))
723 rc = rc2;
724
725 if (RT_FAILURE(rc))
726 LogFlowFuncLeaveRC(rc);
727
728 return rc;
729}
730
731static DECLCALLBACK(int) drvAudioGetDataOut(PPDMIAUDIOCONNECTOR pInterface, uint32_t *pcbFreeOut, uint32_t *pcSamplesLive)
732{
733 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
734 /* pcbFreeOut is optional. */
735 /* pcSamplesLive is optional. */
736
737 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
738
739 int rc = RTCritSectEnter(&pThis->CritSect);
740 if (RT_FAILURE(rc))
741 return rc;
742
743 static uint64_t s_tsLast = 0;
744
745
746 uint64_t s_tsDelta = RTTimeNanoTS() - s_tsLast;
747 LogFlowFunc(("delta=%RU64 (%RU64ms) -> ", s_tsDelta, s_tsDelta / 1000 / 1000));
748
749 #if 0
750 PPDMAUDIOHSTSTRMOUT pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, NULL);
751 uint64_t ns = s_tsDelta / pHstStrmOut->Props.uHz;
752
753
754
755 uint32_t cSamplesMin = (cTicksElapsed / pSink->PCMProps.uHz) * pSink->PCMProps.cChannels;
756 #endif
757
758 s_tsLast = RTTimeNanoTS();
759
760 /*
761 * Playback.
762 */
763 uint32_t cSamplesLive = 0;
764 uint32_t cbFreeOut = UINT32_MAX;
765
766 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
767 while ((pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, pHstStrmOut)))
768 {
769 cSamplesLive = AudioMixBufAvail(&pHstStrmOut->MixBuf);
770
771 /* Has this stream marked as disabled but there still were guest streams relying
772 * on it? Check if this stream now can be closed and do so, if possible. */
773 if ( (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
774 && !cSamplesLive)
775 {
776 /* Stop playing the current (pending) stream. */
777 int rc2 = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
778 if (RT_SUCCESS(rc2))
779 {
780 pHstStrmOut->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
781
782 LogFunc(("[%s] Disabling stream\n", pHstStrmOut->MixBuf.pszName));
783 }
784 else
785 LogFunc(("[%s] Backend vetoed against closing output stream, rc=%Rrc\n", pHstStrmOut->MixBuf.pszName, rc2));
786
787 continue;
788 }
789
790 LogFlowFunc(("[%s] cSamplesLive=%RU32\n", pHstStrmOut->MixBuf.pszName, cSamplesLive));
791
792 /*
793 * No live samples to play at the moment?
794 *
795 * Tell the device emulation for each connected guest stream how many
796 * bytes are free so that the device emulation can continue writing data to
797 * these streams.
798 */
799 PPDMAUDIOGSTSTRMOUT pGstStrmOut;
800 uint32_t cbFree2 = UINT32_MAX;
801 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
802 {
803 if (pGstStrmOut->State.fActive)
804 {
805 /* Tell the sound device emulation how many samples are free
806 * so that it can start writing PCM data to us. */
807 cbFree2 = RT_MIN(cbFree2, AUDIOMIXBUF_S2B_RATIO(&pGstStrmOut->MixBuf,
808 AudioMixBufFree(&pGstStrmOut->MixBuf)));
809#ifdef DEBUG_andy
810 LogFlowFunc(("\t[%s] cbFreeOut=%RU32\n", pGstStrmOut->MixBuf.pszName, cbFree2));
811#endif
812 }
813 }
814
815 cbFreeOut = RT_MIN(cbFreeOut, cbFree2);
816 }
817
818 if (RT_SUCCESS(rc))
819 {
820 if (cbFreeOut == UINT32_MAX)
821 cbFreeOut = 0;
822
823 if (pcbFreeOut)
824 *pcbFreeOut = cbFreeOut;
825
826 if (pcSamplesLive)
827 *pcSamplesLive = cSamplesLive;
828 }
829
830 int rc2 = RTCritSectLeave(&pThis->CritSect);
831 if (RT_SUCCESS(rc))
832 rc = rc2;
833
834 if (RT_FAILURE(rc))
835 LogFlowFuncLeaveRC(rc);
836
837 return rc;
838}
839#endif
840
841#if 1
842static DECLCALLBACK(int) drvAudioStreamPlay(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, uint32_t *pcSamplesPlayed)
843{
844 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
845 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
846 /* pcSamplesPlayed is optional. */
847
848 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
849
850 int rc = RTCritSectEnter(&pThis->CritSect);
851 if (RT_FAILURE(rc))
852 return rc;
853
854 /* Backend output (temporarily) disabled / unavailable? */
855 if (pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_OUT) != PDMAUDIOBACKENDSTS_RUNNING)
856 {
857 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
858 AssertRC(rc);
859
860 if ( !pThis->BackendCfg.cSinks
861 || !pThis->BackendCfg.cMaxStreamsOut)
862 {
863 int rc2 = RTCritSectLeave(&pThis->CritSect);
864 AssertRC(rc2);
865
866 return VERR_NOT_AVAILABLE;
867 }
868 }
869
870 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
871
872 uint32_t cSamplesPlayed = 0;
873 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pHstStream, &cSamplesPlayed);
874 if (RT_FAILURE(rc))
875 {
876 int rc3 = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
877 AssertRC(rc3);
878 }
879
880 LogFlowFunc(("[%s] cSamplesPlayed=%RU32, rc=%Rrc\n", pStream->szName, cSamplesPlayed, rc));
881
882 PPDMAUDIOSTREAM pGstStream = pHstStream->pPair;
883 if (pGstStream)
884 {
885 bool fIsEmpty = AudioMixBufIsEmpty(&pGstStream->MixBuf);
886
887
888 }
889
890#if 0
891 if (pStream->pPair)
892 {
893 bool fIsEmpty = AudioMixBufIsEmpty(&pStream->pPair->MixBuf);
894
895 if ( !pPair->State.fActive
896 && fIsEmpty)
897 continue;
898
899 if ()
900 {
901 pGstStrmOut->State.fEmpty = true;
902 fNeedsCleanup |= !pGstStrmOut->State.fActive;
903 }
904 }
905
906 if (fNeedsCleanup)
907 {
908 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
909 {
910 if (!pGstStrmOut->State.fActive)
911 drvAudioDestroyGstOut(pThis, pGstStrmOut);
912 }
913 }
914#endif
915
916 if (RT_SUCCESS(rc))
917 {
918 if (pcSamplesPlayed)
919 *pcSamplesPlayed = cSamplesPlayed;
920 }
921
922 int rc2 = RTCritSectLeave(&pThis->CritSect);
923 if (RT_SUCCESS(rc))
924 rc = rc2;
925
926 if (RT_FAILURE(rc))
927 LogFlowFuncLeaveRC(rc);
928
929 return rc;
930}
931#else
932static DECLCALLBACK(int) drvAudioPlayOut(PPDMIAUDIOCONNECTOR pInterface, uint32_t *pcSamplesPlayed)
933{
934 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
935 /* pcSamplesPlayed is optional. */
936
937 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
938
939 int rc = RTCritSectEnter(&pThis->CritSect);
940 if (RT_FAILURE(rc))
941 return rc;
942
943 /* Backend output (temporarily) disabled / unavailable? */
944 if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_OUT))
945 {
946 rc = pThis->pHostDrvAudio->pfnGetConf(pThis->pHostDrvAudio, &pThis->BackendCfg);
947 AssertRC(rc);
948
949 if ( !pThis->BackendCfg.cSinks
950 || !pThis->BackendCfg.cMaxStreamsOut)
951 {
952 int rc2 = RTCritSectLeave(&pThis->CritSect);
953 AssertRC(rc2);
954
955 return VERR_NOT_AVAILABLE;
956 }
957 }
958
959 /*
960 * Process all enabled host output streams.
961 */
962 uint32_t cSamplesPlayedMax = 0;
963 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
964 while ((pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, pHstStrmOut)))
965 {
966#if 0
967 uint32_t cStreamsLive;
968 uint32_t cSamplesLive = drvAudioHstOutSamplesLive(pHstStrmOut, &cStreamsLive);
969 if (!cStreamsLive)
970 cSamplesLive = 0;
971
972 /* Has this stream marked as disabled but there still were guest streams relying
973 * on it? Check if this stream now can be closed and do so, if possible. */
974 if ( pHstStrmOut->fPendingDisable
975 && !cStreamsLive)
976 {
977 /* Stop playing the current (pending) stream. */
978 int rc2 = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut,
979 PDMAUDIOSTREAMCMD_DISABLE);
980 if (RT_SUCCESS(rc2))
981 {
982 pHstStrmOut->fEnabled = false;
983 pHstStrmOut->fPendingDisable = false;
984
985 LogFunc(("\t%p: Disabling stream\n", pHstStrmOut));
986 }
987 else
988 LogFunc(("\t%p: Backend vetoed against closing output stream, rc=%Rrc\n",
989 pHstStrmOut, rc2));
990
991 continue;
992 }
993#endif
994 uint32_t cSamplesPlayed = 0;
995 int rc2 = pThis->pHostDrvAudio->pfnPlayOut(pThis->pHostDrvAudio, pHstStrmOut, &cSamplesPlayed);
996 if (RT_FAILURE(rc2))
997 {
998 int rc3 = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
999 AssertRC(rc3);
1000 }
1001 else
1002 cSamplesPlayedMax = RT_MAX(cSamplesPlayed, cSamplesPlayedMax);
1003
1004 LogFlowFunc(("\t[%s] cSamplesPlayed=%RU32, cSamplesPlayedMax=%RU32, rc=%Rrc\n",
1005 pHstStrmOut->MixBuf.pszName, cSamplesPlayed, cSamplesPlayedMax, rc2));
1006
1007 bool fNeedsCleanup = false;
1008
1009 PPDMAUDIOGSTSTRMOUT pGstStrmOut;
1010 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
1011 {
1012 if ( !pGstStrmOut->State.fActive
1013 && pGstStrmOut->State.fEmpty)
1014 continue;
1015
1016 if (AudioMixBufIsEmpty(&pGstStrmOut->MixBuf))
1017 {
1018 pGstStrmOut->State.fEmpty = true;
1019 fNeedsCleanup |= !pGstStrmOut->State.fActive;
1020 }
1021 }
1022
1023 if (fNeedsCleanup)
1024 {
1025 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
1026 {
1027 if (!pGstStrmOut->State.fActive)
1028 drvAudioDestroyGstOut(pThis, pGstStrmOut);
1029 }
1030 }
1031 }
1032
1033 if (RT_SUCCESS(rc))
1034 {
1035 if (pcSamplesPlayed)
1036 *pcSamplesPlayed = cSamplesPlayedMax;
1037 }
1038
1039 int rc2 = RTCritSectLeave(&pThis->CritSect);
1040 if (RT_SUCCESS(rc))
1041 rc = rc2;
1042
1043 if (RT_FAILURE(rc))
1044 LogFlowFuncLeaveRC(rc);
1045
1046 return rc;
1047}
1048#endif
1049
1050#ifdef VBOX_WITH_AUDIO_CALLBACKS
1051static PPDMAUDIOCALLBACK drvAudioCallbackDuplicate(PPDMAUDIOCALLBACK pCB)
1052{
1053 PPDMAUDIOCALLBACK pCBCopy = (PPDMAUDIOCALLBACK)RTMemDup((void *)pCB, sizeof(PDMAUDIOCALLBACK));
1054 if (!pCBCopy)
1055 return NULL;
1056
1057 if (pCB->pvCtx)
1058 {
1059 pCBCopy->pvCtx = RTMemDup(pCB->pvCtx, pCB->cbCtx);
1060 if (!pCBCopy->pvCtx)
1061 {
1062 RTMemFree(pCBCopy);
1063 return NULL;
1064 }
1065
1066 pCBCopy->cbCtx = pCB->cbCtx;
1067 }
1068
1069 return pCBCopy;
1070}
1071
1072static void drvAudioCallbackDestroy(PPDMAUDIOCALLBACK pCB)
1073{
1074 if (!pCB)
1075 return;
1076
1077 RTListNodeRemove(&pCB->Node);
1078 if (pCB->pvCtx)
1079 {
1080 Assert(pCB->cbCtx);
1081 RTMemFree(pCB->pvCtx);
1082 }
1083 RTMemFree(pCB);
1084}
1085
1086static DECLCALLBACK(int) drvAudioRegisterCallbacks(PPDMIAUDIOCONNECTOR pInterface,
1087 PPDMAUDIOCALLBACK paCallbacks, size_t cCallbacks)
1088{
1089 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1090 AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER);
1091 AssertReturn(cCallbacks, VERR_INVALID_PARAMETER);
1092
1093 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1094
1095 int rc = RTCritSectEnter(&pThis->CritSect);
1096 if (RT_FAILURE(rc))
1097 return rc;
1098
1099 for (size_t i = 0; i < cCallbacks; i++)
1100 {
1101 PPDMAUDIOCALLBACK pCB = drvAudioCallbackDuplicate(&paCallbacks[i]);
1102 if (!pCB)
1103 {
1104 rc = VERR_NO_MEMORY;
1105 break;
1106 }
1107
1108 switch (pCB->enmType)
1109 {
1110 case PDMAUDIOCALLBACKTYPE_INPUT:
1111 RTListAppend(&pThis->lstCBIn, &pCB->Node);
1112 break;
1113
1114 case PDMAUDIOCALLBACKTYPE_OUTPUT:
1115 RTListAppend(&pThis->lstCBOut, &pCB->Node);
1116 break;
1117
1118 default:
1119 AssertMsgFailed(("Not supported\n"));
1120 break;
1121 }
1122 }
1123
1124 /** @todo Undo allocations on error. */
1125
1126 int rc2 = RTCritSectLeave(&pThis->CritSect);
1127 if (RT_SUCCESS(rc))
1128 rc = rc2;
1129
1130 return rc;
1131}
1132
1133static DECLCALLBACK(int) drvAudioCallback(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIOCALLBACKTYPE enmType,
1134 void *pvUser, size_t cbUser)
1135{
1136 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1137 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
1138 AssertReturn(cbUser, VERR_INVALID_PARAMETER);
1139
1140 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1141 PRTLISTANCHOR pListAnchor = NULL;
1142
1143 switch (enmType)
1144 {
1145 case PDMAUDIOCALLBACKTYPE_INPUT:
1146 pListAnchor = &pThis->lstCBIn;
1147 break;
1148
1149 case PDMAUDIOCALLBACKTYPE_OUTPUT:
1150 pListAnchor = &pThis->lstCBOut;
1151 break;
1152
1153 default:
1154 AssertMsgFailed(("Not supported\n"));
1155 break;
1156 }
1157
1158 if (pListAnchor)
1159 {
1160 PPDMAUDIOCALLBACK pCB;
1161 RTListForEach(pListAnchor, pCB, PDMAUDIOCALLBACK, Node)
1162 {
1163 Assert(pCB->enmType == enmType);
1164 pCB->pfnCallback(enmType, pCB->pvCtx, pCB->cbCtx, pvUser, cbUser);
1165 }
1166 }
1167
1168 return VINF_SUCCESS;
1169}
1170#endif
1171
1172static int drvAudioHostInit(PCFGMNODE pCfgHandle, PDRVAUDIO pThis)
1173{
1174 /* pCfgHandle is optional. */
1175 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1176
1177 NOREF(pCfgHandle);
1178
1179 LogFlowFuncEnter();
1180
1181 AssertPtr(pThis->pHostDrvAudio);
1182 int rc = pThis->pHostDrvAudio->pfnInit(pThis->pHostDrvAudio);
1183 if (RT_FAILURE(rc))
1184 {
1185 LogFlowFunc(("Initialization of lower driver failed with rc=%Rrc\n", rc));
1186 return rc;
1187 }
1188
1189 /* Get the configuration data from backend. */
1190 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
1191 if (RT_FAILURE(rc))
1192 {
1193 LogFlowFunc(("Getting backend configuration failed with rc=%Rrc\n", rc));
1194 return rc;
1195 }
1196
1197 pThis->cStreamsFreeIn = 0;
1198 pThis->cStreamsFreeOut = 0;
1199
1200 if (pThis->BackendCfg.cSinks)
1201 {
1202 Assert(pThis->BackendCfg.cbStreamOut);
1203
1204 pThis->cStreamsFreeOut = pThis->BackendCfg.cMaxStreamsOut;
1205 }
1206
1207 if (pThis->BackendCfg.cSources)
1208 {
1209 Assert(pThis->BackendCfg.cbStreamIn);
1210
1211 pThis->cStreamsFreeIn = pThis->BackendCfg.cMaxStreamsIn;
1212 }
1213
1214 LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->cStreamsFreeIn, pThis->cStreamsFreeOut));
1215
1216 LogRel2(("Audio: Host audio backend supports %RU32 input streams and %RU32 output streams at once\n",
1217 /* Clamp for logging. Unlimited streams are defined by UINT32_MAX. */
1218 RT_MIN(64, pThis->cStreamsFreeIn), RT_MIN(64, pThis->cStreamsFreeOut)));
1219
1220 LogFlowFuncLeave();
1221 return VINF_SUCCESS;
1222}
1223
1224static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
1225{
1226 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1227 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1228
1229 LogFlowFunc(("enmCmd=%ld\n", enmCmd));
1230
1231 if (!pThis->pHostDrvAudio)
1232 return;
1233
1234 PPDMAUDIOSTREAM pStream;
1235 RTListForEach(&pThis->lstStreams, pStream, PDMAUDIOSTREAM, Node)
1236 drvAudioStreamControlInternal(pThis, pStream, enmCmd);
1237}
1238
1239static DECLCALLBACK(int) drvAudioInit(PCFGMNODE pCfgHandle, PPDMDRVINS pDrvIns)
1240{
1241 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
1242 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
1243
1244 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1245 LogFlowFunc(("pThis=%p, pDrvIns=%p\n", pThis, pDrvIns));
1246
1247 RTListInit(&pThis->lstStreams);
1248#ifdef VBOX_WITH_AUDIO_CALLBACKS
1249 RTListInit(&pThis->lstCBIn);
1250 RTListInit(&pThis->lstCBOut);
1251#endif
1252
1253 int rc = RTCritSectInit(&pThis->CritSect);
1254
1255 /** @todo Add audio driver options. */
1256
1257 /*
1258 * If everything went well, initialize the lower driver.
1259 */
1260 if (RT_SUCCESS(rc))
1261 rc = drvAudioHostInit(pCfgHandle, pThis);
1262
1263 LogFlowFuncLeaveRC(rc);
1264 return rc;
1265}
1266
1267#if 1
1268static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
1269 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1270{
1271 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1272 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1273
1274 if (!pStream)
1275 return VERR_NOT_AVAILABLE;
1276
1277 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1278 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1279 /* pcbWritten is optional. */
1280
1281 int rc = RTCritSectEnter(&pThis->CritSect);
1282 if (RT_FAILURE(rc))
1283 return rc;
1284
1285 if (pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_IN) != PDMAUDIOBACKENDSTS_RUNNING)
1286 {
1287 if (pcbRead)
1288 *pcbRead = 0;
1289
1290 return RTCritSectLeave(&pThis->CritSect);
1291 }
1292
1293 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1294
1295 AssertMsg(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
1296 ("Reading from disabled host input stream '%s' not possible\n", pHstStream->szName));
1297
1298 /*
1299 * Read from the parent buffer (that is, the guest buffer) which
1300 * should have the audio data in the format the guest needs.
1301 */
1302 uint32_t cRead;
1303 rc = AudioMixBufReadCirc(&pStream->MixBuf, pvBuf, cbBuf, &cRead);
1304 if (RT_SUCCESS(rc))
1305 {
1306 AudioMixBufFinish(&pStream->MixBuf, cRead);
1307
1308 if (pcbRead)
1309 *pcbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, cRead);
1310 }
1311
1312 LogFlowFunc(("cRead=%RU32 (%RU32 bytes), rc=%Rrc\n",
1313 cRead, AUDIOMIXBUF_S2B(&pStream->MixBuf, cRead), rc));
1314
1315 int rc2 = RTCritSectLeave(&pThis->CritSect);
1316 if (RT_SUCCESS(rc))
1317 rc = rc2;
1318
1319 return rc;
1320}
1321#else
1322static DECLCALLBACK(int) drvAudioRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn,
1323 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1324{
1325 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1326 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1327
1328 if (!pGstStrmIn)
1329 return VERR_NOT_AVAILABLE;
1330
1331 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1332 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1333 /* pcbWritten is optional. */
1334
1335 int rc = RTCritSectEnter(&pThis->CritSect);
1336 if (RT_FAILURE(rc))
1337 return rc;
1338
1339 if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_IN))
1340 {
1341 if (pcbRead)
1342 *pcbRead = 0;
1343
1344 return RTCritSectLeave(&pThis->CritSect);
1345 }
1346
1347 PPDMAUDIOHSTSTRMIN pHstStrmIn = pGstStrmIn->pHstStrmIn;
1348 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1349
1350 AssertMsg(pGstStrmIn->pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
1351 ("Reading from disabled host input stream \"%s\" not possible\n", pGstStrmIn->MixBuf.pszName));
1352
1353 /*
1354 * Read from the parent buffer (that is, the guest buffer) which
1355 * should have the audio data in the format the guest needs.
1356 */
1357 uint32_t cRead;
1358 rc = AudioMixBufReadCirc(&pGstStrmIn->MixBuf, pvBuf, cbBuf, &cRead);
1359 if (RT_SUCCESS(rc))
1360 {
1361 AudioMixBufFinish(&pGstStrmIn->MixBuf, cRead);
1362
1363 if (pcbRead)
1364 *pcbRead = AUDIOMIXBUF_S2B(&pGstStrmIn->MixBuf, cRead);
1365 }
1366
1367 LogFlowFunc(("cRead=%RU32 (%RU32 bytes), rc=%Rrc\n",
1368 cRead, AUDIOMIXBUF_S2B(&pGstStrmIn->MixBuf, cRead), rc));
1369
1370 int rc2 = RTCritSectLeave(&pThis->CritSect);
1371 if (RT_SUCCESS(rc))
1372 rc = rc2;
1373
1374 return rc;
1375}
1376#endif
1377
1378#if 0
1379static DECLCALLBACK(int) drvAudioEnableOut(PPDMIAUDIOCONNECTOR pInterface,
1380 PPDMAUDIOGSTSTRMOUT pGstStrmOut, bool fEnable)
1381{
1382 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1383
1384 if (!pGstStrmOut)
1385 return VERR_NOT_AVAILABLE;
1386
1387 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1388
1389 int rc = VINF_SUCCESS;
1390
1391 PPDMAUDIOHSTSTRMOUT pHstStrmOut = pGstStrmOut->pHstStrmOut;
1392 AssertPtr(pHstStrmOut);
1393
1394 if (fEnable)
1395 {
1396 /* Is a pending disable outstanding? Then disable first. */
1397 if (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
1398 {
1399 rc = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
1400 if (RT_SUCCESS(rc))
1401 pHstStrmOut->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
1402 }
1403
1404 if (RT_SUCCESS(rc))
1405 rc = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_ENABLE);
1406
1407 if (RT_SUCCESS(rc))
1408 pGstStrmOut->State.fActive = fEnable;
1409 }
1410 else /* Disable */
1411 {
1412 if (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
1413 {
1414 size_t cGstStrmsActive = 0;
1415
1416 /*
1417 * Check if there are any active guest streams assigned
1418 * to this host stream which still are being marked as active.
1419 *
1420 * In that case we have to defer closing the host stream and
1421 * wait until all guest streams have been finished.
1422 */
1423 PPDMAUDIOGSTSTRMOUT pIter;
1424 RTListForEach(&pHstStrmOut->lstGstStrmOut, pIter, PDMAUDIOGSTSTRMOUT, Node)
1425 {
1426 if (pIter->State.fActive)
1427 {
1428 cGstStrmsActive++;
1429 break; /* At least one assigned & active guest stream is enough. */
1430 }
1431 }
1432
1433 /* Do we need to defer closing the host stream? */
1434 if (cGstStrmsActive)
1435 {
1436 LogFlowFunc(("Closing stream deferred: %zu guest stream(s) active\n", cGstStrmsActive));
1437 pHstStrmOut->fStatus |= PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
1438
1439 rc = VERR_AUDIO_STREAM_PENDING_DISABLE;
1440 }
1441 else
1442 {
1443 rc = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
1444 if (RT_SUCCESS(rc))
1445 pGstStrmOut->State.fActive = fEnable;
1446 }
1447 }
1448 }
1449
1450 LogFlowFunc(("%s: fEnable=%RTbool, fStatus=0x%x, rc=%Rrc\n",
1451 pGstStrmOut->MixBuf.pszName, fEnable, pHstStrmOut->fStatus, rc));
1452
1453 return rc;
1454}
1455#endif
1456
1457static DECLCALLBACK(int) drvAudioStreamCreate(PPDMIAUDIOCONNECTOR pInterface,
1458 PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest,
1459 PPDMAUDIOSTREAM *ppStream)
1460{
1461 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1462 AssertPtrReturn(pCfgHost, VERR_INVALID_POINTER);
1463 AssertPtrReturn(pCfgGuest, VERR_INVALID_POINTER);
1464 AssertPtrReturn(ppStream, VERR_INVALID_POINTER);
1465
1466 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1467
1468 int rc = RTCritSectEnter(&pThis->CritSect);
1469 if (RT_FAILURE(rc))
1470 return rc;
1471
1472 LogFlowFunc(("Host=%s, Guest=%s\n", pCfgHost->szName, pCfgGuest->szName));
1473#ifdef DEBUG
1474 DrvAudioHlpStreamCfgPrint(pCfgHost);
1475 DrvAudioHlpStreamCfgPrint(pCfgGuest);
1476#endif
1477
1478 /*
1479 * The guest stream always will get the audio stream configuration told
1480 * by the device emulation (which in turn was/could be set by the guest OS).
1481 */
1482 PPDMAUDIOSTREAM pGstStrm = NULL;
1483
1484 /** @todo Docs! */
1485 PPDMAUDIOSTREAM pHstStrm = NULL;
1486
1487#define RC_BREAK(x) { rc = x; break; }
1488
1489 do
1490 {
1491 if ( !DrvAudioHlpStreamCfgIsValid(pCfgHost)
1492 || !DrvAudioHlpStreamCfgIsValid(pCfgGuest))
1493 {
1494 RC_BREAK(VERR_INVALID_PARAMETER);
1495 }
1496
1497 /* Make sure that both configurations actually intend the same thing. */
1498 if (pCfgHost->enmDir != pCfgGuest->enmDir)
1499 {
1500 AssertMsgFailed(("Stream configuration directions do not match\n"));
1501 RC_BREAK(VERR_INVALID_PARAMETER);
1502 }
1503
1504 /* Note: cbHstStrm will contain sizeof(PDMAUDIOSTREAM) + additional data
1505 * which the host backend will need. */
1506 size_t cbHstStrm;
1507 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
1508 {
1509 if (!pThis->cStreamsFreeIn)
1510 {
1511 LogFlowFunc(("No more input streams free to use, bailing out\n"));
1512 RC_BREAK(VERR_AUDIO_NO_FREE_INPUT_STREAMS);
1513 }
1514
1515 /* Validate backend configuration. */
1516 if (!pThis->BackendCfg.cbStreamIn)
1517 {
1518 LogFlowFunc(("Backend input configuration not valid, bailing out\n"));
1519 RC_BREAK(VERR_INVALID_PARAMETER);
1520 }
1521
1522 cbHstStrm = pThis->BackendCfg.cbStreamIn;
1523 }
1524 else /* Out */
1525 {
1526 if (!pThis->cStreamsFreeOut)
1527 {
1528 LogFlowFunc(("Maximum number of host output streams reached\n"));
1529 RC_BREAK(VERR_NO_MORE_HANDLES); /** @todo Fudge! */
1530 }
1531
1532 /* Validate backend configuration. */
1533 if (!pThis->BackendCfg.cbStreamOut)
1534 {
1535 LogFlowFunc(("Backend output configuration invalid, bailing out\n"));
1536 RC_BREAK(VERR_INVALID_PARAMETER);
1537 }
1538
1539 cbHstStrm = pThis->BackendCfg.cbStreamOut;
1540 }
1541
1542 pHstStrm = (PPDMAUDIOSTREAM)RTMemAllocZ(cbHstStrm);
1543 AssertPtrBreakStmt(pHstStrm, rc = VERR_NO_MEMORY);
1544
1545 RTListAppend(&pThis->lstStreams, &pHstStrm->Node);
1546
1547 pGstStrm = (PPDMAUDIOSTREAM)RTMemAllocZ(sizeof(PDMAUDIOSTREAM));
1548 AssertPtrBreakStmt(pGstStrm, rc = VERR_NO_MEMORY);
1549
1550 RTListAppend(&pThis->lstStreams, &pGstStrm->Node);
1551
1552 /*
1553 * Create host stream.
1554 */
1555
1556 RTStrPrintf(pHstStrm->szName, RT_ELEMENTS(pHstStrm->szName), "%s (Host)",
1557 strlen(pCfgHost->szName) ? pCfgHost->szName : "<Untitled>");
1558
1559 /* Note: Direction is always from child -> parent. */
1560 uint32_t cSamples = 0;
1561 rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pHstStrm, pCfgHost, &cSamples);
1562 if (RT_FAILURE(rc))
1563 {
1564 LogFlowFunc(("Initializing host backend failed with rc=%Rrc\n", rc));
1565 break;
1566 }
1567
1568 pHstStrm->enmCtx = PDMAUDIOSTREAMCTX_HOST;
1569 pHstStrm->enmDir = pCfgHost->enmDir;
1570 pHstStrm->fStatus |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
1571 pHstStrm->pPair = pGstStrm;
1572
1573 rc = DrvAudioHlpStreamCfgToProps(pCfgHost, &pHstStrm->Props);
1574 AssertRCBreak(rc);
1575
1576 rc = AudioMixBufInit(&pHstStrm->MixBuf, pHstStrm->szName, &pHstStrm->Props, cSamples);
1577 AssertRCBreak(rc);
1578
1579 /*
1580 * Create guest stream.
1581 */
1582
1583 RTStrPrintf(pGstStrm->szName, RT_ELEMENTS(pGstStrm->szName), "%s (Guest)",
1584 strlen(pCfgGuest->szName) ? pCfgGuest->szName : "<Untitled>");
1585
1586 rc = DrvAudioHlpStreamCfgToProps(pCfgGuest, &pGstStrm->Props);
1587 AssertRCBreak(rc);
1588
1589 rc = AudioMixBufInit(&pGstStrm->MixBuf, pGstStrm->szName, &pGstStrm->Props, AudioMixBufSize(&pHstStrm->MixBuf));
1590 if (RT_SUCCESS(rc))
1591 {
1592 if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
1593 {
1594 rc = AudioMixBufLinkTo(&pHstStrm->MixBuf, &pGstStrm->MixBuf);
1595 }
1596 else
1597 {
1598 rc = AudioMixBufLinkTo(&pGstStrm->MixBuf, &pHstStrm->MixBuf);
1599 }
1600 }
1601
1602 pGstStrm->enmCtx = PDMAUDIOSTREAMCTX_GUEST;
1603 pGstStrm->enmDir = pCfgGuest->enmDir;
1604 pGstStrm->fStatus |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
1605 pGstStrm->pPair = pHstStrm;
1606
1607 AssertRCBreak(rc);
1608
1609 } while (0);
1610
1611#undef RC_BREAK
1612
1613 if (RT_FAILURE(rc))
1614 {
1615 if ( pHstStrm
1616 && pHstStrm->enmCtx == PDMAUDIOSTREAMCTX_HOST)
1617 {
1618 if (pHstStrm->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
1619 {
1620 rc = drvAudioStreamControlInternal(pThis, pHstStrm, PDMAUDIOSTREAMCMD_DISABLE);
1621 if (RT_SUCCESS(rc))
1622 {
1623 rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pHstStrm);
1624 if (RT_SUCCESS(rc))
1625 pHstStrm->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
1626 }
1627 }
1628
1629 AssertMsgRC(rc, ("Host stream '%s' failed to uninit in backend: %Rrc\n", pHstStrm->szName, rc));
1630 }
1631
1632 drvAudioStreamDestroyInternal(pThis, pGstStrm);
1633 pGstStrm = NULL;
1634
1635 drvAudioStreamDestroyInternal(pThis, pHstStrm);
1636 pHstStrm = NULL;
1637 }
1638 else
1639 {
1640 /* Set initial reference counts. */
1641 pGstStrm->cRefs = 1;
1642 pHstStrm->cRefs = 1;
1643
1644 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
1645 {
1646 Assert(pThis->cStreamsFreeIn);
1647 pThis->cStreamsFreeIn--;
1648 }
1649 else /* Out */
1650 {
1651 Assert(pThis->cStreamsFreeOut);
1652 pThis->cStreamsFreeOut--;
1653 }
1654
1655 *ppStream = pGstStrm;
1656 }
1657
1658 int rc2 = RTCritSectLeave(&pThis->CritSect);
1659 if (RT_SUCCESS(rc))
1660 rc = rc2;
1661
1662 LogFlowFuncLeaveRC(rc);
1663 return rc;
1664}
1665
1666#if 1
1667static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
1668{
1669 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1670 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1671
1672 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1673
1674 int rc = RTCritSectEnter(&pThis->CritSect);
1675 if (RT_FAILURE(rc))
1676 return rc;
1677
1678 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, pCfg);
1679
1680 int rc2 = RTCritSectLeave(&pThis->CritSect);
1681 if (RT_SUCCESS(rc))
1682 rc = rc2;
1683
1684 LogFlowFuncLeaveRC(rc);
1685 return rc;
1686}
1687
1688static DECLCALLBACK(PDMAUDIOSTRMSTS) drvAudioStreamGetStatus(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1689{
1690 AssertPtrReturn(pInterface, false);
1691 /* pStream is optional. */
1692
1693 if (!pStream)
1694 return PDMAUDIOSTRMSTS_FLAG_NONE;
1695
1696 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1697
1698 int rc2 = RTCritSectEnter(&pThis->CritSect);
1699 AssertRC(rc2);
1700
1701 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1702 PDMAUDIOSTRMSTS strmSts = pHstStream->fStatus;
1703
1704 LogFlowFunc(("%s: strmSts=0x%x\n", pHstStream->szName, strmSts));
1705 rc2 = RTCritSectLeave(&pThis->CritSect);
1706 AssertRC(rc2);
1707
1708 return strmSts;
1709}
1710#endif
1711
1712static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
1713{
1714 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1715
1716 int rc = RTCritSectEnter(&pThis->CritSect);
1717 AssertRC(rc);
1718
1719 PDMAUDIODIR enmDir = pStream->enmDir;
1720
1721 LogFlowFunc(("%s: cRefs=%RU32\n", pStream->szName, pStream->cRefs));
1722 if (pStream->cRefs > 1)
1723 rc = VERR_WRONG_ORDER;
1724
1725 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream);
1726 if ( RT_SUCCESS(rc)
1727 && pHstStream->cRefs == 1)
1728 {
1729 rc = drvAudioStreamDestroyInternalBackend(pThis, pHstStream);
1730 if (RT_SUCCESS(rc))
1731 {
1732 if (pStream->pPair)
1733 {
1734 rc = drvAudioStreamDestroyInternal(pThis, pStream->pPair);
1735 pStream->pPair = NULL;
1736 }
1737
1738 if (RT_SUCCESS(rc))
1739 rc = drvAudioStreamDestroyInternal(pThis, pStream);
1740
1741 if (RT_SUCCESS(rc))
1742 {
1743 if (enmDir == PDMAUDIODIR_IN)
1744 {
1745 pThis->cStreamsFreeIn++;
1746 }
1747 else /* Out */
1748 {
1749 pThis->cStreamsFreeOut++;
1750 }
1751 }
1752 }
1753 }
1754 else
1755 rc = VERR_WRONG_ORDER;
1756
1757 int rc2 = RTCritSectLeave(&pThis->CritSect);
1758 if (RT_SUCCESS(rc))
1759 rc = rc2;
1760
1761 if (RT_FAILURE(rc))
1762 LogFlowFunc(("Failed with %Rrc\n", rc));
1763
1764 return rc;
1765}
1766
1767static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream)
1768{
1769 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1770 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER);
1771
1772 int rc = VINF_SUCCESS;
1773
1774 LogFlowFunc(("%s: fStatus=0x%x\n", pHstStream->szName, pHstStream->fStatus));
1775
1776 AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST,
1777 ("Stream '%s' is not a host stream and therefore has no backend\n", pHstStream->szName));
1778
1779 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED)
1780 {
1781 rc = drvAudioStreamControlInternal(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE);
1782 if (RT_SUCCESS(rc))
1783 {
1784 rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pHstStream);
1785 if (RT_SUCCESS(rc))
1786 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
1787 }
1788
1789 AssertMsgRC(rc, ("Host stream '%s' failed to uninit in backend: %Rrc\n", pHstStream->szName, rc));
1790 }
1791 else
1792 rc = VINF_SUCCESS;
1793
1794 LogFlowFunc(("%s: Returning %Rrc\n", pHstStream->szName, rc));
1795 return rc;
1796}
1797
1798static int drvAudioStreamDestroyInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream)
1799{
1800 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1801 /* pStream is optional. */
1802
1803 if (!pStream)
1804 return VINF_SUCCESS;
1805
1806 LogFlowFunc(("%s: cRefs=%RU32\n", pStream->szName, pStream->cRefs));
1807
1808 int rc = VINF_SUCCESS;
1809
1810 if (pStream->cRefs <= 1)
1811 {
1812 /* Unlink from pair. */
1813 if (pStream->pPair)
1814 {
1815 pStream->pPair->pPair = NULL;
1816 pStream->pPair = NULL;
1817 }
1818
1819 /* Destroy mixing buffer. */
1820 AudioMixBufDestroy(&pStream->MixBuf);
1821
1822 /* Remove from list. */
1823 RTListNodeRemove(&pStream->Node);
1824
1825 RTMemFree(pStream);
1826 pStream = NULL;
1827 }
1828 else /* More than our own reference left? Bail out. */
1829 rc = VERR_WRONG_ORDER;
1830
1831 if (RT_FAILURE(rc))
1832 LogFunc(("Failed with %Rrc\n", rc));
1833
1834 return rc;
1835}
1836
1837/********************************************************************/
1838
1839/**
1840 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1841 */
1842static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1843{
1844 LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
1845
1846 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1847 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1848
1849 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1850 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
1851
1852 return NULL;
1853}
1854
1855/**
1856 * Power Off notification.
1857 *
1858 * @param pDrvIns The driver instance data.
1859 */
1860static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
1861{
1862 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1863
1864 LogFlowFuncEnter();
1865
1866 /*
1867 * Second, destroy all audio streams.
1868 */
1869 PPDMAUDIOSTREAM pStream;
1870 RTListForEach(&pThis->lstStreams, pStream, PDMAUDIOSTREAM, Node)
1871 {
1872 if (pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST)
1873 drvAudioStreamDestroyInternalBackend(pThis, pStream);
1874 }
1875
1876 /*
1877 * Last call for the driver below us.
1878 * Let it know that we reached end of life.
1879 */
1880 if (pThis->pHostDrvAudio->pfnShutdown)
1881 pThis->pHostDrvAudio->pfnShutdown(pThis->pHostDrvAudio);
1882
1883 pThis->pHostDrvAudio = NULL;
1884
1885 LogFlowFuncLeave();
1886}
1887
1888/**
1889 * Constructs an audio driver instance.
1890 *
1891 * @copydoc FNPDMDRVCONSTRUCT
1892 */
1893static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
1894{
1895 LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfgHandle, fFlags));
1896
1897 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1898 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1899
1900 /*
1901 * Init the static parts.
1902 */
1903 pThis->pDrvIns = pDrvIns;
1904 /* IBase. */
1905 pDrvIns->IBase.pfnQueryInterface = drvAudioQueryInterface;
1906 /* IAudioConnector. */
1907 pThis->IAudioConnector.pfnGetConfig = drvAudioGetConfig;
1908 pThis->IAudioConnector.pfnStreamCreate = drvAudioStreamCreate;
1909 pThis->IAudioConnector.pfnStreamDestroy = drvAudioStreamDestroy;
1910 pThis->IAudioConnector.pfnStreamAddRef = drvAudioStreamAddRef;
1911 pThis->IAudioConnector.pfnStreamRelease = drvAudioStreamRelease;
1912 pThis->IAudioConnector.pfnStreamControl = drvAudioStreamControl;
1913 pThis->IAudioConnector.pfnStreamRead = drvAudioStreamRead;
1914 pThis->IAudioConnector.pfnStreamWrite = drvAudioStreamWrite;
1915 pThis->IAudioConnector.pfnStreamGetData = drvAudioStreamGetData;
1916 pThis->IAudioConnector.pfnStreamGetStatus = drvAudioStreamGetStatus;
1917 pThis->IAudioConnector.pfnStreamPlay = drvAudioStreamPlay;
1918#ifdef VBOX_WITH_AUDIO_CALLBACKS
1919 pThis->IAudioConnector.pfnRegisterCallbacks = drvAudioRegisterCallbacks;
1920 pThis->IAudioConnector.pfnCallback = drvAudioCallback;
1921#endif
1922
1923 /*
1924 * Attach driver below and query its connector interface.
1925 */
1926 PPDMIBASE pDownBase;
1927 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
1928 if (RT_FAILURE(rc))
1929 {
1930 LogRel(("Audio: Failed to attach to driver %p below (flags=0x%x), rc=%Rrc\n",
1931 pDrvIns, fFlags, rc));
1932 return rc;
1933 }
1934
1935 pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
1936 if (!pThis->pHostDrvAudio)
1937 {
1938 LogRel(("Audio: Failed to query interface for underlying host driver\n"));
1939 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
1940 N_("Host audio backend missing or invalid"));
1941 }
1942
1943 rc = drvAudioInit(pCfgHandle, pDrvIns);
1944 if (RT_SUCCESS(rc))
1945 {
1946 pThis->fTerminate = false;
1947 pThis->pDrvIns = pDrvIns;
1948 }
1949
1950 LogFlowFuncLeaveRC(rc);
1951 return rc;
1952}
1953
1954/**
1955 * Destructs an audio driver instance.
1956 *
1957 * @copydoc FNPDMDRVDESTRUCT
1958 */
1959static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
1960{
1961 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1962 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1963
1964 LogFlowFuncEnter();
1965
1966 int rc2 = RTCritSectEnter(&pThis->CritSect);
1967 AssertRC(rc2);
1968
1969 /*
1970 * Note: No calls here to the driver below us anymore,
1971 * as PDM already has destroyed it.
1972 * If you need to call something from the host driver,
1973 * do this in drvAudioPowerOff() instead.
1974 */
1975 PPDMAUDIOSTREAM pStream, pStreamNext;
1976 RTListForEachSafe(&pThis->lstStreams, pStream, pStreamNext, PDMAUDIOSTREAM, Node)
1977 drvAudioStreamDestroyInternal(pThis, pStream);
1978
1979 /* Sanity. */
1980 Assert(RTListIsEmpty(&pThis->lstStreams));
1981
1982#ifdef VBOX_WITH_AUDIO_CALLBACKS
1983 /*
1984 * Destroy callbacks, if any.
1985 */
1986 PPDMAUDIOCALLBACK pCB, pCBNext;
1987 RTListForEachSafe(&pThis->lstCBIn, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
1988 drvAudioCallbackDestroy(pCB);
1989
1990 RTListForEachSafe(&pThis->lstCBOut, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
1991 drvAudioCallbackDestroy(pCB);
1992#endif
1993
1994 rc2 = RTCritSectLeave(&pThis->CritSect);
1995 AssertRC(rc2);
1996
1997 rc2 = RTCritSectDelete(&pThis->CritSect);
1998 AssertRC(rc2);
1999
2000 LogFlowFuncLeave();
2001}
2002
2003/**
2004 * Suspend notification.
2005 *
2006 * @param pDrvIns The driver instance data.
2007 */
2008static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
2009{
2010 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_PAUSE);
2011}
2012
2013/**
2014 * Resume notification.
2015 *
2016 * @param pDrvIns The driver instance data.
2017 */
2018static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
2019{
2020 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_RESUME);
2021}
2022
2023/**
2024 * Audio driver registration record.
2025 */
2026const PDMDRVREG g_DrvAUDIO =
2027{
2028 /* u32Version */
2029 PDM_DRVREG_VERSION,
2030 /* szName */
2031 "AUDIO",
2032 /* szRCMod */
2033 "",
2034 /* szR0Mod */
2035 "",
2036 /* pszDescription */
2037 "Audio connector driver",
2038 /* fFlags */
2039 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2040 /* fClass */
2041 PDM_DRVREG_CLASS_AUDIO,
2042 /* cMaxInstances */
2043 2,
2044 /* cbInstance */
2045 sizeof(DRVAUDIO),
2046 /* pfnConstruct */
2047 drvAudioConstruct,
2048 /* pfnDestruct */
2049 drvAudioDestruct,
2050 /* pfnRelocate */
2051 NULL,
2052 /* pfnIOCtl */
2053 NULL,
2054 /* pfnPowerOn */
2055 NULL,
2056 /* pfnReset */
2057 NULL,
2058 /* pfnSuspend */
2059 drvAudioSuspend,
2060 /* pfnResume */
2061 drvAudioResume,
2062 /* pfnAttach */
2063 NULL,
2064 /* pfnDetach */
2065 NULL,
2066 /* pfnPowerOff */
2067 drvAudioPowerOff,
2068 /* pfnSoftReset */
2069 NULL,
2070 /* u32EndVersion */
2071 PDM_DRVREG_VERSION
2072};
2073
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette