VirtualBox

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

Last change on this file since 63549 was 63549, checked in by vboxsync, 8 years ago

scm cleanups

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