VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/testcase/tstAudioMixBuffer.cpp@ 88299

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

Audio: Made sure PDMAUDIOPCMPROPS is initialized using a helper function or the initializer macro, and that vital changes are made using setting helper functions. There are now two derived fields (frame size and shift count) that must be maintained, so this was the sanest way of doing it. Added a raw flag to PDMAUDIOPCMPROPS for VRDE/VRDP, since it wants the raw mixer content and we need a way of expressing this (PDMAUDIOSTREAMLAYOUT isn't the right place). The mixer buffers now uses PDMAUDIOPCMPROPS rather than the weird 32-bit format contraption for picking conversion functions. Simplify the drvAudioStreamPlay code by eliminating the PDMAUDIOSTREAMLAYOUT_RAW special case. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.6 KB
Line 
1/* $Id: tstAudioMixBuffer.cpp 88269 2021-03-24 11:45:54Z vboxsync $ */
2/** @file
3 * Audio testcase - Mixing buffer.
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
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <iprt/errcore.h>
23#include <iprt/initterm.h>
24#include <iprt/mem.h>
25#include <iprt/rand.h>
26#include <iprt/stream.h>
27#include <iprt/string.h>
28#include <iprt/test.h>
29
30#include <VBox/vmm/pdmaudioinline.h>
31
32#include "../AudioMixBuffer.h"
33#include "../AudioHlp.h"
34
35
36static void tstBasics(RTTEST hTest)
37{
38 RTTestSub(hTest, "Basics");
39
40 const PDMAUDIOPCMPROPS Cfg441StereoS16 = PDMAUDIOPCMPROPS_INITIALIZER(
41 /* a_cb: */ 2,
42 /* a_fSigned: */ true,
43 /* a_cChannels: */ 2,
44 /* a_uHz: */ 44100,
45 /* a_fSwapEndian: */ false
46 );
47 const PDMAUDIOPCMPROPS Cfg441StereoU16 = PDMAUDIOPCMPROPS_INITIALIZER(
48 /* a_cb: */ 2,
49 /* a_fSigned: */ false,
50 /* a_cChannels: */ 2,
51 /* a_uHz: */ 44100,
52 /* a_fSwapEndian: */ false
53 );
54 const PDMAUDIOPCMPROPS Cfg441StereoU32 = PDMAUDIOPCMPROPS_INITIALIZER(
55 /* a_cb: */ 4,
56 /* a_fSigned: */ false,
57 /* a_cChannels: */ 2,
58 /* a_uHz: */ 44100,
59 /* a_fSwapEndian: */ false
60 );
61
62 RTTESTI_CHECK(PDMAudioPropsGetBitrate(&Cfg441StereoS16) == 44100*4*8);
63 RTTESTI_CHECK(PDMAudioPropsGetBitrate(&Cfg441StereoU16) == 44100*4*8);
64 RTTESTI_CHECK(PDMAudioPropsGetBitrate(&Cfg441StereoU32) == 44100*8*8);
65
66 RTTESTI_CHECK(AudioHlpPcmPropsAreValid(&Cfg441StereoS16));
67 RTTESTI_CHECK(AudioHlpPcmPropsAreValid(&Cfg441StereoU16) == false); /* go figure */
68 RTTESTI_CHECK(AudioHlpPcmPropsAreValid(&Cfg441StereoU32) == false); /* go figure */
69
70
71 RTTESTI_CHECK_MSG(PDMAUDIOPCMPROPS_F2B(&Cfg441StereoS16, 1) == 4,
72 ("got %x, expected 4\n", PDMAUDIOPCMPROPS_F2B(&Cfg441StereoS16, 1)));
73 RTTESTI_CHECK_MSG(PDMAUDIOPCMPROPS_F2B(&Cfg441StereoU16, 1) == 4,
74 ("got %x, expected 4\n", PDMAUDIOPCMPROPS_F2B(&Cfg441StereoU16, 1)));
75 RTTESTI_CHECK_MSG(PDMAUDIOPCMPROPS_F2B(&Cfg441StereoU32, 1) == 8,
76 ("got %x, expected 4\n", PDMAUDIOPCMPROPS_F2B(&Cfg441StereoU32, 1)));
77
78 RTTESTI_CHECK_MSG(PDMAudioPropsBytesPerFrame(&Cfg441StereoS16) == 4,
79 ("got %x, expected 4\n", PDMAudioPropsBytesPerFrame(&Cfg441StereoS16)));
80 RTTESTI_CHECK_MSG(PDMAudioPropsBytesPerFrame(&Cfg441StereoU16) == 4,
81 ("got %x, expected 4\n", PDMAudioPropsBytesPerFrame(&Cfg441StereoU16)));
82 RTTESTI_CHECK_MSG(PDMAudioPropsBytesPerFrame(&Cfg441StereoU32) == 8,
83 ("got %x, expected 4\n", PDMAudioPropsBytesPerFrame(&Cfg441StereoU32)));
84
85 uint32_t u32;
86 for (uint32_t i = 0; i < 256; i += 8)
87 {
88 RTTESTI_CHECK(PDMAudioPropsIsSizeAligned(&Cfg441StereoU32, i) == true);
89 for (uint32_t j = 1; j < 8; j++)
90 RTTESTI_CHECK(PDMAudioPropsIsSizeAligned(&Cfg441StereoU32, i + j) == false);
91 for (uint32_t j = 0; j < 8; j++)
92 RTTESTI_CHECK(PDMAudioPropsFloorBytesToFrame(&Cfg441StereoU32, i + j) == i);
93 }
94 for (uint32_t i = 0; i < 4096; i += 4)
95 {
96 RTTESTI_CHECK(PDMAudioPropsIsSizeAligned(&Cfg441StereoS16, i) == true);
97 for (uint32_t j = 1; j < 4; j++)
98 RTTESTI_CHECK(PDMAudioPropsIsSizeAligned(&Cfg441StereoS16, i + j) == false);
99 for (uint32_t j = 0; j < 4; j++)
100 RTTESTI_CHECK(PDMAudioPropsFloorBytesToFrame(&Cfg441StereoS16, i + j) == i);
101 }
102
103 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsFramesToBytes(&Cfg441StereoS16, 44100)) == 44100 * 2 * 2,
104 ("cb=%RU32\n", u32));
105 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsFramesToBytes(&Cfg441StereoS16, 2)) == 2 * 2 * 2,
106 ("cb=%RU32\n", u32));
107 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsFramesToBytes(&Cfg441StereoS16, 1)) == 4,
108 ("cb=%RU32\n", u32));
109 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsFramesToBytes(&Cfg441StereoU16, 1)) == 4,
110 ("cb=%RU32\n", u32));
111 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsFramesToBytes(&Cfg441StereoU32, 1)) == 8,
112 ("cb=%RU32\n", u32));
113
114 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsBytesToFrames(&Cfg441StereoS16, 4)) == 1, ("cb=%RU32\n", u32));
115 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsBytesToFrames(&Cfg441StereoU16, 4)) == 1, ("cb=%RU32\n", u32));
116 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsBytesToFrames(&Cfg441StereoU32, 8)) == 1, ("cb=%RU32\n", u32));
117
118 uint64_t u64;
119 RTTESTI_CHECK_MSG((u64 = PDMAudioPropsBytesToNano(&Cfg441StereoS16, 44100 * 2 * 2)) == RT_NS_1SEC,
120 ("ns=%RU64\n", u64));
121 RTTESTI_CHECK_MSG((u64 = PDMAudioPropsBytesToMicro(&Cfg441StereoS16, 44100 * 2 * 2)) == RT_US_1SEC,
122 ("us=%RU64\n", u64));
123 RTTESTI_CHECK_MSG((u64 = PDMAudioPropsBytesToMilli(&Cfg441StereoS16, 44100 * 2 * 2)) == RT_MS_1SEC,
124 ("ms=%RU64\n", u64));
125
126 RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToNano(&Cfg441StereoS16, 44100)) == RT_NS_1SEC, ("ns=%RU64\n", u64));
127 RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToNano(&Cfg441StereoS16, 1)) == 22675, ("ns=%RU64\n", u64));
128 RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToNano(&Cfg441StereoS16, 31)) == 702947, ("ns=%RU64\n", u64));
129 RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToNano(&Cfg441StereoS16, 255)) == 5782312, ("ns=%RU64\n", u64));
130 //RTTESTI_CHECK_MSG((u64 = DrvAudioHlpFramesToMicro(&Cfg441StereoS16, 44100)) == RT_US_1SEC,
131 // ("us=%RU64\n", u64));
132 RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToMilli(&Cfg441StereoS16, 44100)) == RT_MS_1SEC, ("ms=%RU64\n", u64));
133 RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToMilli(&Cfg441StereoS16, 255)) == 5, ("ms=%RU64\n", u64));
134
135 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsNanoToFrames(&Cfg441StereoS16, RT_NS_1SEC)) == 44100, ("cb=%RU32\n", u32));
136 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsNanoToFrames(&Cfg441StereoS16, 215876)) == 10, ("cb=%RU32\n", u32));
137 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsMilliToFrames(&Cfg441StereoS16, RT_MS_1SEC)) == 44100, ("cb=%RU32\n", u32));
138 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsMilliToFrames(&Cfg441StereoU32, 6)) == 265, ("cb=%RU32\n", u32));
139
140 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsNanoToBytes(&Cfg441StereoS16, RT_NS_1SEC)) == 44100*2*2, ("cb=%RU32\n", u32));
141 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsNanoToBytes(&Cfg441StereoS16, 702947)) == 31*2*2, ("cb=%RU32\n", u32));
142 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsMilliToBytes(&Cfg441StereoS16, RT_MS_1SEC)) == 44100*2*2, ("cb=%RU32\n", u32));
143 RTTESTI_CHECK_MSG((u32 = PDMAudioPropsMilliToBytes(&Cfg441StereoS16, 5)) == 884, ("cb=%RU32\n", u32));
144
145 /* DrvAudioHlpClearBuf: */
146 uint8_t *pbPage;
147 int rc = RTTestGuardedAlloc(hTest, PAGE_SIZE, 0, false /*fHead*/, (void **)&pbPage);
148 RTTESTI_CHECK_RC_OK_RETV(rc);
149
150 memset(pbPage, 0x42, PAGE_SIZE);
151 PDMAudioPropsClearBuffer(&Cfg441StereoS16, pbPage, PAGE_SIZE, PAGE_SIZE / 4);
152 RTTESTI_CHECK(ASMMemIsZero(pbPage, PAGE_SIZE));
153
154 memset(pbPage, 0x42, PAGE_SIZE);
155 PDMAudioPropsClearBuffer(&Cfg441StereoU16, pbPage, PAGE_SIZE, PAGE_SIZE / 4);
156 for (uint32_t off = 0; off < PAGE_SIZE; off += 2)
157 RTTESTI_CHECK_MSG(pbPage[off] == 0x80 && pbPage[off + 1] == 0, ("off=%#x: %#x %x\n", off, pbPage[off], pbPage[off + 1]));
158
159 memset(pbPage, 0x42, PAGE_SIZE);
160 PDMAudioPropsClearBuffer(&Cfg441StereoU32, pbPage, PAGE_SIZE, PAGE_SIZE / 8);
161 for (uint32_t off = 0; off < PAGE_SIZE; off += 4)
162 RTTESTI_CHECK(pbPage[off] == 0x80 && pbPage[off + 1] == 0 && pbPage[off + 2] == 0 && pbPage[off + 3] == 0);
163
164
165 RTTestDisableAssertions(hTest);
166 memset(pbPage, 0x42, PAGE_SIZE);
167 PDMAudioPropsClearBuffer(&Cfg441StereoS16, pbPage, PAGE_SIZE, PAGE_SIZE); /* should adjust down the frame count. */
168 RTTESTI_CHECK(ASMMemIsZero(pbPage, PAGE_SIZE));
169
170 memset(pbPage, 0x42, PAGE_SIZE);
171 PDMAudioPropsClearBuffer(&Cfg441StereoU16, pbPage, PAGE_SIZE, PAGE_SIZE); /* should adjust down the frame count. */
172 for (uint32_t off = 0; off < PAGE_SIZE; off += 2)
173 RTTESTI_CHECK_MSG(pbPage[off] == 0x80 && pbPage[off + 1] == 0, ("off=%#x: %#x %x\n", off, pbPage[off], pbPage[off + 1]));
174
175 memset(pbPage, 0x42, PAGE_SIZE);
176 PDMAudioPropsClearBuffer(&Cfg441StereoU32, pbPage, PAGE_SIZE, PAGE_SIZE); /* should adjust down the frame count. */
177 for (uint32_t off = 0; off < PAGE_SIZE; off += 4)
178 RTTESTI_CHECK(pbPage[off] == 0x80 && pbPage[off + 1] == 0 && pbPage[off + 2] == 0 && pbPage[off + 3] == 0);
179 RTTestRestoreAssertions(hTest);
180
181 RTTestGuardedFree(hTest, pbPage);
182}
183
184
185static int tstSingle(RTTEST hTest)
186{
187 RTTestSub(hTest, "Single buffer");
188
189 /* 44100Hz, 2 Channels, S16 */
190 PDMAUDIOPCMPROPS config = PDMAUDIOPCMPROPS_INITIALIZER(
191 2, /* Bytes */
192 true, /* Signed */
193 2, /* Channels */
194 44100, /* Hz */
195 false /* Swap Endian */
196 );
197
198 RTTESTI_CHECK(AudioHlpPcmPropsAreValid(&config));
199
200 uint32_t cBufSize = _1K;
201
202 /*
203 * General stuff.
204 */
205 PDMAUDIOMIXBUF mb;
206 RTTESTI_CHECK_RC_OK(AudioMixBufInit(&mb, "Single", &config, cBufSize));
207 RTTESTI_CHECK(AudioMixBufSize(&mb) == cBufSize);
208 RTTESTI_CHECK(AUDIOMIXBUF_B2F(&mb, AudioMixBufSizeBytes(&mb)) == cBufSize);
209 RTTESTI_CHECK(AUDIOMIXBUF_F2B(&mb, AudioMixBufSize(&mb)) == AudioMixBufSizeBytes(&mb));
210 RTTESTI_CHECK(AudioMixBufFree(&mb) == cBufSize);
211 RTTESTI_CHECK(AUDIOMIXBUF_F2B(&mb, AudioMixBufFree(&mb)) == AudioMixBufFreeBytes(&mb));
212
213 /*
214 * Absolute writes.
215 */
216 uint32_t cFramesRead = 0, cFramesWritten = 0, cFramesWrittenAbs = 0;
217 int8_t aFrames8 [2] = { 0x12, 0x34 };
218 int16_t aFrames16[2] = { 0xAA, 0xBB };
219 int32_t aFrames32[2] = { 0xCC, 0xDD };
220
221 RTTESTI_CHECK_RC_OK(AudioMixBufWriteAt(&mb, 0 /* Offset */, &aFrames8, sizeof(aFrames8), &cFramesWritten));
222 RTTESTI_CHECK(cFramesWritten == 0 /* Frames */);
223 RTTESTI_CHECK(AudioMixBufUsed(&mb) == 0);
224
225 RTTESTI_CHECK_RC_OK(AudioMixBufWriteAt(&mb, 0 /* Offset */, &aFrames16, sizeof(aFrames16), &cFramesWritten));
226 RTTESTI_CHECK(cFramesWritten == 1 /* Frames */);
227 RTTESTI_CHECK(AudioMixBufUsed(&mb) == 1);
228
229 RTTESTI_CHECK_RC_OK(AudioMixBufWriteAt(&mb, 2 /* Offset */, &aFrames32, sizeof(aFrames32), &cFramesWritten));
230 RTTESTI_CHECK(cFramesWritten == 2 /* Frames */);
231 RTTESTI_CHECK(AudioMixBufUsed(&mb) == 2);
232
233 /* Beyond buffer. */
234 RTTESTI_CHECK_RC(AudioMixBufWriteAt(&mb, AudioMixBufSize(&mb) + 1, &aFrames16, sizeof(aFrames16),
235 &cFramesWritten), VERR_BUFFER_OVERFLOW);
236
237 /* Offset wrap-around: When writing as much (or more) frames the mixing buffer can hold. */
238 uint32_t cbSamples = cBufSize * sizeof(int16_t) * 2 /* Channels */;
239 RTTESTI_CHECK(cbSamples);
240 uint16_t *paSamples = (uint16_t *)RTMemAlloc(cbSamples);
241 RTTESTI_CHECK(paSamples);
242 RTTESTI_CHECK_RC_OK(AudioMixBufWriteAt(&mb, 0 /* Offset */, paSamples, cbSamples, &cFramesWritten));
243 RTTESTI_CHECK(cFramesWritten == cBufSize /* Frames */);
244 RTTESTI_CHECK(AudioMixBufUsed(&mb) == cBufSize);
245 RTTESTI_CHECK(AudioMixBufReadPos(&mb) == 0);
246 RTTESTI_CHECK(AudioMixBufWritePos(&mb) == 0);
247 RTMemFree(paSamples);
248 cbSamples = 0;
249
250 /*
251 * Circular writes.
252 */
253 AudioMixBufReset(&mb);
254
255 RTTESTI_CHECK_RC_OK(AudioMixBufWriteAt(&mb, 2 /* Offset */, &aFrames32, sizeof(aFrames32), &cFramesWritten));
256 RTTESTI_CHECK(cFramesWritten == 2 /* Frames */);
257 RTTESTI_CHECK(AudioMixBufUsed(&mb) == 2);
258
259 cFramesWrittenAbs = AudioMixBufUsed(&mb);
260
261 uint32_t cToWrite = AudioMixBufSize(&mb) - cFramesWrittenAbs - 1; /* -1 as padding plus -2 frames for above. */
262 for (uint32_t i = 0; i < cToWrite; i++)
263 {
264 RTTESTI_CHECK_RC_OK(AudioMixBufWriteCirc(&mb, &aFrames16, sizeof(aFrames16), &cFramesWritten));
265 RTTESTI_CHECK(cFramesWritten == 1);
266 }
267 RTTESTI_CHECK(!AudioMixBufIsEmpty(&mb));
268 RTTESTI_CHECK(AudioMixBufFree(&mb) == 1);
269 RTTESTI_CHECK(AudioMixBufFreeBytes(&mb) == AUDIOMIXBUF_F2B(&mb, 1U));
270 RTTESTI_CHECK(AudioMixBufUsed(&mb) == cToWrite + cFramesWrittenAbs /* + last absolute write */);
271
272 RTTESTI_CHECK_RC_OK(AudioMixBufWriteCirc(&mb, &aFrames16, sizeof(aFrames16), &cFramesWritten));
273 RTTESTI_CHECK(cFramesWritten == 1);
274 RTTESTI_CHECK(AudioMixBufFree(&mb) == 0);
275 RTTESTI_CHECK(AudioMixBufFreeBytes(&mb) == AUDIOMIXBUF_F2B(&mb, 0U));
276 RTTESTI_CHECK(AudioMixBufUsed(&mb) == cBufSize);
277
278 /* Circular reads. */
279 uint32_t cToRead = AudioMixBufSize(&mb) - cFramesWrittenAbs - 1;
280 for (uint32_t i = 0; i < cToRead; i++)
281 {
282 RTTESTI_CHECK_RC_OK(AudioMixBufAcquireReadBlock(&mb, &aFrames16, sizeof(aFrames16), &cFramesRead));
283 RTTESTI_CHECK(cFramesRead == 1);
284 AudioMixBufReleaseReadBlock(&mb, cFramesRead);
285 AudioMixBufFinish(&mb, cFramesRead);
286 }
287 RTTESTI_CHECK(!AudioMixBufIsEmpty(&mb));
288 RTTESTI_CHECK(AudioMixBufFree(&mb) == AudioMixBufSize(&mb) - cFramesWrittenAbs - 1);
289 RTTESTI_CHECK(AudioMixBufFreeBytes(&mb) == AUDIOMIXBUF_F2B(&mb, cBufSize - cFramesWrittenAbs - 1));
290 RTTESTI_CHECK(AudioMixBufUsed(&mb) == cBufSize - cToRead);
291
292 RTTESTI_CHECK_RC_OK(AudioMixBufAcquireReadBlock(&mb, &aFrames16, sizeof(aFrames16), &cFramesRead));
293 RTTESTI_CHECK(cFramesRead == 1);
294 AudioMixBufReleaseReadBlock(&mb, cFramesRead);
295 AudioMixBufFinish(&mb, cFramesRead);
296 RTTESTI_CHECK(AudioMixBufFree(&mb) == cBufSize - cFramesWrittenAbs);
297 RTTESTI_CHECK(AudioMixBufFreeBytes(&mb) == AUDIOMIXBUF_F2B(&mb, cBufSize - cFramesWrittenAbs));
298 RTTESTI_CHECK(AudioMixBufUsed(&mb) == cFramesWrittenAbs);
299
300 AudioMixBufDestroy(&mb);
301
302 return RTTestSubErrorCount(hTest) ? VERR_GENERAL_FAILURE : VINF_SUCCESS;
303}
304
305static int tstParentChild(RTTEST hTest)
306{
307 uint32_t cParentBufSize = RTRandU32Ex(_1K /* Min */, _16K /* Max */); /* Enough room for random sizes */
308
309 /* 44100Hz, 2 Channels, S16 */
310 PDMAUDIOPCMPROPS cfg_p = PDMAUDIOPCMPROPS_INITIALIZER(
311 2, /* Bytes */
312 true, /* Signed */
313 2, /* Channels */
314 44100, /* Hz */
315 false /* Swap Endian */
316 );
317
318 RTTESTI_CHECK(AudioHlpPcmPropsAreValid(&cfg_p));
319
320 PDMAUDIOMIXBUF parent;
321 RTTESTI_CHECK_RC_OK(AudioMixBufInit(&parent, "Parent", &cfg_p, cParentBufSize));
322
323 /* 22050Hz, 2 Channels, S16 */
324 PDMAUDIOPCMPROPS cfg_c1 = PDMAUDIOPCMPROPS_INITIALIZER(/* Upmixing to parent */
325 2, /* Bytes */
326 true, /* Signed */
327 2, /* Channels */
328 22050, /* Hz */
329 false /* Swap Endian */
330 );
331
332 RTTESTI_CHECK(AudioHlpPcmPropsAreValid(&cfg_c1));
333
334 uint32_t cFrames = 16;
335 uint32_t cChildBufSize = RTRandU32Ex(cFrames /* Min */, 64 /* Max */);
336
337 PDMAUDIOMIXBUF child1;
338 RTTESTI_CHECK_RC_OK(AudioMixBufInit(&child1, "Child1", &cfg_c1, cChildBufSize));
339 RTTESTI_CHECK_RC_OK(AudioMixBufLinkTo(&child1, &parent));
340
341 /* 48000Hz, 2 Channels, S16 */
342 PDMAUDIOPCMPROPS cfg_c2 = PDMAUDIOPCMPROPS_INITIALIZER(/* Downmixing to parent */
343 2, /* Bytes */
344 true, /* Signed */
345 2, /* Channels */
346 48000, /* Hz */
347 false /* Swap Endian */
348 );
349
350 RTTESTI_CHECK(AudioHlpPcmPropsAreValid(&cfg_c2));
351
352 PDMAUDIOMIXBUF child2;
353 RTTESTI_CHECK_RC_OK(AudioMixBufInit(&child2, "Child2", &cfg_c2, cChildBufSize));
354 RTTESTI_CHECK_RC_OK(AudioMixBufLinkTo(&child2, &parent));
355
356 /*
357 * Writing + mixing from child/children -> parent, sequential.
358 */
359 uint32_t cbBuf = _1K;
360 char pvBuf[_1K];
361 int16_t aFrames16[32] = { 0xAA, 0xBB };
362 uint32_t cFramesRead, cFramesWritten, cFramesMixed;
363
364 uint32_t cFramesChild1 = cFrames;
365 uint32_t cFramesChild2 = cFrames;
366
367 uint32_t t = RTRandU32() % 32;
368
369 RTTestPrintf(hTest, RTTESTLVL_DEBUG,
370 "cParentBufSize=%RU32, cChildBufSize=%RU32, %RU32 frames -> %RU32 iterations total\n",
371 cParentBufSize, cChildBufSize, cFrames, t);
372
373 /*
374 * Using AudioMixBufWriteAt for writing to children.
375 */
376 RTTestSub(hTest, "2 Children -> Parent (AudioMixBufWriteAt)");
377
378 uint32_t cChildrenSamplesMixedTotal = 0;
379
380 for (uint32_t i = 0; i < t; i++)
381 {
382 RTTestPrintf(hTest, RTTESTLVL_DEBUG, "i=%RU32\n", i);
383
384 uint32_t cChild1Writes = RTRandU32() % 8;
385
386 for (uint32_t c1 = 0; c1 < cChild1Writes; c1++)
387 {
388 /* Child 1. */
389 RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufWriteAt(&child1, 0, &aFrames16, sizeof(aFrames16), &cFramesWritten));
390 RTTESTI_CHECK_MSG_BREAK(cFramesWritten == cFramesChild1, ("Child1: Expected %RU32 written frames, got %RU32\n", cFramesChild1, cFramesWritten));
391 RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufMixToParent(&child1, cFramesWritten, &cFramesMixed));
392
393 cChildrenSamplesMixedTotal += cFramesMixed;
394
395 RTTESTI_CHECK_MSG_BREAK(cFramesWritten == cFramesMixed, ("Child1: Expected %RU32 mixed frames, got %RU32\n", cFramesWritten, cFramesMixed));
396 RTTESTI_CHECK_MSG_BREAK(AudioMixBufUsed(&child1) == 0, ("Child1: Expected %RU32 used frames, got %RU32\n", 0, AudioMixBufUsed(&child1)));
397 }
398
399 uint32_t cChild2Writes = RTRandU32() % 8;
400
401 for (uint32_t c2 = 0; c2 < cChild2Writes; c2++)
402 {
403 /* Child 2. */
404 RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufWriteAt(&child2, 0, &aFrames16, sizeof(aFrames16), &cFramesWritten));
405 RTTESTI_CHECK_MSG_BREAK(cFramesWritten == cFramesChild2, ("Child2: Expected %RU32 written frames, got %RU32\n", cFramesChild2, cFramesWritten));
406 RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufMixToParent(&child2, cFramesWritten, &cFramesMixed));
407
408 cChildrenSamplesMixedTotal += cFramesMixed;
409
410 RTTESTI_CHECK_MSG_BREAK(cFramesWritten == cFramesMixed, ("Child2: Expected %RU32 mixed frames, got %RU32\n", cFramesWritten, cFramesMixed));
411 RTTESTI_CHECK_MSG_BREAK(AudioMixBufUsed(&child2) == 0, ("Child2: Expected %RU32 used frames, got %RU32\n", 0, AudioMixBufUsed(&child2)));
412 }
413
414 /*
415 * Read out all frames from the parent buffer and also mark the just-read frames as finished
416 * so that both connected children buffers can keep track of their stuff.
417 */
418 uint32_t cParentSamples = AudioMixBufUsed(&parent);
419 while (cParentSamples)
420 {
421 RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufAcquireReadBlock(&parent, pvBuf, cbBuf, &cFramesRead));
422 if (!cFramesRead)
423 break;
424
425 AudioMixBufReleaseReadBlock(&parent, cFramesRead);
426 AudioMixBufFinish(&parent, cFramesRead);
427
428 RTTESTI_CHECK(cParentSamples >= cFramesRead);
429 cParentSamples -= cFramesRead;
430 }
431
432 RTTESTI_CHECK(cParentSamples == 0);
433 }
434
435 RTTESTI_CHECK(AudioMixBufUsed(&parent) == 0);
436 RTTESTI_CHECK(AudioMixBufLive(&child1) == 0);
437 RTTESTI_CHECK(AudioMixBufLive(&child2) == 0);
438
439 AudioMixBufDestroy(&parent);
440 AudioMixBufDestroy(&child1);
441 AudioMixBufDestroy(&child2);
442
443 return RTTestSubErrorCount(hTest) ? VERR_GENERAL_FAILURE : VINF_SUCCESS;
444}
445
446/* Test 8-bit sample conversion (8-bit -> internal -> 8-bit). */
447static int tstConversion8(RTTEST hTest)
448{
449 unsigned i;
450 uint32_t cBufSize = 256;
451
452 RTTestSub(hTest, "Sample conversion (U8)");
453
454 /* 44100Hz, 1 Channel, U8 */
455 PDMAUDIOPCMPROPS cfg_p = PDMAUDIOPCMPROPS_INITIALIZER(
456 1, /* Bytes */
457 false, /* Signed */
458 1, /* Channels */
459 44100, /* Hz */
460 false /* Swap Endian */
461 );
462
463 RTTESTI_CHECK(AudioHlpPcmPropsAreValid(&cfg_p));
464
465 PDMAUDIOMIXBUF parent;
466 RTTESTI_CHECK_RC_OK(AudioMixBufInit(&parent, "Parent", &cfg_p, cBufSize));
467
468 /* Child uses half the sample rate; that ensures the mixing engine can't
469 * take shortcuts and performs conversion. Because conversion to double
470 * the sample rate effectively inserts one additional sample between every
471 * two source frames, N source frames will be converted to N * 2 - 1
472 * frames. However, the last source sample will be saved for later
473 * interpolation and not immediately output.
474 */
475
476 /* 22050Hz, 1 Channel, U8 */
477 PDMAUDIOPCMPROPS cfg_c = PDMAUDIOPCMPROPS_INITIALIZER( /* Upmixing to parent */
478 1, /* Bytes */
479 false, /* Signed */
480 1, /* Channels */
481 22050, /* Hz */
482 false /* Swap Endian */
483 );
484
485 RTTESTI_CHECK(AudioHlpPcmPropsAreValid(&cfg_c));
486
487 PDMAUDIOMIXBUF child;
488 RTTESTI_CHECK_RC_OK(AudioMixBufInit(&child, "Child", &cfg_c, cBufSize));
489 RTTESTI_CHECK_RC_OK(AudioMixBufLinkTo(&child, &parent));
490
491 /* 8-bit unsigned frames. Often used with SB16 device. */
492 uint8_t aFrames8U[16] = { 0xAA, 0xBB, 0, 1, 43, 125, 126, 127,
493 128, 129, 130, 131, 132, UINT8_MAX - 1, UINT8_MAX, 0 };
494
495 /*
496 * Writing + mixing from child -> parent, sequential.
497 */
498 uint32_t cbBuf = 256;
499 char achBuf[256];
500 uint32_t cFramesRead, cFramesWritten, cFramesMixed;
501
502 uint32_t cFramesChild = 16;
503 uint32_t cFramesParent = cFramesChild * 2 - 2;
504 uint32_t cFramesTotalRead = 0;
505
506 /**** 8-bit unsigned samples ****/
507 RTTestPrintf(hTest, RTTESTLVL_DEBUG, "Conversion test %uHz %uch 8-bit\n", cfg_c.uHz, PDMAudioPropsChannels(&cfg_c));
508 RTTESTI_CHECK_RC_OK(AudioMixBufWriteCirc(&child, &aFrames8U, sizeof(aFrames8U), &cFramesWritten));
509 RTTESTI_CHECK_MSG(cFramesWritten == cFramesChild, ("Child: Expected %RU32 written frames, got %RU32\n", cFramesChild, cFramesWritten));
510 RTTESTI_CHECK_RC_OK(AudioMixBufMixToParent(&child, cFramesWritten, &cFramesMixed));
511 uint32_t cFrames = AudioMixBufUsed(&parent);
512 RTTESTI_CHECK_MSG(AudioMixBufLive(&child) == cFrames, ("Child: Expected %RU32 mixed frames, got %RU32\n", AudioMixBufLive(&child), cFrames));
513
514 RTTESTI_CHECK(AudioMixBufUsed(&parent) == AudioMixBufLive(&child));
515
516 for (;;)
517 {
518 RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufAcquireReadBlock(&parent, achBuf, cbBuf, &cFramesRead));
519 if (!cFramesRead)
520 break;
521 cFramesTotalRead += cFramesRead;
522 AudioMixBufReleaseReadBlock(&parent, cFramesRead);
523 AudioMixBufFinish(&parent, cFramesRead);
524 }
525
526 RTTESTI_CHECK_MSG(cFramesTotalRead == cFramesParent, ("Parent: Expected %RU32 mixed frames, got %RU32\n", cFramesParent, cFramesTotalRead));
527
528 /* Check that the frames came out unharmed. Every other sample is interpolated and we ignore it. */
529 /* NB: This also checks that the default volume setting is 0dB attenuation. */
530 uint8_t *pSrc8 = &aFrames8U[0];
531 uint8_t *pDst8 = (uint8_t *)achBuf;
532
533 for (i = 0; i < cFramesChild - 1; ++i)
534 {
535 RTTESTI_CHECK_MSG(*pSrc8 == *pDst8, ("index %u: Dst=%d, Src=%d\n", i, *pDst8, *pSrc8));
536 pSrc8 += 1;
537 pDst8 += 2;
538 }
539
540 RTTESTI_CHECK(AudioMixBufUsed(&parent) == 0);
541 RTTESTI_CHECK(AudioMixBufLive(&child) == 0);
542
543 AudioMixBufDestroy(&parent);
544 AudioMixBufDestroy(&child);
545
546 return RTTestSubErrorCount(hTest) ? VERR_GENERAL_FAILURE : VINF_SUCCESS;
547}
548
549/* Test 16-bit sample conversion (16-bit -> internal -> 16-bit). */
550static int tstConversion16(RTTEST hTest)
551{
552 unsigned i;
553 uint32_t cBufSize = 256;
554
555 RTTestSub(hTest, "Sample conversion (S16)");
556
557 /* 44100Hz, 1 Channel, S16 */
558 PDMAUDIOPCMPROPS cfg_p = PDMAUDIOPCMPROPS_INITIALIZER(
559 2, /* Bytes */
560 true, /* Signed */
561 1, /* Channels */
562 44100, /* Hz */
563 false /* Swap Endian */
564 );
565
566 RTTESTI_CHECK(AudioHlpPcmPropsAreValid(&cfg_p));
567
568 PDMAUDIOMIXBUF parent;
569 RTTESTI_CHECK_RC_OK(AudioMixBufInit(&parent, "Parent", &cfg_p, cBufSize));
570
571 /* 22050Hz, 1 Channel, S16 */
572 PDMAUDIOPCMPROPS cfg_c = PDMAUDIOPCMPROPS_INITIALIZER( /* Upmixing to parent */
573 2, /* Bytes */
574 true, /* Signed */
575 1, /* Channels */
576 22050, /* Hz */
577 false /* Swap Endian */
578 );
579
580 RTTESTI_CHECK(AudioHlpPcmPropsAreValid(&cfg_c));
581
582 PDMAUDIOMIXBUF child;
583 RTTESTI_CHECK_RC_OK(AudioMixBufInit(&child, "Child", &cfg_c, cBufSize));
584 RTTESTI_CHECK_RC_OK(AudioMixBufLinkTo(&child, &parent));
585
586 /* 16-bit signed. More or less exclusively used as output, and usually as input, too. */
587 int16_t aFrames16S[16] = { 0xAA, 0xBB, INT16_MIN, INT16_MIN + 1, INT16_MIN / 2, -3, -2, -1,
588 0, 1, 2, 3, INT16_MAX / 2, INT16_MAX - 1, INT16_MAX, 0 };
589
590 /*
591 * Writing + mixing from child -> parent, sequential.
592 */
593 uint32_t cbBuf = 256;
594 char achBuf[256];
595 uint32_t cFramesRead, cFramesWritten, cFramesMixed;
596
597 uint32_t cFramesChild = 16;
598 uint32_t cFramesParent = cFramesChild * 2 - 2;
599 uint32_t cFramesTotalRead = 0;
600
601 /**** 16-bit signed samples ****/
602 RTTestPrintf(hTest, RTTESTLVL_DEBUG, "Conversion test %uHz %uch 16-bit\n", cfg_c.uHz, PDMAudioPropsChannels(&cfg_c));
603 RTTESTI_CHECK_RC_OK(AudioMixBufWriteCirc(&child, &aFrames16S, sizeof(aFrames16S), &cFramesWritten));
604 RTTESTI_CHECK_MSG(cFramesWritten == cFramesChild, ("Child: Expected %RU32 written frames, got %RU32\n", cFramesChild, cFramesWritten));
605 RTTESTI_CHECK_RC_OK(AudioMixBufMixToParent(&child, cFramesWritten, &cFramesMixed));
606 uint32_t cFrames = AudioMixBufUsed(&parent);
607 RTTESTI_CHECK_MSG(AudioMixBufLive(&child) == cFrames, ("Child: Expected %RU32 mixed frames, got %RU32\n", AudioMixBufLive(&child), cFrames));
608
609 RTTESTI_CHECK(AudioMixBufUsed(&parent) == AudioMixBufLive(&child));
610
611 for (;;)
612 {
613 RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufAcquireReadBlock(&parent, achBuf, cbBuf, &cFramesRead));
614 if (!cFramesRead)
615 break;
616 cFramesTotalRead += cFramesRead;
617 AudioMixBufReleaseReadBlock(&parent, cFramesRead);
618 AudioMixBufFinish(&parent, cFramesRead);
619 }
620 RTTESTI_CHECK_MSG(cFramesTotalRead == cFramesParent, ("Parent: Expected %RU32 mixed frames, got %RU32\n", cFramesParent, cFramesTotalRead));
621
622 /* Check that the frames came out unharmed. Every other sample is interpolated and we ignore it. */
623 /* NB: This also checks that the default volume setting is 0dB attenuation. */
624 int16_t *pSrc16 = &aFrames16S[0];
625 int16_t *pDst16 = (int16_t *)achBuf;
626
627 for (i = 0; i < cFramesChild - 1; ++i)
628 {
629 RTTESTI_CHECK_MSG(*pSrc16 == *pDst16, ("index %u: Dst=%d, Src=%d\n", i, *pDst16, *pSrc16));
630 pSrc16 += 1;
631 pDst16 += 2;
632 }
633
634 RTTESTI_CHECK(AudioMixBufUsed(&parent) == 0);
635 RTTESTI_CHECK(AudioMixBufLive(&child) == 0);
636
637 AudioMixBufDestroy(&parent);
638 AudioMixBufDestroy(&child);
639
640 return RTTestSubErrorCount(hTest) ? VERR_GENERAL_FAILURE : VINF_SUCCESS;
641}
642
643/* Test volume control. */
644static int tstVolume(RTTEST hTest)
645{
646 unsigned i;
647 uint32_t cBufSize = 256;
648
649 RTTestSub(hTest, "Volume control");
650
651 /* Same for parent/child. */
652 /* 44100Hz, 2 Channels, S16 */
653 PDMAUDIOPCMPROPS cfg = PDMAUDIOPCMPROPS_INITIALIZER(
654 2, /* Bytes */
655 true, /* Signed */
656 2, /* Channels */
657 44100, /* Hz */
658 false /* Swap Endian */
659 );
660
661 RTTESTI_CHECK(AudioHlpPcmPropsAreValid(&cfg));
662
663 PDMAUDIOVOLUME vol = { false, 0, 0 }; /* Not muted. */
664 PDMAUDIOMIXBUF parent;
665 RTTESTI_CHECK_RC_OK(AudioMixBufInit(&parent, "Parent", &cfg, cBufSize));
666
667 PDMAUDIOMIXBUF child;
668 RTTESTI_CHECK_RC_OK(AudioMixBufInit(&child, "Child", &cfg, cBufSize));
669 RTTESTI_CHECK_RC_OK(AudioMixBufLinkTo(&child, &parent));
670
671 /* A few 16-bit signed samples. */
672 int16_t aFrames16S[16] = { INT16_MIN, INT16_MIN + 1, -128, -64, -4, -1, 0, 1,
673 2, 255, 256, INT16_MAX / 2, INT16_MAX - 2, INT16_MAX - 1, INT16_MAX, 0 };
674
675 /*
676 * Writing + mixing from child -> parent.
677 */
678 uint32_t cbBuf = 256;
679 char achBuf[256];
680 uint32_t cFramesRead, cFramesWritten, cFramesMixed;
681
682 uint32_t cFramesChild = 8;
683 uint32_t cFramesParent = cFramesChild;
684 uint32_t cFramesTotalRead;
685 int16_t *pSrc16;
686 int16_t *pDst16;
687
688 /**** Volume control test ****/
689 RTTestPrintf(hTest, RTTESTLVL_DEBUG, "Volume control test %uHz %uch \n", cfg.uHz, PDMAudioPropsChannels(&cfg));
690
691 /* 1) Full volume/0dB attenuation (255). */
692 vol.uLeft = vol.uRight = 255;
693 AudioMixBufSetVolume(&child, &vol);
694
695 RTTESTI_CHECK_RC_OK(AudioMixBufWriteCirc(&child, &aFrames16S, sizeof(aFrames16S), &cFramesWritten));
696 RTTESTI_CHECK_MSG(cFramesWritten == cFramesChild, ("Child: Expected %RU32 written frames, got %RU32\n", cFramesChild, cFramesWritten));
697 RTTESTI_CHECK_RC_OK(AudioMixBufMixToParent(&child, cFramesWritten, &cFramesMixed));
698
699 cFramesTotalRead = 0;
700 for (;;)
701 {
702 RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufAcquireReadBlock(&parent, achBuf, cbBuf, &cFramesRead));
703 if (!cFramesRead)
704 break;
705 cFramesTotalRead += cFramesRead;
706 AudioMixBufReleaseReadBlock(&parent, cFramesRead);
707 AudioMixBufFinish(&parent, cFramesRead);
708 }
709 RTTESTI_CHECK_MSG(cFramesTotalRead == cFramesParent, ("Parent: Expected %RU32 mixed frames, got %RU32\n", cFramesParent, cFramesTotalRead));
710
711 /* Check that at 0dB the frames came out unharmed. */
712 pSrc16 = &aFrames16S[0];
713 pDst16 = (int16_t *)achBuf;
714
715 for (i = 0; i < cFramesParent * 2 /* stereo */; ++i)
716 {
717 RTTESTI_CHECK_MSG(*pSrc16 == *pDst16, ("index %u: Dst=%d, Src=%d\n", i, *pDst16, *pSrc16));
718 ++pSrc16;
719 ++pDst16;
720 }
721 AudioMixBufReset(&child);
722
723 /* 2) Half volume/-6dB attenuation (16 steps down). */
724 vol.uLeft = vol.uRight = 255 - 16;
725 AudioMixBufSetVolume(&child, &vol);
726
727 RTTESTI_CHECK_RC_OK(AudioMixBufWriteCirc(&child, &aFrames16S, sizeof(aFrames16S), &cFramesWritten));
728 RTTESTI_CHECK_MSG(cFramesWritten == cFramesChild, ("Child: Expected %RU32 written frames, got %RU32\n", cFramesChild, cFramesWritten));
729 RTTESTI_CHECK_RC_OK(AudioMixBufMixToParent(&child, cFramesWritten, &cFramesMixed));
730
731 cFramesTotalRead = 0;
732 for (;;)
733 {
734 RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufAcquireReadBlock(&parent, achBuf, cbBuf, &cFramesRead));
735 if (!cFramesRead)
736 break;
737 cFramesTotalRead += cFramesRead;
738 AudioMixBufReleaseReadBlock(&parent, cFramesRead);
739 AudioMixBufFinish(&parent, cFramesRead);
740 }
741 RTTESTI_CHECK_MSG(cFramesTotalRead == cFramesParent, ("Parent: Expected %RU32 mixed frames, got %RU32\n", cFramesParent, cFramesTotalRead));
742
743 /* Check that at -6dB the sample values are halved. */
744 pSrc16 = &aFrames16S[0];
745 pDst16 = (int16_t *)achBuf;
746
747 for (i = 0; i < cFramesParent * 2 /* stereo */; ++i)
748 {
749 /* Watch out! For negative values, x >> 1 is not the same as x / 2. */
750 RTTESTI_CHECK_MSG(*pSrc16 >> 1 == *pDst16, ("index %u: Dst=%d, Src=%d\n", i, *pDst16, *pSrc16));
751 ++pSrc16;
752 ++pDst16;
753 }
754
755 AudioMixBufDestroy(&parent);
756 AudioMixBufDestroy(&child);
757
758 return RTTestSubErrorCount(hTest) ? VERR_GENERAL_FAILURE : VINF_SUCCESS;
759}
760
761int main(int argc, char **argv)
762{
763 RTR3InitExe(argc, &argv, 0);
764
765 /*
766 * Initialize IPRT and create the test.
767 */
768 RTTEST hTest;
769 int rc = RTTestInitAndCreate("tstAudioMixBuffer", &hTest);
770 if (rc)
771 return rc;
772 RTTestBanner(hTest);
773
774 tstBasics(hTest);
775 tstSingle(hTest);
776 tstParentChild(hTest);
777 tstConversion8(hTest);
778 tstConversion16(hTest);
779 tstVolume(hTest);
780
781 /*
782 * Summary
783 */
784 return RTTestSummaryAndDestroy(hTest);
785}
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