VirtualBox

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

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

D'oh.

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