VirtualBox

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

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

header fixes

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

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