VirtualBox

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

最後變更 在這個檔案從61706是 61668,由 vboxsync 提交於 9 年 前

Audio: Update.

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

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