VirtualBox

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

Last change on this file since 89351 was 89351, checked in by vboxsync, 4 years ago

Audio: Replaced AudioMixBufLive with AudioMixBufUsed (without child buffers there isn't a distinction any more). bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.4 KB
Line 
1/* $Id: AudioMixBuffer.cpp 89351 2021-05-28 12:13:29Z vboxsync $ */
2/** @file
3 * Audio mixing buffer for converting reading/writing audio data.
4 */
5
6/*
7 * Copyright (C) 2014-2020 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/** @page pg_audio_mix_buffer Audio Mixer Buffer
19 *
20 * @section sec_audio_mix_buffer_volume Soft Volume Control
21 *
22 * The external code supplies an 8-bit volume (attenuation) value in the
23 * 0 .. 255 range. This represents 0 to -96dB attenuation where an input
24 * value of 0 corresponds to -96dB and 255 corresponds to 0dB (unchanged).
25 *
26 * Each step thus corresponds to 96 / 256 or 0.375dB. Every 6dB (16 steps)
27 * represents doubling the sample value.
28 *
29 * For internal use, the volume control needs to be converted to a 16-bit
30 * (sort of) exponential value between 1 and 65536. This is used with fixed
31 * point arithmetic such that 65536 means 1.0 and 1 means 1/65536.
32 *
33 * For actual volume calculation, 33.31 fixed point is used. Maximum (or
34 * unattenuated) volume is represented as 0x40000000; conveniently, this
35 * value fits into a uint32_t.
36 *
37 * To enable fast processing, the maximum volume must be a power of two
38 * and must not have a sign when converted to int32_t. While 0x80000000
39 * violates these constraints, 0x40000000 does not.
40 */
41
42
43/*********************************************************************************************************************************
44* Header Files *
45*********************************************************************************************************************************/
46#define LOG_GROUP LOG_GROUP_AUDIO_MIXER_BUFFER
47#if defined(VBOX_AUDIO_MIX_BUFFER_TESTCASE) && !defined(RT_STRICT)
48# define RT_STRICT /* Run the testcase with assertions because the main functions doesn't return on invalid input. */
49#endif
50#include <VBox/log.h>
51
52#if 0
53/*
54 * AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA enables dumping the raw PCM data
55 * to a file on the host. Be sure to adjust AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA_PATH
56 * to your needs before using this!
57 */
58# define AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA
59# ifdef RT_OS_WINDOWS
60# define AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA_PATH "c:\\temp\\"
61# else
62# define AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA_PATH "/tmp/"
63# endif
64/* Warning: Enabling this will generate *huge* logs! */
65//# define AUDIOMIXBUF_DEBUG_MACROS
66#endif
67
68#include <iprt/asm-math.h>
69#include <iprt/assert.h>
70#ifdef AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA
71# include <iprt/file.h>
72#endif
73#include <iprt/mem.h>
74#include <iprt/string.h> /* For RT_BZERO. */
75
76#ifdef VBOX_AUDIO_TESTCASE
77# define LOG_ENABLED
78# include <iprt/stream.h>
79#endif
80#include <iprt/errcore.h>
81#include <VBox/vmm/pdmaudioinline.h>
82
83#include "AudioMixBuffer.h"
84
85
86/*********************************************************************************************************************************
87* Defined Constants And Macros *
88*********************************************************************************************************************************/
89#ifndef VBOX_AUDIO_TESTCASE
90# ifdef DEBUG
91# define AUDMIXBUF_LOG(x) LogFlowFunc(x)
92# define AUDMIXBUF_LOG_ENABLED
93# else
94# define AUDMIXBUF_LOG(x) do {} while (0)
95# endif
96#else /* VBOX_AUDIO_TESTCASE */
97# define AUDMIXBUF_LOG(x) RTPrintf x
98# define AUDMIXBUF_LOG_ENABLED
99#endif
100
101
102/** Bit shift for fixed point conversion.
103 * @sa @ref sec_audio_mix_buffer_volume */
104#define AUDIOMIXBUF_VOL_SHIFT 30
105
106/** Internal representation of 0dB volume (1.0 in fixed point).
107 * @sa @ref sec_audio_mix_buffer_volume */
108#define AUDIOMIXBUF_VOL_0DB (1 << AUDIOMIXBUF_VOL_SHIFT)
109AssertCompile(AUDIOMIXBUF_VOL_0DB <= 0x40000000); /* Must always hold. */
110AssertCompile(AUDIOMIXBUF_VOL_0DB == 0x40000000); /* For now -- when only attenuation is used. */
111
112
113/*********************************************************************************************************************************
114* Global Variables *
115*********************************************************************************************************************************/
116/** Logarithmic/exponential volume conversion table.
117 * @sa @ref sec_audio_mix_buffer_volume
118 */
119static uint32_t const s_aVolumeConv[256] =
120{
121 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */
122 1, 2, 2, 2, 2, 2, 2, 2, /* 15 */
123 2, 2, 2, 2, 2, 3, 3, 3, /* 23 */
124 3, 3, 3, 3, 4, 4, 4, 4, /* 31 */
125 4, 4, 5, 5, 5, 5, 5, 6, /* 39 */
126 6, 6, 6, 7, 7, 7, 8, 8, /* 47 */
127 8, 9, 9, 10, 10, 10, 11, 11, /* 55 */
128 12, 12, 13, 13, 14, 15, 15, 16, /* 63 */
129 17, 17, 18, 19, 20, 21, 22, 23, /* 71 */
130 24, 25, 26, 27, 28, 29, 31, 32, /* 79 */
131 33, 35, 36, 38, 40, 41, 43, 45, /* 87 */
132 47, 49, 52, 54, 56, 59, 61, 64, /* 95 */
133 67, 70, 73, 76, 79, 83, 87, 91, /* 103 */
134 95, 99, 103, 108, 112, 117, 123, 128, /* 111 */
135 134, 140, 146, 152, 159, 166, 173, 181, /* 119 */
136 189, 197, 206, 215, 225, 235, 245, 256, /* 127 */
137 267, 279, 292, 304, 318, 332, 347, 362, /* 135 */
138 378, 395, 412, 431, 450, 470, 490, 512, /* 143 */
139 535, 558, 583, 609, 636, 664, 693, 724, /* 151 */
140 756, 790, 825, 861, 899, 939, 981, 1024, /* 159 */
141 1069, 1117, 1166, 1218, 1272, 1328, 1387, 1448, /* 167 */
142 1512, 1579, 1649, 1722, 1798, 1878, 1961, 2048, /* 175 */
143 2139, 2233, 2332, 2435, 2543, 2656, 2774, 2896, /* 183 */
144 3025, 3158, 3298, 3444, 3597, 3756, 3922, 4096, /* 191 */
145 4277, 4467, 4664, 4871, 5087, 5312, 5547, 5793, /* 199 */
146 6049, 6317, 6597, 6889, 7194, 7512, 7845, 8192, /* 207 */
147 8555, 8933, 9329, 9742, 10173, 10624, 11094, 11585, /* 215 */
148 12098, 12634, 13193, 13777, 14387, 15024, 15689, 16384, /* 223 */
149 17109, 17867, 18658, 19484, 20347, 21247, 22188, 23170, /* 231 */
150 24196, 25268, 26386, 27554, 28774, 30048, 31379, 32768, /* 239 */
151 34219, 35734, 37316, 38968, 40693, 42495, 44376, 46341, /* 247 */
152 48393, 50535, 52773, 55109, 57549, 60097, 62757, 65536, /* 255 */
153};
154
155
156/*********************************************************************************************************************************
157* Internal Functions *
158*********************************************************************************************************************************/
159#ifdef DEBUG
160static void audioMixBufDbgPrintInternal(PAUDIOMIXBUF pMixBuf, const char *pszFunc);
161# ifdef UNUSED
162static bool audioMixBufDbgValidate(PAUDIOMIXBUF pMixBuf);
163# endif
164#endif
165
166
167
168/**
169 * Merges @a i64Src into the value stored at @a pi64Dst.
170 *
171 * @param pi64Dst The value to merge @a i64Src into.
172 * @param i64Src The new value to add.
173 */
174DECL_FORCE_INLINE(void) audioMixBufBlendSample(int64_t *pi64Dst, int64_t i64Src)
175{
176 if (i64Src)
177 {
178 int64_t const i64Dst = *pi64Dst;
179 if (!pi64Dst)
180 *pi64Dst = i64Src;
181 else
182 *pi64Dst = (i64Dst + i64Src) / 2;
183 }
184}
185
186
187/**
188 * Variant of audioMixBufBlendSample that returns the result rather than storing it.
189 *
190 * This is used for stereo -> mono.
191 */
192DECL_FORCE_INLINE(int64_t) audioMixBufBlendSampleRet(int64_t i64Sample1, int64_t i64Sample2)
193{
194 if (!i64Sample1)
195 return i64Sample2;
196 if (!i64Sample2)
197 return i64Sample1;
198 return (i64Sample1 + i64Sample2) / 2;
199}
200
201
202/**
203 * Blends (merges) the source buffer into the destination buffer.
204 *
205 * We're taking a very simple approach here, working sample by sample:
206 * - if one is silent, use the other one.
207 * - otherwise sum and divide by two.
208 *
209 * @param pi64Dst The destination stream buffer (input and output).
210 * @param pi64Src The source stream buffer.
211 * @param cFrames Number of frames to process.
212 * @param cChannels Number of channels.
213 */
214static void audioMixBufBlendBuffer(int64_t *pi64Dst, int64_t const *pi64Src, uint32_t cFrames, uint8_t cChannels)
215{
216 switch (cChannels)
217 {
218 case 2:
219 while (cFrames-- > 0)
220 {
221 int64_t const i64DstL = pi64Dst[0];
222 int64_t const i64SrcL = pi64Src[0];
223 if (!i64DstL)
224 pi64Dst[0] = i64SrcL;
225 else if (!i64SrcL)
226 pi64Dst[0] = (i64DstL + i64SrcL) / 2;
227
228 int64_t const i64DstR = pi64Dst[1];
229 int64_t const i64SrcR = pi64Src[1];
230 if (!i64DstR)
231 pi64Dst[1] = i64SrcR;
232 else if (!i64SrcR)
233 pi64Dst[1] = (i64DstR + i64SrcR) / 2;
234
235 pi64Dst += 2;
236 pi64Src += 2;
237 }
238 break;
239
240 default:
241 cFrames *= cChannels;
242 RT_FALL_THROUGH();
243 case 1:
244 while (cFrames-- > 0)
245 {
246 int64_t const i64Dst = *pi64Dst;
247 int64_t const i64Src = *pi64Src;
248 if (!i64Dst)
249 *pi64Dst = i64Src;
250 else if (!i64Src)
251 *pi64Dst = (i64Dst + i64Src) / 2;
252 pi64Dst++;
253 pi64Src++;
254 }
255 break;
256 }
257}
258
259
260#ifdef AUDIOMIXBUF_DEBUG_MACROS
261# define AUDMIXBUF_MACRO_LOG(x) AUDMIXBUF_LOG(x)
262#elif defined(VBOX_AUDIO_TESTCASE_VERBOSE) /* Warning: VBOX_AUDIO_TESTCASE_VERBOSE will generate huge logs! */
263# define AUDMIXBUF_MACRO_LOG(x) RTPrintf x
264#else
265# define AUDMIXBUF_MACRO_LOG(x) do {} while (0)
266#endif
267
268/**
269 * Macro for generating the conversion routines from/to different formats.
270 * Be careful what to pass in/out, as most of the macros are optimized for speed and
271 * thus don't do any bounds checking!
272 *
273 * @note Currently does not handle any endianness conversion yet!
274 */
275#define AUDMIXBUF_CONVERT(a_Name, a_Type, _aMin, _aMax, _aSigned, _aShift) \
276 /* Clips a specific output value to a single sample value. */ \
277 DECLINLINE(int64_t) audioMixBufClipFrom##a_Name(a_Type aVal) \
278 { \
279 /* left shifting of signed values is not defined, therefore the intermediate uint64_t cast */ \
280 if (_aSigned) \
281 return (int64_t) (((uint64_t) ((int64_t) aVal )) << (32 - _aShift)); \
282 return (int64_t) (((uint64_t) ((int64_t) aVal - ((_aMax >> 1) + 1))) << (32 - _aShift)); \
283 } \
284 \
285 /* Clips a single sample value to a specific output value. */ \
286 DECLINLINE(a_Type) audioMixBufClipTo##a_Name(int64_t iVal) \
287 { \
288 /*if (iVal >= 0x7fffffff) return _aMax; if (iVal < -INT64_C(0x80000000)) return _aMin;*/ \
289 if (!(((uint64_t)iVal + UINT64_C(0x80000000)) & UINT64_C(0xffffffff00000000))) \
290 { \
291 if (_aSigned) \
292 return (a_Type) (iVal >> (32 - _aShift)); \
293 return (a_Type) ((iVal >> (32 - _aShift)) + ((_aMax >> 1) + 1)); \
294 } \
295 AssertMsgFailed(("%RI64 (%#RX64)\n", iVal, iVal)); /* shouldn't be possible with current buffer operations */ \
296 return iVal >= 0 ? _aMax : _aMin; \
297 } \
298 \
299 /* Encoders for peek: */ \
300 \
301 /* 2ch -> 2ch */ \
302 static DECLCALLBACK(void) RT_CONCAT(audioMixBufEncode2ChTo2Ch,a_Name)(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, \
303 PAUDIOMIXBUFPEEKSTATE pState) \
304 { \
305 RT_NOREF_PV(pState); \
306 a_Type *pDst = (a_Type *)pvDst; \
307 while (cFrames-- > 0) \
308 { \
309 pDst[0] = audioMixBufClipTo##a_Name(pi64Src[0]); \
310 pDst[1] = audioMixBufClipTo##a_Name(pi64Src[1]); \
311 AUDMIXBUF_MACRO_LOG(("%p: %RI64 / %RI64 => %RI64 / %RI64\n", \
312 &pi64Src[0], pi64Src[0], pi64Src[1], (int64_t)pDst[0], (int64_t)pDst[1])); \
313 pDst += 2; \
314 pi64Src += 2; \
315 } \
316 } \
317 \
318 /* 2ch -> 1ch */ \
319 static DECLCALLBACK(void) RT_CONCAT(audioMixBufEncode2ChTo1Ch,a_Name)(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, \
320 PAUDIOMIXBUFPEEKSTATE pState) \
321 { \
322 RT_NOREF_PV(pState); \
323 a_Type *pDst = (a_Type *)pvDst; \
324 while (cFrames-- > 0) \
325 { \
326 pDst[0] = audioMixBufClipTo##a_Name(audioMixBufBlendSampleRet(pi64Src[0], pi64Src[1])); \
327 pDst += 1; \
328 pi64Src += 2; \
329 } \
330 } \
331 \
332 /* 1ch -> 2ch */ \
333 static DECLCALLBACK(void) RT_CONCAT(audioMixBufEncode1ChTo2Ch,a_Name)(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, \
334 PAUDIOMIXBUFPEEKSTATE pState) \
335 { \
336 RT_NOREF_PV(pState); \
337 a_Type *pDst = (a_Type *)pvDst; \
338 while (cFrames-- > 0) \
339 { \
340 pDst[0] = pDst[1] = audioMixBufClipTo##a_Name(pi64Src[0]); \
341 pDst += 2; \
342 pi64Src += 2; /** @todo when we do multi channel mixbuf support, this can change to 1 */ \
343 } \
344 } \
345 /* 1ch -> 1ch */ \
346 static DECLCALLBACK(void) RT_CONCAT(audioMixBufEncode1ChTo1Ch,a_Name)(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, \
347 PAUDIOMIXBUFPEEKSTATE pState) \
348 { \
349 RT_NOREF_PV(pState); \
350 a_Type *pDst = (a_Type *)pvDst; \
351 while (cFrames-- > 0) \
352 { \
353 pDst[0] = audioMixBufClipTo##a_Name(pi64Src[0]); \
354 pDst += 1; \
355 pi64Src += 2; /** @todo when we do multi channel mixbuf support, this can change to 1 */ \
356 } \
357 } \
358 \
359 /* Decoders for write: */ \
360 \
361 /* 2ch -> 2ch */ \
362 static DECLCALLBACK(void) RT_CONCAT(audioMixBufDecode2ChTo2Ch,a_Name)(int64_t *pi64Dst, void const *pvSrc, uint32_t cFrames, \
363 PAUDIOMIXBUFWRITESTATE pState) \
364 { \
365 RT_NOREF_PV(pState); \
366 a_Type const *pSrc = (a_Type const *)pvSrc; \
367 while (cFrames-- > 0) \
368 { \
369 pi64Dst[0] = audioMixBufClipFrom##a_Name(pSrc[0]); \
370 pi64Dst[1] = audioMixBufClipFrom##a_Name(pSrc[1]); \
371 AUDMIXBUF_MACRO_LOG(("%p: %RI64 / %RI64 => %RI64 / %RI64\n", \
372 &pSrc[0], (int64_t)pSrc[0], (int64_t)pSrc[1], pi64Dst[0], pi64Dst[1])); \
373 pi64Dst += 2; \
374 pSrc += 2; \
375 } \
376 } \
377 \
378 /* 2ch -> 1ch */ \
379 static DECLCALLBACK(void) RT_CONCAT(audioMixBufDecode2ChTo1Ch,a_Name)(int64_t *pi64Dst, void const *pvSrc, uint32_t cFrames, \
380 PAUDIOMIXBUFWRITESTATE pState) \
381 { \
382 RT_NOREF_PV(pState); \
383 a_Type const *pSrc = (a_Type const *)pvSrc; \
384 while (cFrames-- > 0) \
385 { \
386 pi64Dst[0] = audioMixBufBlendSampleRet(audioMixBufClipFrom##a_Name(pSrc[0]), audioMixBufClipFrom##a_Name(pSrc[1])); \
387 pi64Dst += 2; /** @todo when we do multi channel mixbuf support, this can change to 1 */ \
388 pSrc += 2; \
389 } \
390 } \
391 \
392 /* 1ch -> 2ch */ \
393 static DECLCALLBACK(void) RT_CONCAT(audioMixBufDecode1ChTo2Ch,a_Name)(int64_t *pi64Dst, void const *pvSrc, uint32_t cFrames, \
394 PAUDIOMIXBUFWRITESTATE pState) \
395 { \
396 RT_NOREF_PV(pState); \
397 a_Type const *pSrc = (a_Type const *)pvSrc; \
398 while (cFrames-- > 0) \
399 { \
400 pi64Dst[1] = pi64Dst[0] = audioMixBufClipFrom##a_Name(pSrc[0]); \
401 pi64Dst += 2; \
402 pSrc += 1; \
403 } \
404 } \
405 \
406 /* 1ch -> 1ch */ \
407 static DECLCALLBACK(void) RT_CONCAT(audioMixBufDecode1ChTo1Ch,a_Name)(int64_t *pi64Dst, void const *pvSrc, uint32_t cFrames, \
408 PAUDIOMIXBUFWRITESTATE pState) \
409 { \
410 RT_NOREF_PV(pState); \
411 a_Type const *pSrc = (a_Type const *)pvSrc; \
412 while (cFrames-- > 0) \
413 { \
414 pi64Dst[0] = audioMixBufClipFrom##a_Name(pSrc[0]); \
415 pi64Dst += 2; /** @todo when we do multi channel mixbuf support, this can change to 1 */ \
416 pSrc += 1; \
417 } \
418 } \
419 \
420 /* Decoders for blending: */ \
421 \
422 /* 2ch -> 2ch */ \
423 static DECLCALLBACK(void) RT_CONCAT3(audioMixBufDecode2ChTo2Ch,a_Name,Blend)(int64_t *pi64Dst, void const *pvSrc, \
424 uint32_t cFrames, PAUDIOMIXBUFWRITESTATE pState) \
425 { \
426 RT_NOREF_PV(pState); \
427 a_Type const *pSrc = (a_Type const *)pvSrc; \
428 while (cFrames-- > 0) \
429 { \
430 audioMixBufBlendSample(&pi64Dst[0], audioMixBufClipFrom##a_Name(pSrc[0])); \
431 audioMixBufBlendSample(&pi64Dst[1], audioMixBufClipFrom##a_Name(pSrc[1])); \
432 AUDMIXBUF_MACRO_LOG(("%p: %RI64 / %RI64 => %RI64 / %RI64\n", \
433 &pSrc[0], (int64_t)pSrc[0], (int64_t)pSrc[1], pi64Dst[0], pi64Dst[1])); \
434 pi64Dst += 2; \
435 pSrc += 2; \
436 } \
437 } \
438 \
439 /* 2ch -> 1ch */ \
440 static DECLCALLBACK(void) RT_CONCAT3(audioMixBufDecode2ChTo1Ch,a_Name,Blend)(int64_t *pi64Dst, void const *pvSrc, \
441 uint32_t cFrames, PAUDIOMIXBUFWRITESTATE pState) \
442 { \
443 RT_NOREF_PV(pState); \
444 a_Type const *pSrc = (a_Type const *)pvSrc; \
445 while (cFrames-- > 0) \
446 { \
447 audioMixBufBlendSample(&pi64Dst[0], audioMixBufBlendSampleRet(audioMixBufClipFrom##a_Name(pSrc[0]), \
448 audioMixBufClipFrom##a_Name(pSrc[1]))); \
449 pi64Dst += 2; /** @todo when we do multi channel mixbuf support, this can change to 1 */ \
450 pSrc += 2; \
451 } \
452 } \
453 \
454 /* 1ch -> 2ch */ \
455 static DECLCALLBACK(void) RT_CONCAT3(audioMixBufDecode1ChTo2Ch,a_Name,Blend)(int64_t *pi64Dst, void const *pvSrc, \
456 uint32_t cFrames, PAUDIOMIXBUFWRITESTATE pState) \
457 { \
458 RT_NOREF_PV(pState); \
459 a_Type const *pSrc = (a_Type const *)pvSrc; \
460 while (cFrames-- > 0) \
461 { \
462 int64_t const i64Src = audioMixBufClipFrom##a_Name(pSrc[0]); \
463 audioMixBufBlendSample(&pi64Dst[0], i64Src); \
464 audioMixBufBlendSample(&pi64Dst[1], i64Src); \
465 pi64Dst += 2; \
466 pSrc += 1; \
467 } \
468 } \
469 \
470 /* 1ch -> 1ch */ \
471 static DECLCALLBACK(void) RT_CONCAT3(audioMixBufDecode1ChTo1Ch,a_Name,Blend)(int64_t *pi64Dst, void const *pvSrc, \
472 uint32_t cFrames, PAUDIOMIXBUFWRITESTATE pState) \
473 { \
474 RT_NOREF_PV(pState); \
475 a_Type const *pSrc = (a_Type const *)pvSrc; \
476 while (cFrames-- > 0) \
477 { \
478 audioMixBufBlendSample(&pi64Dst[0], audioMixBufClipFrom##a_Name(pSrc[0])); \
479 pi64Dst += 2; /** @todo when we do multi channel mixbuf support, this can change to 1 */ \
480 pSrc += 1; \
481 } \
482 }
483
484
485/* audioMixBufConvXXXS8: 8 bit, signed. */
486AUDMIXBUF_CONVERT(S8 /* Name */, int8_t, INT8_MIN /* Min */, INT8_MAX /* Max */, true /* fSigned */, 8 /* cShift */)
487/* audioMixBufConvXXXU8: 8 bit, unsigned. */
488AUDMIXBUF_CONVERT(U8 /* Name */, uint8_t, 0 /* Min */, UINT8_MAX /* Max */, false /* fSigned */, 8 /* cShift */)
489/* audioMixBufConvXXXS16: 16 bit, signed. */
490AUDMIXBUF_CONVERT(S16 /* Name */, int16_t, INT16_MIN /* Min */, INT16_MAX /* Max */, true /* fSigned */, 16 /* cShift */)
491/* audioMixBufConvXXXU16: 16 bit, unsigned. */
492AUDMIXBUF_CONVERT(U16 /* Name */, uint16_t, 0 /* Min */, UINT16_MAX /* Max */, false /* fSigned */, 16 /* cShift */)
493/* audioMixBufConvXXXS32: 32 bit, signed. */
494AUDMIXBUF_CONVERT(S32 /* Name */, int32_t, INT32_MIN /* Min */, INT32_MAX /* Max */, true /* fSigned */, 32 /* cShift */)
495/* audioMixBufConvXXXU32: 32 bit, unsigned. */
496AUDMIXBUF_CONVERT(U32 /* Name */, uint32_t, 0 /* Min */, UINT32_MAX /* Max */, false /* fSigned */, 32 /* cShift */)
497
498#undef AUDMIXBUF_CONVERT
499
500/*
501 * Manually coded signed 64-bit conversion.
502 */
503
504/* Encoders for peek: */
505
506static DECLCALLBACK(void)
507audioMixBufEncode2ChTo2ChRaw(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, PAUDIOMIXBUFPEEKSTATE pState)
508{
509 RT_NOREF_PV(pState);
510 memcpy(pvDst, pi64Src, sizeof(int64_t) * 2 * cFrames);
511}
512
513static DECLCALLBACK(void)
514audioMixBufEncode2ChTo1ChRaw(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, PAUDIOMIXBUFPEEKSTATE pState)
515{
516 RT_NOREF_PV(pState);
517 int64_t *pi64Dst = (int64_t *)pvDst;
518 while (cFrames-- > 0)
519 {
520 *pi64Dst = (pi64Src[0] + pi64Src[1]) / 2;
521 pi64Dst += 1;
522 pi64Src += 2;
523 }
524}
525
526static DECLCALLBACK(void)
527audioMixBufEncode1ChTo2ChRaw(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, PAUDIOMIXBUFPEEKSTATE pState)
528{
529 RT_NOREF_PV(pState);
530 int64_t *pi64Dst = (int64_t *)pvDst;
531 while (cFrames-- > 0)
532 {
533 pi64Dst[0] = pi64Dst[1] = *pi64Src;
534 pi64Dst += 2;
535 pi64Src += 2; /** @todo when we do multi channel mixbuf support, this can change to 1 */
536 }
537}
538
539static DECLCALLBACK(void)
540audioMixBufEncode1ChTo1ChRaw(void *pvDst, int64_t const *pi64Src, uint32_t cFrames, PAUDIOMIXBUFPEEKSTATE pState)
541{
542 RT_NOREF_PV(pState);
543 /** @todo memcpy(pvDst, pi64Src, sizeof(int64_t) * 1 * cFrames); when we do
544 * multi channel mixbuf support. */
545 int64_t *pi64Dst = (int64_t *)pvDst;
546 while (cFrames-- > 0)
547 {
548 *pi64Dst = *pi64Src;
549 pi64Dst += 1;
550 pi64Src += 2;
551 }
552}
553
554
555/* Decoders for write: */
556
557static DECLCALLBACK(void)
558audioMixBufDecode2ChTo2ChRaw(int64_t *pi64Dst, void const *pvSrc, uint32_t cFrames, PAUDIOMIXBUFWRITESTATE pState)
559{
560 RT_NOREF_PV(pState);
561 memcpy(pi64Dst, pvSrc, sizeof(int64_t) * 2 * cFrames);
562}
563
564static DECLCALLBACK(void)
565audioMixBufDecode2ChTo1ChRaw(int64_t *pi64Dst, void const *pvSrc, uint32_t cFrames, PAUDIOMIXBUFWRITESTATE pState)
566{
567 RT_NOREF_PV(pState);
568 int64_t const *pi64Src = (int64_t const *)pvSrc;
569 while (cFrames-- > 0)
570 {
571 *pi64Dst = (pi64Src[0] + pi64Src[1]) / 2;
572 pi64Dst += 2; /** @todo when we do multi channel mixbuf support, this can change to 1 */
573 pi64Src += 2;
574 }
575}
576
577static DECLCALLBACK(void)
578audioMixBufDecode1ChTo2ChRaw(int64_t *pi64Dst, void const *pvSrc, uint32_t cFrames, PAUDIOMIXBUFWRITESTATE pState)
579{
580 RT_NOREF_PV(pState);
581 int64_t const *pi64Src = (int64_t const *)pvSrc;
582 while (cFrames-- > 0)
583 {
584 pi64Dst[0] = pi64Dst[1] = *pi64Src;
585 pi64Dst += 2;
586 pi64Src += 1;
587 }
588}
589
590static DECLCALLBACK(void)
591audioMixBufDecode1ChTo1ChRaw(int64_t *pi64Dst, void const *pvSrc, uint32_t cFrames, PAUDIOMIXBUFWRITESTATE pState)
592{
593 RT_NOREF_PV(pState);
594 /** @todo memcpy(pi64Dst, pvSrc, sizeof(int64_t) * 1 * cFrames); when we do
595 * multi channel mixbuf support. */
596 int64_t const *pi64Src = (int64_t const *)pvSrc;
597 while (cFrames-- > 0)
598 {
599 *pi64Dst = *pi64Src;
600 pi64Dst += 2; /** @todo when we do multi channel mixbuf support, this can change to 1 */
601 pi64Src += 1;
602 }
603}
604
605
606/* Decoders for blending: */
607
608static DECLCALLBACK(void)
609audioMixBufDecode2ChTo2ChRawBlend(int64_t *pi64Dst, void const *pvSrc, uint32_t cFrames, PAUDIOMIXBUFWRITESTATE pState)
610{
611 RT_NOREF_PV(pState);
612 audioMixBufBlendBuffer(pi64Dst, (int64_t const *)pvSrc, cFrames, 2);
613}
614
615static DECLCALLBACK(void)
616audioMixBufDecode2ChTo1ChRawBlend(int64_t *pi64Dst, void const *pvSrc, uint32_t cFrames, PAUDIOMIXBUFWRITESTATE pState)
617{
618 RT_NOREF_PV(pState);
619 int64_t const *pi64Src = (int64_t const *)pvSrc;
620 while (cFrames-- > 0)
621 {
622 audioMixBufBlendSample(pi64Dst, audioMixBufBlendSampleRet(pi64Src[0], pi64Src[1]));
623 pi64Dst += 2; /** @todo when we do multi channel mixbuf support, this can change to 1 */
624 pi64Src += 2;
625 }
626}
627
628static DECLCALLBACK(void)
629audioMixBufDecode1ChTo2ChRawBlend(int64_t *pi64Dst, void const *pvSrc, uint32_t cFrames, PAUDIOMIXBUFWRITESTATE pState)
630{
631 RT_NOREF_PV(pState);
632 int64_t const *pi64Src = (int64_t const *)pvSrc;
633 while (cFrames-- > 0)
634 {
635 int64_t const i64Src = *pi64Src;
636 audioMixBufBlendSample(&pi64Dst[0], i64Src);
637 audioMixBufBlendSample(&pi64Dst[1], i64Src);
638 pi64Dst += 2;
639 pi64Src += 1;
640 }
641}
642
643static DECLCALLBACK(void)
644audioMixBufDecode1ChTo1ChRawBlend(int64_t *pi64Dst, void const *pvSrc, uint32_t cFrames, PAUDIOMIXBUFWRITESTATE pState)
645{
646 RT_NOREF_PV(pState);
647 /** @todo memcpy(pi64Dst, pvSrc, sizeof(int64_t) * 1 * cFrames); when we do
648 * multi channel mixbuf support. */
649 int64_t const *pi64Src = (int64_t const *)pvSrc;
650 while (cFrames-- > 0)
651 {
652 audioMixBufBlendSample(&pi64Dst[0], *pi64Src);
653 pi64Dst += 2; /** @todo when we do multi channel mixbuf support, this can change to 1 */
654 pi64Src += 1;
655 }
656}
657
658#undef AUDMIXBUF_MACRO_LOG
659
660/**
661 * Initializes a mixing buffer.
662 *
663 * @returns VBox status code.
664 * @param pMixBuf Mixing buffer to initialize.
665 * @param pszName Name of mixing buffer for easier identification. Optional.
666 * @param pProps PCM audio properties to use for the mixing buffer.
667 * @param cFrames Maximum number of audio frames the mixing buffer can hold.
668 */
669int AudioMixBufInit(PAUDIOMIXBUF pMixBuf, const char *pszName, PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
670{
671 AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
672 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
673 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
674 Assert(PDMAudioPropsAreValid(pProps));
675
676 pMixBuf->uMagic = AUDIOMIXBUF_MAGIC;
677
678 pMixBuf->pFrames = NULL;
679 pMixBuf->cFrames = 0;
680
681 pMixBuf->offRead = 0;
682 pMixBuf->offWrite = 0;
683 pMixBuf->cMixed = 0;
684 pMixBuf->cUsed = 0;
685
686 /* Set initial volume to max. */
687 pMixBuf->Volume.fMuted = false;
688 pMixBuf->Volume.uLeft = AUDIOMIXBUF_VOL_0DB;
689 pMixBuf->Volume.uRight = AUDIOMIXBUF_VOL_0DB;
690
691 pMixBuf->Props = *pProps;
692
693 pMixBuf->pszName = RTStrDup(pszName);
694 if (pMixBuf->pszName)
695 {
696 pMixBuf->pFrames = (PPDMAUDIOFRAME)RTMemAllocZ(cFrames * sizeof(PDMAUDIOFRAME));
697 if (pMixBuf->pFrames)
698 {
699 pMixBuf->cFrames = cFrames;
700#ifdef AUDMIXBUF_LOG_ENABLED
701 char szTmp[PDMAUDIOPROPSTOSTRING_MAX];
702 AUDMIXBUF_LOG(("%s: %s - cFrames=%#x (%d)\n",
703 pMixBuf->pszName, PDMAudioPropsToString(pProps, szTmp, sizeof(szTmp)), cFrames, cFrames));
704#endif
705 return VINF_SUCCESS;
706 }
707 RTStrFree(pMixBuf->pszName);
708 pMixBuf->pszName = NULL;
709 }
710 pMixBuf->uMagic = AUDIOMIXBUF_MAGIC_DEAD;
711 return VERR_NO_MEMORY;
712}
713
714/**
715 * Destroys (uninitializes) a mixing buffer.
716 *
717 * @param pMixBuf Mixing buffer to destroy.
718 */
719void AudioMixBufDestroy(PAUDIOMIXBUF pMixBuf)
720{
721 if (!pMixBuf)
722 return;
723
724 /* Ignore calls for an uninitialized (zeroed) or already destroyed instance. Happens a lot. */
725 if ( pMixBuf->uMagic == 0
726 || pMixBuf->uMagic == ~AUDIOMIXBUF_MAGIC)
727 {
728 Assert(!pMixBuf->pszName);
729 Assert(!pMixBuf->pFrames);
730 Assert(!pMixBuf->cFrames);
731 return;
732 }
733
734 Assert(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
735 pMixBuf->uMagic = ~AUDIOMIXBUF_MAGIC;
736
737 if (pMixBuf->pszName)
738 {
739 AUDMIXBUF_LOG(("%s\n", pMixBuf->pszName));
740
741 RTStrFree(pMixBuf->pszName);
742 pMixBuf->pszName = NULL;
743 }
744
745 if (pMixBuf->pFrames)
746 {
747 Assert(pMixBuf->cFrames);
748
749 RTMemFree(pMixBuf->pFrames);
750 pMixBuf->pFrames = NULL;
751 }
752
753 pMixBuf->cFrames = 0;
754}
755
756
757/**
758 * Drops all the frames in the given mixing buffer
759 *
760 * This will reset the read and write offsets to zero.
761 *
762 * @param pMixBuf The mixing buffer.
763 */
764void AudioMixBufDrop(PAUDIOMIXBUF pMixBuf)
765{
766 AssertPtrReturnVoid(pMixBuf);
767 AssertReturnVoid(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
768
769 AUDMIXBUF_LOG(("%s\n", pMixBuf->pszName));
770
771 pMixBuf->offRead = 0;
772 pMixBuf->offWrite = 0;
773 pMixBuf->cMixed = 0;
774 pMixBuf->cUsed = 0;
775}
776
777
778/**
779 * Gets the maximum number of audio frames this buffer can hold.
780 *
781 * @returns Number of frames.
782 * @param pMixBuf The mixing buffer.
783 */
784uint32_t AudioMixBufSize(PCAUDIOMIXBUF pMixBuf)
785{
786 AssertPtrReturn(pMixBuf, 0);
787 Assert(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
788 return pMixBuf->cFrames;
789}
790
791
792/**
793 * Gets the maximum number of bytes this buffer can hold.
794 *
795 * @returns Number of bytes.
796 * @param pMixBuf The mixing buffer.
797 */
798uint32_t AudioMixBufSizeBytes(PCAUDIOMIXBUF pMixBuf)
799{
800 AssertPtrReturn(pMixBuf, 0);
801 AssertReturn(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC, 0);
802 return AUDIOMIXBUF_F2B(pMixBuf, pMixBuf->cFrames);
803}
804
805
806/**
807 * Worker for AudioMixBufUsed and AudioMixBufUsedBytes.
808 */
809DECLINLINE(uint32_t) audioMixBufUsedInternal(PCAUDIOMIXBUF pMixBuf)
810{
811 uint32_t const cFrames = pMixBuf->cFrames;
812 uint32_t cUsed = pMixBuf->cUsed;
813 AssertStmt(cUsed <= cFrames, cUsed = cFrames);
814 return cUsed;
815}
816
817
818/**
819 * Get the number of used (readable) frames in the buffer.
820 *
821 * @returns Number of frames.
822 * @param pMixBuf The mixing buffer.
823 */
824uint32_t AudioMixBufUsed(PCAUDIOMIXBUF pMixBuf)
825{
826 AssertPtrReturn(pMixBuf, 0);
827 Assert(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
828 return audioMixBufUsedInternal(pMixBuf);
829}
830
831
832/**
833 * Get the number of (readable) bytes in the buffer.
834 *
835 * @returns Number of bytes.
836 * @param pMixBuf The mixing buffer.
837 */
838uint32_t AudioMixBufUsedBytes(PCAUDIOMIXBUF pMixBuf)
839{
840 AssertPtrReturn(pMixBuf, 0);
841 Assert(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
842 return AUDIOMIXBUF_F2B(pMixBuf, audioMixBufUsedInternal(pMixBuf));
843}
844
845
846/**
847 * Worker for AudioMixBufFree and AudioMixBufFreeBytes.
848 */
849DECLINLINE(uint32_t) audioMixBufFreeInternal(PCAUDIOMIXBUF pMixBuf)
850{
851 uint32_t const cFrames = pMixBuf->cFrames;
852 uint32_t cUsed = pMixBuf->cUsed;
853 AssertStmt(cUsed <= cFrames, cUsed = cFrames);
854 uint32_t const cFramesFree = cFrames - cUsed;
855
856 AUDMIXBUF_LOG(("%s: %RU32 of %RU32\n", pMixBuf->pszName, cFramesFree, cFrames));
857 return cFramesFree;
858}
859
860/**
861 * Gets the free buffer space in frames.
862 *
863 * @return Number of frames.
864 * @param pMixBuf The mixing buffer.
865 */
866uint32_t AudioMixBufFree(PCAUDIOMIXBUF pMixBuf)
867{
868 AssertPtrReturn(pMixBuf, 0);
869 Assert(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
870 return audioMixBufFreeInternal(pMixBuf);
871}
872
873/**
874 * Gets the free buffer space in bytes.
875 *
876 * @return Number of bytes.
877 * @param pMixBuf The mixing buffer.
878 */
879uint32_t AudioMixBufFreeBytes(PCAUDIOMIXBUF pMixBuf)
880{
881 AssertPtrReturn(pMixBuf, 0);
882 Assert(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
883 return AUDIOMIXBUF_F2B(pMixBuf, audioMixBufFreeInternal(pMixBuf));
884}
885
886
887/**
888 * Checks if the buffer is empty.
889 *
890 * @retval true if empty buffer.
891 * @retval false if not empty and there are frames to be processed.
892 * @param pMixBuf The mixing buffer.
893 */
894bool AudioMixBufIsEmpty(PCAUDIOMIXBUF pMixBuf)
895{
896 AssertPtrReturn(pMixBuf, true);
897 Assert(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
898 return pMixBuf->cUsed == 0;
899}
900
901
902/**
903 * Get the current read position.
904 *
905 * This is for the testcase.
906 *
907 * @returns Frame number.
908 * @param pMixBuf The mixing buffer.
909 */
910uint32_t AudioMixBufReadPos(PCAUDIOMIXBUF pMixBuf)
911{
912 AssertPtrReturn(pMixBuf, 0);
913 Assert(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
914 return pMixBuf->offRead;
915}
916
917
918/**
919 * Gets the current write position.
920 *
921 * This is for the testcase.
922 *
923 * @returns Frame number.
924 * @param pMixBuf The mixing buffer.
925 */
926uint32_t AudioMixBufWritePos(PCAUDIOMIXBUF pMixBuf)
927{
928 AssertPtrReturn(pMixBuf, 0);
929 Assert(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
930 return pMixBuf->offWrite;
931}
932
933#ifdef DEBUG
934
935/**
936 * Prints a single mixing buffer.
937 * Internal helper function for debugging. Do not use directly.
938 *
939 * @returns VBox status code.
940 * @param pMixBuf Mixing buffer to print.
941 * @param pszFunc Function name to log this for.
942 * @param uIdtLvl Indention level to use.
943 */
944static void audioMixBufDbgPrintSingle(PAUDIOMIXBUF pMixBuf, const char *pszFunc, uint16_t uIdtLvl)
945{
946 Log(("%s: %*s %s: offRead=%RU32, offWrite=%RU32, cMixed=%RU32 -> %RU32/%RU32\n",
947 pszFunc, uIdtLvl * 4, "",
948 pMixBuf->pszName, pMixBuf->offRead, pMixBuf->offWrite, pMixBuf->cMixed, pMixBuf->cUsed, pMixBuf->cFrames));
949}
950
951# ifdef UNUSED
952/**
953 * Validates a single mixing buffer.
954 *
955 * @return @true if the buffer state is valid or @false if not.
956 * @param pMixBuf Mixing buffer to validate.
957 */
958static bool audioMixBufDbgValidate(PAUDIOMIXBUF pMixBuf)
959{
960 //const uint32_t offReadEnd = (pMixBuf->offRead + pMixBuf->cUsed) % pMixBuf->cFrames;
961 //const uint32_t offWriteEnd = (pMixBuf->offWrite + (pMixBuf->cFrames - pMixBuf->cUsed)) % pMixBuf->cFrames;
962
963 bool fValid = true;
964
965 AssertStmt(pMixBuf->offRead <= pMixBuf->cFrames, fValid = false);
966 AssertStmt(pMixBuf->offWrite <= pMixBuf->cFrames, fValid = false);
967 AssertStmt(pMixBuf->cUsed <= pMixBuf->cFrames, fValid = false);
968
969 if (pMixBuf->offWrite > pMixBuf->offRead)
970 {
971 if (pMixBuf->offWrite - pMixBuf->offRead != pMixBuf->cUsed)
972 fValid = false;
973 }
974 else if (pMixBuf->offWrite < pMixBuf->offRead)
975 {
976 if (pMixBuf->offWrite + pMixBuf->cFrames - pMixBuf->offRead != pMixBuf->cUsed)
977 fValid = false;
978 }
979
980 if (!fValid)
981 {
982 audioMixBufDbgPrintInternal(pMixBuf, __FUNCTION__);
983 AssertFailed();
984 }
985
986 return fValid;
987}
988# endif
989
990/**
991 * Prints statistics and status of the full chain of a mixing buffer to the logger,
992 * starting from the top root mixing buffer.
993 * For debug versions only.
994 *
995 * @returns VBox status code.
996 * @param pMixBuf Mixing buffer to print.
997 */
998void AudioMixBufDbgPrintChain(PAUDIOMIXBUF pMixBuf)
999{
1000 audioMixBufDbgPrintSingle(pMixBuf, __FUNCTION__, 0 /* uIdtLvl */);
1001}
1002
1003DECL_FORCE_INLINE(void) audioMixBufDbgPrintInternal(PAUDIOMIXBUF pMixBuf, const char *pszFunc)
1004{
1005 audioMixBufDbgPrintSingle(pMixBuf, pszFunc, 0 /* iIdtLevel */);
1006}
1007
1008/**
1009 * Prints statistics and status of a mixing buffer to the logger.
1010 * For debug versions only.
1011 *
1012 * @returns VBox status code.
1013 * @param pMixBuf Mixing buffer to print.
1014 */
1015void AudioMixBufDbgPrint(PAUDIOMIXBUF pMixBuf)
1016{
1017 audioMixBufDbgPrintInternal(pMixBuf, __FUNCTION__);
1018}
1019
1020#endif /* DEBUG */
1021
1022
1023/*
1024 * Resampling core.
1025 */
1026/** @todo Separate down- and up-sampling, borrow filter code from RDP. */
1027#define COPY_LAST_FRAME_1CH(a_pi64Dst, a_pi64Src, a_cChannels) do { \
1028 (a_pi64Dst)[0] = (a_pi64Src)[0]; \
1029 } while (0)
1030#define COPY_LAST_FRAME_2CH(a_pi64Dst, a_pi64Src, a_cChannels) do { \
1031 (a_pi64Dst)[0] = (a_pi64Src)[0]; \
1032 (a_pi64Dst)[1] = (a_pi64Src)[1]; \
1033 } while (0)
1034
1035#define INTERPOLATE_ONE(a_pi64Dst, a_pi64Src, a_pi64Last, a_i64FactorCur, a_i64FactorLast, a_iCh) \
1036 (a_pi64Dst)[a_iCh] = ((a_pi64Last)[a_iCh] * a_i64FactorLast + (a_pi64Src)[a_iCh] * a_i64FactorCur) >> 32
1037#define INTERPOLATE_1CH(a_pi64Dst, a_pi64Src, a_pi64Last, a_i64FactorCur, a_i64FactorLast, a_cChannels) do { \
1038 INTERPOLATE_ONE(a_pi64Dst, a_pi64Src, a_pi64Last, a_i64FactorCur, a_i64FactorLast, 0); \
1039 } while (0)
1040#define INTERPOLATE_2CH(a_pi64Dst, a_pi64Src, a_pi64Last, a_i64FactorCur, a_i64FactorLast, a_cChannels) do { \
1041 INTERPOLATE_ONE(a_pi64Dst, a_pi64Src, a_pi64Last, a_i64FactorCur, a_i64FactorLast, 0); \
1042 INTERPOLATE_ONE(a_pi64Dst, a_pi64Src, a_pi64Last, a_i64FactorCur, a_i64FactorLast, 1); \
1043 } while (0)
1044
1045#define AUDIOMIXBUF_RESAMPLE(a_cChannels, a_Suffix) \
1046 /** @returns Number of destination frames written. */ \
1047 static DECLCALLBACK(uint32_t) \
1048 audioMixBufResample##a_cChannels##Ch##a_Suffix(int64_t *pi64Dst, uint32_t cDstFrames, \
1049 int64_t const *pi64Src, uint32_t cSrcFrames, uint32_t *pcSrcFramesRead, \
1050 PAUDIOSTREAMRATE pRate) \
1051 { \
1052 Log5(("Src: %RU32 L %RU32; Dst: %RU32 L%RU32; uDstInc=%#RX64\n", \
1053 pRate->offSrc, cSrcFrames, RT_HI_U32(pRate->offDst), cDstFrames, pRate->uDstInc)); \
1054 int64_t * const pi64DstStart = pi64Dst; \
1055 int64_t const * const pi64SrcStart = pi64Src; \
1056 \
1057 int64_t ai64LastFrame[a_cChannels]; \
1058 COPY_LAST_FRAME_##a_cChannels##CH(ai64LastFrame, pRate->SrcLast.ai64Samples, a_cChannels); \
1059 \
1060 while (cDstFrames > 0 && cSrcFrames > 0) \
1061 { \
1062 int32_t const cSrcNeeded = RT_HI_U32(pRate->offDst) - pRate->offSrc + 1; \
1063 if (cSrcNeeded > 0) \
1064 { \
1065 if ((uint32_t)cSrcNeeded + 1 < cSrcFrames) \
1066 { \
1067 pRate->offSrc += (uint32_t)cSrcNeeded; \
1068 cSrcFrames -= (uint32_t)cSrcNeeded; \
1069 pi64Src += (uint32_t)cSrcNeeded * a_cChannels; \
1070 COPY_LAST_FRAME_##a_cChannels##CH(ai64LastFrame, &pi64Src[-a_cChannels], a_cChannels); \
1071 } \
1072 else \
1073 { \
1074 pi64Src += cSrcFrames * a_cChannels; \
1075 pRate->offSrc += cSrcFrames; \
1076 COPY_LAST_FRAME_##a_cChannels##CH(pRate->SrcLast.ai64Samples, &pi64Src[-a_cChannels], a_cChannels); \
1077 *pcSrcFramesRead = (pi64Src - pi64SrcStart) / a_cChannels; \
1078 return (pi64Dst - pi64DstStart) / a_cChannels; \
1079 } \
1080 } \
1081 \
1082 /* Interpolate. */ \
1083 int64_t const offFactorCur = pRate->offDst & UINT32_MAX; \
1084 int64_t const offFactorLast = (int64_t)_4G - offFactorCur; \
1085 INTERPOLATE_##a_cChannels##CH(pi64Dst, pi64Src, ai64LastFrame, offFactorCur, offFactorLast, a_cChannels); \
1086 \
1087 /* Advance. */ \
1088 pRate->offDst += pRate->uDstInc; \
1089 pi64Dst += a_cChannels; \
1090 cDstFrames -= 1; \
1091 } \
1092 \
1093 COPY_LAST_FRAME_##a_cChannels##CH(pRate->SrcLast.ai64Samples, ai64LastFrame, a_cChannels); \
1094 *pcSrcFramesRead = (pi64Src - pi64SrcStart) / a_cChannels; \
1095 return (pi64Dst - pi64DstStart) / a_cChannels; \
1096 }
1097
1098AUDIOMIXBUF_RESAMPLE(1,Generic)
1099AUDIOMIXBUF_RESAMPLE(2,Generic)
1100
1101
1102/**
1103 * Resets the resampling state unconditionally.
1104 *
1105 * @param pRate The state to reset.
1106 */
1107static void audioMixBufRateResetAlways(PAUDIOSTREAMRATE pRate)
1108{
1109 pRate->offDst = 0;
1110 pRate->offSrc = 0;
1111 for (uintptr_t i = 0; i < RT_ELEMENTS(pRate->SrcLast.ai64Samples); i++)
1112 pRate->SrcLast.ai64Samples[0] = 0;
1113}
1114
1115
1116/**
1117 * Resets the resampling state.
1118 *
1119 * @param pRate The state to reset.
1120 */
1121DECLINLINE(void) audioMixBufRateReset(PAUDIOSTREAMRATE pRate)
1122{
1123 if (pRate->offDst == 0)
1124 { /* likely */ }
1125 else
1126 {
1127 Assert(!pRate->fNoConversionNeeded);
1128 audioMixBufRateResetAlways(pRate);
1129 }
1130}
1131
1132
1133/**
1134 * Initializes the frame rate converter state.
1135 *
1136 * @returns VBox status code.
1137 * @param pRate The state to initialize.
1138 * @param uSrcHz The source frame rate.
1139 * @param uDstHz The destination frame rate.
1140 * @param cChannels The number of channels in a frame.
1141 */
1142DECLINLINE(int) audioMixBufRateInit(PAUDIOSTREAMRATE pRate, uint32_t uSrcHz, uint32_t uDstHz, uint8_t cChannels)
1143{
1144 /*
1145 * Do we need to set up frequency conversion?
1146 *
1147 * Some examples to get an idea of what uDstInc holds:
1148 * 44100 to 44100 -> (44100<<32) / 44100 = 0x01'00000000 (4294967296)
1149 * 22050 to 44100 -> (22050<<32) / 44100 = 0x00'80000000 (2147483648)
1150 * 44100 to 22050 -> (44100<<32) / 22050 = 0x02'00000000 (8589934592)
1151 * 44100 to 48000 -> (44100<<32) / 48000 = 0x00'EB333333 (3946001203.2)
1152 * 48000 to 44100 -> (48000<<32) / 44100 = 0x01'16A3B35F (4674794335.7823129251700680272109)
1153 */
1154 audioMixBufRateResetAlways(pRate);
1155 if (uSrcHz == uDstHz)
1156 {
1157 pRate->fNoConversionNeeded = true;
1158 pRate->uDstInc = RT_BIT_64(32);
1159 pRate->pfnResample = NULL;
1160 }
1161 else
1162 {
1163 pRate->fNoConversionNeeded = false;
1164 pRate->uDstInc = ((uint64_t)uSrcHz << 32) / uDstHz;
1165 AssertReturn(uSrcHz != 0, VERR_INVALID_PARAMETER);
1166 switch (cChannels)
1167 {
1168 case 1: pRate->pfnResample = audioMixBufResample1ChGeneric; break;
1169 case 2: pRate->pfnResample = audioMixBufResample2ChGeneric; break;
1170 default:
1171 AssertMsgFailedReturn(("resampling %u changes is not implemented yet\n", cChannels), VERR_OUT_OF_RANGE);
1172 }
1173 }
1174 return VINF_SUCCESS;
1175}
1176
1177
1178
1179/**
1180 * Initializes the peek state, setting up encoder and (if necessary) resampling.
1181 *
1182 * @returns VBox status code.
1183 */
1184int AudioMixBufInitPeekState(PCAUDIOMIXBUF pMixBuf, PAUDIOMIXBUFPEEKSTATE pState, PCPDMAUDIOPCMPROPS pProps)
1185{
1186 AssertPtr(pMixBuf);
1187 AssertPtr(pState);
1188 AssertPtr(pProps);
1189
1190 /*
1191 * Pick the encoding function first.
1192 */
1193 uint8_t const cSrcCh = PDMAudioPropsChannels(&pMixBuf->Props);
1194 uint8_t const cDstCh = PDMAudioPropsChannels(pProps);
1195 pState->cSrcChannels = cSrcCh;
1196 pState->cDstChannels = cDstCh;
1197 pState->cbDstFrame = PDMAudioPropsFrameSize(pProps);
1198 if (PDMAudioPropsIsSigned(pProps))
1199 {
1200 switch (cDstCh)
1201 {
1202 case 1:
1203 AssertReturn(cSrcCh == 1 || cSrcCh == 2, VERR_OUT_OF_RANGE);
1204 switch (PDMAudioPropsSampleSize(pProps))
1205 {
1206 case 1:
1207 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo1ChS8 : audioMixBufEncode2ChTo1ChS8;
1208 break;
1209 case 2:
1210 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo1ChS16 : audioMixBufEncode2ChTo1ChS16;
1211 break;
1212 case 4:
1213 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo1ChS32 : audioMixBufEncode2ChTo1ChS32;
1214 break;
1215 case 8:
1216 AssertReturn(pProps->fRaw, VERR_DISK_INVALID_FORMAT);
1217 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo1ChRaw : audioMixBufEncode2ChTo1ChRaw;
1218 break;
1219 default:
1220 AssertMsgFailedReturn(("%u bytes\n", PDMAudioPropsSampleSize(pProps)), VERR_OUT_OF_RANGE);
1221 }
1222 break;
1223 case 2:
1224 AssertReturn(cSrcCh == 1 || cSrcCh == 2, VERR_OUT_OF_RANGE);
1225 switch (PDMAudioPropsSampleSize(pProps))
1226 {
1227 case 1:
1228 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo2ChS8 : audioMixBufEncode2ChTo2ChS8;
1229 break;
1230 case 2:
1231 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo2ChS16 : audioMixBufEncode2ChTo2ChS16;
1232 break;
1233 case 4:
1234 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo2ChS32 : audioMixBufEncode2ChTo2ChS32;
1235 break;
1236 case 8:
1237 AssertReturn(pProps->fRaw, VERR_DISK_INVALID_FORMAT);
1238 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo2ChRaw : audioMixBufEncode2ChTo2ChRaw;
1239 break;
1240 default:
1241 AssertMsgFailedReturn(("%u bytes\n", PDMAudioPropsSampleSize(pProps)), VERR_OUT_OF_RANGE);
1242 }
1243 break;
1244 default:
1245 /* Note: We may have dedicated encoders for a few selected multichannel
1246 configurations, and generic ones that encodes channel by channel (i.e.
1247 add the mixer channel count, destination frame size, and an array of
1248 destination channel frame offsets to the state). */
1249 AssertMsgFailedReturn(("from %u to %u channels is not implemented yet\n", cSrcCh, cDstCh), VERR_OUT_OF_RANGE);
1250 }
1251 }
1252 else
1253 {
1254 switch (cDstCh)
1255 {
1256 case 1:
1257 AssertReturn(cSrcCh == 1 || cSrcCh == 2, VERR_OUT_OF_RANGE);
1258 switch (PDMAudioPropsSampleSize(pProps))
1259 {
1260 case 1:
1261 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo1ChU8 : audioMixBufEncode2ChTo1ChU8;
1262 break;
1263 case 2:
1264 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo1ChU16 : audioMixBufEncode2ChTo1ChU16;
1265 break;
1266 case 4:
1267 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo1ChU32 : audioMixBufEncode2ChTo1ChU32;
1268 break;
1269 default:
1270 AssertMsgFailedReturn(("%u bytes\n", PDMAudioPropsSampleSize(pProps)), VERR_OUT_OF_RANGE);
1271 }
1272 break;
1273 case 2:
1274 AssertReturn(cSrcCh == 1 || cSrcCh == 2, VERR_OUT_OF_RANGE);
1275 switch (PDMAudioPropsSampleSize(pProps))
1276 {
1277 case 1:
1278 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo2ChU8 : audioMixBufEncode2ChTo2ChU8;
1279 break;
1280 case 2:
1281 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo2ChU16 : audioMixBufEncode2ChTo2ChU16;
1282 break;
1283 case 4:
1284 pState->pfnEncode = cSrcCh == 1 ? audioMixBufEncode1ChTo2ChU32 : audioMixBufEncode2ChTo2ChU32;
1285 break;
1286 default:
1287 AssertMsgFailedReturn(("%u bytes\n", PDMAudioPropsSampleSize(pProps)), VERR_OUT_OF_RANGE);
1288 }
1289 break;
1290 default:
1291 /* Note: We may have dedicated encoders for a few selected multichannel
1292 configurations, and generic ones that encodes channel by channel (i.e.
1293 add an array of destination channel frame offsets to the state). */
1294 AssertMsgFailedReturn(("from %u to %u channels is not implemented yet\n", cSrcCh, cDstCh), VERR_OUT_OF_RANGE);
1295 }
1296 }
1297
1298 int rc = audioMixBufRateInit(&pState->Rate, PDMAudioPropsHz(&pMixBuf->Props), PDMAudioPropsHz(pProps), cSrcCh);
1299 AUDMIXBUF_LOG(("%s: %RU32 Hz to %RU32 Hz => uDstInc=0x%'RX64\n", pMixBuf->pszName, PDMAudioPropsHz(&pMixBuf->Props),
1300 PDMAudioPropsHz(pProps), pState->Rate.uDstInc));
1301 return rc;
1302}
1303
1304
1305/**
1306 * Initializes the write/blend state, setting up decoders and (if necessary)
1307 * resampling.
1308 *
1309 * @returns VBox status code.
1310 */
1311int AudioMixBufInitWriteState(PCAUDIOMIXBUF pMixBuf, PAUDIOMIXBUFWRITESTATE pState, PCPDMAUDIOPCMPROPS pProps)
1312{
1313 AssertPtr(pMixBuf);
1314 AssertPtr(pState);
1315 AssertPtr(pProps);
1316
1317 /*
1318 * Pick the encoding function first.
1319 */
1320 uint8_t const cSrcCh = PDMAudioPropsChannels(pProps);
1321 uint8_t const cDstCh = PDMAudioPropsChannels(&pMixBuf->Props);
1322 pState->cSrcChannels = cSrcCh;
1323 pState->cDstChannels = cDstCh;
1324 pState->cbSrcFrame = PDMAudioPropsFrameSize(pProps);
1325 if (PDMAudioPropsIsSigned(pProps))
1326 {
1327 switch (cDstCh)
1328 {
1329 case 1:
1330 AssertReturn(cSrcCh == 1 || cSrcCh == 2, VERR_OUT_OF_RANGE);
1331 switch (PDMAudioPropsSampleSize(pProps))
1332 {
1333 case 1:
1334 pState->pfnDecode = cSrcCh == 1 ? audioMixBufDecode1ChTo1ChS8 : audioMixBufDecode2ChTo1ChS8;
1335 pState->pfnDecodeBlend = cSrcCh == 1 ? audioMixBufDecode1ChTo1ChS8Blend : audioMixBufDecode2ChTo1ChS8Blend;
1336 break;
1337 case 2:
1338 pState->pfnDecode = cSrcCh == 1 ? audioMixBufDecode1ChTo1ChS16 : audioMixBufDecode2ChTo1ChS16;
1339 pState->pfnDecodeBlend = cSrcCh == 1 ? audioMixBufDecode1ChTo1ChS16Blend : audioMixBufDecode2ChTo1ChS16Blend;
1340 break;
1341 case 4:
1342 pState->pfnDecode = cSrcCh == 1 ? audioMixBufDecode1ChTo1ChS32 : audioMixBufDecode2ChTo1ChS32;
1343 pState->pfnDecodeBlend = cSrcCh == 1 ? audioMixBufDecode1ChTo1ChS32Blend : audioMixBufDecode2ChTo1ChS32Blend;
1344 break;
1345 case 8:
1346 AssertReturn(pProps->fRaw, VERR_DISK_INVALID_FORMAT);
1347 pState->pfnDecode = cSrcCh == 1 ? audioMixBufDecode1ChTo1ChRaw : audioMixBufDecode2ChTo1ChRaw;
1348 pState->pfnDecodeBlend = cSrcCh == 1 ? audioMixBufDecode1ChTo1ChRawBlend : audioMixBufDecode2ChTo1ChRawBlend;
1349 break;
1350 default:
1351 AssertMsgFailedReturn(("%u bytes\n", PDMAudioPropsSampleSize(pProps)), VERR_OUT_OF_RANGE);
1352 }
1353 break;
1354 case 2:
1355 AssertReturn(cSrcCh == 1 || cSrcCh == 2, VERR_OUT_OF_RANGE);
1356 switch (PDMAudioPropsSampleSize(pProps))
1357 {
1358 case 1:
1359 pState->pfnDecode = cSrcCh == 1 ? audioMixBufDecode1ChTo2ChS8 : audioMixBufDecode2ChTo2ChS8;
1360 pState->pfnDecodeBlend = cSrcCh == 1 ? audioMixBufDecode1ChTo2ChS8Blend : audioMixBufDecode2ChTo2ChS8Blend;
1361 break;
1362 case 2:
1363 pState->pfnDecode = cSrcCh == 1 ? audioMixBufDecode1ChTo2ChS16 : audioMixBufDecode2ChTo2ChS16;
1364 pState->pfnDecodeBlend = cSrcCh == 1 ? audioMixBufDecode1ChTo2ChS16Blend : audioMixBufDecode2ChTo2ChS16Blend;
1365 break;
1366 case 4:
1367 pState->pfnDecode = cSrcCh == 1 ? audioMixBufDecode1ChTo2ChS32 : audioMixBufDecode2ChTo2ChS32;
1368 pState->pfnDecodeBlend = cSrcCh == 1 ? audioMixBufDecode1ChTo2ChS32Blend : audioMixBufDecode2ChTo2ChS32Blend;
1369 break;
1370 case 8:
1371 AssertReturn(pProps->fRaw, VERR_DISK_INVALID_FORMAT);
1372 pState->pfnDecode = cSrcCh == 1 ? audioMixBufDecode1ChTo2ChRaw : audioMixBufDecode2ChTo2ChRaw;
1373 pState->pfnDecodeBlend = cSrcCh == 1 ? audioMixBufDecode1ChTo2ChRawBlend : audioMixBufDecode2ChTo2ChRawBlend;
1374 break;
1375 default:
1376 AssertMsgFailedReturn(("%u bytes\n", PDMAudioPropsSampleSize(pProps)), VERR_OUT_OF_RANGE);
1377 }
1378 break;
1379 default:
1380 /* Note: We may have dedicated encoders for a few selected multichannel
1381 configurations, and generic ones that encodes channel by channel (i.e.
1382 add the mixer channel count, destination frame size, and an array of
1383 destination channel frame offsets to the state). */
1384 AssertMsgFailedReturn(("from %u to %u channels is not implemented yet\n", cSrcCh, cDstCh), VERR_OUT_OF_RANGE);
1385 }
1386 }
1387 else
1388 {
1389 switch (cDstCh)
1390 {
1391 case 1:
1392 AssertReturn(cSrcCh == 1 || cSrcCh == 2, VERR_OUT_OF_RANGE);
1393 switch (PDMAudioPropsSampleSize(pProps))
1394 {
1395 case 1:
1396 pState->pfnDecode = cSrcCh == 1 ? audioMixBufDecode1ChTo1ChU8 : audioMixBufDecode2ChTo1ChU8;
1397 pState->pfnDecodeBlend = cSrcCh == 1 ? audioMixBufDecode1ChTo1ChU8Blend : audioMixBufDecode2ChTo1ChU8Blend;
1398 break;
1399 case 2:
1400 pState->pfnDecode = cSrcCh == 1 ? audioMixBufDecode1ChTo1ChU16 : audioMixBufDecode2ChTo1ChU16;
1401 pState->pfnDecodeBlend = cSrcCh == 1 ? audioMixBufDecode1ChTo1ChU16Blend : audioMixBufDecode2ChTo1ChU16Blend;
1402 break;
1403 case 4:
1404 pState->pfnDecode = cSrcCh == 1 ? audioMixBufDecode1ChTo1ChU32 : audioMixBufDecode2ChTo1ChU32;
1405 pState->pfnDecodeBlend = cSrcCh == 1 ? audioMixBufDecode1ChTo1ChU32Blend : audioMixBufDecode2ChTo1ChU32Blend;
1406 break;
1407 default:
1408 AssertMsgFailedReturn(("%u bytes\n", PDMAudioPropsSampleSize(pProps)), VERR_OUT_OF_RANGE);
1409 }
1410 break;
1411 case 2:
1412 AssertReturn(cSrcCh == 1 || cSrcCh == 2, VERR_OUT_OF_RANGE);
1413 switch (PDMAudioPropsSampleSize(pProps))
1414 {
1415 case 1:
1416 pState->pfnDecode = cSrcCh == 1 ? audioMixBufDecode1ChTo2ChU8 : audioMixBufDecode2ChTo2ChU8;
1417 pState->pfnDecodeBlend = cSrcCh == 1 ? audioMixBufDecode1ChTo2ChU8Blend : audioMixBufDecode2ChTo2ChU8Blend;
1418 break;
1419 case 2:
1420 pState->pfnDecode = cSrcCh == 1 ? audioMixBufDecode1ChTo2ChU16 : audioMixBufDecode2ChTo2ChU16;
1421 pState->pfnDecodeBlend = cSrcCh == 1 ? audioMixBufDecode1ChTo2ChU16Blend : audioMixBufDecode2ChTo2ChU16Blend;
1422 break;
1423 case 4:
1424 pState->pfnDecode = cSrcCh == 1 ? audioMixBufDecode1ChTo2ChU32 : audioMixBufDecode2ChTo2ChU32;
1425 pState->pfnDecodeBlend = cSrcCh == 1 ? audioMixBufDecode1ChTo2ChU32Blend : audioMixBufDecode2ChTo2ChU32Blend;
1426 break;
1427 default:
1428 AssertMsgFailedReturn(("%u bytes\n", PDMAudioPropsSampleSize(pProps)), VERR_OUT_OF_RANGE);
1429 }
1430 break;
1431 default:
1432 /* Note: We may have dedicated encoders for a few selected multichannel
1433 configurations, and generic ones that encodes channel by channel (i.e.
1434 add an array of destination channel frame offsets to the state). */
1435 AssertMsgFailedReturn(("from %u to %u channels is not implemented yet\n", cSrcCh, cDstCh), VERR_OUT_OF_RANGE);
1436 }
1437 }
1438
1439 int rc = audioMixBufRateInit(&pState->Rate, PDMAudioPropsHz(pProps), PDMAudioPropsHz(&pMixBuf->Props), cDstCh);
1440 AUDMIXBUF_LOG(("%s: %RU32 Hz to %RU32 Hz => uDstInc=0x%'RX64\n", pMixBuf->pszName, PDMAudioPropsHz(pProps),
1441 PDMAudioPropsHz(&pMixBuf->Props), pState->Rate.uDstInc));
1442 return rc;
1443}
1444
1445
1446/**
1447 * Worker for AudioMixBufPeek that handles the rate conversion case.
1448 */
1449DECL_NO_INLINE(static, void)
1450AudioMixBufPeekResampling(PCAUDIOMIXBUF pMixBuf, uint32_t offSrcFrame, uint32_t cMaxSrcFrames, uint32_t *pcSrcFramesPeeked,
1451 PAUDIOMIXBUFPEEKSTATE pState, void *pvDst, uint32_t cbDst, uint32_t *pcbDstPeeked)
1452{
1453 *pcSrcFramesPeeked = 0;
1454 *pcbDstPeeked = 0;
1455 while (cMaxSrcFrames > 0 && cbDst >= pState->cbDstFrame)
1456 {
1457 /* Rate conversion into temporary buffer. */
1458 int64_t ai64DstRate[1024];
1459 uint32_t cSrcFrames = RT_MIN(pMixBuf->cFrames - offSrcFrame, cMaxSrcFrames);
1460 uint32_t cMaxDstFrames = RT_MIN(RT_ELEMENTS(ai64DstRate) / pState->cDstChannels, cbDst / pState->cbDstFrame);
1461 uint32_t const cDstFrames = pState->Rate.pfnResample(ai64DstRate, cMaxDstFrames,
1462 &pMixBuf->pFrames[offSrcFrame].i64LSample, cSrcFrames, &cSrcFrames,
1463 &pState->Rate);
1464 *pcSrcFramesPeeked += cSrcFrames;
1465 cMaxSrcFrames -= cSrcFrames;
1466 offSrcFrame = (offSrcFrame + cSrcFrames) % pMixBuf->cFrames;
1467
1468 /* Encode the converted frames. */
1469 uint32_t const cbDstEncoded = cDstFrames * pState->cbDstFrame;
1470 pState->pfnEncode(pvDst, ai64DstRate, cDstFrames, pState);
1471 *pcbDstPeeked += cbDstEncoded;
1472 cbDst -= cbDstEncoded;
1473 pvDst = (uint8_t *)pvDst + cbDstEncoded;
1474 }
1475}
1476
1477
1478/**
1479 * Copies data out of the mixing buffer, converting it if needed, but leaves the
1480 * read offset untouched.
1481 *
1482 * @param pMixBuf The mixing buffer.
1483 * @param offSrcFrame The offset to start reading at relative to
1484 * current read position (offRead). The caller has
1485 * made sure there is at least this number of
1486 * frames available in the buffer before calling.
1487 * @param cMaxSrcFrames Maximum number of frames to read.
1488 * @param pcSrcFramesPeeked Where to return the actual number of frames read
1489 * from the mixing buffer.
1490 * @param pState Output configuration & conversion state.
1491 * @param pvDst The destination buffer.
1492 * @param cbDst The size of the destination buffer in bytes.
1493 * @param pcbDstPeeked Where to put the actual number of bytes
1494 * returned.
1495 */
1496void AudioMixBufPeek(PCAUDIOMIXBUF pMixBuf, uint32_t offSrcFrame, uint32_t cMaxSrcFrames, uint32_t *pcSrcFramesPeeked,
1497 PAUDIOMIXBUFPEEKSTATE pState, void *pvDst, uint32_t cbDst, uint32_t *pcbDstPeeked)
1498{
1499 /*
1500 * Check inputs.
1501 */
1502 AssertPtr(pMixBuf);
1503 Assert(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
1504 AssertPtr(pState);
1505 AssertPtr(pState->pfnEncode);
1506 Assert(pState->cSrcChannels == PDMAudioPropsChannels(&pMixBuf->Props));
1507 Assert(cMaxSrcFrames > 0);
1508 Assert(cMaxSrcFrames <= pMixBuf->cFrames);
1509 Assert(offSrcFrame <= pMixBuf->cFrames);
1510 Assert(offSrcFrame + cMaxSrcFrames <= pMixBuf->cUsed);
1511 AssertPtr(pcSrcFramesPeeked);
1512 AssertPtr(pvDst);
1513 Assert(cbDst >= pState->cbDstFrame);
1514 AssertPtr(pcbDstPeeked);
1515
1516 /*
1517 * Make start frame absolute.
1518 */
1519 offSrcFrame = (pMixBuf->offRead + offSrcFrame) % pMixBuf->cFrames;
1520
1521 /*
1522 * Hopefully no sample rate conversion is necessary...
1523 */
1524 if (pState->Rate.fNoConversionNeeded)
1525 {
1526 /* Figure out how much we should convert. */
1527 cMaxSrcFrames = RT_MIN(cMaxSrcFrames, cbDst / pState->cbDstFrame);
1528 *pcSrcFramesPeeked = cMaxSrcFrames;
1529 *pcbDstPeeked = cMaxSrcFrames * pState->cbDstFrame;
1530
1531 /* First chunk. */
1532 uint32_t const cSrcFrames1 = RT_MIN(pMixBuf->cFrames - offSrcFrame, cMaxSrcFrames);
1533 pState->pfnEncode(pvDst, &pMixBuf->pFrames[offSrcFrame].i64LSample, cSrcFrames1, pState);
1534
1535 /* Another chunk from the start of the mixing buffer? */
1536 if (cMaxSrcFrames > cSrcFrames1)
1537 pState->pfnEncode((uint8_t *)pvDst + cSrcFrames1 * pState->cbDstFrame,
1538 &pMixBuf->pFrames[0].i64LSample, cMaxSrcFrames - cSrcFrames1, pState);
1539 }
1540 else
1541 AudioMixBufPeekResampling(pMixBuf, offSrcFrame, cMaxSrcFrames, pcSrcFramesPeeked, pState, pvDst, cbDst, pcbDstPeeked);
1542}
1543
1544
1545/**
1546 * Worker for AudioMixBufWrite that handles the rate conversion case.
1547 */
1548DECL_NO_INLINE(static, void)
1549audioMixBufWriteResampling(PAUDIOMIXBUF pMixBuf, PAUDIOMIXBUFWRITESTATE pState, const void *pvSrcBuf, uint32_t cbSrcBuf,
1550 uint32_t offDstFrame, uint32_t cMaxDstFrames, uint32_t *pcDstFramesWritten)
1551{
1552 *pcDstFramesWritten = 0;
1553 while (cMaxDstFrames > 0 && cbSrcBuf >= pState->cbSrcFrame)
1554 {
1555 /* Decode into temporary buffer. */
1556 int64_t ai64SrcDecoded[1024];
1557 uint32_t cFramesDecoded = RT_MIN(RT_ELEMENTS(ai64SrcDecoded) / pState->cSrcChannels, cbSrcBuf / pState->cbSrcFrame);
1558 pState->pfnDecode(ai64SrcDecoded, pvSrcBuf, cFramesDecoded, pState);
1559 cbSrcBuf -= cFramesDecoded * pState->cbSrcFrame;
1560 pvSrcBuf = (uint8_t const *)pvSrcBuf + cFramesDecoded * pState->cbSrcFrame;
1561
1562 /* Rate convert that into the mixer. */
1563 uint32_t iFrameDecoded = 0;
1564 while (iFrameDecoded < cFramesDecoded)
1565 {
1566 uint32_t cDstMaxFrames = RT_MIN(pMixBuf->cFrames - offDstFrame, cMaxDstFrames);
1567 uint32_t cSrcFrames = cFramesDecoded - iFrameDecoded;
1568 uint32_t const cDstFrames = pState->Rate.pfnResample(&pMixBuf->pFrames[offDstFrame].i64LSample, cDstMaxFrames,
1569 &ai64SrcDecoded[iFrameDecoded * pState->cSrcChannels],
1570 cSrcFrames, &cSrcFrames, &pState->Rate);
1571
1572 iFrameDecoded += cSrcFrames;
1573 *pcDstFramesWritten += cDstFrames;
1574 offDstFrame = (offDstFrame + cDstFrames) % pMixBuf->cFrames;
1575 }
1576 }
1577
1578 /** @todo How to squeeze odd frames out of 22050 => 44100 conversion? */
1579}
1580
1581
1582/**
1583 * Writes @a cbSrcBuf bytes to the mixer buffer starting at @a offDstFrame,
1584 * converting it as needed, leaving the write offset untouched.
1585 *
1586 * @param pMixBuf The mixing buffer.
1587 * @param pState Source configuration & conversion state.
1588 * @param pvSrcBuf The source frames.
1589 * @param cbSrcBuf Number of bytes of source frames. This will be
1590 * convered in full.
1591 * @param offDstFrame Mixing buffer offset relative to the write
1592 * position.
1593 * @param cMaxDstFrames Max number of frames to write.
1594 * @param pcDstFramesWritten Where to return the number of frames actually
1595 * written.
1596 *
1597 * @note Does not advance the write position, please call AudioMixBufCommit()
1598 * to do that.
1599 */
1600void AudioMixBufWrite(PAUDIOMIXBUF pMixBuf, PAUDIOMIXBUFWRITESTATE pState, const void *pvSrcBuf, uint32_t cbSrcBuf,
1601 uint32_t offDstFrame, uint32_t cMaxDstFrames, uint32_t *pcDstFramesWritten)
1602{
1603 /*
1604 * Check inputs.
1605 */
1606 AssertPtr(pMixBuf);
1607 Assert(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
1608 AssertPtr(pState);
1609 AssertPtr(pState->pfnDecode);
1610 Assert(pState->cDstChannels == PDMAudioPropsChannels(&pMixBuf->Props));
1611 Assert(cMaxDstFrames > 0);
1612 Assert(cMaxDstFrames <= pMixBuf->cFrames - pMixBuf->cUsed);
1613 Assert(offDstFrame <= pMixBuf->cFrames);
1614 AssertPtr(pvSrcBuf);
1615 Assert(!(cbSrcBuf % pState->cbSrcFrame));
1616 AssertPtr(pcDstFramesWritten);
1617
1618 /*
1619 * Make start frame absolute.
1620 */
1621 offDstFrame = (pMixBuf->offWrite + offDstFrame) % pMixBuf->cFrames;
1622
1623 /*
1624 * Hopefully no sample rate conversion is necessary...
1625 */
1626 if (pState->Rate.fNoConversionNeeded)
1627 {
1628 /* Figure out how much we should convert. */
1629 Assert(cMaxDstFrames >= cbSrcBuf / pState->cbSrcFrame);
1630 cMaxDstFrames = RT_MIN(cMaxDstFrames, cbSrcBuf / pState->cbSrcFrame);
1631 *pcDstFramesWritten = cMaxDstFrames;
1632
1633 /* First chunk. */
1634 uint32_t const cDstFrames1 = RT_MIN(pMixBuf->cFrames - offDstFrame, cMaxDstFrames);
1635 pState->pfnDecode(&pMixBuf->pFrames[offDstFrame].i64LSample, pvSrcBuf, cDstFrames1, pState);
1636
1637 /* Another chunk from the start of the mixing buffer? */
1638 if (cMaxDstFrames > cDstFrames1)
1639 pState->pfnDecode(&pMixBuf->pFrames[0].i64LSample, (uint8_t *)pvSrcBuf + cDstFrames1 * pState->cbSrcFrame,
1640 cMaxDstFrames - cDstFrames1, pState);
1641 }
1642 else
1643 audioMixBufWriteResampling(pMixBuf, pState, pvSrcBuf, cbSrcBuf, offDstFrame, cMaxDstFrames, pcDstFramesWritten);
1644}
1645
1646
1647/**
1648 * Worker for AudioMixBufBlend that handles the rate conversion case.
1649 */
1650DECL_NO_INLINE(static, void)
1651audioMixBufBlendResampling(PAUDIOMIXBUF pMixBuf, PAUDIOMIXBUFWRITESTATE pState, const void *pvSrcBuf, uint32_t cbSrcBuf,
1652 uint32_t offDstFrame, uint32_t cMaxDstFrames, uint32_t *pcDstFramesBlended)
1653{
1654 *pcDstFramesBlended = 0;
1655 while (cMaxDstFrames > 0 && cbSrcBuf >= pState->cbSrcFrame)
1656 {
1657 /* Decode into temporary buffer. */
1658 int64_t ai64SrcDecoded[1024];
1659 uint32_t cFramesDecoded = RT_MIN(RT_ELEMENTS(ai64SrcDecoded) / pState->cSrcChannels, cbSrcBuf / pState->cbSrcFrame);
1660 pState->pfnDecode(ai64SrcDecoded, pvSrcBuf, cFramesDecoded, pState);
1661 cbSrcBuf -= cFramesDecoded * pState->cbSrcFrame;
1662 pvSrcBuf = (uint8_t const *)pvSrcBuf + cFramesDecoded * pState->cbSrcFrame;
1663
1664 /* Rate convert that into another temporary buffer and then blend that into the mixer. */
1665 uint32_t iFrameDecoded = 0;
1666 while (iFrameDecoded < cFramesDecoded)
1667 {
1668 int64_t ai64SrcRate[1024];
1669 uint32_t cDstMaxFrames = RT_MIN(RT_ELEMENTS(ai64SrcRate), cMaxDstFrames);
1670 uint32_t cSrcFrames = cFramesDecoded - iFrameDecoded;
1671 uint32_t const cDstFrames = pState->Rate.pfnResample(&ai64SrcRate[0], cDstMaxFrames,
1672 &ai64SrcDecoded[iFrameDecoded * pState->cSrcChannels],
1673 cSrcFrames, &cSrcFrames, &pState->Rate);
1674
1675 /* First chunk.*/
1676 uint32_t const cDstFrames1 = RT_MIN(pMixBuf->cFrames - offDstFrame, cDstFrames);
1677 audioMixBufBlendBuffer(&pMixBuf->pFrames[offDstFrame].i64LSample, ai64SrcRate, cDstFrames1, pState->cSrcChannels);
1678
1679 /* Another chunk from the start of the mixing buffer? */
1680 if (cDstFrames > cDstFrames1)
1681 audioMixBufBlendBuffer(&pMixBuf->pFrames[0].i64LSample, &ai64SrcRate[cDstFrames1 * pState->cSrcChannels],
1682 cDstFrames - cDstFrames1, pState->cSrcChannels);
1683
1684 /* Advance */
1685 iFrameDecoded += cSrcFrames;
1686 *pcDstFramesBlended += cDstFrames;
1687 offDstFrame = (offDstFrame + cDstFrames) % pMixBuf->cFrames;
1688 }
1689 }
1690
1691 /** @todo How to squeeze odd frames out of 22050 => 44100 conversion? */
1692}
1693
1694
1695/**
1696 * @todo not sure if 'blend' is the appropriate term here, but you know what
1697 * we mean.
1698 */
1699void AudioMixBufBlend(PAUDIOMIXBUF pMixBuf, PAUDIOMIXBUFWRITESTATE pState, const void *pvSrcBuf, uint32_t cbSrcBuf,
1700 uint32_t offDstFrame, uint32_t cMaxDstFrames, uint32_t *pcDstFramesBlended)
1701{
1702 /*
1703 * Check inputs.
1704 */
1705 AssertPtr(pMixBuf);
1706 Assert(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
1707 AssertPtr(pState);
1708 AssertPtr(pState->pfnDecode);
1709 AssertPtr(pState->pfnDecodeBlend);
1710 Assert(pState->cDstChannels == PDMAudioPropsChannels(&pMixBuf->Props));
1711 Assert(cMaxDstFrames > 0);
1712 Assert(cMaxDstFrames <= pMixBuf->cFrames - pMixBuf->cUsed);
1713 Assert(offDstFrame <= pMixBuf->cFrames);
1714 AssertPtr(pvSrcBuf);
1715 Assert(!(cbSrcBuf % pState->cbSrcFrame));
1716 AssertPtr(pcDstFramesBlended);
1717
1718 /*
1719 * Make start frame absolute.
1720 */
1721 offDstFrame = (pMixBuf->offWrite + offDstFrame) % pMixBuf->cFrames;
1722
1723 /*
1724 * Hopefully no sample rate conversion is necessary...
1725 */
1726 if (pState->Rate.fNoConversionNeeded)
1727 {
1728 /* Figure out how much we should convert. */
1729 Assert(cMaxDstFrames >= cbSrcBuf / pState->cbSrcFrame);
1730 cMaxDstFrames = RT_MIN(cMaxDstFrames, cbSrcBuf / pState->cbSrcFrame);
1731 *pcDstFramesBlended = cMaxDstFrames;
1732
1733 /* First chunk. */
1734 uint32_t const cDstFrames1 = RT_MIN(pMixBuf->cFrames - offDstFrame, cMaxDstFrames);
1735 pState->pfnDecodeBlend(&pMixBuf->pFrames[offDstFrame].i64LSample, pvSrcBuf, cDstFrames1, pState);
1736
1737 /* Another chunk from the start of the mixing buffer? */
1738 if (cMaxDstFrames > cDstFrames1)
1739 pState->pfnDecodeBlend(&pMixBuf->pFrames[0].i64LSample, (uint8_t *)pvSrcBuf + cDstFrames1 * pState->cbSrcFrame,
1740 cMaxDstFrames - cDstFrames1, pState);
1741 }
1742 else
1743 audioMixBufBlendResampling(pMixBuf, pState, pvSrcBuf, cbSrcBuf, offDstFrame, cMaxDstFrames, pcDstFramesBlended);
1744}
1745
1746
1747/**
1748 * Writes @a cFrames of silence at @a offFrame relative to current write pos.
1749 *
1750 * This will also adjust the resampling state.
1751 *
1752 * @param pMixBuf The mixing buffer.
1753 * @param pState The write state.
1754 * @param offFrame Where to start writing silence relative to the current
1755 * write position.
1756 * @param cFrames Number of frames of silence.
1757 * @sa AudioMixBufSilence
1758 *
1759 * @note Does not advance the write position, please call AudioMixBufCommit()
1760 * to do that.
1761 */
1762void AudioMixBufSilence(PAUDIOMIXBUF pMixBuf, PAUDIOMIXBUFWRITESTATE pState, uint32_t offFrame, uint32_t cFrames)
1763{
1764 /*
1765 * Check inputs.
1766 */
1767 AssertPtr(pMixBuf);
1768 Assert(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
1769 AssertPtr(pState);
1770 AssertPtr(pState->pfnDecode);
1771 Assert(pState->cDstChannels == PDMAudioPropsChannels(&pMixBuf->Props));
1772 Assert(cFrames > 0);
1773 Assert(cFrames <= pMixBuf->cFrames);
1774 Assert(offFrame <= pMixBuf->cFrames);
1775 Assert(offFrame + cFrames <= pMixBuf->cUsed);
1776
1777 /*
1778 * Make start frame absolute.
1779 */
1780 offFrame = (pMixBuf->offWrite + offFrame) % pMixBuf->cFrames;
1781
1782 /*
1783 * First chunk.
1784 */
1785 uint32_t cChunk = RT_MIN(pMixBuf->cFrames - offFrame, cFrames);
1786 RT_BZERO(&pMixBuf->pFrames[offFrame], cChunk * sizeof(pMixBuf->pFrames[0]));
1787 cFrames -= cChunk;
1788
1789 /*
1790 * Second chunk, if needed.
1791 */
1792 if (cFrames > 0)
1793 {
1794 AssertStmt(cFrames <= pMixBuf->cFrames, cFrames = pMixBuf->cFrames);
1795 RT_BZERO(&pMixBuf->pFrames[0], cFrames * sizeof(pMixBuf->pFrames[0]));
1796 }
1797
1798 /*
1799 * Reset the resampling state.
1800 */
1801 audioMixBufRateReset(&pState->Rate);
1802}
1803
1804
1805/**
1806 * Records a blending gap (silence) of @a cFrames.
1807 *
1808 * This is used to adjust or reset the resampling state so we start from a
1809 * silence state the next time we need to blend or write using @a pState.
1810 *
1811 * @param pMixBuf The mixing buffer.
1812 * @param pState The write state.
1813 * @param cFrames Number of frames of silence.
1814 * @sa AudioMixBufSilence
1815 */
1816void AudioMixBufBlendGap(PAUDIOMIXBUF pMixBuf, PAUDIOMIXBUFWRITESTATE pState, uint32_t cFrames)
1817{
1818 /*
1819 * For now we'll just reset the resampling state regardless of how many
1820 * frames of silence there is.
1821 */
1822 audioMixBufRateReset(&pState->Rate);
1823 RT_NOREF(pMixBuf, cFrames);
1824}
1825
1826
1827/**
1828 * Advances the read position of the buffer.
1829 *
1830 * For use after done peeking with AudioMixBufPeek().
1831 *
1832 * @param pMixBuf The mixing buffer.
1833 * @param cFrames Number of frames to advance.
1834 * @sa AudioMixBufCommit
1835 */
1836void AudioMixBufAdvance(PAUDIOMIXBUF pMixBuf, uint32_t cFrames)
1837{
1838 AssertPtrReturnVoid(pMixBuf);
1839 AssertReturnVoid(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
1840
1841 AssertStmt(cFrames <= pMixBuf->cUsed, cFrames = pMixBuf->cUsed);
1842 pMixBuf->cUsed -= cFrames;
1843 pMixBuf->offRead = (pMixBuf->offRead + cFrames) % pMixBuf->cFrames;
1844 LogFlowFunc(("%s: Advanced %u frames: offRead=%u cUsed=%u\n", pMixBuf->pszName, cFrames, pMixBuf->offRead, pMixBuf->cUsed));
1845}
1846
1847
1848/**
1849 * Worker for audioMixAdjustVolume that adjust one contiguous chunk.
1850 */
1851static void audioMixAdjustVolumeWorker(PAUDIOMIXBUF pMixBuf, uint32_t off, uint32_t cFrames)
1852{
1853 PPDMAUDIOFRAME const paFrames = pMixBuf->pFrames;
1854 uint32_t const uLeft = pMixBuf->Volume.uLeft;
1855 uint32_t const uRight = pMixBuf->Volume.uRight;
1856 while (cFrames-- > 0)
1857 {
1858 paFrames[off].i64LSample = ASMMult2xS32RetS64(paFrames[off].i64LSample, uLeft) >> AUDIOMIXBUF_VOL_SHIFT;
1859 paFrames[off].i64RSample = ASMMult2xS32RetS64(paFrames[off].i64RSample, uRight) >> AUDIOMIXBUF_VOL_SHIFT;
1860 off++;
1861 }
1862}
1863
1864
1865/**
1866 * Does volume adjustments for the given stretch of the buffer.
1867 *
1868 * @param pMixBuf The mixing buffer.
1869 * @param offFirst Where to start (validated).
1870 * @param cFrames How many frames (validated).
1871 */
1872static void audioMixAdjustVolume(PAUDIOMIXBUF pMixBuf, uint32_t offFirst, uint32_t cFrames)
1873{
1874 /* Caller has already validated these, so we don't need to repeat that in non-strict builds. */
1875 Assert(offFirst < pMixBuf->cFrames);
1876 Assert(cFrames <= pMixBuf->cFrames);
1877
1878 /*
1879 * Muted?
1880 */
1881 if (pMixBuf->Volume.fMuted)
1882 {
1883 uint32_t const cFramesChunk1 = RT_MIN(pMixBuf->cFrames - offFirst, cFrames);
1884 RT_BZERO(&pMixBuf->pFrames[offFirst], sizeof(pMixBuf->pFrames[0]) * cFramesChunk1);
1885 if (cFramesChunk1 < cFrames)
1886 RT_BZERO(&pMixBuf->pFrames[0], sizeof(pMixBuf->pFrames[0]) * (cFrames - cFramesChunk1));
1887 }
1888 /*
1889 * Less than max volume?
1890 */
1891 else if ( pMixBuf->Volume.uLeft != AUDIOMIXBUF_VOL_0DB
1892 || pMixBuf->Volume.uRight != AUDIOMIXBUF_VOL_0DB)
1893 {
1894 /* first chunk */
1895 uint32_t const cFramesChunk1 = RT_MIN(pMixBuf->cFrames - offFirst, cFrames);
1896 audioMixAdjustVolumeWorker(pMixBuf, offFirst, cFramesChunk1);
1897 if (cFramesChunk1 < cFrames)
1898 audioMixAdjustVolumeWorker(pMixBuf, 0, cFrames - cFramesChunk1);
1899 }
1900}
1901
1902
1903/**
1904 * Adjust for volume settings and advances the write position of the buffer.
1905 *
1906 * For use after done peeking with AudioMixBufWrite(), AudioMixBufSilence(),
1907 * AudioMixBufBlend() and AudioMixBufBlendGap().
1908 *
1909 * @param pMixBuf The mixing buffer.
1910 * @param cFrames Number of frames to advance.
1911 * @sa AudioMixBufAdvance, AudioMixBufSetVolume
1912 */
1913void AudioMixBufCommit(PAUDIOMIXBUF pMixBuf, uint32_t cFrames)
1914{
1915 AssertPtrReturnVoid(pMixBuf);
1916 AssertReturnVoid(pMixBuf->uMagic == AUDIOMIXBUF_MAGIC);
1917
1918 AssertStmt(cFrames <= pMixBuf->cFrames - pMixBuf->cUsed, cFrames = pMixBuf->cFrames - pMixBuf->cUsed);
1919
1920 audioMixAdjustVolume(pMixBuf, pMixBuf->offWrite, cFrames);
1921
1922 pMixBuf->cUsed += cFrames;
1923 pMixBuf->offWrite = (pMixBuf->offWrite + cFrames) % pMixBuf->cFrames;
1924 LogFlowFunc(("%s: Advanced %u frames: offWrite=%u cUsed=%u\n", pMixBuf->pszName, cFrames, pMixBuf->offWrite, pMixBuf->cUsed));
1925}
1926
1927
1928/**
1929 * Sets the volume.
1930 *
1931 * The volume adjustments are applied by AudioMixBufCommit().
1932 *
1933 * @param pMixBuf Mixing buffer to set volume for.
1934 * @param pVol Pointer to volume structure to set.
1935 */
1936void AudioMixBufSetVolume(PAUDIOMIXBUF pMixBuf, PCPDMAUDIOVOLUME pVol)
1937{
1938 AssertPtrReturnVoid(pMixBuf);
1939 AssertPtrReturnVoid(pVol);
1940
1941 LogFlowFunc(("%s: lVol=%RU8, rVol=%RU8, fMuted=%RTbool\n", pMixBuf->pszName, pVol->uLeft, pVol->uRight, pVol->fMuted));
1942
1943 /*
1944 * Convert PDM audio volume to the internal format.
1945 */
1946 if (!pVol->fMuted)
1947 {
1948 pMixBuf->Volume.fMuted = false;
1949 AssertCompileSize(pVol->uLeft, sizeof(uint8_t));
1950 pMixBuf->Volume.uLeft = s_aVolumeConv[pVol->uLeft ] * (AUDIOMIXBUF_VOL_0DB >> 16);
1951 pMixBuf->Volume.uRight = s_aVolumeConv[pVol->uRight] * (AUDIOMIXBUF_VOL_0DB >> 16);
1952 }
1953 else
1954 {
1955 pMixBuf->Volume.fMuted = true;
1956 pMixBuf->Volume.uLeft = 0;
1957 pMixBuf->Volume.uRight = 0;
1958 }
1959}
1960
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