VirtualBox

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

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

Audio: Introduced reference counting for guest audio streams; this should prevent that the audio connector interface is destroying streams which still are being used by other parties, e.g. the audio mixer (bugref:8054).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.0 KB
Line 
1/* $Id: AudioMixer.cpp 58600 2015-11-06 12:26:32Z 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 /* Increase the stream's reference count to let others know
110 * we're reyling on it to be around now. */
111 pStream->State.cRefs++;
112
113 if (ppStream)
114 *ppStream = pMixStream;
115
116 rc = VINF_SUCCESS;
117 }
118 else
119 rc = VERR_NO_MEMORY;
120
121 return rc;
122}
123
124int AudioMixerAddStreamOut(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConnector, PPDMAUDIOGSTSTRMOUT pStream,
125 uint32_t uFlags, PAUDMIXSTREAM *ppStream)
126{
127 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
128 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
129 /** @todo Add flag validation. */
130 /* ppStream is optional. */
131
132 int rc;
133
134 if (pSink->cStreams == UINT8_MAX) /* 255 streams per sink max. */
135 return VERR_TOO_MUCH_DATA;
136
137 PAUDMIXSTREAM pMixStream
138 = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
139 if (pMixStream)
140 {
141 pMixStream->pConn = pConnector;
142 pMixStream->pOut = pStream;
143 /** @todo Process flags. */
144
145 RTListAppend(&pSink->lstStreams, &pMixStream->Node);
146 pSink->cStreams++;
147
148 LogFlowFunc(("%s: pStream=%p, cStreams=%RU8\n",
149 pSink->pszName, pMixStream, pSink->cStreams));
150
151 /* Increase the stream's reference count to let others know
152 * we're reyling on it to be around now. */
153 pStream->State.cRefs++;
154
155 if (ppStream)
156 *ppStream = pMixStream;
157
158 rc = VINF_SUCCESS;
159 }
160 else
161 rc = VERR_NO_MEMORY;
162
163 return rc;
164}
165
166int AudioMixerControlStream(PAUDIOMIXER pMixer, PAUDMIXSTREAM pHandle)
167{
168 return VERR_NOT_IMPLEMENTED;
169}
170
171int AudioMixerCreate(const char *pszName, uint32_t uFlags, PAUDIOMIXER *ppMixer)
172{
173 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
174 /** @todo Add flag validation. */
175 AssertPtrReturn(ppMixer, VERR_INVALID_POINTER);
176
177 int rc = VINF_SUCCESS;
178
179 PAUDIOMIXER pMixer = (PAUDIOMIXER)RTMemAllocZ(sizeof(AUDIOMIXER));
180 if (pMixer)
181 {
182 pMixer->pszName = RTStrDup(pszName);
183 if (!pMixer->pszName)
184 rc = VERR_NO_MEMORY;
185
186 if (RT_SUCCESS(rc))
187 {
188 pMixer->cSinks = 0;
189 RTListInit(&pMixer->lstSinks);
190
191 pMixer->VolMaster.fMuted = false;
192 pMixer->VolMaster.uLeft = UINT32_MAX;
193 pMixer->VolMaster.uRight = UINT32_MAX;
194
195 LogFlowFunc(("Created %p ...\n", pMixer));
196
197 *ppMixer = pMixer;
198 }
199 else
200 RTMemFree(pMixer);
201 }
202 else
203 rc = VERR_NO_MEMORY;
204
205 LogFlowFuncLeaveRC(rc);
206 return rc;
207}
208
209void AudioMixerDestroy(PAUDIOMIXER pMixer)
210{
211 if (!pMixer)
212 return;
213
214 LogFlowFunc(("Destroying %s ...\n", pMixer->pszName));
215
216 PAUDMIXSINK pSink, pSinkNext;
217 RTListForEachSafe(&pMixer->lstSinks, pSink, pSinkNext, AUDMIXSINK, Node)
218 AudioMixerRemoveSink(pMixer, pSink);
219
220 Assert(pMixer->cSinks == 0);
221
222 if (pMixer->pszName)
223 {
224 RTStrFree(pMixer->pszName);
225 pMixer->pszName = NULL;
226 }
227
228 RTMemFree(pMixer);
229}
230
231static void audioMixerDestroySink(PAUDMIXSINK pSink)
232{
233 AssertPtrReturnVoid(pSink);
234 if (!pSink)
235 return;
236
237 if (pSink->pszName)
238 RTStrFree(pSink->pszName);
239
240 RTMemFree(pSink);
241}
242
243static void audioMixerDestroyStream(PAUDMIXSTREAM pStream)
244{
245 AssertPtrReturnVoid(pStream);
246 if (!pStream)
247 return;
248
249 RTMemFree(pStream);
250}
251
252uint32_t AudioMixerGetStreamCount(PAUDIOMIXER pMixer)
253{
254 AssertPtrReturn(pMixer, 0);
255
256 uint32_t cStreams = 0;
257
258 PAUDMIXSINK pSink;
259 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
260 cStreams += pSink->cStreams;
261
262 return cStreams;
263}
264
265void AudioMixerInvalidate(PAUDIOMIXER pMixer)
266{
267 AssertPtrReturnVoid(pMixer);
268
269 LogFlowFunc(("%s: Invalidating ...\n", pMixer->pszName));
270
271 /* Propagate new master volume to all connected sinks. */
272 PAUDMIXSINK pSink;
273 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
274 {
275 int rc2 = audioMixerUpdateSinkVolume(pSink, &pMixer->VolMaster);
276 AssertRC(rc2);
277 }
278}
279
280int AudioMixerProcessSinkIn(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbProcessed)
281{
282 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
283 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
284 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
285 /* pcbProcessed is optional. */
286
287 /** @todo Handle mixing operation enmOp! */
288
289 uint8_t *pvMixBuf = (uint8_t *)RTMemAlloc(cbBuf);
290 if (!pvMixBuf)
291 return VERR_NO_MEMORY;
292
293 int rc = VERR_NOT_FOUND;
294 uint32_t cbProcessed = 0;
295
296 LogFlowFunc(("%s: pvBuf=%p, cbBuf=%zu\n", pSink->pszName, pvBuf, cbBuf));
297
298 PAUDMIXSTREAM pStream;
299 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
300 {
301 /** @todo Support output sinks as well! */
302 if (!pStream->pConn->pfnIsActiveIn(pStream->pConn, pStream->pIn))
303 continue;
304
305 uint32_t cbTotalRead = 0;
306 uint32_t cbToRead = cbBuf;
307
308 while (cbToRead)
309 {
310 uint32_t cbRead;
311 AssertPtr(pStream->pConn);
312 rc = pStream->pConn->pfnRead(pStream->pConn, pStream->pIn,
313 (uint8_t *)pvMixBuf + cbTotalRead, cbToRead, &cbRead);
314 if ( RT_FAILURE(rc)
315 || !cbRead)
316 break;
317
318 AssertBreakStmt(cbRead <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
319 cbToRead -= cbRead;
320 cbTotalRead += cbRead;
321 }
322
323 if (RT_FAILURE(rc))
324 continue;
325
326 cbProcessed = RT_MAX(cbProcessed, cbTotalRead);
327 }
328
329 if (RT_SUCCESS(rc))
330 {
331 memcpy(pvBuf, pvMixBuf, cbProcessed); /* @todo Use an intermediate mixing buffer per sink! */
332
333 if (pcbProcessed)
334 *pcbProcessed = cbProcessed;
335 }
336
337 RTMemFree(pvMixBuf);
338
339 LogFlowFunc(("cbProcessed=%RU32, rc=%Rrc\n", cbProcessed, rc));
340 return rc;
341}
342
343int AudioMixerProcessSinkOut(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbProcessed)
344{
345 return VERR_NOT_IMPLEMENTED;
346}
347
348void AudioMixerRemoveSink(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
349{
350 AssertPtrReturnVoid(pMixer);
351 if (!pSink)
352 return;
353
354 PAUDMIXSTREAM pStream, pStreamNext;
355 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
356 AudioMixerRemoveStream(pSink, pStream);
357
358 Assert(pSink->cStreams == 0);
359
360 RTListNodeRemove(&pSink->Node);
361 Assert(pMixer->cSinks);
362 pMixer->cSinks--;
363
364 LogFlowFunc(("%s: pSink=%s, cSinks=%RU8\n",
365 pMixer->pszName, pSink->pszName, pMixer->cSinks));
366
367 audioMixerDestroySink(pSink);
368}
369
370void AudioMixerRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
371{
372 AssertPtrReturnVoid(pSink);
373 if (!pStream)
374 return;
375
376 Assert(pSink->cStreams);
377 RTListNodeRemove(&pStream->Node);
378 pSink->cStreams--;
379
380#ifdef DEBUG
381 const char *pszStream = pSink->enmDir == AUDMIXSINKDIR_INPUT
382 ? pStream->pIn->MixBuf.pszName : pStream->pOut->MixBuf.pszName;
383
384 LogFlowFunc(("%s: pStream=%s, cStreams=%RU8\n",
385 pSink->pszName, pszStream ? pszStream : "<Unnamed>", pSink->cStreams));
386#endif
387
388 /* Decrease the reference count again. */
389 switch (pSink->enmDir)
390 {
391 case AUDMIXSINKDIR_INPUT:
392 {
393 Assert(pStream->pIn->State.cRefs);
394 pStream->pIn->State.cRefs--;
395 break;
396 }
397
398 case AUDMIXSINKDIR_OUTPUT:
399 {
400 Assert(pStream->pOut->State.cRefs);
401 pStream->pOut->State.cRefs--;
402 break;
403 }
404
405 default:
406 AssertMsgFailed(("Not implemented\n"));
407 break;
408 }
409
410 audioMixerDestroyStream(pStream);
411}
412
413int AudioMixerSetDeviceFormat(PAUDIOMIXER pMixer, PPDMAUDIOSTREAMCFG pCfg)
414{
415 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
416 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
417
418 /** @todo Perform a deep copy, if needed. */
419 pMixer->devFmt = *pCfg;
420
421 return VINF_SUCCESS;
422}
423
424static int audioMixerUpdateSinkVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
425{
426 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
427 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
428
429 LogFlowFunc(("Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
430 pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
431 LogFlowFunc(("%s: fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
432 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
433
434 /** @todo Very crude implementation for now -- needs more work! */
435
436 PDMAUDIOVOLUME volSink;
437 volSink.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
438 volSink.uLeft = (pSink->Volume.uLeft * pVolMaster->uLeft) / UINT8_MAX;
439 volSink.uRight = (pSink->Volume.uRight * pVolMaster->uRight) / UINT8_MAX;
440
441 LogFlowFunc(("\t-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
442 volSink.fMuted, volSink.uLeft, volSink.uRight));
443
444 bool fOut = pSink->enmDir == AUDMIXSINKDIR_OUTPUT;
445
446 /* Propagate new sink volume to all streams in the sink. */
447 PAUDMIXSTREAM pStream;
448 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
449 {
450 if (fOut)
451 AudioMixBufSetVolume(&pStream->pOut->MixBuf, &volSink);
452 else
453 AudioMixBufSetVolume(&pStream->pIn->MixBuf, &volSink);
454 }
455
456 return VINF_SUCCESS;
457}
458
459/** Set the master volume of the mixer. */
460int AudioMixerSetMasterVolume(PAUDIOMIXER pMixer, PPDMAUDIOVOLUME pVol)
461{
462 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
463 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
464
465 pMixer->VolMaster = *pVol;
466
467 LogFlowFunc(("%s: lVol=%RU32, rVol=%RU32 => fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
468 pMixer->pszName, pVol->uLeft, pVol->uRight,
469 pMixer->VolMaster.fMuted, pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight));
470
471 AudioMixerInvalidate(pMixer);
472 return VINF_SUCCESS;
473}
474
475/** Set the volume of an individual sink. */
476int AudioMixerSetSinkVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
477{
478 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
479 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
480 AssertPtr(pSink->pParent);
481
482 LogFlowFunc(("%s: fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n", pSink->pszName, pVol->fMuted, pVol->uLeft, pVol->uRight));
483
484 pSink->Volume = *pVol;
485
486 return audioMixerUpdateSinkVolume(pSink, &pSink->pParent->VolMaster);
487}
488
489void AudioMixerDebug(PAUDIOMIXER pMixer, PCDBGFINFOHLP pHlp, const char *pszArgs)
490{
491 PAUDMIXSINK pSink;
492 unsigned iSink = 0;
493
494 pHlp->pfnPrintf(pHlp, "[Master] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", pMixer->pszName,
495 pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight, pMixer->VolMaster.fMuted);
496
497 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
498 {
499 pHlp->pfnPrintf(pHlp, "[Sink %u] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", iSink, pSink->pszName,
500 pSink->Volume.uLeft, pSink->Volume.uRight, pSink->Volume.fMuted);
501 ++iSink;
502 }
503}
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