VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostNullAudio.cpp@ 60925

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

Audio: Update on infrastructure:

  • More work on HDA stream interleaving + surround support
  • The mixer can now (optionally) act as a supplemental layer between audio connector interface and device emulation (where applicable)
  • Multiple LUN streams can be bound to a certain sink, which in turn then can be treated as separate input/output channels
  • Unified more code which was duplicated between different audio device emulations
  • Tiny bit of documentation

Work in progress.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.6 KB
Line 
1/* $Id: DrvHostNullAudio.cpp 60925 2016-05-10 13:27:44Z vboxsync $ */
2/** @file
3 * NULL audio driver -- also acts as a fallback if no
4 * other backend is available.
5 */
6
7/*
8 * Copyright (C) 2006-2016 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 * --------------------------------------------------------------------
18 *
19 * This code is based on: noaudio.c QEMU based code.
20 *
21 * QEMU Timer based audio emulation
22 *
23 * Copyright (c) 2004-2005 Vassili Karpov (malc)
24 *
25 * Permission is hereby granted, free of charge, to any person obtaining a copy
26 * of this software and associated documentation files (the "Software"), to deal
27 * in the Software without restriction, including without limitation the rights
28 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
29 * copies of the Software, and to permit persons to whom the Software is
30 * furnished to do so, subject to the following conditions:
31 *
32 * The above copyright notice and this permission notice shall be included in
33 * all copies or substantial portions of the Software.
34 *
35 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
38 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
41 * THE SOFTWARE.
42 */
43#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
44#include <VBox/log.h>
45#include "DrvAudio.h"
46#include "AudioMixBuffer.h"
47
48#include "VBoxDD.h"
49
50#include <iprt/alloc.h>
51#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
52#include <VBox/vmm/pdmaudioifs.h>
53
54typedef struct NULLAUDIOSTREAMOUT
55{
56 /** Note: Always must come first! */
57 PDMAUDIOHSTSTRMOUT streamOut;
58 uint64_t u64TicksLast;
59 uint64_t csPlayBuffer;
60 uint8_t *pu8PlayBuffer;
61} NULLAUDIOSTREAMOUT, *PNULLAUDIOSTREAMOUT;
62
63typedef struct NULLAUDIOSTREAMIN
64{
65 /** Note: Always must come first! */
66 PDMAUDIOHSTSTRMIN streamIn;
67} NULLAUDIOSTREAMIN, *PNULLAUDIOSTREAMIN;
68
69/**
70 * NULL audio driver instance data.
71 * @implements PDMIAUDIOCONNECTOR
72 */
73typedef struct DRVHOSTNULLAUDIO
74{
75 /** Pointer to the driver instance structure. */
76 PPDMDRVINS pDrvIns;
77 /** Pointer to host audio interface. */
78 PDMIHOSTAUDIO IHostAudio;
79} DRVHOSTNULLAUDIO, *PDRVHOSTNULLAUDIO;
80
81/*******************************************PDM_AUDIO_DRIVER******************************/
82
83
84static DECLCALLBACK(int) drvHostNullAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
85{
86 NOREF(pInterface);
87 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
88
89 pCfg->cbStreamOut = sizeof(NULLAUDIOSTREAMOUT);
90 pCfg->cbStreamIn = sizeof(NULLAUDIOSTREAMIN);
91
92 /* The NULL backend has exactly one input source and one output sink. */
93 pCfg->cSources = 1;
94 pCfg->cSinks = 1;
95
96 pCfg->cMaxStreamsOut = 1; /* Output */
97 pCfg->cMaxStreamsIn = 2; /* Line input + microphone input. */
98
99 return VINF_SUCCESS;
100}
101
102static DECLCALLBACK(int) drvHostNullAudioInit(PPDMIHOSTAUDIO pInterface)
103{
104 NOREF(pInterface);
105
106 LogFlowFuncLeaveRC(VINF_SUCCESS);
107 return VINF_SUCCESS;
108}
109
110static DECLCALLBACK(int) drvHostNullAudioInitIn(PPDMIHOSTAUDIO pInterface,
111 PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
112 PDMAUDIORECSOURCE enmRecSource,
113 uint32_t *pcSamples)
114{
115 NOREF(pInterface);
116 NOREF(enmRecSource);
117
118 /* Just adopt the wanted stream configuration. */
119 int rc = DrvAudioStreamCfgToProps(pCfg, &pHstStrmIn->Props);
120 if (RT_SUCCESS(rc))
121 {
122 if (pcSamples)
123 *pcSamples = _1K;
124 }
125
126 LogFlowFuncLeaveRC(rc);
127 return rc;
128}
129
130static DECLCALLBACK(int) drvHostNullAudioInitOut(PPDMIHOSTAUDIO pInterface,
131 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
132 uint32_t *pcSamples)
133{
134 NOREF(pInterface);
135
136 /* Just adopt the wanted stream configuration. */
137 int rc = DrvAudioStreamCfgToProps(pCfg, &pHstStrmOut->Props);
138 if (RT_SUCCESS(rc))
139 {
140 PNULLAUDIOSTREAMOUT pNullStrmOut = (PNULLAUDIOSTREAMOUT)pHstStrmOut;
141 pNullStrmOut->u64TicksLast = 0;
142 pNullStrmOut->csPlayBuffer = _1K;
143 pNullStrmOut->pu8PlayBuffer = (uint8_t *)RTMemAlloc(_1K << pHstStrmOut->Props.cShift);
144 if (pNullStrmOut->pu8PlayBuffer)
145 {
146 if (pcSamples)
147 *pcSamples = pNullStrmOut->csPlayBuffer;
148 }
149 else
150 rc = VERR_NO_MEMORY;
151 }
152
153 LogFlowFuncLeaveRC(rc);
154 return rc;
155}
156
157static DECLCALLBACK(bool) drvHostNullAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
158{
159 NOREF(pInterface);
160 NOREF(enmDir);
161 return true; /* Always all enabled. */
162}
163
164static DECLCALLBACK(int) drvHostNullAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
165 uint32_t *pcSamplesPlayed)
166{
167 PDRVHOSTNULLAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTNULLAUDIO, IHostAudio);
168 PNULLAUDIOSTREAMOUT pNullStrmOut = (PNULLAUDIOSTREAMOUT)pHstStrmOut;
169
170 /* Consume as many samples as would be played at the current frequency since last call. */
171 uint32_t csLive = AudioMixBufAvail(&pHstStrmOut->MixBuf);
172 uint64_t u64TicksNow = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
173 uint64_t u64TicksElapsed = u64TicksNow - pNullStrmOut->u64TicksLast;
174 uint64_t u64TicksFreq = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns);
175
176 /* Remember when samples were consumed. */
177 pNullStrmOut->u64TicksLast = u64TicksNow;
178
179 /*
180 * Minimize the rounding error by adding 0.5: samples = int((u64TicksElapsed * samplesFreq) / u64TicksFreq + 0.5).
181 * If rounding is not taken into account then the playback rate will be consistently lower that expected.
182 */
183 uint64_t cSamplesPlayed = (2 * u64TicksElapsed * pHstStrmOut->Props.uHz + u64TicksFreq) / u64TicksFreq / 2;
184
185 /* Don't play more than available. */
186 if (cSamplesPlayed > csLive)
187 cSamplesPlayed = csLive;
188
189 cSamplesPlayed = RT_MIN(cSamplesPlayed, pNullStrmOut->csPlayBuffer);
190
191 uint32_t csRead = 0;
192 AudioMixBufReadCirc(&pHstStrmOut->MixBuf, pNullStrmOut->pu8PlayBuffer,
193 AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cSamplesPlayed), &csRead);
194 AudioMixBufFinish(&pHstStrmOut->MixBuf, csRead);
195
196 if (pcSamplesPlayed)
197 *pcSamplesPlayed = csRead;
198
199 return VINF_SUCCESS;
200}
201
202static DECLCALLBACK(int) drvHostNullAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
203 uint32_t *pcSamplesCaptured)
204{
205 /* Never capture anything. */
206 if (pcSamplesCaptured)
207 *pcSamplesCaptured = 0;
208
209 return VINF_SUCCESS;
210}
211
212static DECLCALLBACK(int) drvHostNullAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
213 PDMAUDIOSTREAMCMD enmStreamCmd)
214{
215 NOREF(pInterface);
216 NOREF(pHstStrmIn);
217 NOREF(enmStreamCmd);
218
219 return VINF_SUCCESS;
220}
221
222static DECLCALLBACK(int) drvHostNullAudioControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
223 PDMAUDIOSTREAMCMD enmStreamCmd)
224{
225 NOREF(pInterface);
226 NOREF(pHstStrmOut);
227 NOREF(enmStreamCmd);
228
229 return VINF_SUCCESS;
230}
231
232static DECLCALLBACK(int) drvHostNullAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
233{
234 LogFlowFuncLeaveRC(VINF_SUCCESS);
235 return VINF_SUCCESS;
236}
237
238static DECLCALLBACK(int) drvHostNullAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
239{
240 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
241
242 PNULLAUDIOSTREAMOUT pNullStrmOut = (PNULLAUDIOSTREAMOUT)pHstStrmOut;
243 if ( pNullStrmOut
244 && pNullStrmOut->pu8PlayBuffer)
245 {
246 RTMemFree(pNullStrmOut->pu8PlayBuffer);
247 pNullStrmOut->pu8PlayBuffer = NULL;
248 }
249
250 LogFlowFuncLeaveRC(VINF_SUCCESS);
251 return VINF_SUCCESS;
252}
253
254/**
255 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
256 */
257static DECLCALLBACK(void *) drvHostNullAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
258{
259 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
260 PDRVHOSTNULLAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTNULLAUDIO);
261
262 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
263 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
264 return NULL;
265}
266
267static DECLCALLBACK(void) drvHostNullAudioShutdown(PPDMIHOSTAUDIO pInterface)
268{
269 NOREF(pInterface);
270}
271
272/**
273 * Constructs a Null audio driver instance.
274 *
275 * @copydoc FNPDMDRVCONSTRUCT
276 */
277static DECLCALLBACK(int) drvHostNullAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
278{
279 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
280 /* pCfg is optional. */
281
282 PDRVHOSTNULLAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTNULLAUDIO);
283 LogRel(("Audio: Initializing NULL driver\n"));
284
285 /*
286 * Init the static parts.
287 */
288 pThis->pDrvIns = pDrvIns;
289 /* IBase */
290 pDrvIns->IBase.pfnQueryInterface = drvHostNullAudioQueryInterface;
291 /* IHostAudio */
292 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostNullAudio);
293
294 return VINF_SUCCESS;
295}
296
297/**
298 * Char driver registration record.
299 */
300const PDMDRVREG g_DrvHostNullAudio =
301{
302 /* u32Version */
303 PDM_DRVREG_VERSION,
304 /* szName */
305 "NullAudio",
306 /* szRCMod */
307 "",
308 /* szR0Mod */
309 "",
310 /* pszDescription */
311 "NULL audio host driver",
312 /* fFlags */
313 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
314 /* fClass. */
315 PDM_DRVREG_CLASS_AUDIO,
316 /* cMaxInstances */
317 ~0U,
318 /* cbInstance */
319 sizeof(DRVHOSTNULLAUDIO),
320 /* pfnConstruct */
321 drvHostNullAudioConstruct,
322 /* pfnDestruct */
323 NULL,
324 /* pfnRelocate */
325 NULL,
326 /* pfnIOCtl */
327 NULL,
328 /* pfnPowerOn */
329 NULL,
330 /* pfnReset */
331 NULL,
332 /* pfnSuspend */
333 NULL,
334 /* pfnResume */
335 NULL,
336 /* pfnAttach */
337 NULL,
338 /* pfnDetach */
339 NULL,
340 /* pfnPowerOff */
341 NULL,
342 /* pfnSoftReset */
343 NULL,
344 /* u32EndVersion */
345 PDM_DRVREG_VERSION
346};
347
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