VirtualBox

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

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

Audio/AudioMixBuffer.cpp: Logging.

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

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