VirtualBox

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

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

Forgot to commit a file.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.6 KB
Line 
1/* $Id: AudioMixer.cpp 56692 2015-06-29 22:09:04Z 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#define LOG_GROUP LOG_GROUP_AUDIO_MIXER
20#include <VBox/log.h>
21#include "AudioMixer.h"
22#include "AudioMixBuffer.h"
23
24#include <VBox/vmm/pdm.h>
25#include <VBox/err.h>
26#include <VBox/vmm/mm.h>
27#include <VBox/vmm/pdmaudioifs.h>
28
29#include <iprt/alloc.h>
30#include <iprt/asm-math.h>
31#include <iprt/assert.h>
32#include <iprt/string.h>
33
34static int audioMixerUpdateSinkVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster);
35
36
37int AudioMixerAddSink(PAUDIOMIXER pMixer, const char *pszName, AUDMIXSINKDIR enmDir, PAUDMIXSINK *ppSink)
38{
39 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
40 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
41 /** ppSink is optional. */
42
43 int rc = VINF_SUCCESS;
44
45 PAUDMIXSINK pSink = (PAUDMIXSINK)RTMemAllocZ(sizeof(AUDMIXSINK));
46 if (pSink)
47 {
48 pSink->pszName = RTStrDup(pszName);
49 if (!pSink->pszName)
50 rc = VERR_NO_MEMORY;
51
52 if (RT_SUCCESS(rc))
53 {
54 pSink->pParent = pMixer;
55 pSink->cStreams = 0;
56 pSink->enmDir = enmDir;
57 RTListInit(&pSink->lstStreams);
58
59 /* Set initial volume to max. */
60 pSink->Volume.fMuted = false;
61 pSink->Volume.uLeft = 0x7F;
62 pSink->Volume.uRight = 0x7F;
63
64 RTListAppend(&pMixer->lstSinks, &pSink->Node);
65 pMixer->cSinks++;
66
67 LogFlowFunc(("pMixer=%p, pSink=%p, cSinks=%RU8\n",
68 pMixer, pSink, pMixer->cSinks));
69
70 if (ppSink)
71 *ppSink = pSink;
72 }
73 else
74 RTMemFree(pSink);
75 }
76 else
77 rc = VERR_NO_MEMORY;
78
79 return rc;
80}
81
82int AudioMixerAddStreamIn(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConnector, PPDMAUDIOGSTSTRMIN pStream,
83 uint32_t uFlags, PAUDMIXSTREAM *ppStream)
84{
85 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
86 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
87 /** @todo Add flag validation. */
88 /* ppStream is optional. */
89
90 int rc;
91
92 if (pSink->cStreams == UINT8_MAX) /* 255 streams per sink max. */
93 return VERR_TOO_MUCH_DATA;
94
95 PAUDMIXSTREAM pMixStream
96 = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
97 if (pMixStream)
98 {
99 pMixStream->pConn = pConnector;
100 pMixStream->pIn = pStream;
101 /** @todo Process flags. */
102
103 RTListAppend(&pSink->lstStreams, &pMixStream->Node);
104 pSink->cStreams++;
105
106 LogFlowFunc(("%s: pStream=%p, cStreams=%RU8\n",
107 pSink->pszName, pMixStream, pSink->cStreams));
108
109 if (ppStream)
110 *ppStream = pMixStream;
111
112 rc = VINF_SUCCESS;
113 }
114 else
115 rc = VERR_NO_MEMORY;
116
117 return rc;
118}
119
120int AudioMixerAddStreamOut(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConnector, PPDMAUDIOGSTSTRMOUT pStream,
121 uint32_t uFlags, PAUDMIXSTREAM *ppStream)
122{
123 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
124 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
125 /** @todo Add flag validation. */
126 /* ppStream is optional. */
127
128 int rc;
129
130 if (pSink->cStreams == UINT8_MAX) /* 255 streams per sink max. */
131 return VERR_TOO_MUCH_DATA;
132
133 PAUDMIXSTREAM pMixStream
134 = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
135 if (pMixStream)
136 {
137 pMixStream->pConn = pConnector;
138 pMixStream->pOut = pStream;
139 /** @todo Process flags. */
140
141 RTListAppend(&pSink->lstStreams, &pMixStream->Node);
142 pSink->cStreams++;
143
144 LogFlowFunc(("%s: pStream=%p, cStreams=%RU8\n",
145 pSink->pszName, pMixStream, pSink->cStreams));
146
147 if (ppStream)
148 *ppStream = pMixStream;
149
150 rc = VINF_SUCCESS;
151 }
152 else
153 rc = VERR_NO_MEMORY;
154
155 return rc;
156}
157
158int AudioMixerControlStream(PAUDIOMIXER pMixer, PAUDMIXSTREAM pHandle)
159{
160 return VERR_NOT_IMPLEMENTED;
161}
162
163int AudioMixerCreate(const char *pszName, uint32_t uFlags, PAUDIOMIXER *ppMixer)
164{
165 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
166 /** @todo Add flag validation. */
167 AssertPtrReturn(ppMixer, VERR_INVALID_POINTER);
168
169 int rc = VINF_SUCCESS;
170
171 PAUDIOMIXER pMixer = (PAUDIOMIXER)RTMemAllocZ(sizeof(AUDIOMIXER));
172 if (pMixer)
173 {
174 pMixer->pszName = RTStrDup(pszName);
175 if (!pMixer->pszName)
176 rc = VERR_NO_MEMORY;
177
178 if (RT_SUCCESS(rc))
179 {
180 pMixer->cSinks = 0;
181 RTListInit(&pMixer->lstSinks);
182
183 pMixer->VolMaster.fMuted = false;
184 pMixer->VolMaster.uLeft = UINT32_MAX;
185 pMixer->VolMaster.uRight = UINT32_MAX;
186
187 LogFlowFunc(("Created %p ...\n", pMixer));
188
189 *ppMixer = pMixer;
190 }
191 else
192 RTMemFree(pMixer);
193 }
194 else
195 rc = VERR_NO_MEMORY;
196
197 LogFlowFuncLeaveRC(rc);
198 return rc;
199}
200
201void AudioMixerDestroy(PAUDIOMIXER pMixer)
202{
203 if (pMixer)
204 {
205 LogFlowFunc(("Destroying %s ...\n", pMixer->pszName));
206
207 PAUDMIXSINK pSink = RTListGetFirst(&pMixer->lstSinks, AUDMIXSINK, Node);
208 while (pSink)
209 {
210 PAUDMIXSINK pNext = RTListNodeGetNext(&pSink->Node, AUDMIXSINK, Node);
211 bool fLast = RTListNodeIsLast(&pMixer->lstSinks, &pSink->Node);
212
213 AudioMixerRemoveSink(pMixer, pSink);
214
215 if (fLast)
216 break;
217
218 pSink = pNext;
219 }
220
221 Assert(pMixer->cSinks == 0);
222
223 RTStrFree(pMixer->pszName);
224
225 RTMemFree(pMixer);
226 }
227}
228
229static void audioMixerDestroySink(PAUDMIXSINK pSink)
230{
231 AssertPtrReturnVoid(pSink);
232 if (!pSink)
233 return;
234
235 RTStrFree(pSink->pszName);
236
237 RTMemFree(pSink);
238}
239
240static void audioMixerDestroyStream(PAUDMIXSTREAM pStream)
241{
242 AssertPtrReturnVoid(pStream);
243 if (!pStream)
244 return;
245
246 RTMemFree(pStream);
247}
248
249uint32_t AudioMixerGetStreamCount(PAUDIOMIXER pMixer)
250{
251 AssertPtrReturn(pMixer, 0);
252
253 uint32_t cStreams = 0;
254
255 PAUDMIXSINK pSink;
256 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
257 cStreams += pSink->cStreams;
258
259 return cStreams;
260}
261
262void AudioMixerInvalidate(PAUDIOMIXER pMixer)
263{
264 AssertPtrReturnVoid(pMixer);
265
266 LogFlowFunc(("%s: Invalidating ...\n", pMixer->pszName));
267
268 /* Propagate new master volume to all connected sinks. */
269 PAUDMIXSINK pSink;
270 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
271 {
272 int rc2 = audioMixerUpdateSinkVolume(pSink, &pMixer->VolMaster);
273 AssertRC(rc2);
274 }
275}
276
277int AudioMixerProcessSinkIn(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbProcessed)
278{
279 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
280 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
281 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
282 /* pcbProcessed is optional. */
283
284 /** @todo Handle mixing operation enmOp! */
285
286 uint8_t *pvMixBuf = (uint8_t *)RTMemAlloc(cbBuf);
287 if (!pvMixBuf)
288 return VERR_NO_MEMORY;
289
290 int rc = VERR_NOT_FOUND;
291 uint32_t cbProcessed = 0;
292
293 LogFlowFunc(("%s: pvBuf=%p, cbBuf=%zu\n", pSink->pszName, pvBuf, cbBuf));
294
295 PAUDMIXSTREAM pStream;
296 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
297 {
298 /** @todo Support output sinks as well! */
299 if (!pStream->pConn->pfnIsActiveIn(pStream->pConn, pStream->pIn))
300 continue;
301
302 uint32_t cbTotalRead = 0;
303 uint32_t cbToRead = cbBuf;
304
305 while (cbToRead)
306 {
307 uint32_t cbRead;
308 AssertPtr(pStream->pConn);
309 rc = pStream->pConn->pfnRead(pStream->pConn, pStream->pIn,
310 (uint8_t *)pvMixBuf + cbTotalRead, cbToRead, &cbRead);
311 if ( RT_FAILURE(rc)
312 || !cbRead)
313 break;
314
315 AssertBreakStmt(cbRead <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
316 cbToRead -= cbRead;
317 cbTotalRead += cbRead;
318 }
319
320 if (RT_FAILURE(rc))
321 continue;
322
323 cbProcessed = RT_MAX(cbProcessed, cbTotalRead);
324 }
325
326 if (RT_SUCCESS(rc))
327 {
328 memcpy(pvBuf, pvMixBuf, cbProcessed); /* @todo Use an intermediate mixing buffer per sink! */
329
330 if (pcbProcessed)
331 *pcbProcessed = cbProcessed;
332 }
333
334 RTMemFree(pvMixBuf);
335
336 LogFlowFunc(("cbProcessed=%RU32, rc=%Rrc\n", cbProcessed, rc));
337 return rc;
338}
339
340int AudioMixerProcessSinkOut(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbProcessed)
341{
342 return VERR_NOT_IMPLEMENTED;
343}
344
345void AudioMixerRemoveSink(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
346{
347 AssertPtrReturnVoid(pMixer);
348 if (!pSink)
349 return;
350
351 PAUDMIXSTREAM pStream = RTListGetFirst(&pSink->lstStreams, AUDMIXSTREAM, Node);
352 while (pStream)
353 {
354 PAUDMIXSTREAM pNext = RTListNodeGetNext(&pStream->Node, AUDMIXSTREAM, Node);
355 bool fLast = RTListNodeIsLast(&pSink->lstStreams, &pStream->Node);
356
357 AudioMixerRemoveStream(pSink, pStream);
358
359 if (fLast)
360 break;
361
362 pStream = pNext;
363 }
364
365 Assert(pSink->cStreams == 0);
366
367 RTListNodeRemove(&pSink->Node);
368 Assert(pMixer->cSinks);
369 pMixer->cSinks--;
370
371 LogFlowFunc(("%s: pSink=%s, cSinks=%RU8\n",
372 pMixer->pszName, pSink->pszName, pMixer->cSinks));
373
374 audioMixerDestroySink(pSink);
375}
376
377void AudioMixerRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
378{
379 AssertPtrReturnVoid(pSink);
380 if (!pStream)
381 return;
382
383 Assert(pSink->cStreams);
384 RTListNodeRemove(&pStream->Node);
385 pSink->cStreams--;
386
387 const char *pszStream = pSink->enmDir == AUDMIXSINKDIR_INPUT
388 ? pStream->pIn->MixBuf.pszName : pStream->pOut->MixBuf.pszName;
389
390 LogFlowFunc(("%s: pStream=%s, cStreams=%RU8\n",
391 pSink->pszName, pszStream, pSink->cStreams));
392
393 audioMixerDestroyStream(pStream);
394}
395
396int AudioMixerSetDeviceFormat(PAUDIOMIXER pMixer, PPDMAUDIOSTREAMCFG pCfg)
397{
398 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
399 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
400
401 /** @todo Perform a deep copy, if needed. */
402 pMixer->devFmt = *pCfg;
403
404 return VINF_SUCCESS;
405}
406
407static int audioMixerUpdateSinkVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
408{
409 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
410 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
411
412 LogFlowFunc(("Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
413 pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
414 LogFlowFunc(("%s: fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
415 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
416
417 /** @todo Very crude implementation for now -- needs more work! */
418
419 PDMAUDIOVOLUME volSink;
420 volSink.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
421 volSink.uLeft = (pSink->Volume.uLeft * pVolMaster->uLeft) / UINT8_MAX;
422 volSink.uRight = (pSink->Volume.uRight * pVolMaster->uRight) / UINT8_MAX;
423
424 LogFlowFunc(("\t-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
425 volSink.fMuted, volSink.uLeft, volSink.uRight));
426
427 bool fOut = pSink->enmDir == AUDMIXSINKDIR_OUTPUT;
428
429 /* Propagate new sink volume to all streams in the sink. */
430 PAUDMIXSTREAM pStream;
431 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
432 {
433 if (fOut)
434 AudioMixBufSetVolume(&pStream->pOut->MixBuf, &volSink);
435 else
436 AudioMixBufSetVolume(&pStream->pIn->MixBuf, &volSink);
437 }
438
439 return VINF_SUCCESS;
440}
441
442/** Set the master volume of the mixer. */
443int AudioMixerSetMasterVolume(PAUDIOMIXER pMixer, PPDMAUDIOVOLUME pVol)
444{
445 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
446 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
447
448 pMixer->VolMaster = *pVol;
449
450 LogFlowFunc(("%s: lVol=%RU32, rVol=%RU32 => fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
451 pMixer->pszName, pVol->uLeft, pVol->uRight,
452 pMixer->VolMaster.fMuted, pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight));
453
454 AudioMixerInvalidate(pMixer);
455 return VINF_SUCCESS;
456}
457
458/** Set the volume of an individual sink. */
459int AudioMixerSetSinkVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
460{
461 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
462 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
463 AssertPtr(pSink->pParent);
464
465 LogFlowFunc(("%s: fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n", pSink->pszName, pVol->fMuted, pVol->uLeft, pVol->uRight));
466
467 pSink->Volume = *pVol;
468
469 return audioMixerUpdateSinkVolume(pSink, &pSink->pParent->VolMaster);
470}
471
472void AudioMixerDebug(PAUDIOMIXER pMixer, PCDBGFINFOHLP pHlp, const char *pszArgs)
473{
474 PAUDMIXSINK pSink;
475 unsigned iSink = 0;
476
477 pHlp->pfnPrintf(pHlp, "[Master] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", pMixer->pszName,
478 pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight, pMixer->VolMaster.fMuted);
479
480 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
481 {
482 pHlp->pfnPrintf(pHlp, "[Sink %u] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", iSink, pSink->pszName,
483 pSink->Volume.uLeft, pSink->Volume.uRight, pSink->Volume.fMuted);
484 ++iSink;
485 }
486}
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