VirtualBox

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

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

DrvAudio.cpp: Logging.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.1 KB
Line 
1/* $Id: DrvAudio.cpp 54942 2015-03-25 15:09:28Z 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 PPDMAUDIOSTREAMCFG pThisCfg;
619 if (conf.fixed_out.enabled)
620 pThisCfg = &conf.fixed_out.settings;
621 else
622 pThisCfg = pCfg;
623
624 AssertPtrReturn(pThisCfg, VERR_INVALID_POINTER);
625
626 LogFlowFunc(("Using fixed audio output settings: %RTbool\n",
627 RT_BOOL(conf.fixed_out.enabled)));
628
629 PPDMAUDIOGSTSTRMOUT pGstStrmOut =
630 (PPDMAUDIOGSTSTRMOUT)RTMemAllocZ(sizeof(PDMAUDIOGSTSTRMOUT));
631 if (!pGstStrmOut)
632 {
633 LogFlowFunc(("Failed to allocate memory for guest output stream \"%s\"\n", pszName));
634 return VERR_NO_MEMORY;
635 }
636
637 PPDMAUDIOHSTSTRMOUT pHstStrmOut;
638 int rc = drvAudioAddHstOut(pThis, pszName, pThisCfg, &pHstStrmOut);
639 if (RT_FAILURE(rc))
640 {
641 LogFlowFunc(("Error adding host output stream \"%s\", rc=%Rrc\n", pszName, rc));
642
643 RTMemFree(pGstStrmOut);
644 return rc;
645 }
646
647 rc = drvAudioGstOutInit(pGstStrmOut, pHstStrmOut, pszName, pThisCfg);
648 if (RT_SUCCESS(rc))
649 {
650 RTListPrepend(&pHstStrmOut->lstGstStrmOut, &pGstStrmOut->Node);
651
652 if (ppGstStrmOut)
653 *ppGstStrmOut = pGstStrmOut;
654 }
655
656 if (RT_FAILURE(rc))
657 drvAudioDestroyGstOut(pThis, pGstStrmOut);
658
659 LogFlowFuncLeaveRC(rc);
660 return rc;
661}
662
663static int drvAudioCreateStreamPairIn(PDRVAUDIO pThis, const char *pszName, PDMAUDIORECSOURCE enmRecSource,
664 PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOGSTSTRMIN *ppGstStrmIn)
665{
666 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
667 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
668
669 PPDMAUDIOSTREAMCFG pThisCfg;
670 if (conf.fixed_in.enabled)
671 {
672 pThisCfg = &conf.fixed_in.settings;
673 LogFlowFunc(("Using fixed audio settings\n"));
674 }
675 else
676 pThisCfg = pCfg;
677 AssertPtrReturn(pThisCfg, VERR_INVALID_POINTER);
678
679 PPDMAUDIOGSTSTRMIN pGstStrmIn = (PPDMAUDIOGSTSTRMIN)RTMemAllocZ(sizeof(PDMAUDIOGSTSTRMIN));
680 if (!pGstStrmIn)
681 return VERR_NO_MEMORY;
682
683
684 PPDMAUDIOHSTSTRMIN pHstStrmIn;
685 int rc = drvAudioHstInAdd(pThis, pszName, pThisCfg, enmRecSource, &pHstStrmIn);
686 if (RT_FAILURE(rc))
687 {
688 LogFunc(("Failed to add host audio input stream \"%s\", rc=%Rrc\n", pszName, rc));
689
690 RTMemFree(pGstStrmIn);
691 return rc;
692 }
693
694 rc = drvAudioGstInInit(pGstStrmIn, pHstStrmIn, pszName, pThisCfg);
695 if (RT_SUCCESS(rc))
696 {
697 pHstStrmIn->pGstStrmIn = pGstStrmIn;
698
699 if (ppGstStrmIn)
700 *ppGstStrmIn = pGstStrmIn;
701 }
702 else
703 drvAudioDestroyGstIn(pThis, pGstStrmIn);
704
705 LogFlowFuncLeaveRC(rc);
706 return rc;
707}
708
709/**
710 * Initializes a guest input stream.
711 *
712 * @return IPRT status code.
713 * @param pGstStrmIn Pointer to guest stream to initialize.
714 * @param pHstStrmIn Pointer to host input stream to associate this guest
715 * stream with.
716 * @param pszName Pointer to stream name to use for this stream.
717 * @param pCfg Pointer to stream configuration to use.
718 */
719int drvAudioGstInInit(PPDMAUDIOGSTSTRMIN pGstStrmIn, PPDMAUDIOHSTSTRMIN pHstStrmIn,
720 const char *pszName, PPDMAUDIOSTREAMCFG pCfg)
721{
722 AssertPtrReturn(pGstStrmIn, VERR_INVALID_POINTER);
723 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
724 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
725 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
726
727 int rc = drvAudioStreamCfgToProps(pCfg, &pGstStrmIn->Props);
728 if (RT_SUCCESS(rc))
729 {
730 char *pszTemp;
731 if (RTStrAPrintf(&pszTemp, "%s (Guest)", pszName) <= 0)
732 return VERR_NO_MEMORY;
733
734 rc = audioMixBufInit(&pGstStrmIn->MixBuf, pszTemp, &pHstStrmIn->Props,
735 audioMixBufSize(&pHstStrmIn->MixBuf));
736 if (RT_SUCCESS(rc))
737 rc = audioMixBufLinkTo(&pHstStrmIn->MixBuf, &pGstStrmIn->MixBuf);
738
739 RTStrFree(pszTemp);
740
741 if (RT_SUCCESS(rc))
742 {
743#ifdef DEBUG
744 drvAudioStreamCfgPrint(pCfg);
745#endif
746 pGstStrmIn->State.fActive = false;
747 pGstStrmIn->State.fEmpty = true;
748
749 pGstStrmIn->State.pszName = RTStrDup(pszName);
750 if (!pGstStrmIn->State.pszName)
751 return VERR_NO_MEMORY;
752
753 pGstStrmIn->pHstStrmIn = pHstStrmIn;
754 }
755 }
756
757 LogFlowFunc(("pszName=%s, rc=%Rrc\n", pszName, rc));
758 return rc;
759}
760
761static int drvAudioAllocHstIn(PDRVAUDIO pThis, const char *pszName, PPDMAUDIOSTREAMCFG pCfg,
762 PDMAUDIORECSOURCE enmRecSource, PPDMAUDIOHSTSTRMIN *ppHstStrmIn)
763{
764 if (!pThis->cFreeInputStreams)
765 {
766 LogFlowFunc(("No more input streams free to use, bailing out\n"));
767 return VERR_NO_MORE_HANDLES;
768 }
769
770 /* Validate backend configuration. */
771 if (!pThis->BackendCfg.cbStreamIn)
772 {
773 LogFlowFunc(("Backend input configuration not valid, bailing out\n"));
774 return VERR_INVALID_PARAMETER;
775 }
776
777 PPDMAUDIOHSTSTRMIN pHstStrmIn =
778 (PPDMAUDIOHSTSTRMIN)RTMemAllocZ(pThis->BackendCfg.cbStreamIn);
779 if (!pHstStrmIn)
780 {
781 LogFlowFunc(("Error allocating host innput stream with %RU32 bytes\n",
782 pThis->BackendCfg.cbStreamOut));
783 return VERR_NO_MEMORY;
784 }
785
786 int rc;
787 bool fInitialized = false;
788
789 do
790 {
791 uint32_t cSamples;
792 rc = pThis->pHostDrvAudio->pfnInitIn(pThis->pHostDrvAudio, pHstStrmIn,
793 pCfg, enmRecSource, &cSamples);
794 if (RT_FAILURE(rc))
795 {
796 LogFlowFunc(("Initializing host backend failed with rc=%Rrc\n", rc));
797 break;
798 }
799
800 fInitialized = true;
801
802 char *pszTemp;
803 if (RTStrAPrintf(&pszTemp, "%s (Host)", pszName) <= 0)
804 {
805 rc = VERR_NO_MEMORY;
806 break;
807 }
808
809 rc = audioMixBufInit(&pHstStrmIn->MixBuf, pszTemp, &pHstStrmIn->Props, cSamples);
810 if (RT_SUCCESS(rc))
811 {
812 RTListPrepend(&pThis->lstHstStrmIn, &pHstStrmIn->Node);
813 pThis->cFreeInputStreams--;
814 }
815
816 RTStrFree(pszTemp);
817
818 } while (0);
819
820 if (RT_FAILURE(rc))
821 {
822 if (fInitialized)
823 {
824 int rc2 = pThis->pHostDrvAudio->pfnFiniIn(pThis->pHostDrvAudio,
825 pHstStrmIn);
826 AssertRC(rc2);
827 }
828
829 drvAudioHstInFreeRes(pHstStrmIn);
830 RTMemFree(pHstStrmIn);
831 }
832 else
833 *ppHstStrmIn = pHstStrmIn;
834
835 LogFlowFuncLeaveRC(rc);
836 return rc;
837}
838
839/**
840 * Writes VM audio output data from the guest stream into the host stream.
841 * The attached host driver backend then will play out the audio in a
842 * later step then.
843 *
844 * @return IPRT status code.
845 * @return int
846 * @param pThis
847 * @param pGstStrmOut
848 * @param pvBuf
849 * @param cbBuf
850 * @param pcbWritten
851 */
852int drvAudioWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut,
853 const void *pvBuf, size_t cbBuf, uint32_t *pcbWritten)
854{
855 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
856 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
857
858 AssertPtrReturn(pGstStrmOut, VERR_INVALID_POINTER);
859 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
860 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
861 /* pcbWritten is optional. */
862
863 if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_OUT))
864 return VERR_NOT_AVAILABLE;
865
866 PPDMAUDIOHSTSTRMOUT pHstStrmOut = pGstStrmOut->pHstStrmOut;
867 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
868
869 AssertMsg(pGstStrmOut->pHstStrmOut->fEnabled,
870 ("Writing to disabled host output stream \"%s\" not possible\n",
871 pHstStrmOut->MixBuf.pszName));
872
873 /*
874 * First, write data from the device emulation into our
875 * guest mixing buffer.
876 */
877 uint32_t cWritten;
878 int rc = audioMixBufWriteAt(&pGstStrmOut->MixBuf, 0 /* Offset in samples */, pvBuf, cbBuf, &cWritten);
879
880 /*
881 * Second, mix the guest mixing buffer with the host mixing
882 * buffer so that the host backend can play the data lateron.
883 */
884 uint32_t cMixed;
885 if ( RT_SUCCESS(rc)
886 && cWritten)
887 {
888 rc = audioMixBufMixToParent(&pGstStrmOut->MixBuf, cWritten, &cMixed);
889 }
890 else
891 cMixed = 0;
892
893 if (RT_SUCCESS(rc))
894 {
895 /* Return the number of samples which actually have been mixed
896 * down to the parent, regardless how much samples were written
897 * into the children buffer. */
898 if (pcbWritten)
899 *pcbWritten = AUDIOMIXBUF_S2B(&pGstStrmOut->MixBuf, cMixed);
900 }
901
902 LogFlowFunc(("%s -> %s: Written pvBuf=%p, cbBuf=%zu, cWritten=%RU32 (%RU32 bytes), cMixed=%RU32, rc=%Rrc\n",
903 pGstStrmOut->MixBuf.pszName, pHstStrmOut->MixBuf.pszName, pvBuf, cbBuf, cWritten,
904 AUDIOMIXBUF_S2B(&pGstStrmOut->MixBuf, cWritten), cMixed, rc));
905 return rc;
906}
907
908PPDMAUDIOHSTSTRMOUT drvAudioFindAnyHstOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
909{
910 if (pHstStrmOut)
911 {
912 if (RTListNodeIsLast(&pThis->lstHstStrmOut, &pHstStrmOut->Node))
913 return NULL;
914
915 return RTListNodeGetNext(&pHstStrmOut->Node, PDMAUDIOHSTSTRMOUT, Node);
916 }
917
918 return RTListGetFirst(&pThis->lstHstStrmOut, PDMAUDIOHSTSTRMOUT, Node);
919}
920
921PPDMAUDIOHSTSTRMOUT drvAudioHstFindAnyEnabledOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHostStrmOut)
922{
923 while ((pHostStrmOut = drvAudioFindAnyHstOut(pThis, pHostStrmOut)))
924 {
925 if (pHostStrmOut->fEnabled)
926 return pHostStrmOut;
927 }
928
929 return NULL;
930}
931
932PPDMAUDIOHSTSTRMOUT drvAudioFindSpecificOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
933 PPDMAUDIOSTREAMCFG pCfg)
934{
935 while ((pHstStrmOut = drvAudioFindAnyHstOut(pThis, pHstStrmOut)))
936 {
937 if (drvAudioPCMPropsAreEqual(&pHstStrmOut->Props, pCfg))
938 return pHstStrmOut;
939 }
940
941 return NULL;
942}
943
944int drvAudioDestroyHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn)
945{
946 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
947 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
948
949 LogFlowFunc(("%s\n", pHstStrmIn->MixBuf.pszName));
950
951 int rc;
952 if (!pHstStrmIn->pGstStrmIn) /* No parent anymore? */
953 {
954 rc = pThis->pHostDrvAudio->pfnFiniIn(pThis->pHostDrvAudio, pHstStrmIn);
955 if (RT_SUCCESS(rc))
956 {
957 drvAudioHstInFreeRes(pHstStrmIn);
958
959 /* Remove from driver instance list. */
960 RTListNodeRemove(&pHstStrmIn->Node);
961
962 RTMemFree(pHstStrmIn);
963 pThis->cFreeInputStreams++;
964 }
965 }
966 else
967 rc = VERR_ACCESS_DENIED;
968
969 LogFlowFunc(("[%s] Still is being used, rc=%Rrc\n", pHstStrmIn->MixBuf.pszName, rc));
970 return rc;
971}
972
973static int drvAudioDestroyGstIn(PDRVAUDIO pThis, PPDMAUDIOGSTSTRMIN pGstStrmIn)
974{
975 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
976
977 LogFlowFunc(("%s\n", pGstStrmIn->MixBuf.pszName));
978
979 if (pGstStrmIn)
980 {
981 drvAudioGstInFreeRes(pGstStrmIn);
982
983 if (pGstStrmIn->pHstStrmIn)
984 {
985 /* Unlink child. */
986 pGstStrmIn->pHstStrmIn->pGstStrmIn = NULL;
987
988 /* Try destroying the associated host input stream. This could
989 * be skipped if there are other guest input streams with this
990 * host stream. */
991 drvAudioDestroyHstIn(pThis, pGstStrmIn->pHstStrmIn);
992 }
993
994 RTMemFree(pGstStrmIn);
995 }
996
997 return VINF_SUCCESS;
998}
999
1000static DECLCALLBACK(int) drvAudioQueryStatus(PPDMIAUDIOCONNECTOR pInterface,
1001 uint32_t *pcbAvailIn, uint32_t *pcbFreeOut,
1002 uint32_t *pcSamplesLive)
1003{
1004 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1005 /* pcbAvailIn is optional. */
1006 /* pcbFreeOut is optional. */
1007 /* pcSamplesLive is optional. */
1008
1009 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1010
1011 if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_OUT))
1012 return VERR_NOT_AVAILABLE;
1013
1014 int rc = VINF_SUCCESS;
1015 uint32_t cSamplesLive = 0;
1016
1017 /*
1018 * Playback.
1019 */
1020 uint32_t cbFreeOut = UINT32_MAX;
1021
1022 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
1023 while ((pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, pHstStrmOut)))
1024 {
1025 uint32_t cStreamsLive;
1026 cSamplesLive = drvAudioHstOutSamplesLive(pHstStrmOut, &cStreamsLive);
1027 if (!cStreamsLive)
1028 cSamplesLive = 0;
1029
1030 /* Has this stream marked as disabled but there still were guest streams relying
1031 * on it? Check if this stream now can be closed and do so, if possible. */
1032 if ( pHstStrmOut->fPendingDisable
1033 && !cStreamsLive)
1034 {
1035 /* Stop playing the current (pending) stream. */
1036 int rc2 = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut,
1037 PDMAUDIOSTREAMCMD_DISABLE);
1038 if (RT_SUCCESS(rc2))
1039 {
1040 pHstStrmOut->fEnabled = false;
1041 pHstStrmOut->fPendingDisable = false;
1042
1043 LogFunc(("[%s] Disabling stream\n", pHstStrmOut->MixBuf.pszName));
1044 }
1045 else
1046 LogFunc(("[%s] Backend vetoed against closing output stream, rc=%Rrc\n", pHstStrmOut->MixBuf.pszName, rc2));
1047
1048 continue;
1049 }
1050
1051 LogFlowFunc(("[%s] cSamplesLive=%RU32\n", pHstStrmOut->MixBuf.pszName, cSamplesLive));
1052
1053 /*
1054 * No live samples to play at the moment?
1055 *
1056 * Tell the device emulation for each connected guest stream how many
1057 * bytes are free so that the device emulation can continue writing data to
1058 * these streams.
1059 */
1060 PPDMAUDIOGSTSTRMOUT pGstStrmOut;
1061 if (!cSamplesLive)
1062 {
1063 uint32_t cbFree2 = UINT32_MAX;
1064 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
1065 {
1066 if (pGstStrmOut->State.fActive)
1067 {
1068 /* Tell the sound device emulation how many samples are free
1069 * so that it can start writing PCM data to us. */
1070 cbFree2 = RT_MIN(cbFree2, AUDIOMIXBUF_S2B_RATIO(&pGstStrmOut->MixBuf,
1071 audioMixBufFree(&pGstStrmOut->MixBuf)));
1072
1073 LogFlowFunc(("\t[%s] cbFree=%RU32\n", pGstStrmOut->MixBuf.pszName, cbFree2));
1074 }
1075 }
1076
1077 cbFreeOut = RT_MIN(cbFreeOut, cbFree2);
1078 }
1079 }
1080
1081 /*
1082 * Recording.
1083 */
1084 uint32_t cbAvailIn = 0;
1085
1086 PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
1087 while ((pHstStrmIn = drvAudioFindNextEnabledHstIn(pThis, pHstStrmIn)))
1088 {
1089 /* Call the host backend to capture the audio input data. */
1090 uint32_t cSamplesCaptured;
1091 int rc2 = pThis->pHostDrvAudio->pfnCaptureIn(pThis->pHostDrvAudio, pHstStrmIn,
1092 &cSamplesCaptured);
1093 if (RT_FAILURE(rc2))
1094 continue;
1095
1096 PPDMAUDIOGSTSTRMIN pGstStrmIn = pHstStrmIn->pGstStrmIn;
1097 AssertPtrBreak(pGstStrmIn);
1098
1099 if (pGstStrmIn->State.fActive)
1100 {
1101 cbAvailIn = RT_MAX(cbAvailIn, AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf,
1102 audioMixBufMixed(&pHstStrmIn->MixBuf)));
1103
1104 LogFlowFunc(("\t[%s] cbFree=%RU32\n", pHstStrmIn->MixBuf.pszName, cbAvailIn));
1105 }
1106 }
1107
1108 if (RT_SUCCESS(rc))
1109 {
1110 if (cbFreeOut == UINT32_MAX)
1111 cbFreeOut = 0;
1112
1113 if (pcbAvailIn)
1114 *pcbAvailIn = cbAvailIn;
1115
1116 if (pcbFreeOut)
1117 *pcbFreeOut = cbFreeOut;
1118
1119 if (pcSamplesLive)
1120 *pcSamplesLive = cSamplesLive;
1121 }
1122
1123 return rc;
1124}
1125
1126static DECLCALLBACK(int) drvAudioPlayOut(PPDMIAUDIOCONNECTOR pInterface, uint32_t *pcSamplesPlayed)
1127{
1128 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1129 /* pcSamplesPlayed is optional. */
1130
1131 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1132
1133 int rc = VINF_SUCCESS;
1134 uint32_t cSamplesPlayedMax = 0;
1135
1136 /*
1137 * Process all enabled host output streams.
1138 */
1139 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
1140 while ((pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, pHstStrmOut)))
1141 {
1142#if 0
1143 uint32_t cStreamsLive;
1144 uint32_t cSamplesLive = drvAudioHstOutSamplesLive(pHstStrmOut, &cStreamsLive);
1145 if (!cStreamsLive)
1146 cSamplesLive = 0;
1147
1148 /* Has this stream marked as disabled but there still were guest streams relying
1149 * on it? Check if this stream now can be closed and do so, if possible. */
1150 if ( pHstStrmOut->fPendingDisable
1151 && !cStreamsLive)
1152 {
1153 /* Stop playing the current (pending) stream. */
1154 int rc2 = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut,
1155 PDMAUDIOSTREAMCMD_DISABLE);
1156 if (RT_SUCCESS(rc2))
1157 {
1158 pHstStrmOut->fEnabled = false;
1159 pHstStrmOut->fPendingDisable = false;
1160
1161 LogFunc(("\t%p: Disabling stream\n", pHstStrmOut));
1162 }
1163 else
1164 LogFunc(("\t%p: Backend vetoed against closing output stream, rc=%Rrc\n",
1165 pHstStrmOut, rc2));
1166
1167 continue;
1168 }
1169#endif
1170
1171 uint32_t cSamplesPlayed = 0;
1172 int rc2 = pThis->pHostDrvAudio->pfnPlayOut(pThis->pHostDrvAudio, pHstStrmOut,
1173 &cSamplesPlayed);
1174 if (RT_SUCCESS(rc2))
1175 cSamplesPlayedMax = RT_MAX(cSamplesPlayed, cSamplesPlayedMax);
1176
1177 LogFlowFunc(("\t[%s] cSamplesPlayed=%RU32, rc=%Rrc\n", pHstStrmOut->MixBuf.pszName, cSamplesPlayed, rc2));
1178
1179 bool fNeedsCleanup = false;
1180
1181 PPDMAUDIOGSTSTRMOUT pGstStrmOut;
1182 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
1183 {
1184 if ( !pGstStrmOut->State.fActive
1185 && pGstStrmOut->State.fEmpty)
1186 continue;
1187
1188 if (audioMixBufIsEmpty(&pGstStrmOut->MixBuf))
1189 {
1190 pGstStrmOut->State.fEmpty = true;
1191 fNeedsCleanup |= !pGstStrmOut->State.fActive;
1192 }
1193 }
1194
1195 if (fNeedsCleanup)
1196 {
1197 RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
1198 {
1199 if (!pGstStrmOut->State.fActive)
1200 drvAudioDestroyGstOut(pThis, pGstStrmOut);
1201 }
1202 }
1203 }
1204
1205 if (RT_SUCCESS(rc))
1206 {
1207 if (pcSamplesPlayed)
1208 *pcSamplesPlayed = cSamplesPlayedMax;
1209 }
1210
1211 return rc;
1212}
1213
1214static int drvAudioHostInit(PCFGMNODE pCfgHandle, PDRVAUDIO pThis)
1215{
1216 /* pCfgHandle is optional. */
1217 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1218
1219 NOREF(pCfgHandle);
1220
1221 LogFlowFuncEnter();
1222
1223 int rc = pThis->pHostDrvAudio->pfnInit(pThis->pHostDrvAudio);
1224 if (RT_FAILURE(rc))
1225 {
1226 LogFlowFunc(("Initialization of lower driver failed with rc=%Rrc\n", rc));
1227 return rc;
1228 }
1229
1230 uint32_t cMaxHstStrmsOut = pThis->BackendCfg.cMaxHstStrmsOut;
1231 uint32_t cbHstStrmsOut = pThis->BackendCfg.cbStreamOut;
1232
1233 if (cbHstStrmsOut)
1234 {
1235 pThis->cFreeOutputStreams = 1; /** @todo Make this configurable. */
1236 if (pThis->cFreeOutputStreams > cMaxHstStrmsOut)
1237 {
1238 LogRel(("Audio: Warning: %RU32 output streams were requested, host driver only supports %RU32\n",
1239 pThis->cFreeOutputStreams, cMaxHstStrmsOut));
1240 pThis->cFreeOutputStreams = cMaxHstStrmsOut;
1241 }
1242 }
1243 else
1244 pThis->cFreeOutputStreams = 0;
1245
1246 uint32_t cMaxHstStrmsIn = pThis->BackendCfg.cMaxHstStrmsIn;
1247 uint32_t cbHstStrmIn = pThis->BackendCfg.cbStreamIn;
1248
1249 if (cbHstStrmIn)
1250 {
1251 /*
1252 * Note:
1253 * - Our AC'97 emulation has two inputs, line (P.IN) and microphone (P.MIC).
1254 ** @todo Document HDA.
1255 */
1256 pThis->cFreeInputStreams = 2; /** @todo Make this configurable. */
1257 if (pThis->cFreeInputStreams > cMaxHstStrmsIn)
1258 {
1259 LogRel(("Audio: Warning: %RU32 input streams were requested, host driver only supports %RU32\n",
1260 pThis->cFreeInputStreams, cMaxHstStrmsIn));
1261 pThis->cFreeInputStreams = cMaxHstStrmsIn;
1262 }
1263 }
1264 else
1265 pThis->cFreeInputStreams = 0;
1266
1267 LogFlowFunc(("cMaxHstStrmsOut=%RU32 (cb=%RU32), cMaxHstStrmsIn=%RU32 (cb=%RU32)\n",
1268 cMaxHstStrmsOut, cbHstStrmsOut, cMaxHstStrmsIn, cbHstStrmIn));
1269
1270 LogFlowFunc(("cFreeInputStreams=%RU8, cFreeOutputStreams=%RU8\n",
1271 pThis->cFreeInputStreams, pThis->cFreeOutputStreams));
1272
1273 LogFlowFuncLeave();
1274 return VINF_SUCCESS;
1275}
1276
1277static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
1278{
1279 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1280
1281 LogFlowFunc(("enmCmd=%ld\n", enmCmd));
1282
1283 if (!pThis->pHostDrvAudio)
1284 return;
1285
1286 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
1287 while ((pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, pHstStrmOut)))
1288 pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, enmCmd);
1289
1290 PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
1291 while ((pHstStrmIn = drvAudioFindNextEnabledHstIn(pThis, pHstStrmIn)))
1292 pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn, enmCmd);
1293}
1294
1295static struct audio_option audio_options[] =
1296{
1297 /* DAC */
1298 {"DACFixedSettings", AUD_OPT_BOOL, &conf.fixed_out.enabled,
1299 "Use fixed settings for host DAC", NULL, 0},
1300
1301 {"DACFixedFreq", AUD_OPT_INT, &conf.fixed_out.settings.uHz,
1302 "Frequency for fixed host DAC", NULL, 0},
1303
1304 {"DACFixedFmt", AUD_OPT_FMT, &conf.fixed_out.settings.enmFormat,
1305 "Format for fixed host DAC", NULL, 0},
1306
1307 {"DACFixedChannels", AUD_OPT_INT, &conf.fixed_out.settings.cChannels,
1308 "Number of channels for fixed DAC (1 - mono, 2 - stereo)", NULL, 0},
1309
1310 {"DACVoices", AUD_OPT_INT, &conf.fixed_out.cStreams, /** @todo Rename! */
1311 "Number of streams for DAC", NULL, 0},
1312
1313 /* ADC */
1314 {"ADCFixedSettings", AUD_OPT_BOOL, &conf.fixed_in.enabled,
1315 "Use fixed settings for host ADC", NULL, 0},
1316
1317 {"ADCFixedFreq", AUD_OPT_INT, &conf.fixed_in.settings.uHz,
1318 "Frequency for fixed host ADC", NULL, 0},
1319
1320 {"ADCFixedFmt", AUD_OPT_FMT, &conf.fixed_in.settings.enmFormat,
1321 "Format for fixed host ADC", NULL, 0},
1322
1323 {"ADCFixedChannels", AUD_OPT_INT, &conf.fixed_in.settings.cChannels,
1324 "Number of channels for fixed ADC (1 - mono, 2 - stereo)", NULL, 0},
1325
1326 {"ADCVoices", AUD_OPT_INT, &conf.fixed_in.cStreams, /** @todo Rename! */
1327 "Number of streams for ADC", NULL, 0},
1328
1329 /* Misc */
1330 {"TimerFreq", AUD_OPT_INT, &conf.period.hz,
1331 "Timer frequency in Hz (0 - use lowest possible)", NULL, 0},
1332
1333 {"PLIVE", AUD_OPT_BOOL, &conf.plive,
1334 "(undocumented)", NULL, 0}, /** @todo What is this? */
1335
1336 NULL
1337};
1338
1339static DECLCALLBACK(int) drvAudioInit(PCFGMNODE pCfgHandle, PPDMDRVINS pDrvIns)
1340{
1341 AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
1342 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
1343
1344 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1345 LogFlowFunc(("pDrvAudio=%p, pDrvIns=%p\n", pThis, pDrvIns));
1346
1347 RTListInit(&pThis->lstHstStrmIn);
1348 RTListInit(&pThis->lstHstStrmOut);
1349
1350 int rc = VINF_SUCCESS;
1351
1352 /* Get the configuration data from the selected backend (if available). */
1353 AssertPtr(pThis->pHostDrvAudio);
1354 if (RT_LIKELY(pThis->pHostDrvAudio->pfnGetConf))
1355 rc = pThis->pHostDrvAudio->pfnGetConf(pThis->pHostDrvAudio, &pThis->BackendCfg);
1356
1357 if (RT_SUCCESS(rc))
1358 {
1359 rc = drvAudioProcessOptions(pCfgHandle, "AUDIO", audio_options);
1360 /** @todo Check for invalid options? */
1361
1362 pThis->cFreeOutputStreams = conf.fixed_out.cStreams;
1363 pThis->cFreeInputStreams = conf.fixed_in.cStreams;
1364
1365 if (!pThis->cFreeOutputStreams)
1366 pThis->cFreeOutputStreams = 1;
1367
1368 if (!pThis->cFreeInputStreams)
1369 pThis->cFreeInputStreams = 1;
1370 }
1371
1372 /*
1373 * If everything went well, initialize the lower driver.
1374 */
1375 if (RT_SUCCESS(rc))
1376 rc = drvAudioHostInit(pCfgHandle, pThis);
1377
1378 LogFlowFuncLeaveRC(rc);
1379 return rc;
1380}
1381
1382static DECLCALLBACK(int) drvAudioInitNull(PPDMIAUDIOCONNECTOR pInterface)
1383{
1384 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1385 NOREF(pThis);
1386
1387 LogRel(("Audio: Using NULL driver; no sound will be audible\n"));
1388
1389 /* Nothing to do here yet. */
1390 return VINF_SUCCESS;
1391}
1392
1393static DECLCALLBACK(int) drvAudioRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn,
1394 void *pvBuf, size_t cbBuf, uint32_t *pcbRead)
1395{
1396 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1397 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1398
1399 AssertPtrReturn(pGstStrmIn, VERR_INVALID_POINTER);
1400 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1401 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1402 /* pcbWritten is optional. */
1403
1404 if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_IN))
1405 {
1406 if (pcbRead)
1407 *pcbRead = 0;
1408 return VINF_SUCCESS;
1409 }
1410
1411 PPDMAUDIOHSTSTRMIN pHstStrmIn = pGstStrmIn->pHstStrmIn;
1412 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1413
1414 AssertMsg(pGstStrmIn->pHstStrmIn->fEnabled,
1415 ("Reading from disabled host input stream \"%s\" not possible\n", pGstStrmIn->MixBuf.pszName));
1416
1417 /*
1418 * Read from the parent buffer (that is, the guest buffer) which
1419 * should have the audio data in the format the guest needs.
1420 */
1421 uint32_t cRead;
1422 int rc = audioMixBufReadCirc(&pGstStrmIn->MixBuf,
1423 pvBuf, cbBuf, &cRead);
1424 if (RT_SUCCESS(rc))
1425 {
1426 audioMixBufFinish(&pGstStrmIn->MixBuf, cRead);
1427
1428 if (pcbRead)
1429 *pcbRead = AUDIOMIXBUF_S2B(&pGstStrmIn->MixBuf, cRead);
1430 }
1431
1432 LogFlowFunc(("cRead=%RU32 (%RU32 bytes), rc=%Rrc\n",
1433 cRead, AUDIOMIXBUF_S2B(&pGstStrmIn->MixBuf, cRead), rc));
1434 return rc;
1435}
1436
1437static DECLCALLBACK(int) drvAudioIsSetOutVolume(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut,
1438 bool fMute, uint8_t uVolLeft, uint8_t uVolRight)
1439{
1440 LogFlowFunc(("[%s] fMute=%RTbool, uVolLeft=%RU8, uVolRight=%RU8\n",
1441 pGstStrmOut->MixBuf.pszName, fMute, uVolLeft, uVolRight));
1442
1443 if (pGstStrmOut)
1444 {
1445 pGstStrmOut->State.fMuted = fMute;
1446 pGstStrmOut->State.uVolumeLeft = (uint32_t)uVolLeft * 0x808080; /* maximum is INT_MAX = 0x7fffffff */
1447 pGstStrmOut->State.uVolumeRight = (uint32_t)uVolRight * 0x808080; /* maximum is INT_MAX = 0x7fffffff */
1448 }
1449
1450 return VINF_SUCCESS;
1451}
1452
1453/** @todo Handle more channels than stereo ... */
1454static DECLCALLBACK(int) drvAudioSetVolume(PPDMIAUDIOCONNECTOR pInterface,
1455 bool fMute, uint8_t uVolLeft, uint8_t uVolRight)
1456{
1457 LogFlowFunc(("fMute=%RTbool, uVolLeft=%RU8, uVolRight=%RU8\n",
1458 fMute, uVolLeft, uVolRight));
1459
1460 volume_t vol;
1461
1462 uint32_t u32VolumeLeft = uVolLeft;
1463 uint32_t u32VolumeRight = uVolRight;
1464
1465 /* 0x00..0xff => 0x01..0x100 */
1466 if (u32VolumeLeft)
1467 u32VolumeLeft++;
1468 if (u32VolumeRight)
1469 u32VolumeRight++;
1470 vol.mute = fMute ? 1 : 0;
1471 vol.l = u32VolumeLeft * 0x800000; /* maximum is 0x80000000 */
1472 vol.r = u32VolumeRight * 0x800000; /* maximum is 0x80000000 */
1473
1474 sum_out_volume.mute = 0;
1475 sum_out_volume.l = ASMMultU64ByU32DivByU32(INT_MAX, INT_MAX, 0x80000000U);
1476 sum_out_volume.r = ASMMultU64ByU32DivByU32(INT_MAX, INT_MAX, 0x80000000U);
1477
1478 LogFlowFunc(("sum_out l=%RU32, r=%RU32\n", sum_out_volume.l, sum_out_volume.r));
1479
1480 return VINF_SUCCESS;
1481}
1482
1483static DECLCALLBACK(int) drvAudioEnableOut(PPDMIAUDIOCONNECTOR pInterface,
1484 PPDMAUDIOGSTSTRMOUT pGstStrmOut, bool fEnable)
1485{
1486 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1487 /* pGstStrmOut is optional. */
1488
1489 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1490
1491 if (pGstStrmOut)
1492 {
1493 PPDMAUDIOHSTSTRMOUT pHstStrmOut = pGstStrmOut->pHstStrmOut;
1494 AssertPtr(pHstStrmOut);
1495
1496 LogFlowFunc(("%s: fEnable=%RTbool\n", pGstStrmOut->MixBuf.pszName, fEnable));
1497
1498 if (pGstStrmOut->State.fActive != fEnable)
1499 {
1500 if (fEnable)
1501 {
1502 pHstStrmOut->fPendingDisable = false;
1503 if (!pHstStrmOut->fEnabled)
1504 {
1505 pHstStrmOut->fEnabled = true;
1506 pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut,
1507 PDMAUDIOSTREAMCMD_ENABLE);
1508 /** @todo Check rc. */
1509 }
1510 }
1511 else
1512 {
1513 if (pHstStrmOut->fEnabled)
1514 {
1515 uint32_t cGstStrmsActive = 0;
1516
1517 PPDMAUDIOGSTSTRMOUT pIter;
1518 RTListForEach(&pHstStrmOut->lstGstStrmOut, pIter, PDMAUDIOGSTSTRMOUT, Node)
1519 {
1520 if (pIter->State.fActive)
1521 cGstStrmsActive++;
1522 }
1523
1524 pHstStrmOut->fPendingDisable = cGstStrmsActive == 1;
1525 }
1526 }
1527
1528 pGstStrmOut->State.fActive = fEnable;
1529 }
1530 }
1531
1532 return VINF_SUCCESS;
1533}
1534
1535static DECLCALLBACK(int) drvAudioEnableIn(PPDMIAUDIOCONNECTOR pInterface,
1536 PPDMAUDIOGSTSTRMIN pGstStrmIn, bool fEnable)
1537{
1538 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1539 /* pGstStrmIn is optional. */
1540
1541 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1542
1543 if (pGstStrmIn)
1544 {
1545 PPDMAUDIOHSTSTRMIN pHstStrmIn = pGstStrmIn->pHstStrmIn;
1546 AssertPtr(pHstStrmIn);
1547
1548 LogFlowFunc(("%s: fEnable=%RTbool\n", pGstStrmIn->MixBuf.pszName, fEnable));
1549
1550 if (pGstStrmIn->State.fActive != fEnable)
1551 {
1552 if (fEnable)
1553 {
1554 if (!pHstStrmIn->fEnabled)
1555 {
1556 int rc2 = pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn,
1557 PDMAUDIOSTREAMCMD_ENABLE);
1558 if (RT_LIKELY(RT_SUCCESS(rc2)))
1559 pHstStrmIn->fEnabled = true;
1560 else
1561 LogFlowFunc(("Error opening host input stream in backend, rc=%Rrc\n", rc2));
1562 }
1563 }
1564 else
1565 {
1566 if (pHstStrmIn->fEnabled)
1567 {
1568 int rc2 = pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn,
1569 PDMAUDIOSTREAMCMD_DISABLE);
1570 if (RT_LIKELY(RT_SUCCESS(rc2))) /* Did the backend veto? */
1571 pHstStrmIn->fEnabled = false;
1572 else
1573 LogFlowFunc(("Backend vetoed closing input stream, rc=%Rrc\n", rc2));
1574 }
1575 }
1576
1577 pGstStrmIn->State.fActive = fEnable;
1578 }
1579 }
1580
1581 return VINF_SUCCESS;
1582}
1583
1584static DECLCALLBACK(bool) drvAudioIsInputOK(PPDMIAUDIOCONNECTOR pInterface,
1585 PPDMAUDIOGSTSTRMIN pGstStrmIn)
1586{
1587 return (pGstStrmIn != NULL);
1588}
1589
1590static DECLCALLBACK(bool) drvAudioIsOutputOK(PPDMIAUDIOCONNECTOR pInterface,
1591 PPDMAUDIOGSTSTRMOUT pGstStrmOut)
1592{
1593 return (pGstStrmOut != NULL);
1594}
1595
1596static DECLCALLBACK(int) drvAudioOpenIn(PPDMIAUDIOCONNECTOR pInterface, const char *pszName,
1597 PDMAUDIORECSOURCE enmRecSource, PPDMAUDIOSTREAMCFG pCfg,
1598 PPDMAUDIOGSTSTRMIN *ppGstStrmIn)
1599{
1600 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1601 AssertPtrReturn(ppGstStrmIn, VERR_INVALID_POINTER);
1602 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
1603 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1604 AssertPtrReturn(ppGstStrmIn, VERR_INVALID_POINTER);
1605
1606 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1607
1608 LogFlowFunc(("pszName=%s, pCfg=%p\n", pszName, pCfg));
1609
1610 if (!drvAudioStreamCfgIsValid(pCfg))
1611 {
1612 LogFunc(("Input stream configuration is not valid, bailing out\n"));
1613 return VERR_INVALID_PARAMETER;
1614 }
1615
1616 PPDMAUDIOGSTSTRMIN pGstStrmIn = *ppGstStrmIn;
1617 if ( pGstStrmIn
1618 && drvAudioPCMPropsAreEqual(&pGstStrmIn->Props, pCfg))
1619 {
1620 LogFunc(("[%s] Exists and matches required configuration, skipping creation\n",
1621 pGstStrmIn->MixBuf.pszName));
1622 return VWRN_ALREADY_EXISTS;
1623 }
1624
1625 if ( !conf.fixed_in.enabled
1626 && pGstStrmIn)
1627 {
1628 drvAudioDestroyGstIn(pThis, pGstStrmIn);
1629 pGstStrmIn = NULL;
1630 }
1631
1632 int rc;
1633
1634 if (pGstStrmIn)
1635 {
1636 PPDMAUDIOHSTSTRMIN pHstStrmIn = pGstStrmIn->pHstStrmIn;
1637 AssertPtr(pHstStrmIn);
1638
1639 drvAudioGstInFreeRes(pGstStrmIn);
1640
1641 char *pszTemp;
1642 if (RTStrAPrintf(&pszTemp, "%s (Guest)", pszName) <= 0)
1643 {
1644 RTMemFree(pGstStrmIn);
1645 return VERR_NO_MEMORY;
1646 }
1647
1648 rc = drvAudioGstInInit(pGstStrmIn, pHstStrmIn, pszName, pCfg);
1649
1650 RTStrFree(pszTemp);
1651 }
1652 else
1653 rc = drvAudioCreateStreamPairIn(pThis, pszName, enmRecSource, pCfg, &pGstStrmIn);
1654
1655 if (pGstStrmIn)
1656 {
1657 pGstStrmIn->State.uVolumeLeft = nominal_volume.l;
1658 pGstStrmIn->State.uVolumeRight = nominal_volume.r;
1659 pGstStrmIn->State.fMuted = RT_BOOL(nominal_volume.mute);
1660
1661 *ppGstStrmIn = pGstStrmIn;
1662 }
1663
1664 LogFlowFuncLeaveRC(rc);
1665 return rc;
1666}
1667
1668DECLCALLBACK(int) drvAudioOpenOut(PPDMIAUDIOCONNECTOR pInterface, const char *pszName,
1669 PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOGSTSTRMOUT *ppGstStrmOut)
1670{
1671 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1672 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
1673 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1674 AssertPtrReturn(ppGstStrmOut, VERR_INVALID_POINTER);
1675
1676 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1677
1678 LogFlowFunc(("pszName=%s, pCfg=%p\n", pszName, pCfg));
1679
1680 if (!drvAudioStreamCfgIsValid(pCfg))
1681 {
1682 LogFunc(("Output stream configuration is not valid, bailing out\n"));
1683 return VERR_INVALID_PARAMETER;
1684 }
1685
1686 PPDMAUDIOGSTSTRMOUT pGstStrmOut = *ppGstStrmOut;
1687 if ( pGstStrmOut
1688 && drvAudioPCMPropsAreEqual(&pGstStrmOut->Props, pCfg))
1689 {
1690 LogFunc(("[%s] Exists and matches required configuration, skipping creation\n",
1691 pGstStrmOut->MixBuf.pszName));
1692 return VWRN_ALREADY_EXISTS;
1693 }
1694
1695#if 0
1696 /* Any live samples that need to be updated after
1697 * we set the new parameters? */
1698 PPDMAUDIOGSTSTRMOUT pOldGstStrmOut = NULL;
1699 uint32_t cLiveSamples = 0;
1700
1701 if ( conf.plive
1702 && pGstStrmOut
1703 && ( !pGstStrmOut->State.fActive
1704 && !pGstStrmOut->State.fEmpty))
1705 {
1706 cLiveSamples = pGstStrmOut->cTotalSamplesWritten;
1707 if (cLiveSamples)
1708 {
1709 pOldGstStrmOut = pGstStrmOut;
1710 pGstStrmOut = NULL;
1711 }
1712 }
1713#endif
1714
1715 if ( pGstStrmOut
1716 && !conf.fixed_out.enabled)
1717 {
1718 drvAudioDestroyGstOut(pThis, pGstStrmOut);
1719 pGstStrmOut = NULL;
1720 }
1721
1722 int rc;
1723 if (pGstStrmOut)
1724 {
1725 PPDMAUDIOHSTSTRMOUT pHstStrmOut = pGstStrmOut->pHstStrmOut;
1726 AssertPtr(pHstStrmOut);
1727
1728 drvAudioGstOutFreeRes(pGstStrmOut);
1729
1730 rc = drvAudioGstOutInit(pGstStrmOut, pHstStrmOut, pszName, pCfg);
1731 }
1732 else
1733 {
1734 rc = drvAudioCreateStreamPairOut(pThis, pszName, pCfg, &pGstStrmOut);
1735 if (RT_FAILURE(rc))
1736 LogFunc(("Failed to create output stream \"%s\", rc=%Rrc\n", pszName, rc));
1737 }
1738
1739 if (RT_SUCCESS(rc))
1740 {
1741 AssertPtr(pGstStrmOut);
1742 pGstStrmOut->State.uVolumeLeft = nominal_volume.l;
1743 pGstStrmOut->State.uVolumeRight = nominal_volume.r;
1744 pGstStrmOut->State.fMuted = RT_BOOL(nominal_volume.mute);
1745
1746 *ppGstStrmOut = pGstStrmOut;
1747#if 0
1748 /* Update remaining live samples with new rate. */
1749 if (cLiveSamples)
1750 {
1751 AssertPtr(pOldGstStrmOut);
1752
1753 uint32_t cSamplesMixed =
1754 (cLiveSamples << pOldGstStrmOut->Props.cShift)
1755 * pOldGstStrmOut->Props.cbPerSec
1756 / (*ppGstStrmOut)->Props.cbPerSec;
1757
1758 pGstStrmOut->cTotalSamplesWritten += cSamplesMixed;
1759 }
1760#endif
1761 }
1762
1763 LogFlowFuncLeaveRC(rc);
1764 return rc;
1765}
1766
1767static DECLCALLBACK(bool) drvAudioIsActiveIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn)
1768{
1769 return pGstStrmIn ? pGstStrmIn->State.fActive : false;
1770}
1771
1772static DECLCALLBACK(bool) drvAudioIsActiveOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut)
1773{
1774 return pGstStrmOut ? pGstStrmOut->State.fActive : false;
1775}
1776
1777static DECLCALLBACK(void) drvAudioCloseIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn)
1778{
1779 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1780 if (pGstStrmIn)
1781 drvAudioDestroyGstIn(pThis, pGstStrmIn);
1782}
1783
1784DECLCALLBACK(void) drvAudioCloseOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut)
1785{
1786 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
1787 if (pGstStrmOut)
1788 drvAudioDestroyGstOut(pThis, pGstStrmOut);
1789}
1790
1791/********************************************************************/
1792
1793/**
1794 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1795 */
1796static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1797{
1798 LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
1799
1800 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1801 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1802
1803 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1804 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
1805
1806 return NULL;
1807}
1808
1809/**
1810 * Power Off notification.
1811 *
1812 * @param pDrvIns The driver instance data.
1813 */
1814static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
1815{
1816 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_DISABLE);
1817}
1818
1819/**
1820 * Destructs an audio driver instance.
1821 *
1822 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1823 * resources can be freed correctly.
1824 *
1825 * @param pDrvIns The driver instance data.
1826 */
1827static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
1828{
1829 LogFlowFuncEnter();
1830 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1831
1832 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1833
1834 /* Tear down all host output streams. */
1835 PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
1836 while ((pHstStrmOut = drvAudioFindAnyHstOut(pThis, pHstStrmOut)))
1837 {
1838 pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
1839 pThis->pHostDrvAudio->pfnFiniOut(pThis->pHostDrvAudio, pHstStrmOut);
1840 }
1841
1842 /* Tear down all host input streams. */
1843 PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
1844 while ((pHstStrmIn = drvAudioFindNextHstIn(pThis, pHstStrmIn)))
1845 {
1846 pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn, PDMAUDIOSTREAMCMD_DISABLE);
1847 pThis->pHostDrvAudio->pfnFiniIn(pThis->pHostDrvAudio, pHstStrmIn);
1848 }
1849
1850 if (pThis->pHostDrvAudio->pfnShutdown)
1851 pThis->pHostDrvAudio->pfnShutdown(pThis->pHostDrvAudio);
1852
1853 LogFlowFuncLeave();
1854}
1855
1856/**
1857 * Constructs an audio driver instance.
1858 *
1859 * @copydoc FNPDMDRVCONSTRUCT
1860 */
1861static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
1862{
1863 LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfgHandle, fFlags));
1864
1865 PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
1866 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1867
1868 /*
1869 * Init the static parts.
1870 */
1871 pThis->pDrvIns = pDrvIns;
1872 /* IBase. */
1873 pDrvIns->IBase.pfnQueryInterface = drvAudioQueryInterface;
1874 /* IAudio. */
1875 pThis->IAudioConnector.pfnQueryStatus = drvAudioQueryStatus;
1876 pThis->IAudioConnector.pfnRead = drvAudioRead;
1877 pThis->IAudioConnector.pfnWrite = drvAudioWrite;
1878 pThis->IAudioConnector.pfnIsInputOK = drvAudioIsInputOK;
1879 pThis->IAudioConnector.pfnIsOutputOK = drvAudioIsOutputOK;
1880 pThis->IAudioConnector.pfnInitNull = drvAudioInitNull;
1881 pThis->IAudioConnector.pfnSetVolumeOut = drvAudioIsSetOutVolume;
1882 pThis->IAudioConnector.pfnSetVolume = drvAudioSetVolume;
1883 pThis->IAudioConnector.pfnEnableOut = drvAudioEnableOut;
1884 pThis->IAudioConnector.pfnEnableIn = drvAudioEnableIn;
1885 pThis->IAudioConnector.pfnCloseIn = drvAudioCloseIn;
1886 pThis->IAudioConnector.pfnCloseOut = drvAudioCloseOut;
1887 pThis->IAudioConnector.pfnOpenIn = drvAudioOpenIn;
1888 pThis->IAudioConnector.pfnOpenOut = drvAudioOpenOut;
1889 pThis->IAudioConnector.pfnPlayOut = drvAudioPlayOut;
1890 pThis->IAudioConnector.pfnIsActiveIn = drvAudioIsActiveIn;
1891 pThis->IAudioConnector.pfnIsActiveOut = drvAudioIsActiveOut;
1892
1893 /*
1894 * Attach driver below and query its connector interface.
1895 */
1896 PPDMIBASE pDownBase;
1897 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
1898 if (RT_FAILURE(rc))
1899 {
1900 LogRel(("Audio: Failed to attach to driver %p below (flags=0x%x), rc=%Rrc\n",
1901 pDrvIns, fFlags, rc));
1902 return rc;
1903 }
1904
1905 pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
1906 if (!pThis->pHostDrvAudio)
1907 {
1908 LogRel(("Audio: Failed to query interface for underlying host driver\n"));
1909 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
1910 N_("Host audio backend missing or invalid"));
1911 }
1912
1913#ifdef DEBUG_andy
1914 CFGMR3Dump(pCfgHandle);
1915#endif
1916
1917 rc = drvAudioInit(pCfgHandle, pDrvIns);
1918 if (RT_SUCCESS(rc))
1919 {
1920 pThis->fTerminate = false;
1921 pThis->pDrvIns = pDrvIns;
1922 }
1923
1924 LogFlowFuncLeaveRC(rc);
1925 return rc;
1926}
1927
1928/**
1929 * Suspend notification.
1930 *
1931 * @param pDrvIns The driver instance data.
1932 */
1933static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
1934{
1935 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_DISABLE);
1936}
1937
1938/**
1939 * Resume notification.
1940 *
1941 * @param pDrvIns The driver instance data.
1942 */
1943static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
1944{
1945 drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_ENABLE);
1946}
1947
1948/**
1949 * Audio driver registration record.
1950 */
1951const PDMDRVREG g_DrvAUDIO =
1952{
1953 /* u32Version */
1954 PDM_DRVREG_VERSION,
1955 /* szName */
1956 "AUDIO",
1957 /* szRCMod */
1958 "",
1959 /* szR0Mod */
1960 "",
1961 /* pszDescription */
1962 "Audio connector driver",
1963 /* fFlags */
1964 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1965 /* fClass. */
1966 PDM_DRVREG_CLASS_AUDIO,
1967 /* cMaxInstances */
1968 2,
1969 /* cbInstance */
1970 sizeof(DRVAUDIO),
1971 /* pfnConstruct */
1972 drvAudioConstruct,
1973 /* pfnDestruct */
1974 drvAudioDestruct,
1975 /* pfnRelocate */
1976 NULL,
1977 /* pfnIOCtl */
1978 NULL,
1979 /* pfnPowerOn */
1980 NULL,
1981 /* pfnReset */
1982 NULL,
1983 /* pfnSuspend */
1984 drvAudioSuspend,
1985 /* pfnResume */
1986 drvAudioResume,
1987 /* pfnAttach */
1988 NULL,
1989 /* pfnDetach */
1990 NULL,
1991 /* pfnPowerOff */
1992 drvAudioPowerOff,
1993 /* pfnSoftReset */
1994 NULL,
1995 /* u32EndVersion */
1996 PDM_DRVREG_VERSION
1997};
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