VirtualBox

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

Last change on this file since 57538 was 57451, checked in by vboxsync, 9 years ago

Audio: Try to fix chopped off samples when playing short sound files.

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