VirtualBox

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

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

PDM/Audio: Unified timer work, minor cleanup.

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