VirtualBox

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

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

AudioMixer: Moving a few functions, merging the volume setter worker into the public function. bugref:9890

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