VirtualBox

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

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

PDM/DrvAudio.cpp: Fixed a potential crash.

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