VirtualBox

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

Last change on this file since 59201 was 59102, checked in by vboxsync, 9 years ago

Audio: Fixed recording glitches.

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