VirtualBox

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

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