VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/AudioMixer.cpp@ 55043

Last change on this file since 55043 was 55005, checked in by vboxsync, 10 years ago

PDM/Audio: Added volume through virtual mixer.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.7 KB
Line 
1/* $Id: AudioMixer.cpp 55005 2015-03-30 12:07:39Z vboxsync $ */
2/** @file
3 * VBox audio: Mixing routines, mainly used by the various audio device
4 * emulations to achieve proper multiplexing from/to attached
5 * devices LUNs.
6 */
7
8/*
9 * Copyright (C) 2014-2015 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20#include "AudioMixer.h"
21#include "AudioMixBuffer.h"
22
23#include <VBox/vmm/pdm.h>
24#include <VBox/err.h>
25#include <VBox/vmm/mm.h>
26#include <VBox/vmm/pdmaudioifs.h>
27
28#include <iprt/alloc.h>
29#include <iprt/asm-math.h>
30#include <iprt/assert.h>
31#include <iprt/string.h>
32
33#ifdef LOG_GROUP
34# undef LOG_GROUP
35#endif
36#define LOG_GROUP LOG_GROUP_DEV_AUDIO
37#include <VBox/log.h>
38
39int audioMixerAddSink(PAUDIOMIXER pMixer, const char *pszName, AUDMIXSINKDIR enmDir, PAUDMIXSINK *ppSink)
40{
41 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
42 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
43 /** ppSink is optional. */
44
45 int rc = VINF_SUCCESS;
46
47 PAUDMIXSINK pSink = (PAUDMIXSINK)RTMemAllocZ(sizeof(AUDMIXSINK));
48 if (pSink)
49 {
50 pSink->pszName = RTStrDup(pszName);
51 if (!pSink->pszName)
52 rc = VERR_NO_MEMORY;
53
54 if (RT_SUCCESS(rc))
55 {
56 pSink->pParent = pMixer;
57 pSink->cStreams = 0;
58 pSink->enmDir = enmDir;
59 RTListInit(&pSink->lstStreams);
60
61 /* Set initial volume to max. */
62 pSink->Volume.fMuted = false;
63 pSink->Volume.uLeft = UINT32_MAX;
64 pSink->Volume.uRight = UINT32_MAX;
65
66 RTListAppend(&pMixer->lstSinks, &pSink->Node);
67 pMixer->cSinks++;
68
69 LogFlowFunc(("pMixer=%p, pSink=%p, cSinks=%RU8\n",
70 pMixer, pSink, pMixer->cSinks));
71
72 if (ppSink)
73 *ppSink = pSink;
74 }
75 else
76 RTMemFree(pSink);
77 }
78 else
79 rc = VERR_NO_MEMORY;
80
81 return rc;
82}
83
84int audioMixerAddStreamIn(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConnector, PPDMAUDIOGSTSTRMIN pStream,
85 uint32_t uFlags, PAUDMIXSTREAM *ppStream)
86{
87 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
88 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
89 /** @todo Add flag validation. */
90 /* ppStream is optional. */
91
92 int rc;
93
94 if (pSink->cStreams == UINT8_MAX) /* 255 streams per sink max. */
95 return VERR_TOO_MUCH_DATA;
96
97 PAUDMIXSTREAM pMixStream
98 = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
99 if (pMixStream)
100 {
101 pMixStream->pConn = pConnector;
102 pMixStream->pIn = pStream;
103 /** @todo Process flags. */
104
105 RTListAppend(&pSink->lstStreams, &pMixStream->Node);
106 pSink->cStreams++;
107
108 LogFlowFunc(("pSink=%p, pStream=%p, cStreams=%RU8\n",
109 pSink, pMixStream, pSink->cStreams));
110
111 if (ppStream)
112 *ppStream = pMixStream;
113
114 rc = VINF_SUCCESS;
115 }
116 else
117 rc = VERR_NO_MEMORY;
118
119 return rc;
120}
121
122int audioMixerAddStreamOut(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConnector, PPDMAUDIOGSTSTRMOUT pStream,
123 uint32_t uFlags, PAUDMIXSTREAM *ppStream)
124{
125 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
126 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
127 /** @todo Add flag validation. */
128 /* ppStream is optional. */
129
130 int rc;
131
132 if (pSink->cStreams == UINT8_MAX) /* 255 streams per sink max. */
133 return VERR_TOO_MUCH_DATA;
134
135 PAUDMIXSTREAM pMixStream
136 = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
137 if (pMixStream)
138 {
139 pMixStream->pConn = pConnector;
140 pMixStream->pOut = pStream;
141 /** @todo Process flags. */
142
143 RTListAppend(&pSink->lstStreams, &pMixStream->Node);
144 pSink->cStreams++;
145
146 LogFlowFunc(("pSink=%p, pStream=%p, cStreams=%RU8\n",
147 pSink, pMixStream, pSink->cStreams));
148
149 if (ppStream)
150 *ppStream = pMixStream;
151
152 rc = VINF_SUCCESS;
153 }
154 else
155 rc = VERR_NO_MEMORY;
156
157 return rc;
158}
159
160int audioMixerControlStream(PAUDIOMIXER pMixer, PAUDMIXSTREAM pHandle)
161{
162 return VERR_NOT_IMPLEMENTED;
163}
164
165int audioMixerCreate(const char *pszName, uint32_t uFlags, PAUDIOMIXER *ppMixer)
166{
167 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
168 /** @todo Add flag validation. */
169 AssertPtrReturn(ppMixer, VERR_INVALID_POINTER);
170
171 int rc = VINF_SUCCESS;
172
173 PAUDIOMIXER pMixer = (PAUDIOMIXER)RTMemAllocZ(sizeof(AUDIOMIXER));
174 if (pMixer)
175 {
176 pMixer->pszName = RTStrDup(pszName);
177 if (!pMixer->pszName)
178 rc = VERR_NO_MEMORY;
179
180 if (RT_SUCCESS(rc))
181 {
182 pMixer->cSinks = 0;
183 RTListInit(&pMixer->lstSinks);
184
185 pMixer->VolMaster.fMuted = false;
186 pMixer->VolMaster.uLeft = UINT32_MAX;
187 pMixer->VolMaster.uRight = UINT32_MAX;
188
189 LogFlowFunc(("Created %p ...\n", pMixer));
190
191 *ppMixer = pMixer;
192 }
193 else
194 RTMemFree(pMixer);
195 }
196 else
197 rc = VERR_NO_MEMORY;
198
199 LogFlowFuncLeaveRC(rc);
200 return rc;
201}
202
203void audioMixerDestroy(PAUDIOMIXER pMixer)
204{
205 if (pMixer)
206 {
207 LogFlowFunc(("Destroying %p ...\n", pMixer));
208
209 PAUDMIXSINK pSink = RTListGetFirst(&pMixer->lstSinks, AUDMIXSINK, Node);
210 while (pSink)
211 {
212 PAUDMIXSINK pNext = RTListNodeGetNext(&pSink->Node, AUDMIXSINK, Node);
213 bool fLast = RTListNodeIsLast(&pMixer->lstSinks, &pSink->Node);
214
215 audioMixerRemoveSink(pMixer, pSink);
216
217 if (fLast)
218 break;
219
220 pSink = pNext;
221 }
222
223 Assert(pMixer->cSinks == 0);
224
225 RTStrFree(pMixer->pszName);
226
227 RTMemFree(pMixer);
228 }
229}
230
231static void audioMixerDestroySink(PAUDMIXSINK pSink)
232{
233 AssertPtrReturnVoid(pSink);
234 if (!pSink)
235 return;
236
237 RTStrFree(pSink->pszName);
238
239 RTMemFree(pSink);
240}
241
242static void audioMixerDestroyStream(PAUDMIXSTREAM pStream)
243{
244 AssertPtrReturnVoid(pStream);
245 if (!pStream)
246 return;
247
248 RTMemFree(pStream);
249}
250
251uint32_t audioMixerGetStreamCount(PAUDIOMIXER pMixer)
252{
253 AssertPtrReturn(pMixer, 0);
254
255 uint32_t cStreams = 0;
256
257 PAUDMIXSINK pSink;
258 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
259 cStreams += pSink->cStreams;
260
261 return cStreams;
262}
263
264int audioMixerProcessSinkIn(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, size_t cbBuf, uint32_t *pcbProcessed)
265{
266 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
267 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
268 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
269 /* pcbProcessed is optional. */
270
271 /** @todo Handle mixing operation enmOp! */
272
273 uint8_t *pvMixBuf = (uint8_t *)RTMemAlloc(cbBuf);
274 if (!pvMixBuf)
275 return VERR_NO_MEMORY;
276
277 int rc = VERR_NOT_FOUND;
278 uint32_t cbProcessed = 0;
279
280 LogFlowFunc(("%s: pvBuf=%p, cbBuf=%zu\n", pSink->pszName, pvBuf, cbBuf));
281
282 PAUDMIXSTREAM pStream;
283 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
284 {
285 /** @todo Support output sinks as well! */
286 if (!pStream->pConn->pfnIsActiveIn(pStream->pConn, pStream->pIn))
287 continue;
288
289 uint32_t cbTotalRead = 0;
290 size_t cbToRead = cbBuf;
291
292 while (cbToRead)
293 {
294 uint32_t cbRead;
295 AssertPtr(pStream->pConn);
296 rc = pStream->pConn->pfnRead(pStream->pConn, pStream->pIn,
297 (uint8_t *)pvMixBuf + cbTotalRead, cbToRead, &cbRead);
298 if ( RT_FAILURE(rc)
299 || !cbRead)
300 break;
301
302 AssertBreakStmt(cbRead <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
303 cbToRead -= cbRead;
304 cbTotalRead += cbRead;
305 }
306
307 if (RT_FAILURE(rc))
308 continue;
309
310 cbProcessed = RT_MAX(cbProcessed, cbTotalRead);
311 }
312
313 if (RT_SUCCESS(rc))
314 {
315 memcpy(pvBuf, pvMixBuf, cbProcessed); /* @todo Use an intermediate mixing buffer per sink! */
316
317 if (pcbProcessed)
318 *pcbProcessed = cbProcessed;
319 }
320
321 RTMemFree(pvMixBuf);
322
323 LogFlowFunc(("cbProcessed=%RU32, rc=%Rrc\n", cbProcessed, rc));
324 return rc;
325}
326
327int audioMixerProcessSinkOut(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, size_t cbBuf, uint32_t *pcbProcessed)
328{
329 return VERR_NOT_IMPLEMENTED;
330}
331
332void audioMixerRemoveSink(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
333{
334 AssertPtrReturnVoid(pMixer);
335 if (!pSink)
336 return;
337
338 PAUDMIXSTREAM pStream = RTListGetFirst(&pSink->lstStreams, AUDMIXSTREAM, Node);
339 while (pStream)
340 {
341 PAUDMIXSTREAM pNext = RTListNodeGetNext(&pStream->Node, AUDMIXSTREAM, Node);
342 bool fLast = RTListNodeIsLast(&pSink->lstStreams, &pStream->Node);
343
344 audioMixerRemoveStream(pSink, pStream);
345
346 if (fLast)
347 break;
348
349 pStream = pNext;
350 }
351
352 Assert(pSink->cStreams == 0);
353
354 RTListNodeRemove(&pSink->Node);
355 Assert(pMixer->cSinks);
356 pMixer->cSinks--;
357
358 LogFlowFunc(("pMixer=%p, pSink=%p, cSinks=%RU8\n",
359 pMixer, pSink, pMixer->cSinks));
360
361 audioMixerDestroySink(pSink);
362}
363
364void audioMixerRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
365{
366 AssertPtrReturnVoid(pSink);
367 if (!pStream)
368 return;
369
370 Assert(pSink->cStreams);
371 RTListNodeRemove(&pStream->Node);
372 pSink->cStreams--;
373
374 LogFlowFunc(("pSink=%p, pStream=%p, cStreams=%RU8\n",
375 pSink, pStream, pSink->cStreams));
376
377 audioMixerDestroyStream(pStream);
378}
379
380int audioMixerSetDeviceFormat(PAUDIOMIXER pMixer, PPDMAUDIOSTREAMCFG pCfg)
381{
382 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
383 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
384
385 /** @todo Perform a deep copy, if needed. */
386 pMixer->devFmt = *pCfg;
387
388 return VINF_SUCCESS;
389}
390
391static PDMAUDIOVOLUME audioMixerTranslateVolume(const PPDMAUDIOVOLUME pVolMaster, PPDMAUDIOVOLUME pVol)
392{
393 PDMAUDIOVOLUME volMaster = *pVolMaster;
394
395 uint32_t u32VolumeLeft = (uint32_t)pVol->uLeft;
396 uint32_t u32VolumeRight = (uint32_t)pVol->uRight;
397 /* 0x00..0xff => 0x01..0x100 */
398 if (u32VolumeLeft)
399 u32VolumeLeft++;
400 if (u32VolumeRight)
401 u32VolumeRight++;
402
403 volMaster.uLeft = u32VolumeLeft * 0x800000; /* Maximum is 0x80000000 */
404 volMaster.uRight = u32VolumeRight * 0x800000; /* Maximum is 0x80000000 */
405
406 PDMAUDIOVOLUME volOut;
407 volOut.fMuted = volMaster.fMuted || pVol->fMuted;
408 volOut.uLeft = ASMMultU64ByU32DivByU32(volMaster.uLeft, pVol->uLeft, 0x80000000U); /* Maximum is 0x80000000U */
409 volOut.uRight = ASMMultU64ByU32DivByU32(volMaster.uRight, pVol->uRight, 0x80000000U); /* Maximum is 0x80000000U */
410
411 LogFlowFunc(("pMaster=%p, lVol=%RU32, rVol=%RU32\n",
412 pVolMaster, pVolMaster->uLeft, pVolMaster->uRight));
413 LogFlowFunc(("pVol=%p, lVol=%RU32, rVol=%RU32 => lVol=%RU32, rVol=%RU32\n",
414 pVol, pVol->uLeft, pVol->uRight, volOut.uLeft, volOut.uRight));
415
416 return volOut;
417}
418
419int audioMixerSetMasterVolume(PAUDIOMIXER pMixer, PPDMAUDIOVOLUME pVol)
420{
421 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
422 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
423
424 pMixer->VolMaster = audioMixerTranslateVolume(&pMixer->VolMaster, pVol);
425#if 0
426 .uLeft = pVol->uLeft * 0x800000; /* maximum is 0x80000000 */
427 pMixer->VolMaster.uRight = pVol->uRight * 0x800000; /* maximum is 0x80000000 */
428#endif
429 LogFlowFunc(("%s: lVol=%RU32, rVol=%RU32 => lVol=%RU32, rVol=%RU32\n",
430 pVol->uLeft, pVol->uRight, pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight));
431
432 return VINF_SUCCESS;
433}
434
435int audioMixerSetSinkVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
436{
437 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
438 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
439
440 LogFlowFunc(("%s: lVol=%RU32, rVol=%RU32\n", pSink->pszName, pVol->uLeft, pVol->uRight));
441
442 pSink->Volume.uLeft = pVol->uLeft * 0x808080; /* Maximum is INT_MAX = 0x7fffffff */
443 pSink->Volume.uRight = pVol->uRight * 0x808080; /* Maximum is INT_MAX = 0x7fffffff */
444
445 /** @todo Apply the mixer's master volume to it. */
446
447 /** @todo Very crude implementation for now -- needs more work! */
448
449 bool fOut = pSink->enmDir == AUDMIXSINKDIR_OUTPUT;
450
451 /* Propagate new sink volume to all streams in the sink. */
452 PAUDMIXSTREAM pStream;
453 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
454 {
455 if (fOut)
456 audioMixBufSetVolume(&pStream->pOut->MixBuf, &pSink->Volume);
457 else
458 audioMixBufSetVolume(&pStream->pIn->MixBuf, &pSink->Volume);
459 }
460
461 return VINF_SUCCESS;
462}
463
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