VirtualBox

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

Last change on this file since 54968 was 54968, checked in by vboxsync, 10 years ago

DrvAudio.cpp: Fixed output stream rate conversion.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.6 KB
Line 
1/* $Id: DrvAudio.cpp 54968 2015-03-26 15:33:05Z vboxsync $ */
2/** @file
3 * Intermediate audio driver header.
4 *
5 * @remarks Intermediate audio driver having audio device as one of the sink and
6 * host backend as other.
7 */
8
9/*
10 * Copyright (C) 2006-2014 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
46#include <VBox/vmm/pdm.h>
47#include <VBox/err.h>
48#include <VBox/vmm/mm.h>
49#include <VBox/vmm/pdmaudioifs.h>
50
51#include <iprt/alloc.h>
52#include <iprt/asm-math.h>
53#include <iprt/assert.h>
54#include <iprt/circbuf.h>
55#include <iprt/string.h>
56#include <iprt/uuid.h>
57
58#ifdef LOG_GROUP
59# undef LOG_GROUP
60#endif
61#define LOG_GROUP LOG_GROUP_DEV_AUDIO
62#include <VBox/log.h>
63
64#include "VBoxDD.h"
65#include "vl_vbox.h"
66
67#include <ctype.h>
68#include <stdlib.h>
69
70#include "DrvAudio.h"
71#include "AudioMixBuffer.h"
72
73static int drvAudioDestroyGstIn(PDRVAUDIO pThis, PPDMAUDIOGSTSTRMIN pGstStrmIn);
74
75static int drvAudioAllocHstIn(PDRVAUDIO pThis, const char *pszName, PPDMAUDIOSTREAMCFG pCfg, PDMAUDIORECSOURCE enmRecSource, PPDMAUDIOHSTSTRMIN *ppHstStrmIn);
76static int drvAudioDestroyHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn);
77
78volume_t nominal_volume =
79{
80 0,
81 INT_MAX,
82 INT_MAX
83};
84
85volume_t sum_out_volume =
86{
87 0,
88 INT_MAX,
89 INT_MAX
90};
91
92volume_t pcm_in_volume =
93{
94 0,
95 INT_MAX,
96 INT_MAX
97};
98
99int drvAudioAddHstOut(PDRVAUDIO pThis, const char *pszName, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOHSTSTRMOUT *ppHstStrmOut)
100{
101 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
102 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
103 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
104
105 PPDMAUDIOHSTSTRMOUT pHstStrmOut;
106
107 int rc;
108 if ( conf.fixed_out.enabled /** @todo Get rid of these settings! */
109 && conf.fixed_out.greedy)
110 {
111 rc = drvAudioAllocHstOut(pThis, pszName, pCfg, &pHstStrmOut);
112 }
113 else
114 rc = VERR_NOT_FOUND;
115
116 if (RT_FAILURE(rc))
117 {
118 pHstStrmOut = drvAudioFindSpecificOut(pThis, NULL, pCfg);
119 if (!pHstStrmOut)
120 {
121 rc = drvAudioAllocHstOut(pThis, pszName, pCfg, &pHstStrmOut);
122 if (RT_FAILURE(rc))
123 pHstStrmOut = drvAudioFindAnyHstOut(pThis, NULL /* pHstStrmOut */);
124 }
125
126 rc = pHstStrmOut ? VINF_SUCCESS : rc;
127 }
128
129 if (RT_SUCCESS(rc))
130 *ppHstStrmOut = pHstStrmOut;
131
132 return rc;
133}
134
135static PDMAUDIOFMT drvAudioGetConfFormat(PCFGMNODE pCfgHandle, const char *pszKey,
136 PDMAUDIOFMT enmDefault, bool *pfDefault)
137{
138 if ( pCfgHandle == NULL
139 || pszKey == NULL)
140 {
141 *pfDefault = true;
142 return enmDefault;
143 }
144
145 char *pszValue = NULL;
146 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
147 if (RT_FAILURE(rc))
148 {
149 *pfDefault = true;
150 return enmDefault;
151 }
152
153 PDMAUDIOFMT fmt = drvAudioHlpStringToFormat(pszValue);
154 if (fmt == AUD_FMT_INVALID)
155 {
156 *pfDefault = true;
157 return enmDefault;
158 }
159
160 *pfDefault = false;
161 return fmt;
162}
163
164static int drvAudioGetConfInt(PCFGMNODE pCfgHandle, const char *pszKey,
165 int iDefault, bool *pfDefault)
166{
167
168 if ( pCfgHandle == NULL
169 || pszKey == NULL)
170 {
171 *pfDefault = true;
172 return iDefault;
173 }
174
175 uint64_t u64Data = 0;
176 int rc = CFGMR3QueryInteger(pCfgHandle, pszKey, &u64Data);
177 if (RT_FAILURE(rc))
178 {
179 *pfDefault = true;
180 return iDefault;
181
182 }
183
184 *pfDefault = false;
185 return u64Data;
186}
187
188static const char *drvAudioGetConfStr(PCFGMNODE pCfgHandle, const char *pszKey,
189 const char *pszDefault, bool *pfDefault)
190{
191 if ( pCfgHandle == NULL
192 || pszKey == NULL)
193 {
194 *pfDefault = true;
195 return pszDefault;
196 }
197
198 char *pszValue = NULL;
199 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
200 if (RT_FAILURE(rc))
201 {
202 *pfDefault = true;
203 return pszDefault;
204 }
205
206 *pfDefault = false;
207 return pszValue;
208}
209
210static int drvAudioProcessOptions(PCFGMNODE pCfgHandle, const char *pszPrefix, struct audio_option *opt)
211{
212 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
213 AssertPtrReturn(pszPrefix, VERR_INVALID_POINTER);
214 AssertPtrReturn(opt, VERR_INVALID_POINTER);
215
216 PCFGMNODE pCfgChildHandle = NULL;
217 PCFGMNODE pCfgChildChildHandle = NULL;
218
219 /* If pCfgHandle is NULL, let NULL be passed to get int and get string functions..
220 * The getter function will return default values.
221 */
222 if (pCfgHandle != NULL)
223 {
224 /* If its audio general setting, need to traverse to one child node.
225 * /Devices/ichac97/0/LUN#0/Config/Audio
226 */
227 if(!strncmp(pszPrefix, "AUDIO", 5)) /** @todo Use a #define */
228 {
229 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
230 if(pCfgChildHandle)
231 pCfgHandle = pCfgChildHandle;
232 }
233 else
234 {
235 /* If its driver specific configuration , then need to traverse two level deep child
236 * child nodes. for eg. in case of DirectSoundConfiguration item
237 * /Devices/ichac97/0/LUN#0/Config/Audio/DirectSoundConfig
238 */
239 pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
240 if (pCfgChildHandle)
241 {
242 pCfgChildChildHandle = CFGMR3GetFirstChild(pCfgChildHandle);
243 if (pCfgChildChildHandle)
244 pCfgHandle = pCfgChildChildHandle;
245 }
246 }
247 }
248
249 for (; opt->name; opt++)
250 {
251 LogFlowFunc(("Option value pointer for `%s' is not set\n",
252 opt->name));
253 if (!opt->valp) {
254 LogFlowFunc(("Option value pointer for `%s' is not set\n",
255 opt->name));
256 continue;
257 }
258
259 bool fUseDefault;
260
261 switch (opt->tag)
262 {
263 case AUD_OPT_BOOL:
264 case AUD_OPT_INT:
265 {
266 int *intp = (int *)opt->valp;
267 *intp = drvAudioGetConfInt(pCfgHandle, opt->name, *intp, &fUseDefault);
268
269 break;
270 }
271
272 case AUD_OPT_FMT:
273 {
274 PDMAUDIOFMT *fmtp = (PDMAUDIOFMT *)opt->valp;
275 *fmtp = drvAudioGetConfFormat(pCfgHandle, opt->name, *fmtp, &fUseDefault);
276
277 break;
278 }
279
280 case AUD_OPT_STR:
281 {
282 const char **strp = (const char **)opt->valp;
283 *strp = drvAudioGetConfStr(pCfgHandle, opt->name, *strp, &fUseDefault);
284
285 break;
286 }
287
288 default:
289 LogFlowFunc(("Bad value tag for option `%s' - %d\n", opt->name, opt->tag));
290 fUseDefault = false;
291 break;
292 }
293
294 if (!opt->overridenp)
295 opt->overridenp = &opt->overriden;
296
297 *opt->overridenp = !fUseDefault;
298 }
299
300 return VINF_SUCCESS;
301}
302
303static bool drvAudioStreamCfgIsValid(PPDMAUDIOSTREAMCFG pCfg)
304{
305 bool fValid = ( pCfg->cChannels == 1
306 || pCfg->cChannels == 2); /* Either stereo (2) or mono (1), per stream. */
307
308 fValid |= ( pCfg->enmEndianness == PDMAUDIOENDIANESS_LITTLE
309 || pCfg->enmEndianness == PDMAUDIOENDIANESS_BIG);
310
311 if (fValid)
312 {
313 switch (pCfg->enmFormat)
314 {
315 case AUD_FMT_S8:
316 case AUD_FMT_U8:
317 case AUD_FMT_S16:
318 case AUD_FMT_U16:
319 case AUD_FMT_S32:
320 case AUD_FMT_U32:
321 break;
322 default:
323 fValid = false;
324 break;
325 }
326 }
327
328 /** @todo Check for defined frequencies supported. */
329 fValid |= pCfg->uHz > 0;
330
331#ifdef DEBUG
332 drvAudioStreamCfgPrint(pCfg);
333#endif
334
335 LogFlowFunc(("pCfg=%p, fValid=%RTbool\n", pCfg, fValid));
336 return fValid;
337}
338
339void audio_pcm_info_clear_buf(PPDMPCMPROPS pPCMInfo, void *pvBuf, int len)
340{
341 if (!len)
342 return;
343
344 if (pPCMInfo->fSigned)
345 {
346 memset (pvBuf, 0, len << pPCMInfo->cShift);
347 }
348 else
349 {
350 switch (pPCMInfo->cBits)
351 {
352
353 case 8:
354 memset (pvBuf, 0x80, len << pPCMInfo->cShift);
355 break;
356
357 case 16:
358 {
359 int i;
360 uint16_t *p = (uint16_t *)pvBuf;
361 int shift = pPCMInfo->cChannels - 1;
362 short s = INT16_MAX;
363
364 if (pPCMInfo->fSwapEndian)
365 s = bswap16(s);
366
367 for (i = 0; i < len << shift; i++)
368 p[i] = s;
369 }
370 break;
371
372 case 32:
373 {
374 int i;
375 uint32_t *p = (uint32_t *)pvBuf;
376 int shift = pPCMInfo->cChannels - 1;
377 int32_t s = INT32_MAX;
378
379 if (pPCMInfo->fSwapEndian)
380 s = bswap32(s);
381
382 for (i = 0; i < len << shift; i++)
383 p[i] = s;
384 }
385 break;
386
387 default:
388 LogFlowFunc(("audio_pcm_info_clear_buf: invalid bits %d\n", pPCMInfo->cBits));
389 break;
390 }
391 }
392}
393
394int drvAudioDestroyHstOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
395{
396 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
397 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
398
399 LogFlowFunc(("%s\n", pHstStrmOut->MixBuf.pszName));
400
401 int rc;
402 if (RTListIsEmpty(&pHstStrmOut->lstGstStrmOut))
403 {
404 rc = pThis->pHostDrvAudio->pfnFiniOut(pThis->pHostDrvAudio, pHstStrmOut);
405 if (RT_SUCCESS(rc))
406 {
407 drvAudioHstOutFreeRes(pHstStrmOut);
408
409 /* Remove from driver instance list. */
410 RTListNodeRemove(&pHstStrmOut->Node);
411
412 RTMemFree(pHstStrmOut);
413 pThis->cFreeOutputStreams++;
414 return VINF_SUCCESS;
415 }
416 }
417 else
418 rc = VERR_ACCESS_DENIED;
419
420 LogFlowFunc(("[%s] Still is being used, rc=%Rrc\n", pHstStrmOut->MixBuf.pszName, rc));
421 return rc;
422}
423
424int drvAudioDestroyGstOut(PDRVAUDIO pThis, PPDMAUDIOGSTSTRMOUT pGstStrmOut)
425{
426 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
427
428 if (pGstStrmOut)
429 {
430 drvAudioGstOutFreeRes(pGstStrmOut);
431
432 if (pGstStrmOut->pHstStrmOut)
433 {
434 /* Unregister from parent first. */
435 RTListNodeRemove(&pGstStrmOut->Node);
436
437 /* Try destroying the associated host output stream. This could
438 * be skipped if there are other guest output streams with this
439 * host stream. */
440 drvAudioDestroyHstOut(pThis, pGstStrmOut->pHstStrmOut);
441 }
442
443 RTMemFree(pGstStrmOut);
444 }
445
446 return VINF_SUCCESS;
447}
448
449PPDMAUDIOHSTSTRMIN drvAudioFindNextHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn)
450{
451 if (pHstStrmIn)
452 {
453 if (RTListNodeIsLast(&pThis->lstHstStrmIn, &pHstStrmIn->Node))
454 return NULL;
455
456 return RTListNodeGetNext(&pHstStrmIn->Node, PDMAUDIOHSTSTRMIN, Node);
457 }
458
459 return RTListGetFirst(&pThis->lstHstStrmIn, PDMAUDIOHSTSTRMIN, Node);
460}
461
462PPDMAUDIOHSTSTRMIN drvAudioFindNextEnabledHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn)
463{
464 while ((pHstStrmIn = drvAudioFindNextHstIn(pThis, pHstStrmIn)))
465 if (pHstStrmIn->fEnabled)
466 return pHstStrmIn;
467
468 return NULL;
469}
470
471PPDMAUDIOHSTSTRMIN drvAudioFindNextEqHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn,
472 PPDMAUDIOSTREAMCFG pCfg)
473{
474 while ((pHstStrmIn = drvAudioFindNextHstIn(pThis, pHstStrmIn)))
475 if (drvAudioPCMPropsAreEqual(&pHstStrmIn->Props, pCfg))
476 return pHstStrmIn;
477
478 return NULL;
479}
480
481static int drvAudioHstInAdd(PDRVAUDIO pThis, const char *pszName, PPDMAUDIOSTREAMCFG pCfg, PDMAUDIORECSOURCE enmRecSource,
482 PPDMAUDIOHSTSTRMIN *ppHstStrmIn)
483{
484 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
485 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
486 AssertPtrReturn(ppHstStrmIn, VERR_INVALID_POINTER);
487
488 PPDMAUDIOHSTSTRMIN pHstStrmIn;
489 int rc = drvAudioAllocHstIn(pThis, pszName, pCfg, enmRecSource, &pHstStrmIn);
490 if (RT_SUCCESS(rc))
491 *ppHstStrmIn = pHstStrmIn;
492
493 LogFlowFuncLeaveRC(rc);
494 return rc;
495}
496
497int drvAudioGstOutInit(PPDMAUDIOGSTSTRMOUT pGstStrmOut, PPDMAUDIOHSTSTRMOUT pHostStrmOut,
498 const char *pszName, PPDMAUDIOSTREAMCFG pCfg)
499{
500 int rc = drvAudioStreamCfgToProps(pCfg, &pGstStrmOut->Props);
501 if (RT_SUCCESS(rc))
502 {
503 char *pszTemp;
504 if (RTStrAPrintf(&pszTemp, "%s (Guest)", pszName) <= 0)
505 return VERR_NO_MEMORY;
506
507 rc = audioMixBufInit(&pGstStrmOut->MixBuf, pszTemp, &pGstStrmOut->Props,
508 audioMixBufSize(&pHostStrmOut->MixBuf));
509 if (RT_SUCCESS(rc))
510 rc = audioMixBufLinkTo(&pGstStrmOut->MixBuf, &pHostStrmOut->MixBuf);
511
512 RTStrFree(pszTemp);
513
514 if (RT_SUCCESS(rc))
515 {
516 pGstStrmOut->State.fActive = false;
517 pGstStrmOut->State.fEmpty = true;
518
519 pGstStrmOut->State.pszName = RTStrDup(pszName);
520 if (!pGstStrmOut->State.pszName)
521 return VERR_NO_MEMORY;
522
523 pGstStrmOut->pHstStrmOut = pHostStrmOut;
524 }
525 }
526
527 LogFlowFunc(("pszName=%s, rc=%Rrc\n", pszName, rc));
528 return rc;
529}
530
531int drvAudioAllocHstOut(PDRVAUDIO pThis, const char *pszName, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOHSTSTRMOUT *ppHstStrmOut)
532{
533 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
534 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
535 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
536
537 if (!pThis->cFreeOutputStreams)
538 {
539 LogFlowFunc(("Maximum number of host output streams reached\n"));
540 return VERR_NO_MORE_HANDLES;
541 }
542
543 /* Validate backend configuration. */
544 if (!pThis->BackendCfg.cbStreamOut)
545 {
546 LogFlowFunc(("Backend output configuration not valid, bailing out\n"));
547 return VERR_INVALID_PARAMETER;
548 }
549
550 PPDMAUDIOHSTSTRMOUT pHstStrmOut = (PPDMAUDIOHSTSTRMOUT)RTMemAllocZ(pThis->BackendCfg.cbStreamOut);
551 if (!pHstStrmOut)
552 {
553 LogFlowFunc(("Error allocating host output stream with %zu bytes\n",
554 pThis->BackendCfg.cbStreamOut));
555 return VERR_NO_MEMORY;
556 }
557
558 int rc;
559 bool fInitialized = false;
560
561 do
562 {
563 RTListInit(&pHstStrmOut->lstGstStrmOut);
564
565 uint32_t cSamples;
566 rc = pThis->pHostDrvAudio->pfnInitOut(pThis->pHostDrvAudio, pHstStrmOut, pCfg, &cSamples);
567 if (RT_FAILURE(rc))
568 {
569 LogFlowFunc(("Initializing host backend failed with rc=%Rrc\n", rc));
570 break;
571 }
572
573 fInitialized = true;
574
575 char *pszTemp;
576 if (RTStrAPrintf(&pszTemp, "%s (Host)", pszName) <= 0)
577 {
578 rc = VERR_NO_MEMORY;
579 break;
580 }
581
582 rc = audioMixBufInit(&pHstStrmOut->MixBuf, pszTemp, &pHstStrmOut->Props, cSamples);
583 if (RT_SUCCESS(rc))
584 {
585 RTListPrepend(&pThis->lstHstStrmOut, &pHstStrmOut->Node);
586 pThis->cFreeOutputStreams--;
587 }
588
589 RTStrFree(pszTemp);
590
591 } while (0);
592
593 if (RT_FAILURE(rc))
594 {
595 if (fInitialized)
596 {
597 int rc2 = pThis->pHostDrvAudio->pfnFiniOut(pThis->pHostDrvAudio, pHstStrmOut);
598 AssertRC(rc2);
599 }
600
601 drvAudioHstOutFreeRes(pHstStrmOut);
602 RTMemFree(pHstStrmOut);
603 }
604 else
605 *ppHstStrmOut = pHstStrmOut;
606
607 LogFlowFuncLeaveRC(rc);
608 return rc;
609}
610
611int drvAudioCreateStreamPairOut(PDRVAUDIO pThis, const char *pszName,
612 PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOGSTSTRMOUT *ppGstStrmOut)
613{
614 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
615 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
616 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
617
618 /*
619 * Try figuring out which audio stream configuration this backend
620 * should use. If fixed output is enabled the backend will be tied
621 * to a fixed rate in (Hz), regardless of what the backend could do else.
622 */
623 PPDMAUDIOSTREAMCFG pBackendCfg;
624 if (conf.fixed_out.enabled)
625 pBackendCfg = &conf.fixed_out.settings;
626 else
627 pBackendCfg = pCfg;
628
629 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
630
631 LogFlowFunc(("Using fixed audio output settings: %RTbool\n",
632 RT_BOOL(conf.fixed_out.enabled)));
633
634 PPDMAUDIOGSTSTRMOUT pGstStrmOut =
635 (PPDMAUDIOGSTSTRMOUT)RTMemAllocZ(sizeof(PDMAUDIOGSTSTRMOUT));
636 if (!pGstStrmOut)
637 {
638 LogFlowFunc(("Failed to allocate memory for guest output stream \"%s\"\n", pszName));
639 return VERR_NO_MEMORY;
640 }
641
642 /*
643 * The host stream always will get the backend audio stream configuration.
644 */
645 PPDMAUDIOHSTSTRMOUT pHstStrmOut;
646 int rc = drvAudioAddHstOut(pThis, pszName, pBackendCfg, &pHstStrmOut);
647 if (RT_FAILURE(rc))
648 {
649 LogFlowFunc(("Error adding host output stream \"%s\", rc=%Rrc\n", pszName, rc));
650
651 RTMemFree(pGstStrmOut);
652 return rc;
653 }
654
655 /*
656 * The guest stream always will get the the audio stream configuration told
657 * by the device emulation (which in turn was/could be set by the guest OS).
658 */
659 rc = drvAudioGstOutInit(pGstStrmOut, pHstStrmOut, pszName, pCfg);
660 if (RT_SUCCESS(rc))
661 {
662 RTListPrepend(&pHstStrmOut->lstGstStrmOut, &pGstStrmOut->Node);
663
664 if (ppGstStrmOut)
665 *ppGstStrmOut = pGstStrmOut;
666 }
667
668 if (RT_FAILURE(rc))
669 drvAudioDestroyGstOut(pThis, pGstStrmOut);
670
671 LogFlowFuncLeaveRC(rc);
672 return rc;
673}
674
675static int drvAudioCreateStreamPairIn(PDRVAUDIO pThis, const char *pszName, PDMAUDIORECSOURCE enmRecSource,
676 PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOGSTSTRMIN *ppGstStrmIn)
677{
678 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
679 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
680
681 PPDMAUDIOSTREAMCFG pThisCfg;
682 if (conf.fixed_in.enabled)
683 {
684 pThisCfg = &conf.fixed_in.settings;
685 LogFlowFunc(("Using fixed audio settings\n"));
686 }
687 else
688 pThisCfg = pCfg;
689 AssertPtrReturn(pThisCfg, VERR_INVALID_POINTER);
690
691 PPDMAUDIOGSTSTRMIN pGstStrmIn = (PPDMAUDIOGSTSTRMIN)RTMemAllocZ(sizeof(PDMAUDIOGSTSTRMIN));
692 if (!pGstStrmIn)
693 return VERR_NO_MEMORY;
694
695
696 PPDMAUDIOHSTSTRMIN pHstStrmIn;
697 int rc = drvAudioHstInAdd(pThis, pszName, pThisCfg, enmRecSource, &pHstStrmIn);
698 if (RT_FAILURE(rc))
699 {
700 LogFunc(("Failed to add host audio input stream \"%s\", rc=%Rrc\n", pszName, rc));
701
702 RTMemFree(pGstStrmIn);
703 return rc;
704 }
705
706 rc = drvAudioGstInInit(pGstStrmIn, pHstStrmIn, pszName, pThisCfg);
707 if (RT_SUCCESS(rc))
708 {
709 pHstStrmIn->pGstStrmIn = pGstStrmIn;
710
711 if (ppGstStrmIn)
712 *ppGstStrmIn = pGstStrmIn;
713 }
714 else
715 drvAudioDestroyGstIn(pThis, pGstStrmIn);
716
717 LogFlowFuncLeaveRC(rc);
718 return rc;
719}
720
721/**
722 * Initializes a guest input stream.
723 *
724 * @return IPRT status code.
725 * @param pGstStrmIn Pointer to guest stream to initialize.
726 * @param pHstStrmIn Pointer to host input stream to associate this guest
727 * stream with.
728 * @param pszName Pointer to stream name to use for this stream.
729 * @param pCfg Pointer to stream configuration to use.
730 */
731int drvAudioGstInInit(PPDMAUDIOGSTSTRMIN pGstStrmIn, PPDMAUDIOHSTSTRMIN pHstStrmIn,
732 const char *pszName, PPDMAUDIOSTREAMCFG pCfg)
733{
734 AssertPtrReturn(pGstStrmIn, VERR_INVALID_POINTER);
735 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
736 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
737 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
738
739 int rc = drvAudioStreamCfgToProps(pCfg, &pGstStrmIn->Props);
740 if (RT_SUCCESS(rc))
741 {
742 char *pszTemp;
743 if (RTStrAPrintf(&pszTemp, "%s (Guest)", pszName) <= 0)
744 return VERR_NO_MEMORY;
745
746 rc = audioMixBufInit(&pGstStrmIn->MixBuf, pszTemp, &pHstStrmIn->Props,
747 audioMixBufSize(&pHstStrmIn->MixBuf));
748 if (RT_SUCCESS(rc))
749 rc = audioMixBufLinkTo(&pHstStrmIn->MixBuf, &pGstStrmIn->MixBuf);
750
751 RTStrFree(pszTemp);
752
753 if (RT_SUCCESS(rc))
754 {
755#ifdef DEBUG
756 drvAudioStreamCfgPrint(pCfg);
757#endif
758 pGstStrmIn->State.fActive = false;
759 pGstStrmIn->State.fEmpty = true;
760
761 pGstStrmIn->State.pszName = RTStrDup(pszName);
762 if (!pGstStrmIn->State.pszName)
763 return VERR_NO_MEMORY;
764
765 pGstStrmIn->pHstStrmIn = pHstStrmIn;
766 }
767 }
768
769 LogFlowFunc(("pszName=%s, rc=%Rrc\n", pszName, rc));
770 return rc;
771}
772
773static int drvAudioAllocHstIn(PDRVAUDIO pThis, const char *pszName, PPDMAUDIOSTREAMCFG pCfg,
774 PDMAUDIORECSOURCE enmRecSource, PPDMAUDIOHSTSTRMIN *ppHstStrmIn)
775{
776 if (!pThis->cFreeInputStreams)
777 {
778 LogFlowFunc(("No more input streams free to use, bailing out\n"));
779 return VERR_NO_MORE_HANDLES;
780 }
781
782 /* Validate backend configuration. */
783 if (!pThis->BackendCfg.cbStreamIn)
784 {
785 LogFlowFunc(("Backend input configuration not valid, bailing out\n"));
786 return VERR_INVALID_PARAMETER;
787 }
788
789 PPDMAUDIOHSTSTRMIN pHstStrmIn =
790 (PPDMAUDIOHSTSTRMIN)RTMemAllocZ(pThis->BackendCfg.cbStreamIn);
791 if (!pHstStrmIn)
792 {
793 LogFlowFunc(("Error allocating host innput stream with %RU32 bytes\n",
794 pThis->BackendCfg.cbStreamOut));
795 return VERR_NO_MEMORY;
796 }
797
798 int rc;
799 bool fInitialized = false;
800
801 do
802 {
803 uint32_t cSamples;
804 rc = pThis->pHostDrvAudio->pfnInitIn(pThis->pHostDrvAudio, pHstStrmIn,
805 pCfg, enmRecSource, &cSamples);
806 if (RT_FAILURE(rc))
807 {
808 LogFlowFunc(("Initializing host backend failed with rc=%Rrc\n", rc));
809 break;
810 }
811
812 fInitialized = true;
813
814 char *pszTemp;
815 if (RTStrAPrintf(&pszTemp, "%s (Host)", pszName) <= 0)
816 {
817 rc = VERR_NO_MEMORY;
818 break;
819 }
820
821 rc = audioMixBufInit(&pHstStrmIn->MixBuf, pszTemp, &pHstStrmIn->Props, cSamples);
822 if (RT_SUCCESS(rc))
823 {
824 RTListPrepend(&pThis->lstHstStrmIn, &pHstStrmIn->Node);
825 pThis->cFreeInputStreams--;
826 }
827
828 RTStrFree(pszTemp);
829
830 } while (0);
831
832 if (RT_FAILURE(rc))
833 {
834 if (fInitialized)
835 {
836 int rc2 = pThis->pHostDrvAudio->pfnFiniIn(pThis->pHostDrvAudio,
837 pHstStrmIn);
838 AssertRC(rc2);
839 }
840
841 drvAudioHstInFreeRes(pHstStrmIn);
842 RTMemFree(pHstStrmIn);
843 }
844 else
845 *ppHstStrmIn = pHstStrmIn;
846
847 LogFlowFuncLeaveRC(rc);
848 return rc;
849}
850
851/**
852 * Writes VM audio output data from the guest stream into the host stream.
853 * The attached host driver backend then will play out the audio in a
854 * later step then.
855 *
856 * @return IPRT status code.
857 * @return int
858 * @param pThis
859 * @param pGstStrmOut
860 * @param pvBuf
861 * @param cbBuf
862 * @param pcbWritten
863 */
864int drvAudioWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut,
865 const void *pvBuf, size_t cbBuf, uint32_t *pcbWritten)
866{
867 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
868 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
869
870 AssertPtrReturn(pGstStrmOut, VERR_INVALID_POINTER);
871 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
872 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
873 /* pcbWritten is optional. */
874
875 if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_OUT))
876 return VERR_NOT_AVAILABLE;
877
878 PPDMAUDIOHSTSTRMOUT pHstStrmOut = pGstStrmOut->pHstStrmOut;
879 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
880
881 AssertMsg(pGstStrmOut->pHstStrmOut->fEnabled,
882 ("Writing to disabled host output stream \"%s\" not possible\n",
883 pHstStrmOut->MixBuf.pszName));
884
885 /*
886 * First, write data from the device emulation into our
887 * guest mixing buffer.
888 */
889 uint32_t cWritten;
890 int rc = audioMixBufWriteAt(&pGstStrmOut->MixBuf, 0 /* Offset in samples */, pvBuf, cbBuf, &cWritten);
891
892 /*
893 * Second, mix the guest mixing buffer with the host mixing
894 * buffer so that the host backend can play the data lateron.
895 */
896 uint32_t cMixed;
897 if ( RT_SUCCESS(rc)
898 && cWritten)
899 {
900 rc = audioMixBufMixToParent(&pGstStrmOut->MixBuf, cWritten, &cMixed);
901 }
902 else
903 cMixed = 0;
904
905 if (RT_SUCCESS(rc))
906 {
907 /* Return the number of samples which actually have been mixed
908 * down to the parent, regardless how much samples were written
909 * into the children buffer. */
910 if (pcbWritten)
911 *pcbWritten = AUDIOMIXBUF_S2B(&pGstStrmOut->MixBuf, cMixed);
912 }
913
914 LogFlowFunc(("%s -> %s: Written pvBuf=%p, cbBuf=%zu, cWritten=%RU32 (%RU32 bytes), cMixed=%RU32, rc=%Rrc\n",
915 pGstStrmOut->MixBuf.pszName, pHstStrmOut->MixBuf.pszName, pvBuf, cbBuf, cWritten,
916 AUDIOMIXBUF_S2B(&pGstStrmOut->MixBuf, cWritten), cMixed, rc));
917 return rc;
918}
919
920PPDMAUDIOHSTSTRMOUT drvAudioFindAnyHstOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
921{
922 if (pHstStrmOut)
923 {
924 if (RTListNodeIsLast(&pThis->lstHstStrmOut, &pHstStrmOut->Node))
925 return NULL;
926
927 return RTListNodeGetNext(&pHstStrmOut->Node, PDMAUDIOHSTSTRMOUT, Node);
928 }
929
930 return RTListGetFirst(&pThis->lstHstStrmOut, PDMAUDIOHSTSTRMOUT, Node);
931}
932
933PPDMAUDIOHSTSTRMOUT drvAudioHstFindAnyEnabledOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHostStrmOut)
934{
935 while ((pHostStrmOut = drvAudioFindAnyHstOut(pThis, pHostStrmOut)))
936 {
937 if (pHostStrmOut->fEnabled)
938 return pHostStrmOut;
939 }
940
941 return NULL;
942}
943
944PPDMAUDIOHSTSTRMOUT drvAudioFindSpecificOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
945 PPDMAUDIOSTREAMCFG pCfg)
946{
947 while ((pHstStrmOut = drvAudioFindAnyHstOut(pThis, pHstStrmOut)))
948 {
949 if (drvAudioPCMPropsAreEqual(&pHstStrmOut->Props, pCfg))
950 return pHstStrmOut;
951 }
952
953 return NULL;
954}
955
956int drvAudioDestroyHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn)
957{
958 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
959 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
960
961 LogFlowFunc(("%s\n", pHstStrmIn->MixBuf.pszName));
962
963 int rc;
964 if (!pHstStrmIn->pGstStrmIn) /* No parent anymore? */
965 {
966 rc = pThis->pHostDrvAudio->pfnFiniIn(pThis->pHostDrvAudio, pHstStrmIn);
967 if (RT_SUCCESS(rc))
968 {
969 drvAudioHstInFreeRes(pHstStrmIn);
970
971 /* Remove from driver instance list. */
972 RTListNodeRemove(&pHstStrmIn->Node);
973
974 RTMemFree(pHstStrmIn);
975 pThis->cFreeInputStreams++;
976 }
977 }
978 else
979 rc = VERR_ACCESS_DENIED;
980
981 LogFlowFunc(("[%s] Still is being used, rc=%Rrc\n", pHstStrmIn->MixBuf.pszName, rc));
982 return rc;
983}
984
985static int drvAudioDestroyGstIn(PDRVAUDIO pThis, PPDMAUDIOGSTSTRMIN pGstStrmIn)
986{
987 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
988
989 LogFlowFunc(("%s\n", pGstStrmIn->MixBuf.pszName));
990
991 if (pGstStrmIn)
992 {
993 drvAudioGstInFreeRes(pGstStrmIn);
994
995 if (pGstStrmIn->pHstStrmIn)
996 {
997 /* Unlink child. */
998 pGstStrmIn->pHstStrmIn->pGstStrmIn = NULL;
999
1000 /* Try destroying the associated host input stream. This could
1001 * be skipped if there are other guest input streams with this
1002 * host stream. */
1003 drvAudioDestroyHstIn(pThis, pGstStrmIn->pHstStrmIn);
1004 }
1005
1006 RTMemFree(pGstStrmIn);
1007 }
1008
1009 return VINF_SUCCESS;
1010}
1011
1012static DECLCALLBACK(int) drvAudioQueryStatus(PPDMIAUDIOCONNECTOR pInterface,
1013 uint32_t *pcbAvailIn, uint32_t *pcbFreeOut,
1014 uint32_t *pcSamplesLive)
1015{
1016 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1017 /* pcbAvailIn is optional. */
1018 /* pcbFreeOut is optional. */
1019 /* pcSamplesLive is optional. */
1020
1021 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1022
1023 if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_OUT))
1024 return VERR_NOT_AVAILABLE;
1025
1026 int rc = VINF_SUCCESS;
1027 uint32_t cSamplesLive = 0;
1028
1029 /*
1030 * Playback.
1031 */
1032 uint32_t cbFreeOut = UINT32_MAX;
1033
1034 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
1035 while ((pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, pHstStrmOut)))
1036 {
1037 uint32_t cStreamsLive;
1038 cSamplesLive = drvAudioHstOutSamplesLive(pHstStrmOut, &cStreamsLive);
1039 if (!cStreamsLive)
1040 cSamplesLive = 0;
1041
1042 /* Has this stream marked as disabled but there still were guest streams relying
1043 * on it? Check if this stream now can be closed and do so, if possible. */
1044 if ( pHstStrmOut->fPendingDisable
1045 && !cStreamsLive)
1046 {
1047 /* Stop playing the current (pending) stream. */
1048 int rc2 = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut,
1049 PDMAUDIOSTREAMCMD_DISABLE);
1050 if (RT_SUCCESS(rc2))
1051 {
1052 pHstStrmOut->fEnabled = false;
1053 pHstStrmOut->fPendingDisable = false;
1054
1055 LogFunc(("[%s] Disabling stream\n", pHstStrmOut->MixBuf.pszName));
1056 }
1057 else
1058 LogFunc(("[%s] Backend vetoed against closing output stream, rc=%Rrc\n", pHstStrmOut->MixBuf.pszName, rc2));
1059
1060 continue;
1061 }
1062
1063 LogFlowFunc(("[%s] cSamplesLive=%RU32\n", pHstStrmOut->MixBuf.pszName, cSamplesLive));
1064
1065 /*
1066 * No live samples to play at the moment?
1067 *
1068 * Tell the device emulation for each connected guest stream how many
1069 * bytes are free so that the device emulation can continue writing data to
1070 * these streams.
1071 */
1072 PPDMAUDIOGSTSTRMOUT pGstStrmOut;
1073 if (!cSamplesLive)
1074 {
1075 uint32_t cbFree2 = UINT32_MAX;
1076 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
1077 {
1078 if (pGstStrmOut->State.fActive)
1079 {
1080 /* Tell the sound device emulation how many samples are free
1081 * so that it can start writing PCM data to us. */
1082 cbFree2 = RT_MIN(cbFree2, AUDIOMIXBUF_S2B_RATIO(&pGstStrmOut->MixBuf,
1083 audioMixBufFree(&pGstStrmOut->MixBuf)));
1084
1085 LogFlowFunc(("\t[%s] cbFree=%RU32\n", pGstStrmOut->MixBuf.pszName, cbFree2));
1086 }
1087 }
1088
1089 cbFreeOut = RT_MIN(cbFreeOut, cbFree2);
1090 }
1091 }
1092
1093 /*
1094 * Recording.
1095 */
1096 uint32_t cbAvailIn = 0;
1097
1098 PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
1099 while ((pHstStrmIn = drvAudioFindNextEnabledHstIn(pThis, pHstStrmIn)))
1100 {
1101 /* Call the host backend to capture the audio input data. */
1102 uint32_t cSamplesCaptured;
1103 int rc2 = pThis->pHostDrvAudio->pfnCaptureIn(pThis->pHostDrvAudio, pHstStrmIn,
1104 &cSamplesCaptured);
1105 if (RT_FAILURE(rc2))
1106 continue;
1107
1108 PPDMAUDIOGSTSTRMIN pGstStrmIn = pHstStrmIn->pGstStrmIn;
1109 AssertPtrBreak(pGstStrmIn);
1110
1111 if (pGstStrmIn->State.fActive)
1112 {
1113 cbAvailIn = RT_MAX(cbAvailIn, AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf,
1114 audioMixBufMixed(&pHstStrmIn->MixBuf)));
1115
1116 LogFlowFunc(("\t[%s] cbFree=%RU32\n", pHstStrmIn->MixBuf.pszName, cbAvailIn));
1117 }
1118 }
1119
1120 if (RT_SUCCESS(rc))
1121 {
1122 if (cbFreeOut == UINT32_MAX)
1123 cbFreeOut = 0;
1124
1125 if (pcbAvailIn)
1126 *pcbAvailIn = cbAvailIn;
1127
1128 if (pcbFreeOut)
1129 *pcbFreeOut = cbFreeOut;
1130
1131 if (pcSamplesLive)
1132 *pcSamplesLive = cSamplesLive;
1133 }
1134
1135 return rc;
1136}
1137
1138static DECLCALLBACK(int) drvAudioPlayOut(PPDMIAUDIOCONNECTOR pInterface, uint32_t *pcSamplesPlayed)
1139{
1140 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1141 /* pcSamplesPlayed is optional. */
1142
1143 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1144
1145 int rc = VINF_SUCCESS;
1146 uint32_t cSamplesPlayedMax = 0;
1147
1148 /*
1149 * Process all enabled host output streams.
1150 */
1151 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
1152 while ((pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, pHstStrmOut)))
1153 {
1154#if 0
1155 uint32_t cStreamsLive;
1156 uint32_t cSamplesLive = drvAudioHstOutSamplesLive(pHstStrmOut, &cStreamsLive);
1157 if (!cStreamsLive)
1158 cSamplesLive = 0;
1159
1160 /* Has this stream marked as disabled but there still were guest streams relying
1161 * on it? Check if this stream now can be closed and do so, if possible. */
1162 if ( pHstStrmOut->fPendingDisable
1163 && !cStreamsLive)
1164 {
1165 /* Stop playing the current (pending) stream. */
1166 int rc2 = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut,
1167 PDMAUDIOSTREAMCMD_DISABLE);
1168 if (RT_SUCCESS(rc2))
1169 {
1170 pHstStrmOut->fEnabled = false;
1171 pHstStrmOut->fPendingDisable = false;
1172
1173 LogFunc(("\t%p: Disabling stream\n", pHstStrmOut));
1174 }
1175 else
1176 LogFunc(("\t%p: Backend vetoed against closing output stream, rc=%Rrc\n",
1177 pHstStrmOut, rc2));
1178
1179 continue;
1180 }
1181#endif
1182
1183 uint32_t cSamplesPlayed = 0;
1184 int rc2 = pThis->pHostDrvAudio->pfnPlayOut(pThis->pHostDrvAudio, pHstStrmOut,
1185 &cSamplesPlayed);
1186 if (RT_SUCCESS(rc2))
1187 cSamplesPlayedMax = RT_MAX(cSamplesPlayed, cSamplesPlayedMax);
1188
1189 LogFlowFunc(("\t[%s] cSamplesPlayed=%RU32, rc=%Rrc\n", pHstStrmOut->MixBuf.pszName, cSamplesPlayed, rc2));
1190
1191 bool fNeedsCleanup = false;
1192
1193 PPDMAUDIOGSTSTRMOUT pGstStrmOut;
1194 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
1195 {
1196 if ( !pGstStrmOut->State.fActive
1197 && pGstStrmOut->State.fEmpty)
1198 continue;
1199
1200 if (audioMixBufIsEmpty(&pGstStrmOut->MixBuf))
1201 {
1202 pGstStrmOut->State.fEmpty = true;
1203 fNeedsCleanup |= !pGstStrmOut->State.fActive;
1204 }
1205 }
1206
1207 if (fNeedsCleanup)
1208 {
1209 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
1210 {
1211 if (!pGstStrmOut->State.fActive)
1212 drvAudioDestroyGstOut(pThis, pGstStrmOut);
1213 }
1214 }
1215 }
1216
1217 if (RT_SUCCESS(rc))
1218 {
1219 if (pcSamplesPlayed)
1220 *pcSamplesPlayed = cSamplesPlayedMax;
1221 }
1222
1223 return rc;
1224}
1225
1226static int drvAudioHostInit(PCFGMNODE pCfgHandle, PDRVAUDIO pThis)
1227{
1228 /* pCfgHandle is optional. */
1229 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1230
1231 NOREF(pCfgHandle);
1232
1233 LogFlowFuncEnter();
1234
1235 int rc = pThis->pHostDrvAudio->pfnInit(pThis->pHostDrvAudio);
1236 if (RT_FAILURE(rc))
1237 {
1238 LogFlowFunc(("Initialization of lower driver failed with rc=%Rrc\n", rc));
1239 return rc;
1240 }
1241
1242 uint32_t cMaxHstStrmsOut = pThis->BackendCfg.cMaxHstStrmsOut;
1243 uint32_t cbHstStrmsOut = pThis->BackendCfg.cbStreamOut;
1244
1245 if (cbHstStrmsOut)
1246 {
1247 pThis->cFreeOutputStreams = 1; /** @todo Make this configurable. */
1248 if (pThis->cFreeOutputStreams > cMaxHstStrmsOut)
1249 {
1250 LogRel(("Audio: Warning: %RU32 output streams were requested, host driver only supports %RU32\n",
1251 pThis->cFreeOutputStreams, cMaxHstStrmsOut));
1252 pThis->cFreeOutputStreams = cMaxHstStrmsOut;
1253 }
1254 }
1255 else
1256 pThis->cFreeOutputStreams = 0;
1257
1258 uint32_t cMaxHstStrmsIn = pThis->BackendCfg.cMaxHstStrmsIn;
1259 uint32_t cbHstStrmIn = pThis->BackendCfg.cbStreamIn;
1260
1261 if (cbHstStrmIn)
1262 {
1263 /*
1264 * Note:
1265 * - Our AC'97 emulation has two inputs, line (P.IN) and microphone (P.MIC).
1266 ** @todo Document HDA.
1267 */
1268 pThis->cFreeInputStreams = 2; /** @todo Make this configurable. */
1269 if (pThis->cFreeInputStreams > cMaxHstStrmsIn)
1270 {
1271 LogRel(("Audio: Warning: %RU32 input streams were requested, host driver only supports %RU32\n",
1272 pThis->cFreeInputStreams, cMaxHstStrmsIn));
1273 pThis->cFreeInputStreams = cMaxHstStrmsIn;
1274 }
1275 }
1276 else
1277 pThis->cFreeInputStreams = 0;
1278
1279 LogFlowFunc(("cMaxHstStrmsOut=%RU32 (cb=%RU32), cMaxHstStrmsIn=%RU32 (cb=%RU32)\n",
1280 cMaxHstStrmsOut, cbHstStrmsOut, cMaxHstStrmsIn, cbHstStrmIn));
1281
1282 LogFlowFunc(("cFreeInputStreams=%RU8, cFreeOutputStreams=%RU8\n",
1283 pThis->cFreeInputStreams, pThis->cFreeOutputStreams));
1284
1285 LogFlowFuncLeave();
1286 return VINF_SUCCESS;
1287}
1288
1289static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
1290{
1291 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1292
1293 LogFlowFunc(("enmCmd=%ld\n", enmCmd));
1294
1295 if (!pThis->pHostDrvAudio)
1296 return;
1297
1298 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
1299 while ((pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, pHstStrmOut)))
1300 pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, enmCmd);
1301
1302 PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
1303 while ((pHstStrmIn = drvAudioFindNextEnabledHstIn(pThis, pHstStrmIn)))
1304 pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn, enmCmd);
1305}
1306
1307static struct audio_option audio_options[] =
1308{
1309 /* DAC */
1310 {"DACFixedSettings", AUD_OPT_BOOL, &conf.fixed_out.enabled,
1311 "Use fixed settings for host DAC", NULL, 0},
1312
1313 {"DACFixedFreq", AUD_OPT_INT, &conf.fixed_out.settings.uHz,
1314 "Frequency for fixed host DAC", NULL, 0},
1315
1316 {"DACFixedFmt", AUD_OPT_FMT, &conf.fixed_out.settings.enmFormat,
1317 "Format for fixed host DAC", NULL, 0},
1318
1319 {"DACFixedChannels", AUD_OPT_INT, &conf.fixed_out.settings.cChannels,
1320 "Number of channels for fixed DAC (1 - mono, 2 - stereo)", NULL, 0},
1321
1322 {"DACVoices", AUD_OPT_INT, &conf.fixed_out.cStreams, /** @todo Rename! */
1323 "Number of streams for DAC", NULL, 0},
1324
1325 /* ADC */
1326 {"ADCFixedSettings", AUD_OPT_BOOL, &conf.fixed_in.enabled,
1327 "Use fixed settings for host ADC", NULL, 0},
1328
1329 {"ADCFixedFreq", AUD_OPT_INT, &conf.fixed_in.settings.uHz,
1330 "Frequency for fixed host ADC", NULL, 0},
1331
1332 {"ADCFixedFmt", AUD_OPT_FMT, &conf.fixed_in.settings.enmFormat,
1333 "Format for fixed host ADC", NULL, 0},
1334
1335 {"ADCFixedChannels", AUD_OPT_INT, &conf.fixed_in.settings.cChannels,
1336 "Number of channels for fixed ADC (1 - mono, 2 - stereo)", NULL, 0},
1337
1338 {"ADCVoices", AUD_OPT_INT, &conf.fixed_in.cStreams, /** @todo Rename! */
1339 "Number of streams for ADC", NULL, 0},
1340
1341 /* Misc */
1342 {"TimerFreq", AUD_OPT_INT, &conf.period.hz,
1343 "Timer frequency in Hz (0 - use lowest possible)", NULL, 0},
1344
1345 {"PLIVE", AUD_OPT_BOOL, &conf.plive,
1346 "(undocumented)", NULL, 0}, /** @todo What is this? */
1347
1348 NULL
1349};
1350
1351static DECLCALLBACK(int) drvAudioInit(PCFGMNODE pCfgHandle, PPDMDRVINS pDrvIns)
1352{
1353 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
1354 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
1355
1356 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1357 LogFlowFunc(("pDrvAudio=%p, pDrvIns=%p\n", pThis, pDrvIns));
1358
1359 RTListInit(&pThis->lstHstStrmIn);
1360 RTListInit(&pThis->lstHstStrmOut);
1361
1362 int rc = VINF_SUCCESS;
1363
1364 /* Get the configuration data from the selected backend (if available). */
1365 AssertPtr(pThis->pHostDrvAudio);
1366 if (RT_LIKELY(pThis->pHostDrvAudio->pfnGetConf))
1367 rc = pThis->pHostDrvAudio->pfnGetConf(pThis->pHostDrvAudio, &pThis->BackendCfg);
1368
1369 if (RT_SUCCESS(rc))
1370 {
1371 rc = drvAudioProcessOptions(pCfgHandle, "AUDIO", audio_options);
1372 /** @todo Check for invalid options? */
1373
1374 pThis->cFreeOutputStreams = conf.fixed_out.cStreams;
1375 pThis->cFreeInputStreams = conf.fixed_in.cStreams;
1376
1377 if (!pThis->cFreeOutputStreams)
1378 pThis->cFreeOutputStreams = 1;
1379
1380 if (!pThis->cFreeInputStreams)
1381 pThis->cFreeInputStreams = 1;
1382 }
1383
1384 /*
1385 * If everything went well, initialize the lower driver.
1386 */
1387 if (RT_SUCCESS(rc))
1388 rc = drvAudioHostInit(pCfgHandle, pThis);
1389
1390 LogFlowFuncLeaveRC(rc);
1391 return rc;
1392}
1393
1394static DECLCALLBACK(int) drvAudioInitNull(PPDMIAUDIOCONNECTOR pInterface)
1395{
1396 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1397 NOREF(pThis);
1398
1399 LogRel(("Audio: Using NULL driver; no sound will be audible\n"));
1400
1401 /* Nothing to do here yet. */
1402 return VINF_SUCCESS;
1403}
1404
1405static DECLCALLBACK(int) drvAudioRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn,
1406 void *pvBuf, size_t cbBuf, uint32_t *pcbRead)
1407{
1408 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1409 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1410
1411 AssertPtrReturn(pGstStrmIn, VERR_INVALID_POINTER);
1412 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1413 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1414 /* pcbWritten is optional. */
1415
1416 if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_IN))
1417 {
1418 if (pcbRead)
1419 *pcbRead = 0;
1420 return VINF_SUCCESS;
1421 }
1422
1423 PPDMAUDIOHSTSTRMIN pHstStrmIn = pGstStrmIn->pHstStrmIn;
1424 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1425
1426 AssertMsg(pGstStrmIn->pHstStrmIn->fEnabled,
1427 ("Reading from disabled host input stream \"%s\" not possible\n", pGstStrmIn->MixBuf.pszName));
1428
1429 /*
1430 * Read from the parent buffer (that is, the guest buffer) which
1431 * should have the audio data in the format the guest needs.
1432 */
1433 uint32_t cRead;
1434 int rc = audioMixBufReadCirc(&pGstStrmIn->MixBuf,
1435 pvBuf, cbBuf, &cRead);
1436 if (RT_SUCCESS(rc))
1437 {
1438 audioMixBufFinish(&pGstStrmIn->MixBuf, cRead);
1439
1440 if (pcbRead)
1441 *pcbRead = AUDIOMIXBUF_S2B(&pGstStrmIn->MixBuf, cRead);
1442 }
1443
1444 LogFlowFunc(("cRead=%RU32 (%RU32 bytes), rc=%Rrc\n",
1445 cRead, AUDIOMIXBUF_S2B(&pGstStrmIn->MixBuf, cRead), rc));
1446 return rc;
1447}
1448
1449static DECLCALLBACK(int) drvAudioIsSetOutVolume(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut,
1450 bool fMute, uint8_t uVolLeft, uint8_t uVolRight)
1451{
1452 LogFlowFunc(("[%s] fMute=%RTbool, uVolLeft=%RU8, uVolRight=%RU8\n",
1453 pGstStrmOut->MixBuf.pszName, fMute, uVolLeft, uVolRight));
1454
1455 if (pGstStrmOut)
1456 {
1457 pGstStrmOut->State.fMuted = fMute;
1458 pGstStrmOut->State.uVolumeLeft = (uint32_t)uVolLeft * 0x808080; /* maximum is INT_MAX = 0x7fffffff */
1459 pGstStrmOut->State.uVolumeRight = (uint32_t)uVolRight * 0x808080; /* maximum is INT_MAX = 0x7fffffff */
1460 }
1461
1462 return VINF_SUCCESS;
1463}
1464
1465/** @todo Handle more channels than stereo ... */
1466static DECLCALLBACK(int) drvAudioSetVolume(PPDMIAUDIOCONNECTOR pInterface,
1467 bool fMute, uint8_t uVolLeft, uint8_t uVolRight)
1468{
1469 LogFlowFunc(("fMute=%RTbool, uVolLeft=%RU8, uVolRight=%RU8\n",
1470 fMute, uVolLeft, uVolRight));
1471
1472 volume_t vol;
1473
1474 uint32_t u32VolumeLeft = uVolLeft;
1475 uint32_t u32VolumeRight = uVolRight;
1476
1477 /* 0x00..0xff => 0x01..0x100 */
1478 if (u32VolumeLeft)
1479 u32VolumeLeft++;
1480 if (u32VolumeRight)
1481 u32VolumeRight++;
1482 vol.mute = fMute ? 1 : 0;
1483 vol.l = u32VolumeLeft * 0x800000; /* maximum is 0x80000000 */
1484 vol.r = u32VolumeRight * 0x800000; /* maximum is 0x80000000 */
1485
1486 sum_out_volume.mute = 0;
1487 sum_out_volume.l = ASMMultU64ByU32DivByU32(INT_MAX, INT_MAX, 0x80000000U);
1488 sum_out_volume.r = ASMMultU64ByU32DivByU32(INT_MAX, INT_MAX, 0x80000000U);
1489
1490 LogFlowFunc(("sum_out l=%RU32, r=%RU32\n", sum_out_volume.l, sum_out_volume.r));
1491
1492 return VINF_SUCCESS;
1493}
1494
1495static DECLCALLBACK(int) drvAudioEnableOut(PPDMIAUDIOCONNECTOR pInterface,
1496 PPDMAUDIOGSTSTRMOUT pGstStrmOut, bool fEnable)
1497{
1498 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1499 /* pGstStrmOut is optional. */
1500
1501 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1502
1503 if (pGstStrmOut)
1504 {
1505 PPDMAUDIOHSTSTRMOUT pHstStrmOut = pGstStrmOut->pHstStrmOut;
1506 AssertPtr(pHstStrmOut);
1507
1508 LogFlowFunc(("%s: fEnable=%RTbool\n", pGstStrmOut->MixBuf.pszName, fEnable));
1509
1510 if (pGstStrmOut->State.fActive != fEnable)
1511 {
1512 if (fEnable)
1513 {
1514 pHstStrmOut->fPendingDisable = false;
1515 if (!pHstStrmOut->fEnabled)
1516 {
1517 pHstStrmOut->fEnabled = true;
1518 pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut,
1519 PDMAUDIOSTREAMCMD_ENABLE);
1520 /** @todo Check rc. */
1521 }
1522 }
1523 else
1524 {
1525 if (pHstStrmOut->fEnabled)
1526 {
1527 uint32_t cGstStrmsActive = 0;
1528
1529 PPDMAUDIOGSTSTRMOUT pIter;
1530 RTListForEach(&pHstStrmOut->lstGstStrmOut, pIter, PDMAUDIOGSTSTRMOUT, Node)
1531 {
1532 if (pIter->State.fActive)
1533 cGstStrmsActive++;
1534 }
1535
1536 pHstStrmOut->fPendingDisable = cGstStrmsActive == 1;
1537 }
1538 }
1539
1540 pGstStrmOut->State.fActive = fEnable;
1541 }
1542 }
1543
1544 return VINF_SUCCESS;
1545}
1546
1547static DECLCALLBACK(int) drvAudioEnableIn(PPDMIAUDIOCONNECTOR pInterface,
1548 PPDMAUDIOGSTSTRMIN pGstStrmIn, bool fEnable)
1549{
1550 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1551 /* pGstStrmIn is optional. */
1552
1553 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1554
1555 if (pGstStrmIn)
1556 {
1557 PPDMAUDIOHSTSTRMIN pHstStrmIn = pGstStrmIn->pHstStrmIn;
1558 AssertPtr(pHstStrmIn);
1559
1560 LogFlowFunc(("%s: fEnable=%RTbool\n", pGstStrmIn->MixBuf.pszName, fEnable));
1561
1562 if (pGstStrmIn->State.fActive != fEnable)
1563 {
1564 if (fEnable)
1565 {
1566 if (!pHstStrmIn->fEnabled)
1567 {
1568 int rc2 = pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn,
1569 PDMAUDIOSTREAMCMD_ENABLE);
1570 if (RT_LIKELY(RT_SUCCESS(rc2)))
1571 pHstStrmIn->fEnabled = true;
1572 else
1573 LogFlowFunc(("Error opening host input stream in backend, rc=%Rrc\n", rc2));
1574 }
1575 }
1576 else
1577 {
1578 if (pHstStrmIn->fEnabled)
1579 {
1580 int rc2 = pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn,
1581 PDMAUDIOSTREAMCMD_DISABLE);
1582 if (RT_LIKELY(RT_SUCCESS(rc2))) /* Did the backend veto? */
1583 pHstStrmIn->fEnabled = false;
1584 else
1585 LogFlowFunc(("Backend vetoed closing input stream, rc=%Rrc\n", rc2));
1586 }
1587 }
1588
1589 pGstStrmIn->State.fActive = fEnable;
1590 }
1591 }
1592
1593 return VINF_SUCCESS;
1594}
1595
1596static DECLCALLBACK(bool) drvAudioIsInputOK(PPDMIAUDIOCONNECTOR pInterface,
1597 PPDMAUDIOGSTSTRMIN pGstStrmIn)
1598{
1599 return (pGstStrmIn != NULL);
1600}
1601
1602static DECLCALLBACK(bool) drvAudioIsOutputOK(PPDMIAUDIOCONNECTOR pInterface,
1603 PPDMAUDIOGSTSTRMOUT pGstStrmOut)
1604{
1605 return (pGstStrmOut != NULL);
1606}
1607
1608static DECLCALLBACK(int) drvAudioOpenIn(PPDMIAUDIOCONNECTOR pInterface, const char *pszName,
1609 PDMAUDIORECSOURCE enmRecSource, PPDMAUDIOSTREAMCFG pCfg,
1610 PPDMAUDIOGSTSTRMIN *ppGstStrmIn)
1611{
1612 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1613 AssertPtrReturn(ppGstStrmIn, VERR_INVALID_POINTER);
1614 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
1615 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1616 AssertPtrReturn(ppGstStrmIn, VERR_INVALID_POINTER);
1617
1618 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1619
1620 LogFlowFunc(("pszName=%s, pCfg=%p\n", pszName, pCfg));
1621
1622 if (!drvAudioStreamCfgIsValid(pCfg))
1623 {
1624 LogFunc(("Input stream configuration is not valid, bailing out\n"));
1625 return VERR_INVALID_PARAMETER;
1626 }
1627
1628 PPDMAUDIOGSTSTRMIN pGstStrmIn = *ppGstStrmIn;
1629 if ( pGstStrmIn
1630 && drvAudioPCMPropsAreEqual(&pGstStrmIn->Props, pCfg))
1631 {
1632 LogFunc(("[%s] Exists and matches required configuration, skipping creation\n",
1633 pGstStrmIn->MixBuf.pszName));
1634 return VWRN_ALREADY_EXISTS;
1635 }
1636
1637 if ( !conf.fixed_in.enabled
1638 && pGstStrmIn)
1639 {
1640 drvAudioDestroyGstIn(pThis, pGstStrmIn);
1641 pGstStrmIn = NULL;
1642 }
1643
1644 int rc;
1645
1646 if (pGstStrmIn)
1647 {
1648 PPDMAUDIOHSTSTRMIN pHstStrmIn = pGstStrmIn->pHstStrmIn;
1649 AssertPtr(pHstStrmIn);
1650
1651 drvAudioGstInFreeRes(pGstStrmIn);
1652
1653 char *pszTemp;
1654 if (RTStrAPrintf(&pszTemp, "%s (Guest)", pszName) <= 0)
1655 {
1656 RTMemFree(pGstStrmIn);
1657 return VERR_NO_MEMORY;
1658 }
1659
1660 rc = drvAudioGstInInit(pGstStrmIn, pHstStrmIn, pszName, pCfg);
1661
1662 RTStrFree(pszTemp);
1663 }
1664 else
1665 rc = drvAudioCreateStreamPairIn(pThis, pszName, enmRecSource, pCfg, &pGstStrmIn);
1666
1667 if (pGstStrmIn)
1668 {
1669 pGstStrmIn->State.uVolumeLeft = nominal_volume.l;
1670 pGstStrmIn->State.uVolumeRight = nominal_volume.r;
1671 pGstStrmIn->State.fMuted = RT_BOOL(nominal_volume.mute);
1672
1673 *ppGstStrmIn = pGstStrmIn;
1674 }
1675
1676 LogFlowFuncLeaveRC(rc);
1677 return rc;
1678}
1679
1680DECLCALLBACK(int) drvAudioOpenOut(PPDMIAUDIOCONNECTOR pInterface, const char *pszName,
1681 PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOGSTSTRMOUT *ppGstStrmOut)
1682{
1683 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1684 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
1685 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1686 AssertPtrReturn(ppGstStrmOut, VERR_INVALID_POINTER);
1687
1688 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1689
1690 LogFlowFunc(("pszName=%s, pCfg=%p\n", pszName, pCfg));
1691
1692 if (!drvAudioStreamCfgIsValid(pCfg))
1693 {
1694 LogFunc(("Output stream configuration is not valid, bailing out\n"));
1695 return VERR_INVALID_PARAMETER;
1696 }
1697
1698 PPDMAUDIOGSTSTRMOUT pGstStrmOut = *ppGstStrmOut;
1699 if ( pGstStrmOut
1700 && drvAudioPCMPropsAreEqual(&pGstStrmOut->Props, pCfg))
1701 {
1702 LogFunc(("[%s] Exists and matches required configuration, skipping creation\n",
1703 pGstStrmOut->MixBuf.pszName));
1704 return VWRN_ALREADY_EXISTS;
1705 }
1706
1707#if 0
1708 /* Any live samples that need to be updated after
1709 * we set the new parameters? */
1710 PPDMAUDIOGSTSTRMOUT pOldGstStrmOut = NULL;
1711 uint32_t cLiveSamples = 0;
1712
1713 if ( conf.plive
1714 && pGstStrmOut
1715 && ( !pGstStrmOut->State.fActive
1716 && !pGstStrmOut->State.fEmpty))
1717 {
1718 cLiveSamples = pGstStrmOut->cTotalSamplesWritten;
1719 if (cLiveSamples)
1720 {
1721 pOldGstStrmOut = pGstStrmOut;
1722 pGstStrmOut = NULL;
1723 }
1724 }
1725#endif
1726
1727 if ( pGstStrmOut
1728 && !conf.fixed_out.enabled)
1729 {
1730 drvAudioDestroyGstOut(pThis, pGstStrmOut);
1731 pGstStrmOut = NULL;
1732 }
1733
1734 int rc;
1735 if (pGstStrmOut)
1736 {
1737 PPDMAUDIOHSTSTRMOUT pHstStrmOut = pGstStrmOut->pHstStrmOut;
1738 AssertPtr(pHstStrmOut);
1739
1740 drvAudioGstOutFreeRes(pGstStrmOut);
1741
1742 rc = drvAudioGstOutInit(pGstStrmOut, pHstStrmOut, pszName, pCfg);
1743 }
1744 else
1745 {
1746 rc = drvAudioCreateStreamPairOut(pThis, pszName, pCfg, &pGstStrmOut);
1747 if (RT_FAILURE(rc))
1748 LogFunc(("Failed to create output stream \"%s\", rc=%Rrc\n", pszName, rc));
1749 }
1750
1751 if (RT_SUCCESS(rc))
1752 {
1753 AssertPtr(pGstStrmOut);
1754 pGstStrmOut->State.uVolumeLeft = nominal_volume.l;
1755 pGstStrmOut->State.uVolumeRight = nominal_volume.r;
1756 pGstStrmOut->State.fMuted = RT_BOOL(nominal_volume.mute);
1757
1758 *ppGstStrmOut = pGstStrmOut;
1759#if 0
1760 /* Update remaining live samples with new rate. */
1761 if (cLiveSamples)
1762 {
1763 AssertPtr(pOldGstStrmOut);
1764
1765 uint32_t cSamplesMixed =
1766 (cLiveSamples << pOldGstStrmOut->Props.cShift)
1767 * pOldGstStrmOut->Props.cbPerSec
1768 / (*ppGstStrmOut)->Props.cbPerSec;
1769
1770 pGstStrmOut->cTotalSamplesWritten += cSamplesMixed;
1771 }
1772#endif
1773 }
1774
1775 LogFlowFuncLeaveRC(rc);
1776 return rc;
1777}
1778
1779static DECLCALLBACK(bool) drvAudioIsActiveIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn)
1780{
1781 return pGstStrmIn ? pGstStrmIn->State.fActive : false;
1782}
1783
1784static DECLCALLBACK(bool) drvAudioIsActiveOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut)
1785{
1786 return pGstStrmOut ? pGstStrmOut->State.fActive : false;
1787}
1788
1789static DECLCALLBACK(void) drvAudioCloseIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn)
1790{
1791 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1792 if (pGstStrmIn)
1793 drvAudioDestroyGstIn(pThis, pGstStrmIn);
1794}
1795
1796DECLCALLBACK(void) drvAudioCloseOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut)
1797{
1798 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1799 if (pGstStrmOut)
1800 drvAudioDestroyGstOut(pThis, pGstStrmOut);
1801}
1802
1803/********************************************************************/
1804
1805/**
1806 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1807 */
1808static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1809{
1810 LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
1811
1812 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1813 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1814
1815 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1816 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
1817
1818 return NULL;
1819}
1820
1821/**
1822 * Power Off notification.
1823 *
1824 * @param pDrvIns The driver instance data.
1825 */
1826static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
1827{
1828 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_DISABLE);
1829}
1830
1831/**
1832 * Destructs an audio driver instance.
1833 *
1834 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1835 * resources can be freed correctly.
1836 *
1837 * @param pDrvIns The driver instance data.
1838 */
1839static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
1840{
1841 LogFlowFuncEnter();
1842 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1843
1844 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1845
1846 /* Tear down all host output streams. */
1847 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
1848 while ((pHstStrmOut = drvAudioFindAnyHstOut(pThis, pHstStrmOut)))
1849 {
1850 pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
1851 pThis->pHostDrvAudio->pfnFiniOut(pThis->pHostDrvAudio, pHstStrmOut);
1852 }
1853
1854 /* Tear down all host input streams. */
1855 PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
1856 while ((pHstStrmIn = drvAudioFindNextHstIn(pThis, pHstStrmIn)))
1857 {
1858 pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn, PDMAUDIOSTREAMCMD_DISABLE);
1859 pThis->pHostDrvAudio->pfnFiniIn(pThis->pHostDrvAudio, pHstStrmIn);
1860 }
1861
1862 if (pThis->pHostDrvAudio->pfnShutdown)
1863 pThis->pHostDrvAudio->pfnShutdown(pThis->pHostDrvAudio);
1864
1865 LogFlowFuncLeave();
1866}
1867
1868/**
1869 * Constructs an audio driver instance.
1870 *
1871 * @copydoc FNPDMDRVCONSTRUCT
1872 */
1873static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
1874{
1875 LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfgHandle, fFlags));
1876
1877 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1878 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1879
1880 /*
1881 * Init the static parts.
1882 */
1883 pThis->pDrvIns = pDrvIns;
1884 /* IBase. */
1885 pDrvIns->IBase.pfnQueryInterface = drvAudioQueryInterface;
1886 /* IAudio. */
1887 pThis->IAudioConnector.pfnQueryStatus = drvAudioQueryStatus;
1888 pThis->IAudioConnector.pfnRead = drvAudioRead;
1889 pThis->IAudioConnector.pfnWrite = drvAudioWrite;
1890 pThis->IAudioConnector.pfnIsInputOK = drvAudioIsInputOK;
1891 pThis->IAudioConnector.pfnIsOutputOK = drvAudioIsOutputOK;
1892 pThis->IAudioConnector.pfnInitNull = drvAudioInitNull;
1893 pThis->IAudioConnector.pfnSetVolumeOut = drvAudioIsSetOutVolume;
1894 pThis->IAudioConnector.pfnSetVolume = drvAudioSetVolume;
1895 pThis->IAudioConnector.pfnEnableOut = drvAudioEnableOut;
1896 pThis->IAudioConnector.pfnEnableIn = drvAudioEnableIn;
1897 pThis->IAudioConnector.pfnCloseIn = drvAudioCloseIn;
1898 pThis->IAudioConnector.pfnCloseOut = drvAudioCloseOut;
1899 pThis->IAudioConnector.pfnOpenIn = drvAudioOpenIn;
1900 pThis->IAudioConnector.pfnOpenOut = drvAudioOpenOut;
1901 pThis->IAudioConnector.pfnPlayOut = drvAudioPlayOut;
1902 pThis->IAudioConnector.pfnIsActiveIn = drvAudioIsActiveIn;
1903 pThis->IAudioConnector.pfnIsActiveOut = drvAudioIsActiveOut;
1904
1905 /*
1906 * Attach driver below and query its connector interface.
1907 */
1908 PPDMIBASE pDownBase;
1909 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
1910 if (RT_FAILURE(rc))
1911 {
1912 LogRel(("Audio: Failed to attach to driver %p below (flags=0x%x), rc=%Rrc\n",
1913 pDrvIns, fFlags, rc));
1914 return rc;
1915 }
1916
1917 pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
1918 if (!pThis->pHostDrvAudio)
1919 {
1920 LogRel(("Audio: Failed to query interface for underlying host driver\n"));
1921 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
1922 N_("Host audio backend missing or invalid"));
1923 }
1924
1925#ifdef DEBUG_andy
1926 CFGMR3Dump(pCfgHandle);
1927#endif
1928
1929 rc = drvAudioInit(pCfgHandle, pDrvIns);
1930 if (RT_SUCCESS(rc))
1931 {
1932 pThis->fTerminate = false;
1933 pThis->pDrvIns = pDrvIns;
1934 }
1935
1936 LogFlowFuncLeaveRC(rc);
1937 return rc;
1938}
1939
1940/**
1941 * Suspend notification.
1942 *
1943 * @param pDrvIns The driver instance data.
1944 */
1945static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
1946{
1947 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_DISABLE);
1948}
1949
1950/**
1951 * Resume notification.
1952 *
1953 * @param pDrvIns The driver instance data.
1954 */
1955static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
1956{
1957 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_ENABLE);
1958}
1959
1960/**
1961 * Audio driver registration record.
1962 */
1963const PDMDRVREG g_DrvAUDIO =
1964{
1965 /* u32Version */
1966 PDM_DRVREG_VERSION,
1967 /* szName */
1968 "AUDIO",
1969 /* szRCMod */
1970 "",
1971 /* szR0Mod */
1972 "",
1973 /* pszDescription */
1974 "Audio connector driver",
1975 /* fFlags */
1976 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1977 /* fClass. */
1978 PDM_DRVREG_CLASS_AUDIO,
1979 /* cMaxInstances */
1980 2,
1981 /* cbInstance */
1982 sizeof(DRVAUDIO),
1983 /* pfnConstruct */
1984 drvAudioConstruct,
1985 /* pfnDestruct */
1986 drvAudioDestruct,
1987 /* pfnRelocate */
1988 NULL,
1989 /* pfnIOCtl */
1990 NULL,
1991 /* pfnPowerOn */
1992 NULL,
1993 /* pfnReset */
1994 NULL,
1995 /* pfnSuspend */
1996 drvAudioSuspend,
1997 /* pfnResume */
1998 drvAudioResume,
1999 /* pfnAttach */
2000 NULL,
2001 /* pfnDetach */
2002 NULL,
2003 /* pfnPowerOff */
2004 drvAudioPowerOff,
2005 /* pfnSoftReset */
2006 NULL,
2007 /* u32EndVersion */
2008 PDM_DRVREG_VERSION
2009};
Note: See TracBrowser for help on using the repository browser.

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