VirtualBox

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

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

Audio: Don't crash if backend is unable to initialize (bugref:8180).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.3 KB
Line 
1/* $Id: AudioMixer.cpp 59275 2016-01-07 11:57:56Z 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-2016 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
252int AudioMixerGetDeviceFormat(PAUDIOMIXER pMixer, PPDMAUDIOSTREAMCFG pCfg)
253{
254 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
255 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
256
257 /** @todo Perform a deep copy, if needed. */
258 *pCfg = pMixer->devFmt;
259
260 return VINF_SUCCESS;
261}
262
263uint32_t AudioMixerGetStreamCount(PAUDIOMIXER pMixer)
264{
265 AssertPtrReturn(pMixer, 0);
266
267 uint32_t cStreams = 0;
268
269 PAUDMIXSINK pSink;
270 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
271 cStreams += pSink->cStreams;
272
273 return cStreams;
274}
275
276void AudioMixerInvalidate(PAUDIOMIXER pMixer)
277{
278 AssertPtrReturnVoid(pMixer);
279
280 LogFlowFunc(("%s: Invalidating ...\n", pMixer->pszName));
281
282 /* Propagate new master volume to all connected sinks. */
283 PAUDMIXSINK pSink;
284 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
285 {
286 int rc2 = audioMixerUpdateSinkVolume(pSink, &pMixer->VolMaster);
287 AssertRC(rc2);
288 }
289}
290
291int AudioMixerProcessSinkIn(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbProcessed)
292{
293 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
294 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
295 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
296 /* pcbProcessed is optional. */
297
298 /** @todo Handle mixing operation enmOp! */
299
300 uint8_t *pvMixBuf = (uint8_t *)RTMemAlloc(cbBuf);
301 if (!pvMixBuf)
302 return VERR_NO_MEMORY;
303
304 int rc = VERR_NOT_FOUND;
305 uint32_t cbProcessed = 0;
306
307 LogFlowFunc(("%s: pvBuf=%p, cbBuf=%zu\n", pSink->pszName, pvBuf, cbBuf));
308
309 PAUDMIXSTREAM pStream;
310 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
311 {
312 /** @todo Support output sinks as well! */
313 if (!pStream->pConn->pfnIsActiveIn(pStream->pConn, pStream->pIn))
314 continue;
315
316 uint32_t cbTotalRead = 0;
317 uint32_t cbToRead = cbBuf;
318
319 while (cbToRead)
320 {
321 uint32_t cbRead;
322 AssertPtr(pStream->pConn);
323 rc = pStream->pConn->pfnRead(pStream->pConn, pStream->pIn,
324 (uint8_t *)pvMixBuf + cbTotalRead, cbToRead, &cbRead);
325 if ( RT_FAILURE(rc)
326 || !cbRead)
327 break;
328
329 AssertBreakStmt(cbRead <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
330 cbToRead -= cbRead;
331 cbTotalRead += cbRead;
332 }
333
334 if (RT_FAILURE(rc))
335 continue;
336
337 cbProcessed = RT_MAX(cbProcessed, cbTotalRead);
338 }
339
340 if (RT_SUCCESS(rc))
341 {
342 memcpy(pvBuf, pvMixBuf, cbProcessed); /* @todo Use an intermediate mixing buffer per sink! */
343
344 if (pcbProcessed)
345 *pcbProcessed = cbProcessed;
346 }
347
348 RTMemFree(pvMixBuf);
349
350 LogFlowFunc(("cbProcessed=%RU32, rc=%Rrc\n", cbProcessed, rc));
351 return rc;
352}
353
354int AudioMixerProcessSinkOut(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbProcessed)
355{
356 return VERR_NOT_IMPLEMENTED;
357}
358
359void AudioMixerRemoveSink(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
360{
361 AssertPtrReturnVoid(pMixer);
362 if (!pSink)
363 return;
364
365 PAUDMIXSTREAM pStream, pStreamNext;
366 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
367 AudioMixerRemoveStream(pSink, pStream);
368
369 Assert(pSink->cStreams == 0);
370
371 RTListNodeRemove(&pSink->Node);
372 Assert(pMixer->cSinks);
373 pMixer->cSinks--;
374
375 LogFlowFunc(("%s: pSink=%s, cSinks=%RU8\n",
376 pMixer->pszName, pSink->pszName, pMixer->cSinks));
377
378 audioMixerDestroySink(pSink);
379}
380
381void AudioMixerRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
382{
383 AssertPtrReturnVoid(pSink);
384 if (!pStream)
385 return;
386
387 Assert(pSink->cStreams);
388 RTListNodeRemove(&pStream->Node);
389 pSink->cStreams--;
390
391#ifdef DEBUG
392 const char *pszStream = pSink->enmDir == AUDMIXSINKDIR_INPUT
393 ? pStream->pIn->MixBuf.pszName : pStream->pOut->MixBuf.pszName;
394
395 LogFlowFunc(("%s: pStream=%s, cStreams=%RU8\n",
396 pSink->pszName, pszStream ? pszStream : "<Unnamed>", pSink->cStreams));
397#endif
398
399 /* Decrease the reference count again. */
400 switch (pSink->enmDir)
401 {
402 case AUDMIXSINKDIR_INPUT:
403 {
404 Assert(pStream->pIn->State.cRefs);
405 pStream->pIn->State.cRefs--;
406 break;
407 }
408
409 case AUDMIXSINKDIR_OUTPUT:
410 {
411 Assert(pStream->pOut->State.cRefs);
412 pStream->pOut->State.cRefs--;
413 break;
414 }
415
416 default:
417 AssertMsgFailed(("Not implemented\n"));
418 break;
419 }
420
421 audioMixerDestroyStream(pStream);
422}
423
424int AudioMixerSetDeviceFormat(PAUDIOMIXER pMixer, PPDMAUDIOSTREAMCFG pCfg)
425{
426 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
427 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
428
429 /** @todo Perform a deep copy, if needed. */
430 pMixer->devFmt = *pCfg;
431
432 return VINF_SUCCESS;
433}
434
435static int audioMixerUpdateSinkVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
436{
437 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
438 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
439
440 LogFlowFunc(("Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
441 pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
442 LogFlowFunc(("%s: fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
443 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
444
445 /** @todo Very crude implementation for now -- needs more work! */
446
447 PDMAUDIOVOLUME volSink;
448 volSink.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
449 volSink.uLeft = (pSink->Volume.uLeft * pVolMaster->uLeft) / UINT8_MAX;
450 volSink.uRight = (pSink->Volume.uRight * pVolMaster->uRight) / UINT8_MAX;
451
452 LogFlowFunc(("\t-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
453 volSink.fMuted, volSink.uLeft, volSink.uRight));
454
455 bool fOut = pSink->enmDir == AUDMIXSINKDIR_OUTPUT;
456
457 /* Propagate new sink volume to all streams in the sink. */
458 PAUDMIXSTREAM pStream;
459 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
460 {
461 if (fOut)
462 AudioMixBufSetVolume(&pStream->pOut->MixBuf, &volSink);
463 else
464 AudioMixBufSetVolume(&pStream->pIn->MixBuf, &volSink);
465 }
466
467 return VINF_SUCCESS;
468}
469
470/** Set the master volume of the mixer. */
471int AudioMixerSetMasterVolume(PAUDIOMIXER pMixer, PPDMAUDIOVOLUME pVol)
472{
473 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
474 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
475
476 pMixer->VolMaster = *pVol;
477
478 LogFlowFunc(("%s: lVol=%RU32, rVol=%RU32 => fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
479 pMixer->pszName, pVol->uLeft, pVol->uRight,
480 pMixer->VolMaster.fMuted, pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight));
481
482 AudioMixerInvalidate(pMixer);
483 return VINF_SUCCESS;
484}
485
486/** Set the volume of an individual sink. */
487int AudioMixerSetSinkVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
488{
489 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
490 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
491 AssertPtr(pSink->pParent);
492
493 LogFlowFunc(("%s: fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n", pSink->pszName, pVol->fMuted, pVol->uLeft, pVol->uRight));
494
495 pSink->Volume = *pVol;
496
497 return audioMixerUpdateSinkVolume(pSink, &pSink->pParent->VolMaster);
498}
499
500void AudioMixerDebug(PAUDIOMIXER pMixer, PCDBGFINFOHLP pHlp, const char *pszArgs)
501{
502 PAUDMIXSINK pSink;
503 unsigned iSink = 0;
504
505 pHlp->pfnPrintf(pHlp, "[Master] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", pMixer->pszName,
506 pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight, pMixer->VolMaster.fMuted);
507
508 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
509 {
510 pHlp->pfnPrintf(pHlp, "[Sink %u] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", iSink, pSink->pszName,
511 pSink->Volume.uLeft, pSink->Volume.uRight, pSink->Volume.fMuted);
512 ++iSink;
513 }
514}
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