VirtualBox

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

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

Devices: Updated (C) year.

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