VirtualBox

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

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

PDM/Audio: Update.

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