VirtualBox

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

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

PDM/Audio/AudioMixBuffer.cpp: Fixes.

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