VirtualBox

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

Last change on this file since 58450 was 58378, checked in by vboxsync, 9 years ago

Audio: Renaming / cleanups:

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

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