VirtualBox

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

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

Audio/AudioMixBuffer: Bugfixes, updated testcase.

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