VirtualBox

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

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

Audio: Spelling, Hungarian fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.2 KB
Line 
1/* $Id: AudioMixBuffer.cpp 55335 2015-04-17 16:00:22Z 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, size_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
233size_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 endianess 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, size_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, size_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)\n", cSamples, sizeof(_aType))); \
323 for (uint32_t i = 0; i < cSamples; i++) \
324 { \
325 paDst->i64LSample = ASMMult2xS32RetS64(audioMixBufClipFrom##_aName(pSrc[0]), pOpts->Volume.uLeft); \
326 paDst->i64RSample = paDst->i64LSample; \
327 pSrc++; \
328 paDst++; \
329 } \
330 \
331 return cSamples; \
332 } \
333 \
334 AUDMIXBUF_MACRO_FN void audioMixBufConvTo##_aName##Stereo(void *pvDst, const PPDMAUDIOSAMPLE paSrc, \
335 const PAUDMIXBUF_CONVOPTS pOpts) \
336 { \
337 PPDMAUDIOSAMPLE pSrc = paSrc; \
338 _aType *pDst = (_aType *)pvDst; \
339 _aType l, r; \
340 uint32_t cSamples = pOpts->cSamples; \
341 while (cSamples--) \
342 { \
343 AUDMIXBUF_MACRO_LOG(("%p: l=%RI64, r=%RI64\n", pSrc, pSrc->i64LSample, pSrc->i64RSample)); \
344 l = audioMixBufClipTo##_aName(pSrc->i64LSample); \
345 r = audioMixBufClipTo##_aName(pSrc->i64RSample); \
346 AUDMIXBUF_MACRO_LOG(("\t-> l=%RI16, r=%RI16\n", l, r)); \
347 *pDst++ = l; \
348 *pDst++ = r; \
349 pSrc++; \
350 } \
351 } \
352 \
353 AUDMIXBUF_MACRO_FN void audioMixBufConvTo##_aName##Mono(void *pvDst, const PPDMAUDIOSAMPLE paSrc, \
354 const PAUDMIXBUF_CONVOPTS pOpts) \
355 { \
356 PPDMAUDIOSAMPLE pSrc = paSrc; \
357 _aType *pDst = (_aType *)pvDst; \
358 uint32_t cSamples = pOpts->cSamples; \
359 while (cSamples--) \
360 { \
361 *pDst++ = audioMixBufClipTo##_aName(pSrc->i64LSample + pSrc->i64RSample); \
362 pSrc++; \
363 } \
364 }
365
366/* audioMixBufConvXXXS8: 8 bit, signed. */
367AUDMIXBUF_CONVERT(S8 /* Name */, int8_t, INT8_MIN /* Min */, INT8_MAX /* Max */, true /* fSigned */, 8 /* cShift */)
368/* audioMixBufConvXXXU8: 8 bit, unsigned. */
369AUDMIXBUF_CONVERT(U8 /* Name */, uint8_t, 0 /* Min */, UINT8_MAX /* Max */, false /* fSigned */, 8 /* cShift */)
370/* audioMixBufConvXXXS16: 16 bit, signed. */
371AUDMIXBUF_CONVERT(S16 /* Name */, int16_t, INT16_MIN /* Min */, INT16_MAX /* Max */, true /* fSigned */, 16 /* cShift */)
372/* audioMixBufConvXXXU16: 16 bit, unsigned. */
373AUDMIXBUF_CONVERT(U16 /* Name */, uint16_t, 0 /* Min */, UINT16_MAX /* Max */, false /* fSigned */, 16 /* cShift */)
374/* audioMixBufConvXXXS32: 32 bit, signed. */
375AUDMIXBUF_CONVERT(S32 /* Name */, int32_t, INT32_MIN /* Min */, INT32_MAX /* Max */, true /* fSigned */, 32 /* cShift */)
376/* audioMixBufConvXXXU32: 32 bit, unsigned. */
377AUDMIXBUF_CONVERT(U32 /* Name */, uint32_t, 0 /* Min */, UINT32_MAX /* Max */, false /* fSigned */, 32 /* cShift */)
378
379#undef AUDMIXBUF_CONVERT
380
381#define AUDMIXBUF_MIXOP(_aName, _aOp) \
382 AUDMIXBUF_MACRO_FN void audioMixBufOp##_aName(PPDMAUDIOSAMPLE paDst, uint32_t cDstSamples, \
383 PPDMAUDIOSAMPLE paSrc, uint32_t cSrcSamples, \
384 PPDMAUDIOSTRMRATE pRate, \
385 uint32_t *pcDstWritten, uint32_t *pcSrcRead) \
386 { \
387 AUDMIXBUF_MACRO_LOG(("cSrcSamples=%RU32, cDstSamples=%RU32\n", cSrcSamples, cDstSamples)); \
388 AUDMIXBUF_MACRO_LOG(("pRate=%p: srcOffset=0x%x (%RU32), dstOffset=0x%x (%RU32), dstInc=0x%RX64 (%RU64)\n", \
389 pRate, pRate->srcOffset, pRate->srcOffset, pRate->dstOffset >> 32, pRate->dstOffset >> 32, \
390 pRate->dstInc, pRate->dstInc)); \
391 \
392 if (pRate->dstInc == (UINT64_C(1) + UINT32_MAX)) /* No conversion needed? */ \
393 { \
394 uint32_t cSamples = RT_MIN(cSrcSamples, cDstSamples); \
395 AUDMIXBUF_MACRO_LOG(("cSamples=%RU32\n", cSamples)); \
396 for (uint32_t i = 0; i < cSamples; i++) \
397 { \
398 paDst[i].i64LSample _aOp paSrc[i].i64LSample; \
399 paDst[i].i64RSample _aOp paSrc[i].i64RSample; \
400 } \
401 \
402 if (pcDstWritten) \
403 *pcDstWritten = cSamples; \
404 if (pcSrcRead) \
405 *pcSrcRead = cSamples; \
406 return; \
407 } \
408 \
409 PPDMAUDIOSAMPLE paSrcStart = paSrc; \
410 PPDMAUDIOSAMPLE paSrcEnd = paSrc + cSrcSamples; \
411 PPDMAUDIOSAMPLE paDstStart = paDst; \
412 PPDMAUDIOSAMPLE paDstEnd = paDst + cDstSamples; \
413 PDMAUDIOSAMPLE samCur = { 0 }; \
414 PDMAUDIOSAMPLE samOut; \
415 PDMAUDIOSAMPLE samLast = pRate->srcSampleLast; \
416 uint64_t lDelta = 0; \
417 \
418 AUDMIXBUF_MACRO_LOG(("Start: paDstEnd=%p - paDstStart=%p -> %zu\n", paDstEnd, paDst, paDstEnd - paDstStart)); \
419 AUDMIXBUF_MACRO_LOG(("Start: paSrcEnd=%p - paSrcStart=%p -> %zu\n", paSrcEnd, paSrc, paSrcEnd - paSrcStart)); \
420 \
421 while (paDst < paDstEnd) \
422 { \
423 Assert(paSrc <= paSrcEnd); \
424 Assert(paDst <= paDstEnd); \
425 if (paSrc == paSrcEnd) \
426 break; \
427 \
428 lDelta = 0; \
429 while (pRate->srcOffset <= (pRate->dstOffset >> 32)) \
430 { \
431 Assert(paSrc <= paSrcEnd); \
432 samLast = *paSrc++; \
433 pRate->srcOffset++; \
434 lDelta++; \
435 if (paSrc == paSrcEnd) \
436 break; \
437 } \
438 \
439 Assert(paSrc <= paSrcEnd); \
440 if (paSrc == paSrcEnd) \
441 break; \
442 \
443 samCur = *paSrc; \
444 \
445 /* Interpolate. */ \
446 int64_t iDstOffInt = pRate->dstOffset & UINT32_MAX; \
447 \
448 samOut.i64LSample = (samLast.i64LSample * ((int64_t) UINT32_MAX - iDstOffInt) + samCur.i64LSample * iDstOffInt) >> 32; \
449 samOut.i64RSample = (samLast.i64RSample * ((int64_t) UINT32_MAX - iDstOffInt) + samCur.i64RSample * iDstOffInt) >> 32; \
450 \
451 paDst->i64LSample _aOp samOut.i64LSample; \
452 paDst->i64RSample _aOp samOut.i64RSample; \
453 paDst++; \
454 \
455 AUDMIXBUF_MACRO_LOG(("\tlDelta=0x%RX64 (%RU64), iDstOffInt=0x%RX64 (%RI64), l=%RI64, r=%RI64 (cur l=%RI64, r=%RI64)\n", \
456 lDelta, lDelta, iDstOffInt, iDstOffInt, \
457 paDst->i64LSample, paDst->i64RSample, \
458 samCur.i64LSample, samCur.i64RSample)); \
459 \
460 pRate->dstOffset += pRate->dstInc; \
461 \
462 AUDMIXBUF_MACRO_LOG(("\t\tpRate->dstOffset=0x%RX32 (%RU32)\n", pRate->dstOffset, pRate->dstOffset >> 32)); \
463 \
464 } \
465 \
466 AUDMIXBUF_MACRO_LOG(("End: paDst=%p - paDstStart=%p -> %zu\n", paDst, paDstStart, paDst - paDstStart)); \
467 AUDMIXBUF_MACRO_LOG(("End: paSrc=%p - paSrcStart=%p -> %zu\n", paSrc, paSrcStart, paSrc - paSrcStart)); \
468 \
469 pRate->srcSampleLast = samLast; \
470 \
471 AUDMIXBUF_MACRO_LOG(("pRate->srcSampleLast l=%RI64, r=%RI64, lDelta=0x%RX64 (%RU64)\n", \
472 pRate->srcSampleLast.i64LSample, pRate->srcSampleLast.i64RSample, lDelta, lDelta)); \
473 \
474 if (pcDstWritten) \
475 *pcDstWritten = paDst - paDstStart; \
476 if (pcSrcRead) \
477 *pcSrcRead = paSrc - paSrcStart; \
478 }
479
480/* audioMixBufOpAssign: Assigns values from source buffer to destination bufffer, overwriting the destination. */
481AUDMIXBUF_MIXOP(Assign /* Name */, = /* Operation */)
482/* audioMixBufOpBlend: Blends together the values from both, the source and the destination buffer. */
483AUDMIXBUF_MIXOP(Blend /* Name */, += /* Operation */)
484
485#undef AUDMIXBUF_MIXOP
486#undef AUDMIXBUF_MACRO_LOG
487
488/**
489 *
490 ** @todo Speed up the lookup by binding it to the actual stream state.
491 *
492 * @return IPRT status code.
493 * @return PAUDMIXBUF_FN_CONVFROM
494 * @param enmFmt
495 */
496static inline PAUDMIXBUF_FN_CONVFROM audioMixBufConvFromLookup(PDMAUDIOMIXBUFFMT enmFmt)
497{
498 if (AUDMIXBUF_FMT_SIGNED(enmFmt))
499 {
500 if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 2)
501 {
502 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
503 {
504 case 8: return audioMixBufConvFromS8Stereo;
505 case 16: return audioMixBufConvFromS16Stereo;
506 case 32: return audioMixBufConvFromS32Stereo;
507 default: return NULL;
508 }
509 }
510 else if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 1)
511 {
512 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
513 {
514 case 8: return audioMixBufConvFromS8Mono;
515 case 16: return audioMixBufConvFromS16Mono;
516 case 32: return audioMixBufConvFromS32Mono;
517 default: return NULL;
518 }
519 }
520 }
521 else /* Unsigned */
522 {
523 if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 2)
524 {
525 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
526 {
527 case 8: return audioMixBufConvFromU8Stereo;
528 case 16: return audioMixBufConvFromU16Stereo;
529 case 32: return audioMixBufConvFromU32Stereo;
530 default: return NULL;
531 }
532 }
533 else if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 1)
534 {
535 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
536 {
537 case 8: return audioMixBufConvFromU8Mono;
538 case 16: return audioMixBufConvFromU16Mono;
539 case 32: return audioMixBufConvFromU32Mono;
540 default: return NULL;
541 }
542 }
543 }
544
545 return NULL;
546}
547
548/**
549 *
550 ** @todo Speed up the lookup by binding it to the actual stream state.
551 *
552 * @return PAUDMIXBUF_FN_CONVTO
553 * @param enmFmt
554 */
555static inline PAUDMIXBUF_FN_CONVTO audioMixBufConvToLookup(PDMAUDIOMIXBUFFMT enmFmt)
556{
557 if (AUDMIXBUF_FMT_SIGNED(enmFmt))
558 {
559 if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 2)
560 {
561 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
562 {
563 case 8: return audioMixBufConvToS8Stereo;
564 case 16: return audioMixBufConvToS16Stereo;
565 case 32: return audioMixBufConvToS32Stereo;
566 default: return NULL;
567 }
568 }
569 else if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 1)
570 {
571 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
572 {
573 case 8: return audioMixBufConvToS8Mono;
574 case 16: return audioMixBufConvToS16Mono;
575 case 32: return audioMixBufConvToS32Mono;
576 default: return NULL;
577 }
578 }
579 }
580 else /* Unsigned */
581 {
582 if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 2)
583 {
584 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
585 {
586 case 8: return audioMixBufConvToU8Stereo;
587 case 16: return audioMixBufConvToU16Stereo;
588 case 32: return audioMixBufConvToU32Stereo;
589 default: return NULL;
590 }
591 }
592 else if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 1)
593 {
594 switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
595 {
596 case 8: return audioMixBufConvToU8Mono;
597 case 16: return audioMixBufConvToU16Mono;
598 case 32: return audioMixBufConvToU32Mono;
599 default: return NULL;
600 }
601 }
602 }
603
604 return NULL;
605}
606
607static void audioMixBufFreeBuf(PPDMAUDIOMIXBUF pMixBuf)
608{
609 if (pMixBuf)
610 {
611 if (pMixBuf->pSamples)
612 {
613 RTMemFree(pMixBuf->pSamples);
614 pMixBuf->pSamples = NULL;
615 }
616
617 pMixBuf->cSamples = 0;
618 }
619}
620
621int audioMixBufInit(PPDMAUDIOMIXBUF pMixBuf, const char *pszName, PPDMPCMPROPS pProps, uint32_t cSamples)
622{
623 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
624 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
625 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
626
627 pMixBuf->pParent = NULL;
628 RTListInit(&pMixBuf->lstBuffers);
629
630 pMixBuf->pSamples = NULL;
631 pMixBuf->cSamples = 0;
632
633 pMixBuf->offReadWrite = 0;
634 pMixBuf->cMixed = 0;
635 pMixBuf->cProcessed = 0;
636
637 /* Set initial volume to max. */
638 pMixBuf->Volume.fMuted = false;
639 pMixBuf->Volume.uLeft = 0x7F;
640 pMixBuf->Volume.uRight = 0x7F;
641
642 /* Prevent division by zero.
643 * Do a 1:1 conversion according to AUDIOMIXBUF_S2B_RATIO. */
644 pMixBuf->iFreqRatio = 1 << 20;
645
646 pMixBuf->pRate = NULL;
647
648 pMixBuf->AudioFmt = AUDMIXBUF_AUDIO_FMT_MAKE(pProps->uHz,
649 pProps->cChannels,
650 pProps->cBits,
651 pProps->fSigned);
652 pMixBuf->cShift = pProps->cShift;
653 pMixBuf->pszName = RTStrDup(pszName);
654 if (!pMixBuf->pszName)
655 return VERR_NO_MEMORY;
656
657 AUDMIXBUF_LOG(("%s: uHz=%RU32, cChan=%RU8, cBits=%RU8, fSigned=%RTbool\n",
658 pMixBuf->pszName,
659 AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt),
660 AUDMIXBUF_FMT_CHANNELS(pMixBuf->AudioFmt),
661 AUDMIXBUF_FMT_BITS_PER_SAMPLE(pMixBuf->AudioFmt),
662 RT_BOOL(AUDMIXBUF_FMT_SIGNED(pMixBuf->AudioFmt))));
663
664 return audioMixBufAllocBuf(pMixBuf, cSamples);
665}
666
667bool audioMixBufIsEmpty(PPDMAUDIOMIXBUF pMixBuf)
668{
669 AssertPtrReturn(pMixBuf, true);
670
671 if (pMixBuf->pParent)
672 return (pMixBuf->cMixed == 0);
673 return (pMixBuf->cProcessed == 0);
674}
675
676int audioMixBufLinkTo(PPDMAUDIOMIXBUF pMixBuf, PPDMAUDIOMIXBUF pParent)
677{
678 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
679 AssertPtrReturn(pParent, VERR_INVALID_POINTER);
680
681 AssertMsgReturn(AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt),
682 ("Parent sample frequency (Hz) not set\n"), VERR_INVALID_PARAMETER);
683 AssertMsgReturn(AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt),
684 ("Buffer sample frequency (Hz) not set\n"), VERR_INVALID_PARAMETER);
685 AssertMsgReturn(pMixBuf != pParent,
686 ("Circular linking not allowed\n"), VERR_INVALID_PARAMETER);
687
688 if (pMixBuf->pParent) /* Already linked? */
689 {
690 AUDMIXBUF_LOG(("%s: Already linked to \"%s\"\n",
691 pMixBuf->pszName, pMixBuf->pParent->pszName));
692 return VERR_ACCESS_DENIED;
693 }
694
695 RTListAppend(&pParent->lstBuffers, &pMixBuf->Node);
696 pMixBuf->pParent = pParent;
697
698 /* Calculate the frequency ratio. */
699 pMixBuf->iFreqRatio = ((int64_t)AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt) << 32)
700 / AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt);
701
702 if (pMixBuf->iFreqRatio == 0) /* Catch division by zero. */
703 pMixBuf->iFreqRatio = 1 << 20; /* Do a 1:1 conversion instead. */
704
705 uint32_t cSamples = RT_MIN( ((uint64_t)pParent->cSamples << 32)
706 / pMixBuf->iFreqRatio, _64K /* 64K samples max. */);
707 if (!cSamples)
708 cSamples = pParent->cSamples;
709
710 int rc = VINF_SUCCESS;
711
712 if (cSamples != pMixBuf->cSamples)
713 {
714 AUDMIXBUF_LOG(("%s: Reallocating samples %RU32 -> %RU32\n",
715 pMixBuf->pszName, pMixBuf->cSamples, cSamples));
716
717 uint32_t cbSamples = cSamples * sizeof(PDMAUDIOSAMPLE);
718 Assert(cbSamples);
719 pMixBuf->pSamples = (PPDMAUDIOSAMPLE)RTMemRealloc(pMixBuf->pSamples, cbSamples);
720 if (!pMixBuf->pSamples)
721 rc = VERR_NO_MEMORY;
722
723 if (RT_SUCCESS(rc))
724 {
725 pMixBuf->cSamples = cSamples;
726
727 /* Make sure to zero the reallocated buffer so that it can be
728 * used properly when blending with another buffer later. */
729 RT_BZERO(pMixBuf->pSamples, cbSamples);
730 }
731 }
732
733 if (RT_SUCCESS(rc))
734 {
735 if (!pMixBuf->pRate)
736 {
737 /* Create rate conversion. */
738 pMixBuf->pRate = (PPDMAUDIOSTRMRATE)RTMemAllocZ(sizeof(PDMAUDIOSTRMRATE));
739 if (!pMixBuf->pRate)
740 return VERR_NO_MEMORY;
741 }
742 else
743 RT_BZERO(pMixBuf->pRate, sizeof(PDMAUDIOSTRMRATE));
744
745 pMixBuf->pRate->dstInc = ((uint64_t)AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt) << 32)
746 / AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt);
747
748 AUDMIXBUF_LOG(("uThisHz=%RU32, uParentHz=%RU32, iFreqRatio=0x%RX64 (%RI64), uRateInc=0x%RX64 (%RU64), cSamples=%RU32 (%RU32 parent)\n",
749 AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt),
750 AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt),
751 pMixBuf->iFreqRatio, pMixBuf->iFreqRatio,
752 pMixBuf->pRate->dstInc, pMixBuf->pRate->dstInc,
753 pMixBuf->cSamples,
754 pParent->cSamples));
755 AUDMIXBUF_LOG(("%s (%RU32Hz) -> %s (%RU32Hz)\n",
756 pMixBuf->pszName, AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt),
757 pMixBuf->pParent->pszName, AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt)));
758 }
759
760 return rc;
761}
762
763uint32_t audioMixBufMixed(PPDMAUDIOMIXBUF pMixBuf)
764{
765 AssertPtrReturn(pMixBuf, 0);
766
767 AssertMsgReturn(VALID_PTR(pMixBuf->pParent),
768 ("Buffer is not linked to a parent buffer\n"),
769 0);
770
771 AUDMIXBUF_LOG(("%s: cMixed=%RU32\n", pMixBuf->pszName, pMixBuf->cMixed));
772 return pMixBuf->cMixed;
773}
774
775static int audioMixBufMixTo(PPDMAUDIOMIXBUF pDst, PPDMAUDIOMIXBUF pSrc, uint32_t cSamples, uint32_t *pcProcessed)
776{
777 AssertPtrReturn(pDst, VERR_INVALID_POINTER);
778 AssertPtrReturn(pSrc, VERR_INVALID_POINTER);
779 /* pcProcessed is optional. */
780
781 /* Live samples indicate how many samples there are in the source buffer
782 * which have not been processed yet by the destination buffer. */
783 uint32_t cLive = pSrc->cMixed;
784 if (cLive >= pDst->cSamples)
785 AUDMIXBUF_LOG(("Destination buffer \"%s\" full (%RU32 samples max), live samples = %RU32\n",
786 pDst->pszName, pDst->cSamples, cLive));
787
788 /* Dead samples are the number of samples in the destination buffer which
789 * will not be needed, that is, are not needed in order to process the live
790 * samples of the source buffer. */
791 uint32_t cDead = pDst->cSamples - cLive;
792
793 uint32_t cToReadTotal = RT_MIN(cSamples, AUDIOMIXBUF_S2S_RATIO(pSrc, cDead));
794 uint32_t offRead = 0;
795
796 uint32_t cReadTotal = 0;
797 uint32_t cWrittenTotal = 0;
798 uint32_t offWrite = (pDst->offReadWrite + cLive) % pDst->cSamples;
799
800 AUDMIXBUF_LOG(("pSrc=%s (%RU32 samples), pDst=%s (%RU32 samples), cLive=%RU32, cDead=%RU32, cToReadTotal=%RU32, offWrite=%RU32\n",
801 pSrc->pszName, pSrc->cSamples, pDst->pszName, pDst->cSamples, cLive, cDead, cToReadTotal, offWrite));
802
803 uint32_t cToRead, cToWrite;
804 uint32_t cWritten, cRead;
805
806 while (cToReadTotal)
807 {
808 cDead = pDst->cSamples - cLive;
809
810 cToRead = cToReadTotal;
811 cToWrite = RT_MIN(cDead, pDst->cSamples - offWrite);
812 if (!cToWrite)
813 {
814 AUDMIXBUF_LOG(("Warning: Destination buffer \"%s\" full\n", pDst->pszName));
815 break;
816 }
817
818 Assert(offWrite + cToWrite <= pDst->cSamples);
819 Assert(offRead + cToRead <= pSrc->cSamples);
820
821 AUDMIXBUF_LOG(("\t%RU32Hz -> %RU32Hz\n", AUDMIXBUF_FMT_SAMPLE_FREQ(pSrc->AudioFmt), AUDMIXBUF_FMT_SAMPLE_FREQ(pDst->AudioFmt)));
822 AUDMIXBUF_LOG(("\tcDead=%RU32, offWrite=%RU32, cToWrite=%RU32, offRead=%RU32, cToRead=%RU32\n",
823 cDead, offWrite, cToWrite, offRead, cToRead));
824
825 audioMixBufOpBlend(pDst->pSamples + offWrite, cToWrite,
826 pSrc->pSamples + offRead, cToRead,
827 pSrc->pRate, &cWritten, &cRead);
828
829 AUDMIXBUF_LOG(("\t\tcWritten=%RU32, cRead=%RU32\n", cWritten, cRead));
830
831 cReadTotal += cRead;
832 cWrittenTotal += cWritten;
833
834 offRead += cRead;
835 Assert(cToReadTotal >= cRead);
836 cToReadTotal -= cRead;
837
838 offWrite = (offWrite + cWritten) % pDst->cSamples;
839
840 cLive += cWritten;
841 }
842
843 pSrc->cMixed += cWrittenTotal;
844 pDst->cProcessed += cWrittenTotal;
845#ifdef DEBUG
846 s_cSamplesMixedTotal += cWrittenTotal;
847 audioMixBufPrint(pDst);
848#endif
849
850 if (pcProcessed)
851 *pcProcessed = cReadTotal;
852
853 AUDMIXBUF_LOG(("cReadTotal=%RU32 (pcProcessed), cWrittenTotal=%RU32, cSrcMixed=%RU32, cDstProc=%RU32\n",
854 cReadTotal, cWrittenTotal, pSrc->cMixed, pDst->cProcessed));
855 return VINF_SUCCESS;
856}
857
858int audioMixBufMixToChildren(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamples,
859 uint32_t *pcProcessed)
860{
861 int rc = VINF_SUCCESS;
862
863 uint32_t cProcessed;
864 uint32_t cProcessedMax = 0;
865
866 PPDMAUDIOMIXBUF pIter;
867 RTListForEach(&pMixBuf->lstBuffers, pIter, PDMAUDIOMIXBUF, Node)
868 {
869 rc = audioMixBufMixTo(pIter, pMixBuf, cSamples, &cProcessed);
870 if (RT_FAILURE(rc))
871 break;
872
873 cProcessedMax = RT_MAX(cProcessedMax, cProcessed);
874 }
875
876 if (pcProcessed)
877 *pcProcessed = cProcessedMax;
878
879 return rc;
880}
881
882int audioMixBufMixToParent(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamples,
883 uint32_t *pcProcessed)
884{
885 AssertMsgReturn(VALID_PTR(pMixBuf->pParent),
886 ("Buffer is not linked to a parent buffer\n"),
887 VERR_INVALID_PARAMETER);
888
889 return audioMixBufMixTo(pMixBuf->pParent, pMixBuf, cSamples, pcProcessed);
890}
891
892static inline void audioMixBufPrint(PPDMAUDIOMIXBUF pMixBuf)
893{
894#ifdef DEBUG_DISABLED
895 PPDMAUDIOMIXBUF pParent = pMixBuf;
896 if (pMixBuf->pParent)
897 pParent = pMixBuf->pParent;
898
899 AUDMIXBUF_LOG(("********************************************\n"));
900 AUDMIXBUF_LOG(("%s: offReadWrite=%RU32, cProcessed=%RU32, cMixed=%RU32 (BpS=%RU32)\n",
901 pParent->pszName,
902 pParent->offReadWrite, pParent->cProcessed, pParent->cMixed,
903 AUDIOMIXBUF_S2B(pParent, 1)));
904
905 PPDMAUDIOMIXBUF pIter;
906 RTListForEach(&pParent->lstBuffers, pIter, PDMAUDIOMIXBUF, Node)
907 {
908 AUDMIXBUF_LOG(("\t%s: offReadWrite=%RU32, cProcessed=%RU32, cMixed=%RU32 (BpS=%RU32)\n",
909 pIter->pszName,
910 pIter->offReadWrite, pIter->cProcessed, pIter->cMixed,
911 AUDIOMIXBUF_S2B(pIter, 1)));
912 }
913 AUDMIXBUF_LOG(("Total samples mixed: %RU64\n", s_cSamplesMixedTotal));
914 AUDMIXBUF_LOG(("********************************************\n"));
915#endif
916}
917
918/**
919 * Returns the total number of samples processed.
920 *
921 * @return uint32_t
922 * @param pMixBuf
923 */
924uint32_t audioMixBufProcessed(PPDMAUDIOMIXBUF pMixBuf)
925{
926 AssertPtrReturn(pMixBuf, 0);
927
928 AUDMIXBUF_LOG(("%s: cProcessed=%RU32\n", pMixBuf->pszName, pMixBuf->cProcessed));
929 return pMixBuf->cProcessed;
930}
931
932int audioMixBufReadAt(PPDMAUDIOMIXBUF pMixBuf,
933 uint32_t offSamples,
934 void *pvBuf, size_t cbBuf,
935 uint32_t *pcbRead)
936{
937 return audioMixBufReadAtEx(pMixBuf, pMixBuf->AudioFmt,
938 offSamples, pvBuf, cbBuf, pcbRead);
939}
940
941int audioMixBufReadAtEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt,
942 uint32_t offSamples,
943 void *pvBuf, size_t cbBuf,
944 uint32_t *pcbRead)
945{
946 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
947 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
948 /* pcbRead is optional. */
949
950 uint32_t cDstSamples = pMixBuf->cSamples;
951 uint32_t cLive = pMixBuf->cProcessed;
952
953 uint32_t cDead = cDstSamples - cLive;
954 uint32_t cToProcess = AUDIOMIXBUF_S2S_RATIO(pMixBuf, cDead);
955 cToProcess = RT_MIN(cToProcess, AUDIOMIXBUF_B2S(pMixBuf, cbBuf));
956
957 AUDMIXBUF_LOG(("%s: offSamples=%RU32, cLive=%RU32, cDead=%RU32, cToProcess=%RU32\n",
958 pMixBuf->pszName, offSamples, cLive, cDead, cToProcess));
959
960 int rc;
961 if (cToProcess)
962 {
963 PAUDMIXBUF_FN_CONVTO pConv = audioMixBufConvToLookup(enmFmt);
964 if (pConv)
965 {
966 AUDMIXBUF_CONVOPTS convOpts = { cToProcess, pMixBuf->Volume };
967 pConv(pvBuf, pMixBuf->pSamples + offSamples, &convOpts);
968
969 rc = VINF_SUCCESS;
970 }
971 else
972 rc = VERR_INVALID_PARAMETER;
973
974 audioMixBufPrint(pMixBuf);
975 }
976 else
977 rc = VINF_SUCCESS;
978
979 if (RT_SUCCESS(rc))
980 {
981 if (pcbRead)
982 *pcbRead = AUDIOMIXBUF_S2B(pMixBuf, cToProcess);
983 }
984
985 AUDMIXBUF_LOG(("cbRead=%RU32, rc=%Rrc\n", AUDIOMIXBUF_S2B(pMixBuf, cToProcess), rc));
986 return rc;
987}
988
989int audioMixBufReadCirc(PPDMAUDIOMIXBUF pMixBuf,
990 void *pvBuf, size_t cbBuf, uint32_t *pcRead)
991{
992 return audioMixBufReadCircEx(pMixBuf, pMixBuf->AudioFmt,
993 pvBuf, cbBuf, pcRead);
994}
995
996int audioMixBufReadCircEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt,
997 void *pvBuf, size_t cbBuf, uint32_t *pcRead)
998{
999 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
1000 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1001 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1002 /* pcbRead is optional. */
1003
1004 if (!cbBuf)
1005 return VINF_SUCCESS;
1006
1007 uint32_t cToRead = RT_MIN(AUDIOMIXBUF_B2S(pMixBuf, cbBuf), pMixBuf->cProcessed);
1008
1009 AUDMIXBUF_LOG(("%s: pvBuf=%p, cbBuf=%zu (%RU32 samples), cToRead=%RU32\n",
1010 pMixBuf->pszName, pvBuf, cbBuf, AUDIOMIXBUF_B2S(pMixBuf, cbBuf), cToRead));
1011
1012 if (!cToRead)
1013 {
1014 audioMixBufPrint(pMixBuf);
1015
1016 if (pcRead)
1017 *pcRead = 0;
1018 return VINF_SUCCESS;
1019 }
1020
1021 PAUDMIXBUF_FN_CONVTO pConv = audioMixBufConvToLookup(enmFmt);
1022 if (!pConv) /* Audio format not supported. */
1023 return VERR_NOT_SUPPORTED;
1024
1025 PPDMAUDIOSAMPLE pSamplesSrc1 = pMixBuf->pSamples + pMixBuf->offReadWrite;
1026 size_t cLenSrc1 = cToRead;
1027
1028 PPDMAUDIOSAMPLE pSamplesSrc2 = NULL;
1029 size_t cLenSrc2 = 0;
1030
1031 uint32_t offRead = pMixBuf->offReadWrite + cToRead;
1032
1033 /*
1034 * Do we need to wrap around to read all requested data, that is,
1035 * starting at the beginning of our circular buffer? This then will
1036 * be the optional second part to do.
1037 */
1038 if (offRead >= pMixBuf->cSamples)
1039 {
1040 Assert(pMixBuf->offReadWrite <= pMixBuf->cSamples);
1041 cLenSrc1 = pMixBuf->cSamples - pMixBuf->offReadWrite;
1042
1043 pSamplesSrc2 = pMixBuf->pSamples;
1044 Assert(cToRead >= cLenSrc1);
1045 cLenSrc2 = RT_MIN(cToRead - cLenSrc1, pMixBuf->cSamples);
1046
1047 /* Save new read offset. */
1048 offRead = cLenSrc2;
1049 }
1050
1051 AUDMIXBUF_CONVOPTS convOpts;
1052 convOpts.Volume = pMixBuf->Volume;
1053
1054 /* Anything to do at all? */
1055 int rc = VINF_SUCCESS;
1056 if (cLenSrc1)
1057 {
1058 convOpts.cSamples = cLenSrc1;
1059
1060 AUDMIXBUF_LOG(("P1: offRead=%RU32, cToRead=%RU32\n", pMixBuf->offReadWrite, cLenSrc1));
1061 pConv(pvBuf, pSamplesSrc1, &convOpts);
1062 }
1063
1064 /* Second part present? */
1065 if ( RT_LIKELY(RT_SUCCESS(rc))
1066 && cLenSrc2)
1067 {
1068 AssertPtr(pSamplesSrc2);
1069
1070 convOpts.cSamples = cLenSrc2;
1071
1072 AUDMIXBUF_LOG(("P2: cToRead=%RU32, offWrite=%RU32 (%zu bytes)\n", cLenSrc2, cLenSrc1,
1073 AUDIOMIXBUF_S2B(pMixBuf, cLenSrc1)));
1074 pConv((uint8_t *)pvBuf + AUDIOMIXBUF_S2B(pMixBuf, cLenSrc1), pSamplesSrc2, &convOpts);
1075 }
1076
1077 if (RT_SUCCESS(rc))
1078 {
1079#ifdef DEBUG_DUMP_PCM_DATA
1080 RTFILE fh;
1081 rc = RTFileOpen(&fh, "c:\\temp\\mixbuf_readcirc.pcm",
1082 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1083 if (RT_SUCCESS(rc))
1084 {
1085 RTFileWrite(fh, pvBuf, AUDIOMIXBUF_S2B(pMixBuf, cLenSrc1 + cLenSrc2), NULL);
1086 RTFileClose(fh);
1087 }
1088#endif
1089 pMixBuf->offReadWrite = offRead % pMixBuf->cSamples;
1090 pMixBuf->cProcessed -= RT_MIN(cLenSrc1 + cLenSrc2, pMixBuf->cProcessed);
1091
1092 if (pcRead)
1093 *pcRead = cLenSrc1 + cLenSrc2;
1094 }
1095
1096 audioMixBufPrint(pMixBuf);
1097
1098 AUDMIXBUF_LOG(("cRead=%RU32 (%zu bytes), rc=%Rrc\n",
1099 cLenSrc1 + cLenSrc2,
1100 AUDIOMIXBUF_S2B(pMixBuf, cLenSrc1 + cLenSrc2), rc));
1101 return rc;
1102}
1103
1104void audioMixBufReset(PPDMAUDIOMIXBUF pMixBuf)
1105{
1106 AssertPtrReturnVoid(pMixBuf);
1107
1108 AUDMIXBUF_LOG(("%s\n", pMixBuf->pszName));
1109
1110 pMixBuf->offReadWrite = 0;
1111 pMixBuf->cMixed = 0;
1112 pMixBuf->cProcessed = 0;
1113
1114 if (pMixBuf->cSamples)
1115 RT_BZERO(pMixBuf->pSamples, pMixBuf->cSamples * sizeof(PDMAUDIOSAMPLE));
1116}
1117
1118void audioMixBufSetVolume(PPDMAUDIOMIXBUF pMixBuf, PPDMAUDIOVOLUME pVol)
1119{
1120 AssertPtrReturnVoid(pMixBuf);
1121 AssertPtrReturnVoid(pVol);
1122
1123 LogFlowFunc(("%s: lVol=%RU32, rVol=%RU32\n", pMixBuf->pszName, pVol->uLeft, pVol->uRight));
1124
1125 pMixBuf->Volume.fMuted = pVol->fMuted;
1126 pMixBuf->Volume.uLeft = (UINT64_C(0x100000000) * pVol->uLeft) / 255;
1127 pMixBuf->Volume.uRight = (UINT64_C(0x100000000) * pVol->uRight) / 255;
1128
1129 LogFlowFunc(("\t-> lVol=%RU32, rVol=%RU32\n", pMixBuf->Volume.uLeft, pMixBuf->Volume.uRight));
1130}
1131
1132uint32_t audioMixBufSize(PPDMAUDIOMIXBUF pMixBuf)
1133{
1134 return pMixBuf->cSamples;
1135}
1136
1137/**
1138 * Returns the maximum amount of bytes this buffer can hold.
1139 *
1140 * @return uint32_t
1141 * @param pMixBuf
1142 */
1143size_t audioMixBufSizeBytes(PPDMAUDIOMIXBUF pMixBuf)
1144{
1145 return AUDIOMIXBUF_S2B(pMixBuf, pMixBuf->cSamples);
1146}
1147
1148void audioMixBufUnlink(PPDMAUDIOMIXBUF pMixBuf)
1149{
1150 if (!pMixBuf || !pMixBuf->pszName)
1151 return;
1152
1153 AUDMIXBUF_LOG(("%s\n", pMixBuf->pszName));
1154
1155 if (pMixBuf->pParent)
1156 {
1157 AUDMIXBUF_LOG(("%s: Unlinking from parent \"%s\"\n",
1158 pMixBuf->pszName, pMixBuf->pParent->pszName));
1159
1160 RTListNodeRemove(&pMixBuf->Node);
1161
1162 /* Make sure to reset the parent mixing buffer each time it gets linked
1163 * to a new child. */
1164 audioMixBufReset(pMixBuf->pParent);
1165 pMixBuf->pParent = NULL;
1166 }
1167
1168 PPDMAUDIOMIXBUF pIter;
1169 while (!RTListIsEmpty(&pMixBuf->lstBuffers))
1170 {
1171 pIter = RTListGetFirst(&pMixBuf->lstBuffers, PDMAUDIOMIXBUF, Node);
1172
1173 AUDMIXBUF_LOG(("\tUnlinking \"%s\"\n", pIter->pszName));
1174
1175 audioMixBufReset(pIter->pParent);
1176 pIter->pParent = NULL;
1177
1178 RTListNodeRemove(&pIter->Node);
1179 }
1180
1181 if (pMixBuf->pRate)
1182 {
1183 pMixBuf->pRate->dstOffset = pMixBuf->pRate->srcOffset = 0;
1184 pMixBuf->pRate->dstInc = 0;
1185 }
1186
1187 pMixBuf->iFreqRatio = 1; /* Prevent division by zero. */
1188}
1189
1190int audioMixBufWriteAt(PPDMAUDIOMIXBUF pMixBuf,
1191 uint32_t offSamples,
1192 const void *pvBuf, size_t cbBuf,
1193 uint32_t *pcWritten)
1194{
1195 return audioMixBufWriteAtEx(pMixBuf, pMixBuf->AudioFmt,
1196 offSamples, pvBuf, cbBuf, pcWritten);
1197}
1198
1199/**
1200 * TODO
1201 *
1202 * @return IPRT status code.
1203 * @return int
1204 * @param pMixBuf
1205 * @param enmFmt
1206 * @param offSamples
1207 * @param pvBuf
1208 * @param cbBuf
1209 * @param pcWritten Returns number of samples written. Optional.
1210 */
1211int audioMixBufWriteAtEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt,
1212 uint32_t offSamples,
1213 const void *pvBuf, size_t cbBuf,
1214 uint32_t *pcWritten)
1215{
1216 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
1217 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1218 /* pcWritten is optional. */
1219
1220 uint32_t cDstSamples = pMixBuf->pParent
1221 ? pMixBuf->pParent->cSamples : pMixBuf->cSamples;
1222 uint32_t cLive = pMixBuf->cProcessed;
1223
1224 uint32_t cDead = cDstSamples - cLive;
1225 uint32_t cToProcess = AUDIOMIXBUF_S2S_RATIO(pMixBuf, cDead);
1226 cToProcess = RT_MIN(cToProcess, AUDIOMIXBUF_B2S(pMixBuf, cbBuf));
1227
1228 AUDMIXBUF_LOG(("%s: offSamples=%RU32, cLive=%RU32, cDead=%RU32, cToProcess=%RU32\n",
1229 pMixBuf->pszName, offSamples, cLive, cDead, cToProcess));
1230
1231 if (offSamples + cToProcess > pMixBuf->cSamples)
1232 return VERR_BUFFER_OVERFLOW;
1233
1234 PAUDMIXBUF_FN_CONVFROM pConv = audioMixBufConvFromLookup(enmFmt);
1235 if (!pConv)
1236 return VERR_NOT_SUPPORTED;
1237
1238 int rc;
1239 uint32_t cWritten;
1240
1241#ifdef DEBUG_DUMP_PCM_DATA
1242 RTFILE fh;
1243 rc = RTFileOpen(&fh, "c:\\temp\\mixbuf_writeat.pcm",
1244 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1245 if (RT_SUCCESS(rc))
1246 {
1247 RTFileWrite(fh, pvBuf, cbBuf, NULL);
1248 RTFileClose(fh);
1249 }
1250#endif
1251
1252 if (cToProcess)
1253 {
1254 AUDMIXBUF_CONVOPTS convOpts = { cToProcess, pMixBuf->Volume };
1255
1256 cWritten = pConv(pMixBuf->pSamples + offSamples, pvBuf, cbBuf, &convOpts);
1257 audioMixBufPrint(pMixBuf);
1258
1259 rc = cWritten ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Fudge! */
1260 }
1261 else
1262 {
1263 cWritten = 0;
1264 rc = VINF_SUCCESS;
1265 }
1266
1267 if (RT_SUCCESS(rc))
1268 {
1269 if (pcWritten)
1270 *pcWritten = cWritten;
1271 }
1272
1273 AUDMIXBUF_LOG(("cWritten=%RU32, rc=%Rrc\n", cWritten, rc));
1274 return rc;
1275}
1276
1277int audioMixBufWriteCirc(PPDMAUDIOMIXBUF pMixBuf,
1278 const void *pvBuf, size_t cbBuf,
1279 uint32_t *pcWritten)
1280{
1281 return audioMixBufWriteCircEx(pMixBuf, pMixBuf->AudioFmt, pvBuf, cbBuf, pcWritten);
1282}
1283
1284int audioMixBufWriteCircEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt,
1285 const void *pvBuf, size_t cbBuf,
1286 uint32_t *pcWritten)
1287{
1288 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
1289 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1290 /* pcbWritten is optional. */
1291
1292 if (!cbBuf)
1293 {
1294 if (pcWritten)
1295 *pcWritten = 0;
1296 return VINF_SUCCESS;
1297 }
1298
1299 PPDMAUDIOMIXBUF pParent = pMixBuf->pParent;
1300
1301 AUDMIXBUF_LOG(("%s: enmFmt=%ld, pBuf=%p, cbBuf=%zu, pParent=%p (%RU32)\n",
1302 pMixBuf->pszName, enmFmt, pvBuf, cbBuf, pParent, pParent ? pParent->cSamples : 0));
1303
1304 if ( pParent
1305 && pParent->cSamples <= pMixBuf->cMixed)
1306 {
1307 if (pcWritten)
1308 *pcWritten = 0;
1309
1310 AUDMIXBUF_LOG(("%s: Parent buffer %s is full\n",
1311 pMixBuf->pszName, pMixBuf->pParent->pszName));
1312
1313 return VINF_SUCCESS;
1314 }
1315
1316 PAUDMIXBUF_FN_CONVFROM pConv = audioMixBufConvFromLookup(enmFmt);
1317 if (!pConv)
1318 return VERR_NOT_SUPPORTED;
1319
1320 int rc = VINF_SUCCESS;
1321
1322 uint32_t cToWrite = AUDIOMIXBUF_B2S(pMixBuf, cbBuf);
1323 AssertMsg(cToWrite, ("cToWrite is 0 (cbBuf=%zu)\n", cbBuf));
1324
1325 PPDMAUDIOSAMPLE pSamplesDst1 = pMixBuf->pSamples + pMixBuf->offReadWrite;
1326 size_t cLenDst1 = cToWrite;
1327
1328 PPDMAUDIOSAMPLE pSamplesDst2 = NULL;
1329 size_t cLenDst2 = 0;
1330
1331 uint32_t offWrite = pMixBuf->offReadWrite + cToWrite;
1332
1333 /*
1334 * Do we need to wrap around to write all requested data, that is,
1335 * starting at the beginning of our circular buffer? This then will
1336 * be the optional second part to do.
1337 */
1338 if (offWrite >= pMixBuf->cSamples)
1339 {
1340 Assert(pMixBuf->offReadWrite <= pMixBuf->cSamples);
1341 cLenDst1 = pMixBuf->cSamples - pMixBuf->offReadWrite;
1342
1343 pSamplesDst2 = pMixBuf->pSamples;
1344 Assert(cToWrite >= cLenDst1);
1345 cLenDst2 = RT_MIN(cToWrite - cLenDst1, pMixBuf->cSamples);
1346
1347 /* Save new read offset. */
1348 offWrite = cLenDst2;
1349 }
1350
1351 uint32_t cWrittenTotal = 0;
1352
1353 AUDMIXBUF_CONVOPTS convOpts;
1354 convOpts.Volume = pMixBuf->Volume;
1355
1356 /* Anything to do at all? */
1357 if (cLenDst1)
1358 {
1359 convOpts.cSamples = cLenDst1;
1360 cWrittenTotal = pConv(pSamplesDst1, pvBuf, cbBuf, &convOpts);
1361 }
1362
1363 /* Second part present? */
1364 if ( RT_LIKELY(RT_SUCCESS(rc))
1365 && cLenDst2)
1366 {
1367 AssertPtr(pSamplesDst2);
1368
1369 convOpts.cSamples = cLenDst2;
1370 cWrittenTotal += pConv(pSamplesDst2, (uint8_t *)pvBuf + AUDIOMIXBUF_S2B(pMixBuf, cLenDst1), cbBuf, &convOpts);
1371 }
1372
1373#ifdef DEBUG_DUMP_PCM_DATA
1374 RTFILE fh;
1375 RTFileOpen(&fh, "c:\\temp\\mixbuf_writeex.pcm",
1376 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1377 RTFileWrite(fh, pSamplesDst1, AUDIOMIXBUF_S2B(pMixBuf, cLenDst1), NULL);
1378 RTFileClose(fh);
1379#endif
1380
1381 AUDMIXBUF_LOG(("cLenDst1=%RU32, cLenDst2=%RU32, offWrite=%RU32\n",
1382 cLenDst1, cLenDst2, offWrite));
1383
1384 if (RT_SUCCESS(rc))
1385 {
1386 pMixBuf->offReadWrite = offWrite % pMixBuf->cSamples;
1387 pMixBuf->cProcessed = RT_MIN(pMixBuf->cProcessed + cLenDst1 + cLenDst2,
1388 pMixBuf->cSamples /* Max */);
1389 if (pcWritten)
1390 *pcWritten = cLenDst1 + cLenDst2;
1391 }
1392
1393 audioMixBufPrint(pMixBuf);
1394
1395 AUDMIXBUF_LOG(("cWritten=%RU32 (%zu bytes), rc=%Rrc\n",
1396 cLenDst1 + cLenDst2,
1397 AUDIOMIXBUF_S2B(pMixBuf, cLenDst1 + cLenDst2), rc));
1398 return rc;
1399}
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