VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/AudioMixBuffer.cpp@ 55360

Last change on this file since 55360 was 55351, checked in by vboxsync, 10 years ago

Audio: Set nominal volume correctly.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.5 KB
Line 
1/* $Id: AudioMixBuffer.cpp 55351 2015-04-21 11:34:13Z vboxsync $ */
2/** @file
3 * VBox audio: Audio mixing buffer for converting reading/writing audio
4 * samples.
5 */
6
7/*
8 * Copyright (C) 2014-2015 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19/*
20 * DEBUG_DUMP_PCM_DATA enables dumping the raw PCM data
21 * to a file on the host. Be sure to adjust the dumping path
22 * to your needs before using this!
23 */
24#ifdef DEBUG
25//# define DEBUG_DUMP_PCM_DATA
26#endif
27
28#include <iprt/asm-math.h>
29#include <iprt/assert.h>
30#ifdef DEBUG_DUMP_PCM_DATA
31# include <iprt/file.h>
32#endif
33#include <iprt/mem.h>
34#include <iprt/string.h> /* For RT_BZERO. */
35
36#ifdef LOG_GROUP
37# undef LOG_GROUP
38#endif
39#define LOG_GROUP LOG_GROUP_DEV_AUDIO
40#include <VBox/log.h>
41
42#ifdef TESTCASE
43# define LOG_ENABLED
44# include <iprt/stream.h>
45#endif
46#include <VBox/err.h>
47
48#include "AudioMixBuffer.h"
49
50#if 0
51# define AUDMIXBUF_LOG(x) LogFlowFunc(x)
52#else
53# if defined(TESTCASE)
54# define AUDMIXBUF_LOG(x) LogFunc(x)
55# else
56# define AUDMIXBUF_LOG(x) do {} while (0)
57# endif
58#endif
59
60/**
61 * Structure for holding sample conversion parameters for
62 * the audioMixBufConvFromXXX / audioMixBufConvToXXX macros.
63 */
64typedef struct AUDMIXBUF_CONVOPTS
65{
66 /** Number of audio samples to convert. */
67 uint32_t cSamples;
68 /** Volume to apply during conversion. Pass 0
69 * to convert the original values. May not apply to
70 * all conversion functions. */
71 PDMAUDIOVOLUME Volume;
72} AUDMIXBUF_CONVOPTS, *PAUDMIXBUF_CONVOPTS;
73
74/*
75 * When running the audio testcases we want to verfiy
76 * the macro-generated routines separately, so unmark them as being
77 * inlined + static.
78 */
79#ifdef TESTCASE
80# define AUDMIXBUF_MACRO_FN
81#else
82# define AUDMIXBUF_MACRO_FN static inline
83#endif
84
85#ifdef DEBUG
86static uint64_t s_cSamplesMixedTotal = 0;
87#endif
88
89static void audioMixBufFreeBuf(PPDMAUDIOMIXBUF pMixBuf);
90static inline void audioMixBufPrint(PPDMAUDIOMIXBUF pMixBuf);
91
92typedef uint32_t (AUDMIXBUF_FN_CONVFROM) (PPDMAUDIOSAMPLE paDst, const void *pvSrc, uint32_t cbSrc, const PAUDMIXBUF_CONVOPTS pOpts);
93typedef AUDMIXBUF_FN_CONVFROM *PAUDMIXBUF_FN_CONVFROM;
94
95typedef void (AUDMIXBUF_FN_CONVTO) (void *pvDst, const PPDMAUDIOSAMPLE paSrc, const PAUDMIXBUF_CONVOPTS pOpts);
96typedef AUDMIXBUF_FN_CONVTO *PAUDMIXBUF_FN_CONVTO;
97
98/* Can return VINF_TRY_AGAIN for getting next pointer at beginning (circular) */
99int audioMixBufAcquire(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamplesToRead,
100 PPDMAUDIOSAMPLE *ppvSamples, uint32_t *pcSamplesRead)
101{
102 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
103 AssertPtrReturn(ppvSamples, VERR_INVALID_POINTER);
104 AssertPtrReturn(pcSamplesRead, VERR_INVALID_POINTER);
105
106 int rc;
107
108 if (!cSamplesToRead)
109 {
110 *pcSamplesRead = 0;
111 return VINF_SUCCESS;
112 }
113
114 uint32_t cSamplesRead;
115 if (pMixBuf->offReadWrite + cSamplesToRead > pMixBuf->cSamples)
116 {
117 cSamplesRead = pMixBuf->cSamples - pMixBuf->offReadWrite;
118 rc = VINF_TRY_AGAIN;
119 }
120 else
121 {
122 cSamplesRead = cSamplesToRead;
123 rc = VINF_SUCCESS;
124 }
125
126 *ppvSamples = &pMixBuf->pSamples[pMixBuf->offReadWrite];
127 AssertPtr(ppvSamples);
128
129 pMixBuf->offReadWrite = (pMixBuf->offReadWrite + cSamplesRead) % pMixBuf->cSamples;
130 Assert(pMixBuf->offReadWrite <= pMixBuf->cSamples);
131 pMixBuf->cProcessed -= RT_MIN(cSamplesRead, pMixBuf->cProcessed);
132
133 *pcSamplesRead = cSamplesRead;
134
135 return rc;
136}
137
138/**
139 * Clears (zeroes) the buffer by a certain amount of (processed) samples and
140 * keeps track to eventually assigned children buffers.
141 *
142 * @param pMixBuf
143 * @param cSamplesToClear
144 */
145void audioMixBufFinish(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamplesToClear)
146{
147 AUDMIXBUF_LOG(("cSamples=%RU32\n", cSamplesToClear));
148 AUDMIXBUF_LOG(("%s: offReadWrite=%RU32, cProcessed=%RU32\n",
149 pMixBuf->pszName, pMixBuf->offReadWrite, pMixBuf->cProcessed));
150
151 PPDMAUDIOMIXBUF pIter;
152 RTListForEach(&pMixBuf->lstBuffers, pIter, PDMAUDIOMIXBUF, Node)
153 {
154 AUDMIXBUF_LOG(("\t%s: cMixed=%RU32 -> %RU32\n",
155 pIter->pszName, pIter->cMixed, pIter->cMixed - cSamplesToClear));
156
157 pIter->cMixed -= RT_MIN(pIter->cMixed, cSamplesToClear);
158 pIter->offReadWrite = 0;
159 }
160
161 uint32_t cLeft = RT_MIN(cSamplesToClear, pMixBuf->cSamples);
162 uint32_t offClear;
163
164 if (cLeft > pMixBuf->offReadWrite) /* Zero end of buffer first (wrap-around). */
165 {
166 AUDMIXBUF_LOG(("Clearing1: %RU32 - %RU32\n",
167 (pMixBuf->cSamples - (cLeft - pMixBuf->offReadWrite)),
168 pMixBuf->cSamples));
169
170 RT_BZERO(pMixBuf->pSamples + (pMixBuf->cSamples - (cLeft - pMixBuf->offReadWrite)),
171 (cLeft - pMixBuf->offReadWrite) * sizeof(PDMAUDIOSAMPLE));
172
173 cLeft -= cLeft - pMixBuf->offReadWrite;
174 offClear = 0;
175 }
176 else
177 offClear = pMixBuf->offReadWrite - cLeft;
178
179 if (cLeft)
180 {
181 AUDMIXBUF_LOG(("Clearing2: %RU32 - %RU32\n",
182 offClear, offClear + cLeft));
183 RT_BZERO(pMixBuf->pSamples + offClear, cLeft * sizeof(PDMAUDIOSAMPLE));
184 }
185}
186
187void audioMixBufDestroy(PPDMAUDIOMIXBUF pMixBuf)
188{
189 if (!pMixBuf)
190 return;
191
192 audioMixBufUnlink(pMixBuf);
193
194 if (pMixBuf->pszName)
195 {
196 AUDMIXBUF_LOG(("%s\n", pMixBuf->pszName));
197
198 RTStrFree(pMixBuf->pszName);
199 pMixBuf->pszName = NULL;
200 }
201
202 if (pMixBuf->pRate)
203 {
204 RTMemFree(pMixBuf->pRate);
205 pMixBuf->pRate = NULL;
206 }
207
208 audioMixBufFreeBuf(pMixBuf);
209}
210
211/** @todo Rename this function! Too confusing in combination with audioMixBufFreeBuf(). */
212uint32_t audioMixBufFree(PPDMAUDIOMIXBUF pMixBuf)
213{
214 AssertPtrReturn(pMixBuf, 0);
215
216 uint32_t cFree;
217 if (pMixBuf->pParent)
218 {
219 /*
220 * As a linked child buffer we want to know how many samples
221 * already have been consumed by the parent.
222 */
223 Assert(pMixBuf->cMixed <= pMixBuf->pParent->cSamples);
224 cFree = pMixBuf->pParent->cSamples - pMixBuf->cMixed;
225 }
226 else
227 cFree = pMixBuf->cSamples - pMixBuf->cProcessed;
228
229 AUDMIXBUF_LOG(("%s: cFree=%RU32\n", pMixBuf->pszName, cFree));
230 return cFree;
231}
232
233uint32_t audioMixBufFreeBytes(PPDMAUDIOMIXBUF pMixBuf)
234{
235 return AUDIOMIXBUF_S2B(pMixBuf, audioMixBufFree(pMixBuf));
236}
237
238static int audioMixBufAllocBuf(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamples)
239{
240 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
241 AssertReturn(cSamples, VERR_INVALID_PARAMETER);
242
243 AUDMIXBUF_LOG(("%s: cSamples=%RU32\n", pMixBuf->pszName, cSamples));
244
245 size_t cbSamples = cSamples * sizeof(PDMAUDIOSAMPLE);
246 if (!cbSamples)
247 return VERR_INVALID_PARAMETER;
248
249 pMixBuf->pSamples = (PPDMAUDIOSAMPLE)RTMemAllocZ(cbSamples);
250 if (!pMixBuf->pSamples)
251 return VERR_NO_MEMORY;
252
253 pMixBuf->cSamples = cSamples;
254
255 return VINF_SUCCESS;
256}
257
258/** Note: Enabling this will generate huge logs! */
259//#define DEBUG_MACROS
260
261#ifdef DEBUG_MACROS
262# define AUDMIXBUF_MACRO_LOG(x) AUDMIXBUF_LOG(x)
263#elif defined(TESTCASE)
264# define AUDMIXBUF_MACRO_LOG(x) RTPrintf x
265#else
266# define AUDMIXBUF_MACRO_LOG(x) do {} while (0)
267#endif
268
269/**
270 * Macro for generating the conversion routines from/to different formats.
271 * Be careful what to pass in/out, as most of the macros are optimized for speed and
272 * thus don't do any bounds checking!
273 *
274 * Note: Currently does not handle any endianness conversion yet!
275 */
276#define AUDMIXBUF_CONVERT(_aName, _aType, _aMin, _aMax, _aSigned, _aShift) \
277 /* Clips a specific output value to a single sample value. */ \
278 AUDMIXBUF_MACRO_FN int64_t audioMixBufClipFrom##_aName(_aType aVal) \
279 { \
280 if (_aSigned) \
281 return ((int64_t) aVal) << (32 - _aShift); \
282 return ((int64_t) aVal - (_aMax >> 1)) << (32 - _aShift); \
283 } \
284 \
285 /* Clips a single sample value to a specific output value. */ \
286 AUDMIXBUF_MACRO_FN _aType audioMixBufClipTo##_aName(int64_t iVal) \
287 { \
288 if (iVal >= 0x7f000000) \
289 return _aMax; \
290 else if (iVal < -2147483648LL) \
291 return _aMin; \
292 \
293 if (_aSigned) \
294 return (_aType) (iVal >> (32 - _aShift)); \
295 return ((_aType) ((iVal >> (32 - _aShift)) + (_aMax >> 1))); \
296 } \
297 \
298 AUDMIXBUF_MACRO_FN uint32_t audioMixBufConvFrom##_aName##Stereo(PPDMAUDIOSAMPLE paDst, const void *pvSrc, uint32_t cbSrc, \
299 const PAUDMIXBUF_CONVOPTS pOpts) \
300 { \
301 _aType *pSrc = (_aType *)pvSrc; \
302 uint32_t cSamples = (uint32_t)RT_MIN(pOpts->cSamples, cbSrc / sizeof(_aType)); \
303 AUDMIXBUF_MACRO_LOG(("cSamples=%RU32, sizeof(%zu), lVol=%RU32, rVol=%RU32\n", \
304 pOpts->cSamples, sizeof(_aType), pOpts->Volume.uLeft, pOpts->Volume.uRight)); \
305 for (uint32_t i = 0; i < cSamples; i++) \
306 { \
307 AUDMIXBUF_MACRO_LOG(("%p: l=%RI16, r=%RI16\n", paDst, *pSrc, *(pSrc + 1))); \
308 paDst->i64LSample = ASMMult2xS32RetS64(audioMixBufClipFrom##_aName(*pSrc++), pOpts->Volume.uLeft ) >> 31; \
309 paDst->i64RSample = ASMMult2xS32RetS64(audioMixBufClipFrom##_aName(*pSrc++), pOpts->Volume.uRight) >> 31; \
310 AUDMIXBUF_MACRO_LOG(("\t-> l=%RI64, r=%RI64\n", paDst->i64LSample, paDst->i64RSample)); \
311 paDst++; \
312 } \
313 \
314 return cSamples; \
315 } \
316 \
317 AUDMIXBUF_MACRO_FN uint32_t audioMixBufConvFrom##_aName##Mono(PPDMAUDIOSAMPLE paDst, const void *pvSrc, uint32_t cbSrc, \
318 const PAUDMIXBUF_CONVOPTS pOpts) \
319 { \
320 _aType *pSrc = (_aType *)pvSrc; \
321 uint32_t cSamples = (uint32_t)RT_MIN(pOpts->cSamples, cbSrc / sizeof(_aType)); \
322 AUDMIXBUF_MACRO_LOG(("cSamples=%RU32, sizeof(%zu), lVol=%RU32, rVol=%RU32\n", \
323 cSamples, sizeof(_aType), pOpts->Volume.uLeft, pOpts->Volume.uRight)); \
324 for (uint32_t i = 0; i < cSamples; i++) \
325 { \
326 AUDMIXBUF_MACRO_LOG(("%p: s=%RI16\n", paDst, *pSrc)); \
327 paDst->i64LSample = ASMMult2xS32RetS64(audioMixBufClipFrom##_aName(*pSrc++), pOpts->Volume.uLeft) >> 31; \
328 paDst->i64RSample = paDst->i64LSample; \
329 AUDMIXBUF_MACRO_LOG(("\t-> l=%RI64, r=%RI64\n", paDst->i64LSample, paDst->i64RSample)); \
330 paDst++; \
331 } \
332 \
333 return cSamples; \
334 } \
335 \
336 AUDMIXBUF_MACRO_FN void audioMixBufConvTo##_aName##Stereo(void *pvDst, const PPDMAUDIOSAMPLE paSrc, \
337 const PAUDMIXBUF_CONVOPTS pOpts) \
338 { \
339 PPDMAUDIOSAMPLE pSrc = paSrc; \
340 _aType *pDst = (_aType *)pvDst; \
341 _aType l, r; \
342 uint32_t cSamples = pOpts->cSamples; \
343 while (cSamples--) \
344 { \
345 AUDMIXBUF_MACRO_LOG(("%p: l=%RI64, r=%RI64\n", pSrc, pSrc->i64LSample, pSrc->i64RSample)); \
346 l = audioMixBufClipTo##_aName(pSrc->i64LSample); \
347 r = audioMixBufClipTo##_aName(pSrc->i64RSample); \
348 AUDMIXBUF_MACRO_LOG(("\t-> l=%RI16, r=%RI16\n", l, r)); \
349 *pDst++ = l; \
350 *pDst++ = r; \
351 pSrc++; \
352 } \
353 } \
354 \
355 AUDMIXBUF_MACRO_FN void audioMixBufConvTo##_aName##Mono(void *pvDst, const PPDMAUDIOSAMPLE paSrc, \
356 const PAUDMIXBUF_CONVOPTS pOpts) \
357 { \
358 PPDMAUDIOSAMPLE pSrc = paSrc; \
359 _aType *pDst = (_aType *)pvDst; \
360 uint32_t cSamples = pOpts->cSamples; \
361 while (cSamples--) \
362 { \
363 *pDst++ = audioMixBufClipTo##_aName(pSrc->i64LSample + pSrc->i64RSample); \
364 pSrc++; \
365 } \
366 }
367
368/* audioMixBufConvXXXS8: 8 bit, signed. */
369AUDMIXBUF_CONVERT(S8 /* Name */, int8_t, INT8_MIN /* Min */, INT8_MAX /* Max */, true /* fSigned */, 8 /* cShift */)
370/* audioMixBufConvXXXU8: 8 bit, unsigned. */
371AUDMIXBUF_CONVERT(U8 /* Name */, uint8_t, 0 /* Min */, UINT8_MAX /* Max */, false /* fSigned */, 8 /* cShift */)
372/* audioMixBufConvXXXS16: 16 bit, signed. */
373AUDMIXBUF_CONVERT(S16 /* Name */, int16_t, INT16_MIN /* Min */, INT16_MAX /* Max */, true /* fSigned */, 16 /* cShift */)
374/* audioMixBufConvXXXU16: 16 bit, unsigned. */
375AUDMIXBUF_CONVERT(U16 /* Name */, uint16_t, 0 /* Min */, UINT16_MAX /* Max */, false /* fSigned */, 16 /* cShift */)
376/* audioMixBufConvXXXS32: 32 bit, signed. */
377AUDMIXBUF_CONVERT(S32 /* Name */, int32_t, INT32_MIN /* Min */, INT32_MAX /* Max */, true /* fSigned */, 32 /* cShift */)
378/* audioMixBufConvXXXU32: 32 bit, unsigned. */
379AUDMIXBUF_CONVERT(U32 /* Name */, uint32_t, 0 /* Min */, UINT32_MAX /* Max */, false /* fSigned */, 32 /* cShift */)
380
381#undef AUDMIXBUF_CONVERT
382
383#define AUDMIXBUF_MIXOP(_aName, _aOp) \
384 AUDMIXBUF_MACRO_FN void audioMixBufOp##_aName(PPDMAUDIOSAMPLE paDst, uint32_t cDstSamples, \
385 PPDMAUDIOSAMPLE paSrc, uint32_t cSrcSamples, \
386 PPDMAUDIOSTRMRATE pRate, \
387 uint32_t *pcDstWritten, uint32_t *pcSrcRead) \
388 { \
389 AUDMIXBUF_MACRO_LOG(("cSrcSamples=%RU32, cDstSamples=%RU32\n", cSrcSamples, cDstSamples)); \
390 AUDMIXBUF_MACRO_LOG(("pRate=%p: srcOffset=0x%RX32 (%RU32), dstOffset=0x%RX32 (%RU32), dstInc=0x%RX64 (%RU64)\n", \
391 pRate, pRate->srcOffset, pRate->srcOffset, \
392 (uint32_t)(pRate->dstOffset >> 32), (uint32_t)(pRate->dstOffset >> 32), \
393 pRate->dstInc, pRate->dstInc)); \
394 \
395 if (pRate->dstInc == (UINT64_C(1) + UINT32_MAX)) /* No conversion needed? */ \
396 { \
397 uint32_t cSamples = RT_MIN(cSrcSamples, cDstSamples); \
398 AUDMIXBUF_MACRO_LOG(("cSamples=%RU32\n", cSamples)); \
399 for (uint32_t i = 0; i < cSamples; i++) \
400 { \
401 paDst[i].i64LSample _aOp paSrc[i].i64LSample; \
402 paDst[i].i64RSample _aOp paSrc[i].i64RSample; \
403 } \
404 \
405 if (pcDstWritten) \
406 *pcDstWritten = cSamples; \
407 if (pcSrcRead) \
408 *pcSrcRead = cSamples; \
409 return; \
410 } \
411 \
412 PPDMAUDIOSAMPLE paSrcStart = paSrc; \
413 PPDMAUDIOSAMPLE paSrcEnd = paSrc + cSrcSamples; \
414 PPDMAUDIOSAMPLE paDstStart = paDst; \
415 PPDMAUDIOSAMPLE paDstEnd = paDst + cDstSamples; \
416 PDMAUDIOSAMPLE samCur = { 0 }; \
417 PDMAUDIOSAMPLE samOut; \
418 PDMAUDIOSAMPLE samLast = pRate->srcSampleLast; \
419 uint64_t lDelta = 0; \
420 \
421 AUDMIXBUF_MACRO_LOG(("Start: paDstEnd=%p - paDstStart=%p -> %zu\n", paDstEnd, paDst, paDstEnd - paDstStart)); \
422 AUDMIXBUF_MACRO_LOG(("Start: paSrcEnd=%p - paSrcStart=%p -> %zu\n", paSrcEnd, paSrc, paSrcEnd - paSrcStart)); \
423 \
424 while (paDst < paDstEnd) \
425 { \
426 Assert(paSrc <= paSrcEnd); \
427 Assert(paDst <= paDstEnd); \
428 if (paSrc == paSrcEnd) \
429 break; \
430 \
431 lDelta = 0; \
432 while (pRate->srcOffset <= (pRate->dstOffset >> 32)) \
433 { \
434 Assert(paSrc <= paSrcEnd); \
435 samLast = *paSrc++; \
436 pRate->srcOffset++; \
437 lDelta++; \
438 if (paSrc == paSrcEnd) \
439 break; \
440 } \
441 \
442 Assert(paSrc <= paSrcEnd); \
443 if (paSrc == paSrcEnd) \
444 break; \
445 \
446 samCur = *paSrc; \
447 \
448 /* Interpolate. */ \
449 int64_t iDstOffInt = pRate->dstOffset & UINT32_MAX; \
450 \
451 samOut.i64LSample = (samLast.i64LSample * ((int64_t) UINT32_MAX - iDstOffInt) + samCur.i64LSample * iDstOffInt) >> 32; \
452 samOut.i64RSample = (samLast.i64RSample * ((int64_t) UINT32_MAX - iDstOffInt) + samCur.i64RSample * iDstOffInt) >> 32; \
453 \
454 paDst->i64LSample _aOp samOut.i64LSample; \
455 paDst->i64RSample _aOp samOut.i64RSample; \
456 paDst++; \
457 \
458 AUDMIXBUF_MACRO_LOG(("\tlDelta=0x%RX64 (%RU64), iDstOffInt=0x%RX64 (%RI64), l=%RI64, r=%RI64 (cur l=%RI64, r=%RI64)\n", \
459 lDelta, lDelta, iDstOffInt, iDstOffInt, \
460 paDst->i64LSample, paDst->i64RSample, \
461 samCur.i64LSample, samCur.i64RSample)); \
462 \
463 pRate->dstOffset += pRate->dstInc; \
464 \
465 AUDMIXBUF_MACRO_LOG(("\t\tpRate->dstOffset=0x%RX32 (%RU32)\n", pRate->dstOffset, pRate->dstOffset >> 32)); \
466 \
467 } \
468 \
469 AUDMIXBUF_MACRO_LOG(("End: paDst=%p - paDstStart=%p -> %zu\n", paDst, paDstStart, paDst - paDstStart)); \
470 AUDMIXBUF_MACRO_LOG(("End: paSrc=%p - paSrcStart=%p -> %zu\n", paSrc, paSrcStart, paSrc - paSrcStart)); \
471 \
472 pRate->srcSampleLast = samLast; \
473 \
474 AUDMIXBUF_MACRO_LOG(("pRate->srcSampleLast l=%RI64, r=%RI64, lDelta=0x%RX64 (%RU64)\n", \
475 pRate->srcSampleLast.i64LSample, pRate->srcSampleLast.i64RSample, lDelta, lDelta)); \
476 \
477 if (pcDstWritten) \
478 *pcDstWritten = paDst - paDstStart; \
479 if (pcSrcRead) \
480 *pcSrcRead = paSrc - paSrcStart; \
481 }
482
483/* audioMixBufOpAssign: Assigns values from source buffer to destination bufffer, overwriting the destination. */
484AUDMIXBUF_MIXOP(Assign /* Name */, = /* Operation */)
485/* audioMixBufOpBlend: Blends together the values from both, the source and the destination buffer. */
486AUDMIXBUF_MIXOP(Blend /* Name */, += /* Operation */)
487
488#undef AUDMIXBUF_MIXOP
489#undef AUDMIXBUF_MACRO_LOG
490
491/**
492 *
493 ** @todo Speed up the lookup by binding it to the actual stream state.
494 *
495 * @return IPRT status code.
496 * @return PAUDMIXBUF_FN_CONVFROM
497 * @param enmFmt
498 */
499static inline PAUDMIXBUF_FN_CONVFROM audioMixBufConvFromLookup(PDMAUDIOMIXBUFFMT enmFmt)
500{
501 if (AUDMIXBUF_FMT_SIGNED(enmFmt))
502 {
503 if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 2)
504 {
505 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
506 {
507 case 8: return audioMixBufConvFromS8Stereo;
508 case 16: return audioMixBufConvFromS16Stereo;
509 case 32: return audioMixBufConvFromS32Stereo;
510 default: return NULL;
511 }
512 }
513 else if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 1)
514 {
515 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
516 {
517 case 8: return audioMixBufConvFromS8Mono;
518 case 16: return audioMixBufConvFromS16Mono;
519 case 32: return audioMixBufConvFromS32Mono;
520 default: return NULL;
521 }
522 }
523 }
524 else /* Unsigned */
525 {
526 if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 2)
527 {
528 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
529 {
530 case 8: return audioMixBufConvFromU8Stereo;
531 case 16: return audioMixBufConvFromU16Stereo;
532 case 32: return audioMixBufConvFromU32Stereo;
533 default: return NULL;
534 }
535 }
536 else if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 1)
537 {
538 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
539 {
540 case 8: return audioMixBufConvFromU8Mono;
541 case 16: return audioMixBufConvFromU16Mono;
542 case 32: return audioMixBufConvFromU32Mono;
543 default: return NULL;
544 }
545 }
546 }
547
548 return NULL;
549}
550
551/**
552 *
553 ** @todo Speed up the lookup by binding it to the actual stream state.
554 *
555 * @return PAUDMIXBUF_FN_CONVTO
556 * @param enmFmt
557 */
558static inline PAUDMIXBUF_FN_CONVTO audioMixBufConvToLookup(PDMAUDIOMIXBUFFMT enmFmt)
559{
560 if (AUDMIXBUF_FMT_SIGNED(enmFmt))
561 {
562 if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 2)
563 {
564 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
565 {
566 case 8: return audioMixBufConvToS8Stereo;
567 case 16: return audioMixBufConvToS16Stereo;
568 case 32: return audioMixBufConvToS32Stereo;
569 default: return NULL;
570 }
571 }
572 else if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 1)
573 {
574 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
575 {
576 case 8: return audioMixBufConvToS8Mono;
577 case 16: return audioMixBufConvToS16Mono;
578 case 32: return audioMixBufConvToS32Mono;
579 default: return NULL;
580 }
581 }
582 }
583 else /* Unsigned */
584 {
585 if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 2)
586 {
587 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
588 {
589 case 8: return audioMixBufConvToU8Stereo;
590 case 16: return audioMixBufConvToU16Stereo;
591 case 32: return audioMixBufConvToU32Stereo;
592 default: return NULL;
593 }
594 }
595 else if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 1)
596 {
597 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
598 {
599 case 8: return audioMixBufConvToU8Mono;
600 case 16: return audioMixBufConvToU16Mono;
601 case 32: return audioMixBufConvToU32Mono;
602 default: return NULL;
603 }
604 }
605 }
606
607 return NULL;
608}
609
610static void audioMixBufFreeBuf(PPDMAUDIOMIXBUF pMixBuf)
611{
612 if (pMixBuf)
613 {
614 if (pMixBuf->pSamples)
615 {
616 RTMemFree(pMixBuf->pSamples);
617 pMixBuf->pSamples = NULL;
618 }
619
620 pMixBuf->cSamples = 0;
621 }
622}
623
624int audioMixBufInit(PPDMAUDIOMIXBUF pMixBuf, const char *pszName, PPDMPCMPROPS pProps, uint32_t cSamples)
625{
626 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
627 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
628 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
629
630 pMixBuf->pParent = NULL;
631 RTListInit(&pMixBuf->lstBuffers);
632
633 pMixBuf->pSamples = NULL;
634 pMixBuf->cSamples = 0;
635
636 pMixBuf->offReadWrite = 0;
637 pMixBuf->cMixed = 0;
638 pMixBuf->cProcessed = 0;
639
640 /* Set initial volume to max. */
641 pMixBuf->Volume.fMuted = false;
642 pMixBuf->Volume.uLeft = 0x7FFFFFFF;
643 pMixBuf->Volume.uRight = 0x7FFFFFFF;
644
645 /* Prevent division by zero.
646 * Do a 1:1 conversion according to AUDIOMIXBUF_S2B_RATIO. */
647 pMixBuf->iFreqRatio = 1 << 20;
648
649 pMixBuf->pRate = NULL;
650
651 pMixBuf->AudioFmt = AUDMIXBUF_AUDIO_FMT_MAKE(pProps->uHz,
652 pProps->cChannels,
653 pProps->cBits,
654 pProps->fSigned);
655 pMixBuf->cShift = pProps->cShift;
656 pMixBuf->pszName = RTStrDup(pszName);
657 if (!pMixBuf->pszName)
658 return VERR_NO_MEMORY;
659
660 AUDMIXBUF_LOG(("%s: uHz=%RU32, cChan=%RU8, cBits=%RU8, fSigned=%RTbool\n",
661 pMixBuf->pszName,
662 AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt),
663 AUDMIXBUF_FMT_CHANNELS(pMixBuf->AudioFmt),
664 AUDMIXBUF_FMT_BITS_PER_SAMPLE(pMixBuf->AudioFmt),
665 RT_BOOL(AUDMIXBUF_FMT_SIGNED(pMixBuf->AudioFmt))));
666
667 return audioMixBufAllocBuf(pMixBuf, cSamples);
668}
669
670bool audioMixBufIsEmpty(PPDMAUDIOMIXBUF pMixBuf)
671{
672 AssertPtrReturn(pMixBuf, true);
673
674 if (pMixBuf->pParent)
675 return (pMixBuf->cMixed == 0);
676 return (pMixBuf->cProcessed == 0);
677}
678
679int audioMixBufLinkTo(PPDMAUDIOMIXBUF pMixBuf, PPDMAUDIOMIXBUF pParent)
680{
681 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
682 AssertPtrReturn(pParent, VERR_INVALID_POINTER);
683
684 AssertMsgReturn(AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt),
685 ("Parent sample frequency (Hz) not set\n"), VERR_INVALID_PARAMETER);
686 AssertMsgReturn(AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt),
687 ("Buffer sample frequency (Hz) not set\n"), VERR_INVALID_PARAMETER);
688 AssertMsgReturn(pMixBuf != pParent,
689 ("Circular linking not allowed\n"), VERR_INVALID_PARAMETER);
690
691 if (pMixBuf->pParent) /* Already linked? */
692 {
693 AUDMIXBUF_LOG(("%s: Already linked to \"%s\"\n",
694 pMixBuf->pszName, pMixBuf->pParent->pszName));
695 return VERR_ACCESS_DENIED;
696 }
697
698 RTListAppend(&pParent->lstBuffers, &pMixBuf->Node);
699 pMixBuf->pParent = pParent;
700
701 /* Calculate the frequency ratio. */
702 pMixBuf->iFreqRatio = ((int64_t)AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt) << 32)
703 / AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt);
704
705 if (pMixBuf->iFreqRatio == 0) /* Catch division by zero. */
706 pMixBuf->iFreqRatio = 1 << 20; /* Do a 1:1 conversion instead. */
707
708 uint32_t cSamples = RT_MIN( ((uint64_t)pParent->cSamples << 32)
709 / pMixBuf->iFreqRatio, _64K /* 64K samples max. */);
710 if (!cSamples)
711 cSamples = pParent->cSamples;
712
713 int rc = VINF_SUCCESS;
714
715 if (cSamples != pMixBuf->cSamples)
716 {
717 AUDMIXBUF_LOG(("%s: Reallocating samples %RU32 -> %RU32\n",
718 pMixBuf->pszName, pMixBuf->cSamples, cSamples));
719
720 uint32_t cbSamples = cSamples * sizeof(PDMAUDIOSAMPLE);
721 Assert(cbSamples);
722 pMixBuf->pSamples = (PPDMAUDIOSAMPLE)RTMemRealloc(pMixBuf->pSamples, cbSamples);
723 if (!pMixBuf->pSamples)
724 rc = VERR_NO_MEMORY;
725
726 if (RT_SUCCESS(rc))
727 {
728 pMixBuf->cSamples = cSamples;
729
730 /* Make sure to zero the reallocated buffer so that it can be
731 * used properly when blending with another buffer later. */
732 RT_BZERO(pMixBuf->pSamples, cbSamples);
733 }
734 }
735
736 if (RT_SUCCESS(rc))
737 {
738 if (!pMixBuf->pRate)
739 {
740 /* Create rate conversion. */
741 pMixBuf->pRate = (PPDMAUDIOSTRMRATE)RTMemAllocZ(sizeof(PDMAUDIOSTRMRATE));
742 if (!pMixBuf->pRate)
743 return VERR_NO_MEMORY;
744 }
745 else
746 RT_BZERO(pMixBuf->pRate, sizeof(PDMAUDIOSTRMRATE));
747
748 pMixBuf->pRate->dstInc = ((uint64_t)AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt) << 32)
749 / AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt);
750
751 AUDMIXBUF_LOG(("uThisHz=%RU32, uParentHz=%RU32, iFreqRatio=0x%RX64 (%RI64), uRateInc=0x%RX64 (%RU64), cSamples=%RU32 (%RU32 parent)\n",
752 AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt),
753 AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt),
754 pMixBuf->iFreqRatio, pMixBuf->iFreqRatio,
755 pMixBuf->pRate->dstInc, pMixBuf->pRate->dstInc,
756 pMixBuf->cSamples,
757 pParent->cSamples));
758 AUDMIXBUF_LOG(("%s (%RU32Hz) -> %s (%RU32Hz)\n",
759 pMixBuf->pszName, AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt),
760 pMixBuf->pParent->pszName, AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt)));
761 }
762
763 return rc;
764}
765
766uint32_t audioMixBufMixed(PPDMAUDIOMIXBUF pMixBuf)
767{
768 AssertPtrReturn(pMixBuf, 0);
769
770 AssertMsgReturn(VALID_PTR(pMixBuf->pParent),
771 ("Buffer is not linked to a parent buffer\n"),
772 0);
773
774 AUDMIXBUF_LOG(("%s: cMixed=%RU32\n", pMixBuf->pszName, pMixBuf->cMixed));
775 return pMixBuf->cMixed;
776}
777
778static int audioMixBufMixTo(PPDMAUDIOMIXBUF pDst, PPDMAUDIOMIXBUF pSrc, uint32_t cSamples, uint32_t *pcProcessed)
779{
780 AssertPtrReturn(pDst, VERR_INVALID_POINTER);
781 AssertPtrReturn(pSrc, VERR_INVALID_POINTER);
782 /* pcProcessed is optional. */
783
784 /* Live samples indicate how many samples there are in the source buffer
785 * which have not been processed yet by the destination buffer. */
786 uint32_t cLive = pSrc->cMixed;
787 if (cLive >= pDst->cSamples)
788 AUDMIXBUF_LOG(("Destination buffer \"%s\" full (%RU32 samples max), live samples = %RU32\n",
789 pDst->pszName, pDst->cSamples, cLive));
790
791 /* Dead samples are the number of samples in the destination buffer which
792 * will not be needed, that is, are not needed in order to process the live
793 * samples of the source buffer. */
794 uint32_t cDead = pDst->cSamples - cLive;
795
796 uint32_t cToReadTotal = RT_MIN(cSamples, AUDIOMIXBUF_S2S_RATIO(pSrc, cDead));
797 uint32_t offRead = 0;
798
799 uint32_t cReadTotal = 0;
800 uint32_t cWrittenTotal = 0;
801 uint32_t offWrite = (pDst->offReadWrite + cLive) % pDst->cSamples;
802
803 AUDMIXBUF_LOG(("pSrc=%s (%RU32 samples), pDst=%s (%RU32 samples), cLive=%RU32, cDead=%RU32, cToReadTotal=%RU32, offWrite=%RU32\n",
804 pSrc->pszName, pSrc->cSamples, pDst->pszName, pDst->cSamples, cLive, cDead, cToReadTotal, offWrite));
805
806 uint32_t cToRead, cToWrite;
807 uint32_t cWritten, cRead;
808
809 while (cToReadTotal)
810 {
811 cDead = pDst->cSamples - cLive;
812
813 cToRead = cToReadTotal;
814 cToWrite = RT_MIN(cDead, pDst->cSamples - offWrite);
815 if (!cToWrite)
816 {
817 AUDMIXBUF_LOG(("Warning: Destination buffer \"%s\" full\n", pDst->pszName));
818 break;
819 }
820
821 Assert(offWrite + cToWrite <= pDst->cSamples);
822 Assert(offRead + cToRead <= pSrc->cSamples);
823
824 AUDMIXBUF_LOG(("\t%RU32Hz -> %RU32Hz\n", AUDMIXBUF_FMT_SAMPLE_FREQ(pSrc->AudioFmt), AUDMIXBUF_FMT_SAMPLE_FREQ(pDst->AudioFmt)));
825 AUDMIXBUF_LOG(("\tcDead=%RU32, offWrite=%RU32, cToWrite=%RU32, offRead=%RU32, cToRead=%RU32\n",
826 cDead, offWrite, cToWrite, offRead, cToRead));
827
828 audioMixBufOpBlend(pDst->pSamples + offWrite, cToWrite,
829 pSrc->pSamples + offRead, cToRead,
830 pSrc->pRate, &cWritten, &cRead);
831
832 AUDMIXBUF_LOG(("\t\tcWritten=%RU32, cRead=%RU32\n", cWritten, cRead));
833
834 cReadTotal += cRead;
835 cWrittenTotal += cWritten;
836
837 offRead += cRead;
838 Assert(cToReadTotal >= cRead);
839 cToReadTotal -= cRead;
840
841 offWrite = (offWrite + cWritten) % pDst->cSamples;
842
843 cLive += cWritten;
844 }
845
846 pSrc->cMixed += cWrittenTotal;
847 pDst->cProcessed += cWrittenTotal;
848#ifdef DEBUG
849 s_cSamplesMixedTotal += cWrittenTotal;
850 audioMixBufPrint(pDst);
851#endif
852
853 if (pcProcessed)
854 *pcProcessed = cReadTotal;
855
856 AUDMIXBUF_LOG(("cReadTotal=%RU32 (pcProcessed), cWrittenTotal=%RU32, cSrcMixed=%RU32, cDstProc=%RU32\n",
857 cReadTotal, cWrittenTotal, pSrc->cMixed, pDst->cProcessed));
858 return VINF_SUCCESS;
859}
860
861int audioMixBufMixToChildren(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamples,
862 uint32_t *pcProcessed)
863{
864 int rc = VINF_SUCCESS;
865
866 uint32_t cProcessed;
867 uint32_t cProcessedMax = 0;
868
869 PPDMAUDIOMIXBUF pIter;
870 RTListForEach(&pMixBuf->lstBuffers, pIter, PDMAUDIOMIXBUF, Node)
871 {
872 rc = audioMixBufMixTo(pIter, pMixBuf, cSamples, &cProcessed);
873 if (RT_FAILURE(rc))
874 break;
875
876 cProcessedMax = RT_MAX(cProcessedMax, cProcessed);
877 }
878
879 if (pcProcessed)
880 *pcProcessed = cProcessedMax;
881
882 return rc;
883}
884
885int audioMixBufMixToParent(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamples,
886 uint32_t *pcProcessed)
887{
888 AssertMsgReturn(VALID_PTR(pMixBuf->pParent),
889 ("Buffer is not linked to a parent buffer\n"),
890 VERR_INVALID_PARAMETER);
891
892 return audioMixBufMixTo(pMixBuf->pParent, pMixBuf, cSamples, pcProcessed);
893}
894
895static inline void audioMixBufPrint(PPDMAUDIOMIXBUF pMixBuf)
896{
897#ifdef DEBUG_DISABLED
898 PPDMAUDIOMIXBUF pParent = pMixBuf;
899 if (pMixBuf->pParent)
900 pParent = pMixBuf->pParent;
901
902 AUDMIXBUF_LOG(("********************************************\n"));
903 AUDMIXBUF_LOG(("%s: offReadWrite=%RU32, cProcessed=%RU32, cMixed=%RU32 (BpS=%RU32)\n",
904 pParent->pszName,
905 pParent->offReadWrite, pParent->cProcessed, pParent->cMixed,
906 AUDIOMIXBUF_S2B(pParent, 1)));
907
908 PPDMAUDIOMIXBUF pIter;
909 RTListForEach(&pParent->lstBuffers, pIter, PDMAUDIOMIXBUF, Node)
910 {
911 AUDMIXBUF_LOG(("\t%s: offReadWrite=%RU32, cProcessed=%RU32, cMixed=%RU32 (BpS=%RU32)\n",
912 pIter->pszName,
913 pIter->offReadWrite, pIter->cProcessed, pIter->cMixed,
914 AUDIOMIXBUF_S2B(pIter, 1)));
915 }
916 AUDMIXBUF_LOG(("Total samples mixed: %RU64\n", s_cSamplesMixedTotal));
917 AUDMIXBUF_LOG(("********************************************\n"));
918#endif
919}
920
921/**
922 * Returns the total number of samples processed.
923 *
924 * @return uint32_t
925 * @param pMixBuf
926 */
927uint32_t audioMixBufProcessed(PPDMAUDIOMIXBUF pMixBuf)
928{
929 AssertPtrReturn(pMixBuf, 0);
930
931 AUDMIXBUF_LOG(("%s: cProcessed=%RU32\n", pMixBuf->pszName, pMixBuf->cProcessed));
932 return pMixBuf->cProcessed;
933}
934
935int audioMixBufReadAt(PPDMAUDIOMIXBUF pMixBuf,
936 uint32_t offSamples,
937 void *pvBuf, uint32_t cbBuf,
938 uint32_t *pcbRead)
939{
940 return audioMixBufReadAtEx(pMixBuf, pMixBuf->AudioFmt,
941 offSamples, pvBuf, cbBuf, pcbRead);
942}
943
944int audioMixBufReadAtEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt,
945 uint32_t offSamples,
946 void *pvBuf, uint32_t cbBuf,
947 uint32_t *pcbRead)
948{
949 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
950 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
951 /* pcbRead is optional. */
952
953 uint32_t cDstSamples = pMixBuf->cSamples;
954 uint32_t cLive = pMixBuf->cProcessed;
955
956 uint32_t cDead = cDstSamples - cLive;
957 uint32_t cToProcess = AUDIOMIXBUF_S2S_RATIO(pMixBuf, cDead);
958 cToProcess = RT_MIN(cToProcess, AUDIOMIXBUF_B2S(pMixBuf, cbBuf));
959
960 AUDMIXBUF_LOG(("%s: offSamples=%RU32, cLive=%RU32, cDead=%RU32, cToProcess=%RU32\n",
961 pMixBuf->pszName, offSamples, cLive, cDead, cToProcess));
962
963 int rc;
964 if (cToProcess)
965 {
966 PAUDMIXBUF_FN_CONVTO pConv = audioMixBufConvToLookup(enmFmt);
967 if (pConv)
968 {
969 AUDMIXBUF_CONVOPTS convOpts = { cToProcess, pMixBuf->Volume };
970 pConv(pvBuf, pMixBuf->pSamples + offSamples, &convOpts);
971
972 rc = VINF_SUCCESS;
973 }
974 else
975 rc = VERR_INVALID_PARAMETER;
976
977 audioMixBufPrint(pMixBuf);
978 }
979 else
980 rc = VINF_SUCCESS;
981
982 if (RT_SUCCESS(rc))
983 {
984 if (pcbRead)
985 *pcbRead = AUDIOMIXBUF_S2B(pMixBuf, cToProcess);
986 }
987
988 AUDMIXBUF_LOG(("cbRead=%RU32, rc=%Rrc\n", AUDIOMIXBUF_S2B(pMixBuf, cToProcess), rc));
989 return rc;
990}
991
992int audioMixBufReadCirc(PPDMAUDIOMIXBUF pMixBuf,
993 void *pvBuf, uint32_t cbBuf, uint32_t *pcRead)
994{
995 return audioMixBufReadCircEx(pMixBuf, pMixBuf->AudioFmt,
996 pvBuf, cbBuf, pcRead);
997}
998
999int audioMixBufReadCircEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt,
1000 void *pvBuf, uint32_t cbBuf, uint32_t *pcRead)
1001{
1002 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
1003 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1004 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1005 /* pcbRead is optional. */
1006
1007 if (!cbBuf)
1008 return VINF_SUCCESS;
1009
1010 uint32_t cToRead = RT_MIN(AUDIOMIXBUF_B2S(pMixBuf, cbBuf), pMixBuf->cProcessed);
1011
1012 AUDMIXBUF_LOG(("%s: pvBuf=%p, cbBuf=%zu (%RU32 samples), cToRead=%RU32\n",
1013 pMixBuf->pszName, pvBuf, cbBuf, AUDIOMIXBUF_B2S(pMixBuf, cbBuf), cToRead));
1014
1015 if (!cToRead)
1016 {
1017 audioMixBufPrint(pMixBuf);
1018
1019 if (pcRead)
1020 *pcRead = 0;
1021 return VINF_SUCCESS;
1022 }
1023
1024 PAUDMIXBUF_FN_CONVTO pConv = audioMixBufConvToLookup(enmFmt);
1025 if (!pConv) /* Audio format not supported. */
1026 return VERR_NOT_SUPPORTED;
1027
1028 PPDMAUDIOSAMPLE pSamplesSrc1 = pMixBuf->pSamples + pMixBuf->offReadWrite;
1029 uint32_t cLenSrc1 = cToRead;
1030
1031 PPDMAUDIOSAMPLE pSamplesSrc2 = NULL;
1032 uint32_t cLenSrc2 = 0;
1033
1034 uint32_t offRead = pMixBuf->offReadWrite + cToRead;
1035
1036 /*
1037 * Do we need to wrap around to read all requested data, that is,
1038 * starting at the beginning of our circular buffer? This then will
1039 * be the optional second part to do.
1040 */
1041 if (offRead >= pMixBuf->cSamples)
1042 {
1043 Assert(pMixBuf->offReadWrite <= pMixBuf->cSamples);
1044 cLenSrc1 = pMixBuf->cSamples - pMixBuf->offReadWrite;
1045
1046 pSamplesSrc2 = pMixBuf->pSamples;
1047 Assert(cToRead >= cLenSrc1);
1048 cLenSrc2 = RT_MIN(cToRead - cLenSrc1, pMixBuf->cSamples);
1049
1050 /* Save new read offset. */
1051 offRead = cLenSrc2;
1052 }
1053
1054 AUDMIXBUF_CONVOPTS convOpts;
1055 convOpts.Volume = pMixBuf->Volume;
1056
1057 /* Anything to do at all? */
1058 int rc = VINF_SUCCESS;
1059 if (cLenSrc1)
1060 {
1061 convOpts.cSamples = cLenSrc1;
1062
1063 AUDMIXBUF_LOG(("P1: offRead=%RU32, cToRead=%RU32\n", pMixBuf->offReadWrite, cLenSrc1));
1064 pConv(pvBuf, pSamplesSrc1, &convOpts);
1065 }
1066
1067 /* Second part present? */
1068 if ( RT_LIKELY(RT_SUCCESS(rc))
1069 && cLenSrc2)
1070 {
1071 AssertPtr(pSamplesSrc2);
1072
1073 convOpts.cSamples = cLenSrc2;
1074
1075 AUDMIXBUF_LOG(("P2: cToRead=%RU32, offWrite=%RU32 (%zu bytes)\n", cLenSrc2, cLenSrc1,
1076 AUDIOMIXBUF_S2B(pMixBuf, cLenSrc1)));
1077 pConv((uint8_t *)pvBuf + AUDIOMIXBUF_S2B(pMixBuf, cLenSrc1), pSamplesSrc2, &convOpts);
1078 }
1079
1080 if (RT_SUCCESS(rc))
1081 {
1082#ifdef DEBUG_DUMP_PCM_DATA
1083 RTFILE fh;
1084 rc = RTFileOpen(&fh, "c:\\temp\\mixbuf_readcirc.pcm",
1085 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1086 if (RT_SUCCESS(rc))
1087 {
1088 RTFileWrite(fh, pvBuf, AUDIOMIXBUF_S2B(pMixBuf, cLenSrc1 + cLenSrc2), NULL);
1089 RTFileClose(fh);
1090 }
1091#endif
1092 pMixBuf->offReadWrite = offRead % pMixBuf->cSamples;
1093 pMixBuf->cProcessed -= RT_MIN(cLenSrc1 + cLenSrc2, pMixBuf->cProcessed);
1094
1095 if (pcRead)
1096 *pcRead = cLenSrc1 + cLenSrc2;
1097 }
1098
1099 audioMixBufPrint(pMixBuf);
1100
1101 AUDMIXBUF_LOG(("cRead=%RU32 (%zu bytes), rc=%Rrc\n",
1102 cLenSrc1 + cLenSrc2,
1103 AUDIOMIXBUF_S2B(pMixBuf, cLenSrc1 + cLenSrc2), rc));
1104 return rc;
1105}
1106
1107void audioMixBufReset(PPDMAUDIOMIXBUF pMixBuf)
1108{
1109 AssertPtrReturnVoid(pMixBuf);
1110
1111 AUDMIXBUF_LOG(("%s\n", pMixBuf->pszName));
1112
1113 pMixBuf->offReadWrite = 0;
1114 pMixBuf->cMixed = 0;
1115 pMixBuf->cProcessed = 0;
1116
1117 if (pMixBuf->cSamples)
1118 RT_BZERO(pMixBuf->pSamples, pMixBuf->cSamples * sizeof(PDMAUDIOSAMPLE));
1119}
1120
1121void audioMixBufSetVolume(PPDMAUDIOMIXBUF pMixBuf, PPDMAUDIOVOLUME pVol)
1122{
1123 AssertPtrReturnVoid(pMixBuf);
1124 AssertPtrReturnVoid(pVol);
1125
1126 LogFlowFunc(("%s: lVol=%RU32, rVol=%RU32\n", pMixBuf->pszName, pVol->uLeft, pVol->uRight));
1127
1128 pMixBuf->Volume.fMuted = pVol->fMuted;
1129 pMixBuf->Volume.uLeft = (UINT64_C(0x100000000) * pVol->uLeft) / 255;
1130 pMixBuf->Volume.uRight = (UINT64_C(0x100000000) * pVol->uRight) / 255;
1131
1132 LogFlowFunc(("\t-> lVol=%RU32, rVol=%RU32\n", pMixBuf->Volume.uLeft, pMixBuf->Volume.uRight));
1133}
1134
1135uint32_t audioMixBufSize(PPDMAUDIOMIXBUF pMixBuf)
1136{
1137 return pMixBuf->cSamples;
1138}
1139
1140/**
1141 * Returns the maximum amount of bytes this buffer can hold.
1142 *
1143 * @return uint32_t
1144 * @param pMixBuf
1145 */
1146uint32_t audioMixBufSizeBytes(PPDMAUDIOMIXBUF pMixBuf)
1147{
1148 return AUDIOMIXBUF_S2B(pMixBuf, pMixBuf->cSamples);
1149}
1150
1151void audioMixBufUnlink(PPDMAUDIOMIXBUF pMixBuf)
1152{
1153 if (!pMixBuf || !pMixBuf->pszName)
1154 return;
1155
1156 AUDMIXBUF_LOG(("%s\n", pMixBuf->pszName));
1157
1158 if (pMixBuf->pParent)
1159 {
1160 AUDMIXBUF_LOG(("%s: Unlinking from parent \"%s\"\n",
1161 pMixBuf->pszName, pMixBuf->pParent->pszName));
1162
1163 RTListNodeRemove(&pMixBuf->Node);
1164
1165 /* Make sure to reset the parent mixing buffer each time it gets linked
1166 * to a new child. */
1167 audioMixBufReset(pMixBuf->pParent);
1168 pMixBuf->pParent = NULL;
1169 }
1170
1171 PPDMAUDIOMIXBUF pIter;
1172 while (!RTListIsEmpty(&pMixBuf->lstBuffers))
1173 {
1174 pIter = RTListGetFirst(&pMixBuf->lstBuffers, PDMAUDIOMIXBUF, Node);
1175
1176 AUDMIXBUF_LOG(("\tUnlinking \"%s\"\n", pIter->pszName));
1177
1178 audioMixBufReset(pIter->pParent);
1179 pIter->pParent = NULL;
1180
1181 RTListNodeRemove(&pIter->Node);
1182 }
1183
1184 if (pMixBuf->pRate)
1185 {
1186 pMixBuf->pRate->dstOffset = pMixBuf->pRate->srcOffset = 0;
1187 pMixBuf->pRate->dstInc = 0;
1188 }
1189
1190 pMixBuf->iFreqRatio = 1; /* Prevent division by zero. */
1191}
1192
1193int audioMixBufWriteAt(PPDMAUDIOMIXBUF pMixBuf,
1194 uint32_t offSamples,
1195 const void *pvBuf, uint32_t cbBuf,
1196 uint32_t *pcWritten)
1197{
1198 return audioMixBufWriteAtEx(pMixBuf, pMixBuf->AudioFmt,
1199 offSamples, pvBuf, cbBuf, pcWritten);
1200}
1201
1202/**
1203 * TODO
1204 *
1205 * @return IPRT status code.
1206 * @return int
1207 * @param pMixBuf
1208 * @param enmFmt
1209 * @param offSamples
1210 * @param pvBuf
1211 * @param cbBuf
1212 * @param pcWritten Returns number of samples written. Optional.
1213 */
1214int audioMixBufWriteAtEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt,
1215 uint32_t offSamples,
1216 const void *pvBuf, uint32_t cbBuf,
1217 uint32_t *pcWritten)
1218{
1219 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
1220 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1221 /* pcWritten is optional. */
1222
1223 uint32_t cDstSamples = pMixBuf->pParent
1224 ? pMixBuf->pParent->cSamples : pMixBuf->cSamples;
1225 uint32_t cLive = pMixBuf->cProcessed;
1226
1227 uint32_t cDead = cDstSamples - cLive;
1228 uint32_t cToProcess = AUDIOMIXBUF_S2S_RATIO(pMixBuf, cDead);
1229 cToProcess = RT_MIN(cToProcess, AUDIOMIXBUF_B2S(pMixBuf, cbBuf));
1230
1231 AUDMIXBUF_LOG(("%s: offSamples=%RU32, cLive=%RU32, cDead=%RU32, cToProcess=%RU32\n",
1232 pMixBuf->pszName, offSamples, cLive, cDead, cToProcess));
1233
1234 if (offSamples + cToProcess > pMixBuf->cSamples)
1235 return VERR_BUFFER_OVERFLOW;
1236
1237 PAUDMIXBUF_FN_CONVFROM pConv = audioMixBufConvFromLookup(enmFmt);
1238 if (!pConv)
1239 return VERR_NOT_SUPPORTED;
1240
1241 int rc;
1242 uint32_t cWritten;
1243
1244#ifdef DEBUG_DUMP_PCM_DATA
1245 RTFILE fh;
1246 rc = RTFileOpen(&fh, "c:\\temp\\mixbuf_writeat.pcm",
1247 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1248 if (RT_SUCCESS(rc))
1249 {
1250 RTFileWrite(fh, pvBuf, cbBuf, NULL);
1251 RTFileClose(fh);
1252 }
1253#endif
1254
1255 if (cToProcess)
1256 {
1257 AUDMIXBUF_CONVOPTS convOpts = { cToProcess, pMixBuf->Volume };
1258
1259 cWritten = pConv(pMixBuf->pSamples + offSamples, pvBuf, cbBuf, &convOpts);
1260 audioMixBufPrint(pMixBuf);
1261
1262 rc = cWritten ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Fudge! */
1263 }
1264 else
1265 {
1266 cWritten = 0;
1267 rc = VINF_SUCCESS;
1268 }
1269
1270 if (RT_SUCCESS(rc))
1271 {
1272 if (pcWritten)
1273 *pcWritten = cWritten;
1274 }
1275
1276 AUDMIXBUF_LOG(("cWritten=%RU32, rc=%Rrc\n", cWritten, rc));
1277 return rc;
1278}
1279
1280int audioMixBufWriteCirc(PPDMAUDIOMIXBUF pMixBuf,
1281 const void *pvBuf, uint32_t cbBuf,
1282 uint32_t *pcWritten)
1283{
1284 return audioMixBufWriteCircEx(pMixBuf, pMixBuf->AudioFmt, pvBuf, cbBuf, pcWritten);
1285}
1286
1287int audioMixBufWriteCircEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt,
1288 const void *pvBuf, uint32_t cbBuf,
1289 uint32_t *pcWritten)
1290{
1291 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
1292 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1293 /* pcbWritten is optional. */
1294
1295 if (!cbBuf)
1296 {
1297 if (pcWritten)
1298 *pcWritten = 0;
1299 return VINF_SUCCESS;
1300 }
1301
1302 PPDMAUDIOMIXBUF pParent = pMixBuf->pParent;
1303
1304 AUDMIXBUF_LOG(("%s: enmFmt=%ld, pBuf=%p, cbBuf=%zu, pParent=%p (%RU32)\n",
1305 pMixBuf->pszName, enmFmt, pvBuf, cbBuf, pParent, pParent ? pParent->cSamples : 0));
1306
1307 if ( pParent
1308 && pParent->cSamples <= pMixBuf->cMixed)
1309 {
1310 if (pcWritten)
1311 *pcWritten = 0;
1312
1313 AUDMIXBUF_LOG(("%s: Parent buffer %s is full\n",
1314 pMixBuf->pszName, pMixBuf->pParent->pszName));
1315
1316 return VINF_SUCCESS;
1317 }
1318
1319 PAUDMIXBUF_FN_CONVFROM pConv = audioMixBufConvFromLookup(enmFmt);
1320 if (!pConv)
1321 return VERR_NOT_SUPPORTED;
1322
1323 int rc = VINF_SUCCESS;
1324
1325 uint32_t cToWrite = AUDIOMIXBUF_B2S(pMixBuf, cbBuf);
1326 AssertMsg(cToWrite, ("cToWrite is 0 (cbBuf=%zu)\n", cbBuf));
1327
1328 PPDMAUDIOSAMPLE pSamplesDst1 = pMixBuf->pSamples + pMixBuf->offReadWrite;
1329 uint32_t cLenDst1 = cToWrite;
1330
1331 PPDMAUDIOSAMPLE pSamplesDst2 = NULL;
1332 uint32_t cLenDst2 = 0;
1333
1334 uint32_t offWrite = pMixBuf->offReadWrite + cToWrite;
1335
1336 /*
1337 * Do we need to wrap around to write all requested data, that is,
1338 * starting at the beginning of our circular buffer? This then will
1339 * be the optional second part to do.
1340 */
1341 if (offWrite >= pMixBuf->cSamples)
1342 {
1343 Assert(pMixBuf->offReadWrite <= pMixBuf->cSamples);
1344 cLenDst1 = pMixBuf->cSamples - pMixBuf->offReadWrite;
1345
1346 pSamplesDst2 = pMixBuf->pSamples;
1347 Assert(cToWrite >= cLenDst1);
1348 cLenDst2 = RT_MIN(cToWrite - cLenDst1, pMixBuf->cSamples);
1349
1350 /* Save new read offset. */
1351 offWrite = cLenDst2;
1352 }
1353
1354 uint32_t cWrittenTotal = 0;
1355
1356 AUDMIXBUF_CONVOPTS convOpts;
1357 convOpts.Volume = pMixBuf->Volume;
1358
1359 /* Anything to do at all? */
1360 if (cLenDst1)
1361 {
1362 convOpts.cSamples = cLenDst1;
1363 cWrittenTotal = pConv(pSamplesDst1, pvBuf, cbBuf, &convOpts);
1364 }
1365
1366 /* Second part present? */
1367 if ( RT_LIKELY(RT_SUCCESS(rc))
1368 && cLenDst2)
1369 {
1370 AssertPtr(pSamplesDst2);
1371
1372 convOpts.cSamples = cLenDst2;
1373 cWrittenTotal += pConv(pSamplesDst2, (uint8_t *)pvBuf + AUDIOMIXBUF_S2B(pMixBuf, cLenDst1), cbBuf, &convOpts);
1374 }
1375
1376#ifdef DEBUG_DUMP_PCM_DATA
1377 RTFILE fh;
1378 RTFileOpen(&fh, "c:\\temp\\mixbuf_writeex.pcm",
1379 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1380 RTFileWrite(fh, pSamplesDst1, AUDIOMIXBUF_S2B(pMixBuf, cLenDst1), NULL);
1381 RTFileClose(fh);
1382#endif
1383
1384 AUDMIXBUF_LOG(("cLenDst1=%RU32, cLenDst2=%RU32, offWrite=%RU32\n",
1385 cLenDst1, cLenDst2, offWrite));
1386
1387 if (RT_SUCCESS(rc))
1388 {
1389 pMixBuf->offReadWrite = offWrite % pMixBuf->cSamples;
1390 pMixBuf->cProcessed = RT_MIN(pMixBuf->cProcessed + cLenDst1 + cLenDst2,
1391 pMixBuf->cSamples /* Max */);
1392 if (pcWritten)
1393 *pcWritten = cLenDst1 + cLenDst2;
1394 }
1395
1396 audioMixBufPrint(pMixBuf);
1397
1398 AUDMIXBUF_LOG(("cWritten=%RU32 (%zu bytes), rc=%Rrc\n",
1399 cLenDst1 + cLenDst2,
1400 AUDIOMIXBUF_S2B(pMixBuf, cLenDst1 + cLenDst2), rc));
1401 return rc;
1402}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette