VirtualBox

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

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

Audio: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.6 KB
Line 
1/* $Id: DrvHostNullAudio.cpp 61320 2016-05-31 08:43:19Z 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#include <iprt/alloc.h>
44#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
45
46#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
47#include <VBox/log.h>
48#include <VBox/vmm/pdmaudioifs.h>
49
50#include "DrvAudio.h"
51#include "AudioMixBuffer.h"
52#include "VBoxDD.h"
53
54
55typedef struct NULLAUDIOSTREAMOUT
56{
57 /** Note: Always must come first! */
58 PDMAUDIOSTREAM Stream;
59 uint64_t u64TicksLast;
60 uint64_t csPlayBuffer;
61 uint8_t *pu8PlayBuffer;
62} NULLAUDIOSTREAMOUT, *PNULLAUDIOSTREAMOUT;
63
64typedef struct NULLAUDIOSTREAMIN
65{
66 /** Note: Always must come first! */
67 PDMAUDIOSTREAM Stream;
68} NULLAUDIOSTREAMIN, *PNULLAUDIOSTREAMIN;
69
70/**
71 * NULL audio driver instance data.
72 * @implements PDMIAUDIOCONNECTOR
73 */
74typedef struct DRVHOSTNULLAUDIO
75{
76 /** Pointer to the driver instance structure. */
77 PPDMDRVINS pDrvIns;
78 /** Pointer to host audio interface. */
79 PDMIHOSTAUDIO IHostAudio;
80} DRVHOSTNULLAUDIO, *PDRVHOSTNULLAUDIO;
81
82/*******************************************PDM_AUDIO_DRIVER******************************/
83
84
85static DECLCALLBACK(int) drvHostNullAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
86{
87 NOREF(pInterface);
88 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
89
90 pCfg->cbStreamOut = sizeof(NULLAUDIOSTREAMOUT);
91 pCfg->cbStreamIn = sizeof(NULLAUDIOSTREAMIN);
92
93 /* The NULL backend has exactly one input source and one output sink. */
94 pCfg->cSources = 1;
95 pCfg->cSinks = 1;
96
97 pCfg->cMaxStreamsOut = 1; /* Output */
98 pCfg->cMaxStreamsIn = 2; /* Line input + microphone input. */
99
100 return VINF_SUCCESS;
101}
102
103static DECLCALLBACK(int) drvHostNullAudioInit(PPDMIHOSTAUDIO pInterface)
104{
105 NOREF(pInterface);
106
107 LogFlowFuncLeaveRC(VINF_SUCCESS);
108 return VINF_SUCCESS;
109}
110
111static int nullCreateStreamIn(PPDMIHOSTAUDIO pInterface,
112 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
113{
114 NOREF(pInterface);
115
116 /* Just adopt the wanted stream configuration. */
117 int rc = DrvAudioHlpStreamCfgToProps(pCfg, &pStream->Props);
118 if (RT_SUCCESS(rc))
119 {
120 if (pcSamples)
121 *pcSamples = _1K;
122 }
123
124 LogFlowFuncLeaveRC(rc);
125 return rc;
126}
127
128static int nullCreateStreamOut(PPDMIHOSTAUDIO pInterface,
129 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg,
130 uint32_t *pcSamples)
131{
132 NOREF(pInterface);
133
134 /* Just adopt the wanted stream configuration. */
135 int rc = DrvAudioHlpStreamCfgToProps(pCfg, &pStream->Props);
136 if (RT_SUCCESS(rc))
137 {
138 PNULLAUDIOSTREAMOUT pNullStream = (PNULLAUDIOSTREAMOUT)pStream;
139 pNullStream->u64TicksLast = 0;
140 pNullStream->csPlayBuffer = _1K;
141 pNullStream->pu8PlayBuffer = (uint8_t *)RTMemAlloc(_1K << pStream->Props.cShift);
142 if (pNullStream->pu8PlayBuffer)
143 {
144 if (pcSamples)
145 *pcSamples = pNullStream->csPlayBuffer;
146 }
147 else
148 rc = VERR_NO_MEMORY;
149 }
150
151 LogFlowFuncLeaveRC(rc);
152 return rc;
153}
154
155static DECLCALLBACK(int) drvHostNullAudioStreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
156 uint32_t *pcSamplesPlayed)
157{
158 PDRVHOSTNULLAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTNULLAUDIO, IHostAudio);
159 PNULLAUDIOSTREAMOUT pNullStream = (PNULLAUDIOSTREAMOUT)pStream;
160
161 /* Consume as many samples as would be played at the current frequency since last call. */
162 uint32_t csLive = AudioMixBufAvail(&pStream->MixBuf);
163 uint64_t u64TicksNow = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
164 uint64_t u64TicksElapsed = u64TicksNow - pNullStream->u64TicksLast;
165 uint64_t u64TicksFreq = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns);
166
167 /* Remember when samples were consumed. */
168 pNullStream->u64TicksLast = u64TicksNow;
169
170 /*
171 * Minimize the rounding error by adding 0.5: samples = int((u64TicksElapsed * samplesFreq) / u64TicksFreq + 0.5).
172 * If rounding is not taken into account then the playback rate will be consistently lower that expected.
173 */
174 uint64_t cSamplesPlayed = (2 * u64TicksElapsed * pStream->Props.uHz + u64TicksFreq) / u64TicksFreq / 2;
175
176 /* Don't play more than available. */
177 if (cSamplesPlayed > csLive)
178 cSamplesPlayed = csLive;
179
180 cSamplesPlayed = RT_MIN(cSamplesPlayed, pNullStream->csPlayBuffer);
181
182 uint32_t csRead = 0;
183 AudioMixBufReadCirc(&pStream->MixBuf, pNullStream->pu8PlayBuffer,
184 AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesPlayed), &csRead);
185 AudioMixBufFinish(&pStream->MixBuf, csRead);
186
187 if (pcSamplesPlayed)
188 *pcSamplesPlayed = csRead;
189
190 return VINF_SUCCESS;
191}
192
193static DECLCALLBACK(int) drvHostNullAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
194 uint32_t *pcSamplesCaptured)
195{
196 /* Never capture anything. */
197 if (pcSamplesCaptured)
198 *pcSamplesCaptured = 0;
199
200 return VINF_SUCCESS;
201}
202
203static int nullDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
204{
205 LogFlowFuncLeaveRC(VINF_SUCCESS);
206 return VINF_SUCCESS;
207}
208
209static int nullDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
210{
211 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
212
213 PNULLAUDIOSTREAMOUT pNullStream = (PNULLAUDIOSTREAMOUT)pStream;
214 if ( pNullStream
215 && pNullStream->pu8PlayBuffer)
216 {
217 RTMemFree(pNullStream->pu8PlayBuffer);
218 pNullStream->pu8PlayBuffer = NULL;
219 }
220
221 LogFlowFuncLeaveRC(VINF_SUCCESS);
222 return VINF_SUCCESS;
223}
224
225static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostNullAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
226{
227 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
228
229 return PDMAUDIOBACKENDSTS_RUNNING;
230}
231
232static DECLCALLBACK(int) drvHostNullAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
233 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
234{
235 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
236 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
237 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
238
239 int rc;
240 if (pCfg->enmDir == PDMAUDIODIR_IN)
241 rc = nullCreateStreamIn(pInterface, pStream, pCfg, pcSamples);
242 else
243 rc = nullCreateStreamOut(pInterface, pStream, pCfg, pcSamples);
244
245 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc));
246 return rc;
247}
248
249static DECLCALLBACK(int) drvHostNullAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
250{
251 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
252 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
253
254 int rc;
255 if (pStream->enmDir == PDMAUDIODIR_IN)
256 rc = nullDestroyStreamIn(pInterface, pStream);
257 else
258 rc = nullDestroyStreamOut(pInterface, pStream);
259
260 return rc;
261}
262
263static DECLCALLBACK(int) drvHostNullAudioStreamControl(PPDMIHOSTAUDIO pInterface,
264 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
265{
266 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
267 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
268
269 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
270
271 return VINF_SUCCESS;
272}
273
274static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostNullAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
275{
276 NOREF(pInterface);
277 NOREF(pStream);
278
279 return (PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED);
280}
281
282static DECLCALLBACK(int) drvHostNullAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
283{
284 NOREF(pInterface);
285 NOREF(pStream);
286
287 return VINF_SUCCESS;
288}
289
290/**
291 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
292 */
293static DECLCALLBACK(void *) drvHostNullAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
294{
295 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
296 PDRVHOSTNULLAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTNULLAUDIO);
297
298 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
299 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
300 return NULL;
301}
302
303static DECLCALLBACK(void) drvHostNullAudioShutdown(PPDMIHOSTAUDIO pInterface)
304{
305 NOREF(pInterface);
306}
307
308/**
309 * Constructs a Null audio driver instance.
310 *
311 * @copydoc FNPDMDRVCONSTRUCT
312 */
313static DECLCALLBACK(int) drvHostNullAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
314{
315 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
316 /* pCfg is optional. */
317
318 PDRVHOSTNULLAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTNULLAUDIO);
319 LogRel(("Audio: Initializing NULL driver\n"));
320
321 /*
322 * Init the static parts.
323 */
324 pThis->pDrvIns = pDrvIns;
325 /* IBase */
326 pDrvIns->IBase.pfnQueryInterface = drvHostNullAudioQueryInterface;
327 /* IHostAudio */
328 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostNullAudio);
329
330 return VINF_SUCCESS;
331}
332
333/**
334 * Char driver registration record.
335 */
336const PDMDRVREG g_DrvHostNullAudio =
337{
338 /* u32Version */
339 PDM_DRVREG_VERSION,
340 /* szName */
341 "NullAudio",
342 /* szRCMod */
343 "",
344 /* szR0Mod */
345 "",
346 /* pszDescription */
347 "NULL audio host driver",
348 /* fFlags */
349 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
350 /* fClass. */
351 PDM_DRVREG_CLASS_AUDIO,
352 /* cMaxInstances */
353 ~0U,
354 /* cbInstance */
355 sizeof(DRVHOSTNULLAUDIO),
356 /* pfnConstruct */
357 drvHostNullAudioConstruct,
358 /* pfnDestruct */
359 NULL,
360 /* pfnRelocate */
361 NULL,
362 /* pfnIOCtl */
363 NULL,
364 /* pfnPowerOn */
365 NULL,
366 /* pfnReset */
367 NULL,
368 /* pfnSuspend */
369 NULL,
370 /* pfnResume */
371 NULL,
372 /* pfnAttach */
373 NULL,
374 /* pfnDetach */
375 NULL,
376 /* pfnPowerOff */
377 NULL,
378 /* pfnSoftReset */
379 NULL,
380 /* u32EndVersion */
381 PDM_DRVREG_VERSION
382};
383
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