VirtualBox

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

Last change on this file since 60404 was 60353, checked in by vboxsync, 8 years ago

Audio: Added HDA support for newer Linux guests; more work on surround support (disabled by default).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use