VirtualBox

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

Last change on this file since 55784 was 55387, checked in by vboxsync, 10 years ago

Audio: Keep track of sink volume, drop unused routines.

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