summaryrefslogtreecommitdiff
path: root/src/contrib/SDL-3.2.20/test/testautomation_audio.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-08-30 16:53:58 -0700
committer3gg <3gg@shellblade.net>2025-08-30 16:53:58 -0700
commit6aaedb813fa11ba0679c3051bc2eb28646b9506c (patch)
tree34acbfc9840e02cb4753e6306ea7ce978bf8b58e /src/contrib/SDL-3.2.20/test/testautomation_audio.c
parent8f228ade99dd3d4c8da9b78ade1815c9adf85c8f (diff)
Update to SDL3
Diffstat (limited to 'src/contrib/SDL-3.2.20/test/testautomation_audio.c')
-rw-r--r--src/contrib/SDL-3.2.20/test/testautomation_audio.c1559
1 files changed, 1559 insertions, 0 deletions
diff --git a/src/contrib/SDL-3.2.20/test/testautomation_audio.c b/src/contrib/SDL-3.2.20/test/testautomation_audio.c
new file mode 100644
index 0000000..7c141b3
--- /dev/null
+++ b/src/contrib/SDL-3.2.20/test/testautomation_audio.c
@@ -0,0 +1,1559 @@
1/**
2 * Original code: automated SDL audio test written by Edgar Simo "bobbens"
3 * New/updated tests: aschiffler at ferzkopp dot net
4 */
5
6/* quiet windows compiler warnings */
7#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
8#define _CRT_SECURE_NO_WARNINGS
9#endif
10
11#include <math.h>
12#include <stdio.h>
13
14#include <SDL3/SDL.h>
15#include <SDL3/SDL_test.h>
16#include "testautomation_suites.h"
17
18/* ================= Test Case Implementation ================== */
19
20/* Fixture */
21
22static void SDLCALL audioSetUp(void **arg)
23{
24 /* Start SDL audio subsystem */
25 bool ret = SDL_InitSubSystem(SDL_INIT_AUDIO);
26 SDLTest_AssertPass("Call to SDL_InitSubSystem(SDL_INIT_AUDIO)");
27 SDLTest_AssertCheck(ret == true, "Check result from SDL_InitSubSystem(SDL_INIT_AUDIO)");
28 if (!ret) {
29 SDLTest_LogError("%s", SDL_GetError());
30 }
31}
32
33static void SDLCALL audioTearDown(void *arg)
34{
35 /* Remove a possibly created file from SDL disk writer audio driver; ignore errors */
36 (void)remove("sdlaudio.raw");
37
38 SDLTest_AssertPass("Cleanup of test files completed");
39}
40
41#if 0 /* !!! FIXME: maybe update this? */
42/* Global counter for callback invocation */
43static int g_audio_testCallbackCounter;
44
45/* Global accumulator for total callback length */
46static int g_audio_testCallbackLength;
47
48/* Test callback function */
49static void SDLCALL audio_testCallback(void *userdata, Uint8 *stream, int len)
50{
51 /* track that callback was called */
52 g_audio_testCallbackCounter++;
53 g_audio_testCallbackLength += len;
54}
55#endif
56
57static SDL_AudioDeviceID g_audio_id = 0;
58
59/* Test case functions */
60
61/**
62 * Stop and restart audio subsystem
63 *
64 * \sa SDL_QuitSubSystem
65 * \sa SDL_InitSubSystem
66 */
67static int SDLCALL audio_quitInitAudioSubSystem(void *arg)
68{
69 /* Stop SDL audio subsystem */
70 SDL_QuitSubSystem(SDL_INIT_AUDIO);
71 SDLTest_AssertPass("Call to SDL_QuitSubSystem(SDL_INIT_AUDIO)");
72
73 /* Restart audio again */
74 audioSetUp(NULL);
75
76 return TEST_COMPLETED;
77}
78
79/**
80 * Start and stop audio directly
81 *
82 * \sa SDL_InitAudio
83 * \sa SDL_QuitAudio
84 */
85static int SDLCALL audio_initQuitAudio(void *arg)
86{
87 int result;
88 int i, iMax;
89 const char *audioDriver;
90 const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DRIVER);
91
92 /* Stop SDL audio subsystem */
93 SDL_QuitSubSystem(SDL_INIT_AUDIO);
94 SDLTest_AssertPass("Call to SDL_QuitSubSystem(SDL_INIT_AUDIO)");
95
96 /* Loop over all available audio drivers */
97 iMax = SDL_GetNumAudioDrivers();
98 SDLTest_AssertPass("Call to SDL_GetNumAudioDrivers()");
99 SDLTest_AssertCheck(iMax > 0, "Validate number of audio drivers; expected: >0 got: %d", iMax);
100 for (i = 0; i < iMax; i++) {
101 audioDriver = SDL_GetAudioDriver(i);
102 SDLTest_AssertPass("Call to SDL_GetAudioDriver(%d)", i);
103 SDLTest_Assert(audioDriver != NULL, "Audio driver name is not NULL");
104 SDLTest_AssertCheck(audioDriver[0] != '\0', "Audio driver name is not empty; got: %s", audioDriver); /* NOLINT(clang-analyzer-core.NullDereference): Checked for NULL above */
105
106 if (hint && SDL_strcmp(audioDriver, hint) != 0) {
107 continue;
108 }
109
110 /* Call Init */
111 SDL_SetHint(SDL_HINT_AUDIO_DRIVER, audioDriver);
112 result = SDL_InitSubSystem(SDL_INIT_AUDIO);
113 SDLTest_AssertPass("Call to SDL_InitSubSystem(SDL_INIT_AUDIO) with driver='%s'", audioDriver);
114 SDLTest_AssertCheck(result == true, "Validate result value; expected: true got: %d", result);
115
116 /* Call Quit */
117 SDL_QuitSubSystem(SDL_INIT_AUDIO);
118 SDLTest_AssertPass("Call to SDL_QuitSubSystem(SDL_INIT_AUDIO)");
119 }
120
121 /* NULL driver specification */
122 audioDriver = NULL;
123
124 /* Call Init */
125 SDL_SetHint(SDL_HINT_AUDIO_DRIVER, audioDriver);
126 result = SDL_InitSubSystem(SDL_INIT_AUDIO);
127 SDLTest_AssertPass("Call to SDL_AudioInit(NULL)");
128 SDLTest_AssertCheck(result == true, "Validate result value; expected: true got: %d", result);
129
130 /* Call Quit */
131 SDL_QuitSubSystem(SDL_INIT_AUDIO);
132 SDLTest_AssertPass("Call to SDL_QuitSubSystem(SDL_INIT_AUDIO)");
133
134 /* Restart audio again */
135 audioSetUp(NULL);
136
137 return TEST_COMPLETED;
138}
139
140/**
141 * Start, open, close and stop audio
142 *
143 * \sa SDL_InitAudio
144 * \sa SDL_OpenAudioDevice
145 * \sa SDL_CloseAudioDevice
146 * \sa SDL_QuitAudio
147 */
148static int SDLCALL audio_initOpenCloseQuitAudio(void *arg)
149{
150 int result;
151 int i, iMax, j, k;
152 const char *audioDriver;
153 SDL_AudioSpec desired;
154 const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DRIVER);
155
156 /* Stop SDL audio subsystem */
157 SDL_QuitSubSystem(SDL_INIT_AUDIO);
158 SDLTest_AssertPass("Call to SDL_QuitSubSystem(SDL_INIT_AUDIO)");
159
160 /* Loop over all available audio drivers */
161 iMax = SDL_GetNumAudioDrivers();
162 SDLTest_AssertPass("Call to SDL_GetNumAudioDrivers()");
163 SDLTest_AssertCheck(iMax > 0, "Validate number of audio drivers; expected: >0 got: %d", iMax);
164 for (i = 0; i < iMax; i++) {
165 audioDriver = SDL_GetAudioDriver(i);
166 SDLTest_AssertPass("Call to SDL_GetAudioDriver(%d)", i);
167 SDLTest_Assert(audioDriver != NULL, "Audio driver name is not NULL");
168 SDLTest_AssertCheck(audioDriver[0] != '\0', "Audio driver name is not empty; got: %s", audioDriver); /* NOLINT(clang-analyzer-core.NullDereference): Checked for NULL above */
169
170 if (hint && SDL_strcmp(audioDriver, hint) != 0) {
171 continue;
172 }
173
174 /* Change specs */
175 for (j = 0; j < 2; j++) {
176
177 /* Call Init */
178 SDL_SetHint(SDL_HINT_AUDIO_DRIVER, audioDriver);
179 result = SDL_InitSubSystem(SDL_INIT_AUDIO);
180 SDLTest_AssertPass("Call to SDL_InitSubSystem(SDL_INIT_AUDIO) with driver='%s'", audioDriver);
181 SDLTest_AssertCheck(result == true, "Validate result value; expected: true got: %d", result);
182
183 /* Set spec */
184 SDL_zero(desired);
185 switch (j) {
186 case 0:
187 /* Set standard desired spec */
188 desired.freq = 22050;
189 desired.format = SDL_AUDIO_S16;
190 desired.channels = 2;
191 break;
192
193 case 1:
194 /* Set custom desired spec */
195 desired.freq = 48000;
196 desired.format = SDL_AUDIO_F32;
197 desired.channels = 2;
198 break;
199 }
200
201 /* Call Open (maybe multiple times) */
202 for (k = 0; k <= j; k++) {
203 result = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &desired);
204 if (k == 0) {
205 g_audio_id = result;
206 }
207 SDLTest_AssertPass("Call to SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, desired_spec_%d), call %d", j, k + 1);
208 SDLTest_AssertCheck(result > 0, "Verify return value; expected: > 0, got: %d", result);
209 }
210
211 /* Call Close (maybe multiple times) */
212 for (k = 0; k <= j; k++) {
213 SDL_CloseAudioDevice(g_audio_id);
214 SDLTest_AssertPass("Call to SDL_CloseAudioDevice(), call %d", k + 1);
215 }
216
217 /* Call Quit (maybe multiple times) */
218 for (k = 0; k <= j; k++) {
219 SDL_QuitSubSystem(SDL_INIT_AUDIO);
220 SDLTest_AssertPass("Call to SDL_QuitSubSystem(SDL_INIT_AUDIO), call %d", k + 1);
221 }
222
223 } /* spec loop */
224 } /* driver loop */
225
226 /* Restart audio again */
227 audioSetUp(NULL);
228
229 return TEST_COMPLETED;
230}
231
232/**
233 * Pause and unpause audio
234 *
235 * \sa SDL_PauseAudioDevice
236 * \sa SDL_PlayAudioDevice
237 */
238static int SDLCALL audio_pauseUnpauseAudio(void *arg)
239{
240 int iMax;
241 int i, j /*, k, l*/;
242 int result;
243 const char *audioDriver;
244 SDL_AudioSpec desired;
245 const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DRIVER);
246
247 /* Stop SDL audio subsystem */
248 SDL_QuitSubSystem(SDL_INIT_AUDIO);
249 SDLTest_AssertPass("Call to SDL_QuitSubSystem(SDL_INIT_AUDIO)");
250
251 /* Loop over all available audio drivers */
252 iMax = SDL_GetNumAudioDrivers();
253 SDLTest_AssertPass("Call to SDL_GetNumAudioDrivers()");
254 SDLTest_AssertCheck(iMax > 0, "Validate number of audio drivers; expected: >0 got: %d", iMax);
255 for (i = 0; i < iMax; i++) {
256 audioDriver = SDL_GetAudioDriver(i);
257 SDLTest_AssertPass("Call to SDL_GetAudioDriver(%d)", i);
258 SDLTest_Assert(audioDriver != NULL, "Audio driver name is not NULL");
259 SDLTest_AssertCheck(audioDriver[0] != '\0', "Audio driver name is not empty; got: %s", audioDriver); /* NOLINT(clang-analyzer-core.NullDereference): Checked for NULL above */
260
261 if (hint && SDL_strcmp(audioDriver, hint) != 0) {
262 continue;
263 }
264
265 /* Change specs */
266 for (j = 0; j < 2; j++) {
267
268 /* Call Init */
269 SDL_SetHint(SDL_HINT_AUDIO_DRIVER, audioDriver);
270 result = SDL_InitSubSystem(SDL_INIT_AUDIO);
271 SDLTest_AssertPass("Call to SDL_InitSubSystem(SDL_INIT_AUDIO) with driver='%s'", audioDriver);
272 SDLTest_AssertCheck(result == true, "Validate result value; expected: true got: %d", result);
273
274 /* Set spec */
275 SDL_zero(desired);
276 switch (j) {
277 case 0:
278 /* Set standard desired spec */
279 desired.freq = 22050;
280 desired.format = SDL_AUDIO_S16;
281 desired.channels = 2;
282 break;
283
284 case 1:
285 /* Set custom desired spec */
286 desired.freq = 48000;
287 desired.format = SDL_AUDIO_F32;
288 desired.channels = 2;
289 break;
290 }
291
292 /* Call Open */
293 g_audio_id = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &desired);
294 result = g_audio_id;
295 SDLTest_AssertPass("Call to SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, desired_spec_%d)", j);
296 SDLTest_AssertCheck(result > 0, "Verify return value; expected > 0 got: %d", result);
297
298#if 0 /* !!! FIXME: maybe update this? */
299 /* Start and stop audio multiple times */
300 for (l = 0; l < 3; l++) {
301 SDLTest_Log("Pause/Unpause iteration: %d", l + 1);
302
303 /* Reset callback counters */
304 g_audio_testCallbackCounter = 0;
305 g_audio_testCallbackLength = 0;
306
307 /* Un-pause audio to start playing (maybe multiple times) */
308 for (k = 0; k <= j; k++) {
309 SDL_PlayAudioDevice(g_audio_id);
310 SDLTest_AssertPass("Call to SDL_PlayAudioDevice(g_audio_id), call %d", k + 1);
311 }
312
313 /* Wait for callback */
314 int totalDelay = 0;
315 do {
316 SDL_Delay(10);
317 totalDelay += 10;
318 } while (g_audio_testCallbackCounter == 0 && totalDelay < 1000);
319 SDLTest_AssertCheck(g_audio_testCallbackCounter > 0, "Verify callback counter; expected: >0 got: %d", g_audio_testCallbackCounter);
320 SDLTest_AssertCheck(g_audio_testCallbackLength > 0, "Verify callback length; expected: >0 got: %d", g_audio_testCallbackLength);
321
322 /* Pause audio to stop playing (maybe multiple times) */
323 for (k = 0; k <= j; k++) {
324 const int pause_on = (k == 0) ? 1 : SDLTest_RandomIntegerInRange(99, 9999);
325 if (pause_on) {
326 SDL_PauseAudioDevice(g_audio_id);
327 SDLTest_AssertPass("Call to SDL_PauseAudioDevice(g_audio_id), call %d", k + 1);
328 } else {
329 SDL_PlayAudioDevice(g_audio_id);
330 SDLTest_AssertPass("Call to SDL_PlayAudioDevice(g_audio_id), call %d", k + 1);
331 }
332 }
333
334 /* Ensure callback is not called again */
335 const int originalCounter = g_audio_testCallbackCounter;
336 SDL_Delay(totalDelay + 10);
337 SDLTest_AssertCheck(originalCounter == g_audio_testCallbackCounter, "Verify callback counter; expected: %d, got: %d", originalCounter, g_audio_testCallbackCounter);
338 }
339#endif
340
341 /* Call Close */
342 SDL_CloseAudioDevice(g_audio_id);
343 SDLTest_AssertPass("Call to SDL_CloseAudioDevice()");
344
345 /* Call Quit */
346 SDL_QuitSubSystem(SDL_INIT_AUDIO);
347 SDLTest_AssertPass("Call to SDL_QuitSubSystem(SDL_INIT_AUDIO)");
348
349 } /* spec loop */
350 } /* driver loop */
351
352 /* Restart audio again */
353 audioSetUp(NULL);
354
355 return TEST_COMPLETED;
356}
357
358/**
359 * Enumerate and name available audio devices (playback and recording).
360 *
361 * \sa SDL_GetNumAudioDevices
362 * \sa SDL_GetAudioDeviceName
363 */
364static int SDLCALL audio_enumerateAndNameAudioDevices(void *arg)
365{
366 int t;
367 int i, n;
368 const char *name;
369 SDL_AudioDeviceID *devices;
370
371 /* Iterate over types: t=0 playback device, t=1 recording device */
372 for (t = 0; t < 2; t++) {
373 /* Get number of devices. */
374 devices = (t) ? SDL_GetAudioRecordingDevices(&n) : SDL_GetAudioPlaybackDevices(&n);
375 SDLTest_AssertPass("Call to SDL_GetAudio%sDevices(%i)", (t) ? "Recording" : "Playback", t);
376 SDLTest_Log("Number of %s devices < 0, reported as %i", (t) ? "recording" : "playback", n);
377 SDLTest_AssertCheck(n >= 0, "Validate result is >= 0, got: %i", n);
378
379 /* List devices. */
380 if (n > 0) {
381 SDLTest_AssertCheck(devices != NULL, "Validate devices is not NULL if n > 0");
382 for (i = 0; i < n; i++) {
383 name = SDL_GetAudioDeviceName(devices[i]);
384 SDLTest_AssertPass("Call to SDL_GetAudioDeviceName(%i)", i);
385 SDLTest_AssertCheck(name != NULL, "Verify result from SDL_GetAudioDeviceName(%i) is not NULL", i);
386 if (name != NULL) {
387 SDLTest_AssertCheck(name[0] != '\0', "verify result from SDL_GetAudioDeviceName(%i) is not empty, got: '%s'", i, name);
388 }
389 }
390 }
391 SDL_free(devices);
392 }
393
394 return TEST_COMPLETED;
395}
396
397/**
398 * Negative tests around enumeration and naming of audio devices.
399 *
400 * \sa SDL_GetNumAudioDevices
401 * \sa SDL_GetAudioDeviceName
402 */
403static int SDLCALL audio_enumerateAndNameAudioDevicesNegativeTests(void *arg)
404{
405 return TEST_COMPLETED; /* nothing in here atm since these interfaces changed in SDL3. */
406}
407
408/**
409 * Checks available audio driver names.
410 *
411 * \sa SDL_GetNumAudioDrivers
412 * \sa SDL_GetAudioDriver
413 */
414static int SDLCALL audio_printAudioDrivers(void *arg)
415{
416 int i, n;
417 const char *name;
418
419 /* Get number of drivers */
420 n = SDL_GetNumAudioDrivers();
421 SDLTest_AssertPass("Call to SDL_GetNumAudioDrivers()");
422 SDLTest_AssertCheck(n >= 0, "Verify number of audio drivers >= 0, got: %i", n);
423
424 /* List drivers. */
425 if (n > 0) {
426 for (i = 0; i < n; i++) {
427 name = SDL_GetAudioDriver(i);
428 SDLTest_AssertPass("Call to SDL_GetAudioDriver(%i)", i);
429 SDLTest_AssertCheck(name != NULL, "Verify returned name is not NULL");
430 if (name != NULL) {
431 SDLTest_AssertCheck(name[0] != '\0', "Verify returned name is not empty, got: '%s'", name);
432 }
433 }
434 }
435
436 return TEST_COMPLETED;
437}
438
439/**
440 * Checks current audio driver name with initialized audio.
441 *
442 * \sa SDL_GetCurrentAudioDriver
443 */
444static int SDLCALL audio_printCurrentAudioDriver(void *arg)
445{
446 /* Check current audio driver */
447 const char *name = SDL_GetCurrentAudioDriver();
448 SDLTest_AssertPass("Call to SDL_GetCurrentAudioDriver()");
449 SDLTest_AssertCheck(name != NULL, "Verify returned name is not NULL");
450 if (name != NULL) {
451 SDLTest_AssertCheck(name[0] != '\0', "Verify returned name is not empty, got: '%s'", name);
452 }
453
454 return TEST_COMPLETED;
455}
456
457/* Definition of all formats, channels, and frequencies used to test audio conversions */
458static SDL_AudioFormat g_audioFormats[] = {
459 SDL_AUDIO_S8, SDL_AUDIO_U8,
460 SDL_AUDIO_S16LE, SDL_AUDIO_S16BE,
461 SDL_AUDIO_S32LE, SDL_AUDIO_S32BE,
462 SDL_AUDIO_F32LE, SDL_AUDIO_F32BE
463};
464static const char *g_audioFormatsVerbose[] = {
465 "SDL_AUDIO_S8", "SDL_AUDIO_U8",
466 "SDL_AUDIO_S16LE", "SDL_AUDIO_S16BE",
467 "SDL_AUDIO_S32LE", "SDL_AUDIO_S32BE",
468 "SDL_AUDIO_F32LE", "SDL_AUDIO_F32BE"
469};
470static SDL_AudioFormat g_invalidAudioFormats[] = {
471 (SDL_AudioFormat)SDL_DEFINE_AUDIO_FORMAT(SDL_AUDIO_MASK_SIGNED, SDL_AUDIO_MASK_BIG_ENDIAN, SDL_AUDIO_MASK_FLOAT, SDL_AUDIO_MASK_BITSIZE)
472};
473static const char *g_invalidAudioFormatsVerbose[] = {
474 "SDL_AUDIO_UNKNOWN"
475};
476static const int g_numAudioFormats = SDL_arraysize(g_audioFormats);
477static const int g_numInvalidAudioFormats = SDL_arraysize(g_invalidAudioFormats);
478static Uint8 g_audioChannels[] = { 1, 2, 4, 6 };
479static const int g_numAudioChannels = SDL_arraysize(g_audioChannels);
480static int g_audioFrequencies[] = { 11025, 22050, 44100, 48000 };
481static const int g_numAudioFrequencies = SDL_arraysize(g_audioFrequencies);
482
483/* Verify the audio formats are laid out as expected */
484SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_U8_FORMAT, SDL_AUDIO_U8 == SDL_AUDIO_BITSIZE(8));
485SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_S8_FORMAT, SDL_AUDIO_S8 == (SDL_AUDIO_BITSIZE(8) | SDL_AUDIO_MASK_SIGNED));
486SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_S16LE_FORMAT, SDL_AUDIO_S16LE == (SDL_AUDIO_BITSIZE(16) | SDL_AUDIO_MASK_SIGNED));
487SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_S16BE_FORMAT, SDL_AUDIO_S16BE == (SDL_AUDIO_S16LE | SDL_AUDIO_MASK_BIG_ENDIAN));
488SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_S32LE_FORMAT, SDL_AUDIO_S32LE == (SDL_AUDIO_BITSIZE(32) | SDL_AUDIO_MASK_SIGNED));
489SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_S32BE_FORMAT, SDL_AUDIO_S32BE == (SDL_AUDIO_S32LE | SDL_AUDIO_MASK_BIG_ENDIAN));
490SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_F32LE_FORMAT, SDL_AUDIO_F32LE == (SDL_AUDIO_BITSIZE(32) | SDL_AUDIO_MASK_FLOAT | SDL_AUDIO_MASK_SIGNED));
491SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_F32BE_FORMAT, SDL_AUDIO_F32BE == (SDL_AUDIO_F32LE | SDL_AUDIO_MASK_BIG_ENDIAN));
492
493/**
494 * Call to SDL_GetAudioFormatName
495 *
496 * \sa SDL_GetAudioFormatName
497 */
498static int SDLCALL audio_getAudioFormatName(void *arg)
499{
500 const char *error;
501 int i;
502 SDL_AudioFormat format;
503 const char *result;
504
505 /* audio formats */
506 for (i = 0; i < g_numAudioFormats; i++) {
507 format = g_audioFormats[i];
508 SDLTest_Log("Audio Format: %s (%d)", g_audioFormatsVerbose[i], format);
509
510 /* Get name of format */
511 result = SDL_GetAudioFormatName(format);
512 SDLTest_AssertPass("Call to SDL_GetAudioFormatName()");
513 SDLTest_AssertCheck(result != NULL, "Verify result is not NULL");
514 if (result != NULL) {
515 SDLTest_AssertCheck(result[0] != '\0', "Verify result is non-empty");
516 SDLTest_AssertCheck(SDL_strcmp(result, g_audioFormatsVerbose[i]) == 0,
517 "Verify result text; expected: %s, got %s", g_audioFormatsVerbose[i], result);
518 }
519 }
520
521 /* Negative cases */
522
523 /* Invalid Formats */
524 SDL_ClearError();
525 SDLTest_AssertPass("Call to SDL_ClearError()");
526 for (i = 0; i < g_numInvalidAudioFormats; i++) {
527 format = g_invalidAudioFormats[i];
528 result = SDL_GetAudioFormatName(format);
529 SDLTest_AssertPass("Call to SDL_GetAudioFormatName(%d)", format);
530 SDLTest_AssertCheck(result != NULL, "Verify result is not NULL");
531 if (result != NULL) {
532 SDLTest_AssertCheck(result[0] != '\0',
533 "Verify result is non-empty; got: %s", result);
534 SDLTest_AssertCheck(SDL_strcmp(result, g_invalidAudioFormatsVerbose[i]) == 0,
535 "Validate name is UNKNOWN, expected: '%s', got: '%s'", g_invalidAudioFormatsVerbose[i], result);
536 }
537 error = SDL_GetError();
538 SDLTest_AssertPass("Call to SDL_GetError()");
539 SDLTest_AssertCheck(error == NULL || error[0] == '\0', "Validate that error message is empty");
540 }
541
542 return TEST_COMPLETED;
543}
544
545/**
546 * Builds various audio conversion structures
547 *
548 * \sa SDL_CreateAudioStream
549 */
550static int SDLCALL audio_buildAudioStream(void *arg)
551{
552 SDL_AudioStream *stream;
553 SDL_AudioSpec spec1;
554 SDL_AudioSpec spec2;
555 int i, ii, j, jj, k, kk;
556
557 SDL_zero(spec1);
558 SDL_zero(spec2);
559
560 /* Call Quit */
561 SDL_QuitSubSystem(SDL_INIT_AUDIO);
562 SDLTest_AssertPass("Call to SDL_QuitSubSystem(SDL_INIT_AUDIO)");
563
564 /* No conversion needed */
565 spec1.format = SDL_AUDIO_S16LE;
566 spec1.channels = 2;
567 spec1.freq = 22050;
568 stream = SDL_CreateAudioStream(&spec1, &spec1);
569 SDLTest_AssertPass("Call to SDL_CreateAudioStream(spec1 ==> spec1)");
570 SDLTest_AssertCheck(stream != NULL, "Verify stream value; expected: != NULL, got: %p", stream);
571 SDL_DestroyAudioStream(stream);
572
573 /* Typical conversion */
574 spec1.format = SDL_AUDIO_S8;
575 spec1.channels = 1;
576 spec1.freq = 22050;
577 spec2.format = SDL_AUDIO_S16LE;
578 spec2.channels = 2;
579 spec2.freq = 44100;
580 stream = SDL_CreateAudioStream(&spec1, &spec2);
581 SDLTest_AssertPass("Call to SDL_CreateAudioStream(spec1 ==> spec2)");
582 SDLTest_AssertCheck(stream != NULL, "Verify stream value; expected: != NULL, got: %p", stream);
583 SDL_DestroyAudioStream(stream);
584
585 /* All source conversions with random conversion targets, allow 'null' conversions */
586 for (i = 0; i < g_numAudioFormats; i++) {
587 for (j = 0; j < g_numAudioChannels; j++) {
588 for (k = 0; k < g_numAudioFrequencies; k++) {
589 spec1.format = g_audioFormats[i];
590 spec1.channels = g_audioChannels[j];
591 spec1.freq = g_audioFrequencies[k];
592 ii = SDLTest_RandomIntegerInRange(0, g_numAudioFormats - 1);
593 jj = SDLTest_RandomIntegerInRange(0, g_numAudioChannels - 1);
594 kk = SDLTest_RandomIntegerInRange(0, g_numAudioFrequencies - 1);
595 spec2.format = g_audioFormats[ii];
596 spec2.channels = g_audioChannels[jj];
597 spec2.freq = g_audioFrequencies[kk];
598 stream = SDL_CreateAudioStream(&spec1, &spec2);
599
600 SDLTest_AssertPass("Call to SDL_CreateAudioStream(format[%i]=%s(%i),channels[%i]=%i,freq[%i]=%i ==> format[%i]=%s(%i),channels[%i]=%i,freq[%i]=%i)",
601 i, g_audioFormatsVerbose[i], spec1.format, j, spec1.channels, k, spec1.freq, ii, g_audioFormatsVerbose[ii], spec2.format, jj, spec2.channels, kk, spec2.freq);
602 SDLTest_AssertCheck(stream != NULL, "Verify stream value; expected: != NULL, got: %p", stream);
603 if (stream == NULL) {
604 SDLTest_LogError("%s", SDL_GetError());
605 }
606 SDL_DestroyAudioStream(stream);
607 }
608 }
609 }
610
611 /* Restart audio again */
612 audioSetUp(NULL);
613
614 return TEST_COMPLETED;
615}
616
617/**
618 * Checks calls with invalid input to SDL_CreateAudioStream
619 *
620 * \sa SDL_CreateAudioStream
621 */
622static int SDLCALL audio_buildAudioStreamNegative(void *arg)
623{
624 const char *error;
625 SDL_AudioStream *stream;
626 SDL_AudioSpec spec1;
627 SDL_AudioSpec spec2;
628 int i;
629 char message[256];
630
631 SDL_zero(spec1);
632 SDL_zero(spec2);
633
634 /* Valid format */
635 spec1.format = SDL_AUDIO_S8;
636 spec1.channels = 1;
637 spec1.freq = 22050;
638 spec2.format = SDL_AUDIO_S16LE;
639 spec2.channels = 2;
640 spec2.freq = 44100;
641
642 SDL_ClearError();
643 SDLTest_AssertPass("Call to SDL_ClearError()");
644
645 /* Invalid conversions */
646 for (i = 1; i < 64; i++) {
647 /* Valid format to start with */
648 spec1.format = SDL_AUDIO_S8;
649 spec1.channels = 1;
650 spec1.freq = 22050;
651 spec2.format = SDL_AUDIO_S16LE;
652 spec2.channels = 2;
653 spec2.freq = 44100;
654
655 SDL_ClearError();
656 SDLTest_AssertPass("Call to SDL_ClearError()");
657
658 /* Set various invalid format inputs */
659 SDL_strlcpy(message, "Invalid: ", 256);
660 if (i & 1) {
661 SDL_strlcat(message, " spec1.format", 256);
662 spec1.format = 0;
663 }
664 if (i & 2) {
665 SDL_strlcat(message, " spec1.channels", 256);
666 spec1.channels = 0;
667 }
668 if (i & 4) {
669 SDL_strlcat(message, " spec1.freq", 256);
670 spec1.freq = 0;
671 }
672 if (i & 8) {
673 SDL_strlcat(message, " spec2.format", 256);
674 spec2.format = 0;
675 }
676 if (i & 16) {
677 SDL_strlcat(message, " spec2.channels", 256);
678 spec2.channels = 0;
679 }
680 if (i & 32) {
681 SDL_strlcat(message, " spec2.freq", 256);
682 spec2.freq = 0;
683 }
684 SDLTest_Log("%s", message);
685 stream = SDL_CreateAudioStream(&spec1, &spec2);
686 SDLTest_AssertPass("Call to SDL_CreateAudioStream(spec1 ==> spec2)");
687 SDLTest_AssertCheck(stream == NULL, "Verify stream value; expected: NULL, got: %p", stream);
688 error = SDL_GetError();
689 SDLTest_AssertPass("Call to SDL_GetError()");
690 SDLTest_AssertCheck(error != NULL && error[0] != '\0', "Validate that error message was not NULL or empty");
691 SDL_DestroyAudioStream(stream);
692 }
693
694 SDL_ClearError();
695 SDLTest_AssertPass("Call to SDL_ClearError()");
696
697 return TEST_COMPLETED;
698}
699
700/**
701 * Checks current audio status.
702 *
703 * \sa SDL_GetAudioDeviceStatus
704 */
705static int SDLCALL audio_getAudioStatus(void *arg)
706{
707 return TEST_COMPLETED; /* no longer a thing in SDL3. */
708}
709
710/**
711 * Opens, checks current audio status, and closes a device.
712 *
713 * \sa SDL_GetAudioStatus
714 */
715static int SDLCALL audio_openCloseAndGetAudioStatus(void *arg)
716{
717 return TEST_COMPLETED; /* not a thing in SDL3. */
718}
719
720/**
721 * Locks and unlocks open audio device.
722 *
723 * \sa SDL_LockAudioDevice
724 * \sa SDL_UnlockAudioDevice
725 */
726static int SDLCALL audio_lockUnlockOpenAudioDevice(void *arg)
727{
728 return TEST_COMPLETED; /* not a thing in SDL3 */
729}
730
731/**
732 * Convert audio using various conversion structures
733 *
734 * \sa SDL_CreateAudioStream
735 */
736static int SDLCALL audio_convertAudio(void *arg)
737{
738 SDL_AudioStream *stream;
739 SDL_AudioSpec spec1;
740 SDL_AudioSpec spec2;
741 int c;
742 char message[128];
743 int i, ii, j, jj, k, kk;
744
745 SDL_zero(spec1);
746 SDL_zero(spec2);
747
748 /* Iterate over bitmask that determines which parameters are modified in the conversion */
749 for (c = 1; c < 8; c++) {
750 SDL_strlcpy(message, "Changing:", 128);
751 if (c & 1) {
752 SDL_strlcat(message, " Format", 128);
753 }
754 if (c & 2) {
755 SDL_strlcat(message, " Channels", 128);
756 }
757 if (c & 4) {
758 SDL_strlcat(message, " Frequencies", 128);
759 }
760 SDLTest_Log("%s", message);
761 /* All source conversions with random conversion targets */
762 for (i = 0; i < g_numAudioFormats; i++) {
763 for (j = 0; j < g_numAudioChannels; j++) {
764 for (k = 0; k < g_numAudioFrequencies; k++) {
765 spec1.format = g_audioFormats[i];
766 spec1.channels = g_audioChannels[j];
767 spec1.freq = g_audioFrequencies[k];
768
769 /* Ensure we have a different target format */
770 do {
771 if (c & 1) {
772 ii = SDLTest_RandomIntegerInRange(0, g_numAudioFormats - 1);
773 } else {
774 ii = 1;
775 }
776 if (c & 2) {
777 jj = SDLTest_RandomIntegerInRange(0, g_numAudioChannels - 1);
778 } else {
779 jj = j;
780 }
781 if (c & 4) {
782 kk = SDLTest_RandomIntegerInRange(0, g_numAudioFrequencies - 1);
783 } else {
784 kk = k;
785 }
786 } while ((i == ii) && (j == jj) && (k == kk));
787 spec2.format = g_audioFormats[ii];
788 spec2.channels = g_audioChannels[jj];
789 spec2.freq = g_audioFrequencies[kk];
790
791 stream = SDL_CreateAudioStream(&spec1, &spec2);
792 SDLTest_AssertPass("Call to SDL_CreateAudioStream(format[%i]=%s(%i),channels[%i]=%i,freq[%i]=%i ==> format[%i]=%s(%i),channels[%i]=%i,freq[%i]=%i)",
793 i, g_audioFormatsVerbose[i], spec1.format, j, spec1.channels, k, spec1.freq, ii, g_audioFormatsVerbose[ii], spec2.format, jj, spec2.channels, kk, spec2.freq);
794 SDLTest_AssertCheck(stream != NULL, "Verify stream value; expected: != NULL, got: %p", stream);
795 if (stream == NULL) {
796 SDLTest_LogError("%s", SDL_GetError());
797 } else {
798 Uint8 *dst_buf = NULL, *src_buf = NULL;
799 int dst_len = 0, src_len = 0, real_dst_len = 0;
800 int l = 64, m;
801 int src_framesize, dst_framesize;
802 int src_silence, dst_silence;
803
804 src_framesize = SDL_AUDIO_FRAMESIZE(spec1);
805 dst_framesize = SDL_AUDIO_FRAMESIZE(spec2);
806
807 src_len = l * src_framesize;
808 SDLTest_Log("Creating dummy sample buffer of %i length (%i bytes)", l, src_len);
809 src_buf = (Uint8 *)SDL_malloc(src_len);
810 SDLTest_AssertCheck(src_buf != NULL, "Check src data buffer to convert is not NULL");
811 if (src_buf == NULL) {
812 SDL_DestroyAudioStream(stream);
813 return TEST_ABORTED;
814 }
815
816 src_silence = SDL_GetSilenceValueForFormat(spec1.format);
817 SDL_memset(src_buf, src_silence, src_len);
818
819 dst_len = ((int)((((Sint64)l * spec2.freq) - 1) / spec1.freq) + 1) * dst_framesize;
820 dst_buf = (Uint8 *)SDL_malloc(dst_len);
821 SDLTest_AssertCheck(dst_buf != NULL, "Check dst data buffer to convert is not NULL");
822 if (dst_buf == NULL) {
823 SDL_DestroyAudioStream(stream);
824 SDL_free(src_buf);
825 return TEST_ABORTED;
826 }
827
828 real_dst_len = SDL_GetAudioStreamAvailable(stream);
829 SDLTest_AssertCheck(0 == real_dst_len, "Verify available (pre-put); expected: %i; got: %i", 0, real_dst_len);
830
831 /* Run the audio converter */
832 if (!SDL_PutAudioStreamData(stream, src_buf, src_len) ||
833 !SDL_FlushAudioStream(stream)) {
834 SDL_DestroyAudioStream(stream);
835 SDL_free(src_buf);
836 SDL_free(dst_buf);
837 return TEST_ABORTED;
838 }
839
840 real_dst_len = SDL_GetAudioStreamAvailable(stream);
841 SDLTest_AssertCheck(dst_len == real_dst_len, "Verify available (post-put); expected: %i; got: %i", dst_len, real_dst_len);
842
843 real_dst_len = SDL_GetAudioStreamData(stream, dst_buf, dst_len);
844 SDLTest_AssertCheck(dst_len == real_dst_len, "Verify result value; expected: %i; got: %i", dst_len, real_dst_len);
845 if (dst_len != real_dst_len) {
846 SDL_DestroyAudioStream(stream);
847 SDL_free(src_buf);
848 SDL_free(dst_buf);
849 return TEST_ABORTED;
850 }
851
852 real_dst_len = SDL_GetAudioStreamAvailable(stream);
853 SDLTest_AssertCheck(0 == real_dst_len, "Verify available (post-get); expected: %i; got: %i", 0, real_dst_len);
854
855 dst_silence = SDL_GetSilenceValueForFormat(spec2.format);
856
857 for (m = 0; m < dst_len; ++m) {
858 if (dst_buf[m] != dst_silence) {
859 SDLTest_LogError("Output buffer is not silent");
860 SDL_DestroyAudioStream(stream);
861 SDL_free(src_buf);
862 SDL_free(dst_buf);
863 return TEST_ABORTED;
864 }
865 }
866
867 SDL_DestroyAudioStream(stream);
868 /* Free converted buffer */
869 SDL_free(src_buf);
870 SDL_free(dst_buf);
871 }
872 }
873 }
874 }
875 }
876
877 return TEST_COMPLETED;
878}
879
880/**
881 * Opens, checks current connected status, and closes a device.
882 *
883 * \sa SDL_AudioDeviceConnected
884 */
885static int SDLCALL audio_openCloseAudioDeviceConnected(void *arg)
886{
887 return TEST_COMPLETED; /* not a thing in SDL3. */
888}
889
890static double sine_wave_sample(const Sint64 idx, const Sint64 rate, const Sint64 freq, const double phase)
891{
892 /* Using integer modulo to avoid precision loss caused by large floating
893 * point numbers. Sint64 is needed for the large integer multiplication.
894 * The integers are assumed to be non-negative so that modulo is always
895 * non-negative.
896 * sin(i / rate * freq * 2 * PI + phase)
897 * = sin(mod(i / rate * freq, 1) * 2 * PI + phase)
898 * = sin(mod(i * freq, rate) / rate * 2 * PI + phase) */
899 return SDL_sin(((double)(idx * freq % rate)) / ((double)rate) * (SDL_PI_D * 2) + phase);
900}
901
902/* Split the data into randomly sized chunks */
903static int put_audio_data_split(SDL_AudioStream* stream, const void* buf, int len)
904{
905 SDL_AudioSpec spec;
906 int frame_size;
907 int ret = SDL_GetAudioStreamFormat(stream, &spec, NULL);
908
909 if (!ret) {
910 return -1;
911 }
912
913 frame_size = SDL_AUDIO_FRAMESIZE(spec);
914
915 while (len > 0) {
916 int n = SDLTest_RandomIntegerInRange(1, 10000) * frame_size;
917 n = SDL_min(n, len);
918 ret = SDL_PutAudioStreamData(stream, buf, n);
919
920 if (!ret) {
921 return -1;
922 }
923
924 buf = ((const Uint8*) buf) + n;
925 len -= n;
926 }
927
928 return 0;
929}
930
931/* Read the data in randomly sized chunks */
932static int get_audio_data_split(SDL_AudioStream* stream, void* buf, int len) {
933 SDL_AudioSpec spec;
934 int frame_size;
935 int ret = SDL_GetAudioStreamFormat(stream, NULL, &spec);
936 int total = 0;
937
938 if (!ret) {
939 return -1;
940 }
941
942 frame_size = SDL_AUDIO_FRAMESIZE(spec);
943
944 while (len > 0) {
945 int n = SDLTest_RandomIntegerInRange(1, 10000) * frame_size;
946 n = SDL_min(n, len);
947
948 ret = SDL_GetAudioStreamData(stream, buf, n);
949
950 if (ret <= 0) {
951 return total ? total : -1;
952 }
953
954 buf = ((Uint8*) buf) + ret;
955 total += ret;
956 len -= ret;
957 }
958
959 return total;
960}
961
962/* Convert the data in chunks, putting/getting randomly sized chunks until finished */
963static int convert_audio_chunks(SDL_AudioStream* stream, const void* src, int srclen, void* dst, int dstlen)
964{
965 SDL_AudioSpec src_spec, dst_spec;
966 int src_frame_size, dst_frame_size;
967 int total_in = 0, total_out = 0;
968 int ret = SDL_GetAudioStreamFormat(stream, &src_spec, &dst_spec);
969
970 if (!ret) {
971 return -1;
972 }
973
974 src_frame_size = SDL_AUDIO_FRAMESIZE(src_spec);
975 dst_frame_size = SDL_AUDIO_FRAMESIZE(dst_spec);
976
977 while ((total_in < srclen) || (total_out < dstlen)) {
978 /* Make sure we put in more than the padding frames so we get non-zero output */
979 const int RESAMPLER_MAX_PADDING_FRAMES = 7; /* Should match RESAMPLER_MAX_PADDING_FRAMES in SDL */
980 int to_put = SDLTest_RandomIntegerInRange(RESAMPLER_MAX_PADDING_FRAMES + 1, 40000) * src_frame_size;
981 int to_get = SDLTest_RandomIntegerInRange(1, (int)((40000.0f * dst_spec.freq) / src_spec.freq)) * dst_frame_size;
982 to_put = SDL_min(to_put, srclen - total_in);
983 to_get = SDL_min(to_get, dstlen - total_out);
984
985 if (to_put)
986 {
987 ret = put_audio_data_split(stream, (const Uint8*)(src) + total_in, to_put);
988
989 if (ret < 0) {
990 return total_out ? total_out : ret;
991 }
992
993 total_in += to_put;
994
995 if (total_in == srclen) {
996 ret = SDL_FlushAudioStream(stream);
997
998 if (!ret) {
999 return total_out ? total_out : -1;
1000 }
1001 }
1002 }
1003
1004 if (to_get)
1005 {
1006 ret = get_audio_data_split(stream, (Uint8*)(dst) + total_out, to_get);
1007
1008 if ((ret == 0) && (total_in == srclen)) {
1009 ret = -1;
1010 }
1011
1012 if (ret < 0) {
1013 return total_out ? total_out : ret;
1014 }
1015
1016 total_out += ret;
1017 }
1018 }
1019
1020 return total_out;
1021}
1022
1023/**
1024 * Check signal-to-noise ratio and maximum error of audio resampling.
1025 *
1026 * \sa https://wiki.libsdl.org/SDL_CreateAudioStream
1027 * \sa https://wiki.libsdl.org/SDL_DestroyAudioStream
1028 * \sa https://wiki.libsdl.org/SDL_PutAudioStreamData
1029 * \sa https://wiki.libsdl.org/SDL_FlushAudioStream
1030 * \sa https://wiki.libsdl.org/SDL_GetAudioStreamData
1031 */
1032static int SDLCALL audio_resampleLoss(void *arg)
1033{
1034 /* Note: always test long input time (>= 5s from experience) in some test
1035 * cases because an improper implementation may suffer from low resampling
1036 * precision with long input due to e.g. doing subtraction with large floats. */
1037 struct test_spec_t {
1038 int time;
1039 int freq;
1040 double phase;
1041 int rate_in;
1042 int rate_out;
1043 double signal_to_noise;
1044 double max_error;
1045 } test_specs[] = {
1046 { 50, 440, 0, 44100, 48000, 80, 0.0010 },
1047 { 50, 5000, SDL_PI_D / 2, 20000, 10000, 999, 0.0001 },
1048 { 50, 440, 0, 22050, 96000, 79, 0.0120 },
1049 { 50, 440, 0, 96000, 22050, 80, 0.0002 },
1050 { 0 }
1051 };
1052
1053 int spec_idx = 0;
1054 int min_channels = 1;
1055 int max_channels = 1 /*8*/;
1056 int num_channels = min_channels;
1057
1058 for (spec_idx = 0; test_specs[spec_idx].time > 0;) {
1059 const struct test_spec_t *spec = &test_specs[spec_idx];
1060 const int frames_in = spec->time * spec->rate_in;
1061 const int frames_target = spec->time * spec->rate_out;
1062 const int len_in = (frames_in * num_channels) * (int)sizeof(float);
1063 const int len_target = (frames_target * num_channels) * (int)sizeof(float);
1064 const int max_target = len_target * 2;
1065
1066 SDL_AudioSpec tmpspec1, tmpspec2;
1067 Uint64 tick_beg = 0;
1068 Uint64 tick_end = 0;
1069 int i = 0;
1070 int j = 0;
1071 SDL_AudioStream *stream = NULL;
1072 float *buf_in = NULL;
1073 float *buf_out = NULL;
1074 int len_out = 0;
1075 double max_error = 0;
1076 double sum_squared_error = 0;
1077 double sum_squared_value = 0;
1078 double signal_to_noise = 0;
1079
1080 SDL_zero(tmpspec1);
1081 SDL_zero(tmpspec2);
1082
1083 SDLTest_AssertPass("Test resampling of %i s %i Hz %f phase sine wave from sampling rate of %i Hz to %i Hz",
1084 spec->time, spec->freq, spec->phase, spec->rate_in, spec->rate_out);
1085
1086 tmpspec1.format = SDL_AUDIO_F32;
1087 tmpspec1.channels = num_channels;
1088 tmpspec1.freq = spec->rate_in;
1089 tmpspec2.format = SDL_AUDIO_F32;
1090 tmpspec2.channels = num_channels;
1091 tmpspec2.freq = spec->rate_out;
1092 stream = SDL_CreateAudioStream(&tmpspec1, &tmpspec2);
1093 SDLTest_AssertPass("Call to SDL_CreateAudioStream(SDL_AUDIO_F32, %i, %i, SDL_AUDIO_F32, %i, %i)", num_channels, spec->rate_in, num_channels, spec->rate_out);
1094 SDLTest_AssertCheck(stream != NULL, "Expected SDL_CreateAudioStream to succeed.");
1095 if (stream == NULL) {
1096 return TEST_ABORTED;
1097 }
1098
1099 buf_in = (float *)SDL_malloc(len_in);
1100 SDLTest_AssertCheck(buf_in != NULL, "Expected input buffer to be created.");
1101 if (buf_in == NULL) {
1102 SDL_DestroyAudioStream(stream);
1103 return TEST_ABORTED;
1104 }
1105
1106 for (i = 0; i < frames_in; ++i) {
1107 float f = (float)sine_wave_sample(i, spec->rate_in, spec->freq, spec->phase);
1108 for (j = 0; j < num_channels; ++j) {
1109 *(buf_in + (i * num_channels) + j) = f;
1110 }
1111 }
1112
1113 tick_beg = SDL_GetPerformanceCounter();
1114
1115 buf_out = (float *)SDL_malloc(max_target);
1116 SDLTest_AssertCheck(buf_out != NULL, "Expected output buffer to be created.");
1117 if (buf_out == NULL) {
1118 SDL_DestroyAudioStream(stream);
1119 SDL_free(buf_in);
1120 return TEST_ABORTED;
1121 }
1122
1123 len_out = convert_audio_chunks(stream, buf_in, len_in, buf_out, max_target);
1124 SDLTest_AssertPass("Call to convert_audio_chunks(stream, buf_in, %i, buf_out, %i)", len_in, len_target);
1125 SDLTest_AssertCheck(len_out == len_target, "Expected output length to be %i, got %i.",
1126 len_target, len_out);
1127 SDL_free(buf_in);
1128 if (len_out != len_target) {
1129 SDL_DestroyAudioStream(stream);
1130 SDL_free(buf_out);
1131 return TEST_ABORTED;
1132 }
1133
1134 tick_end = SDL_GetPerformanceCounter();
1135 SDLTest_Log("Resampling used %f seconds.", ((double)(tick_end - tick_beg)) / SDL_GetPerformanceFrequency());
1136
1137 for (i = 0; i < frames_target; ++i) {
1138 const double target = sine_wave_sample(i, spec->rate_out, spec->freq, spec->phase);
1139 for (j = 0; j < num_channels; ++j) {
1140 const float output = *(buf_out + (i * num_channels) + j);
1141 const double error = SDL_fabs(target - output);
1142 max_error = SDL_max(max_error, error);
1143 sum_squared_error += error * error;
1144 sum_squared_value += target * target;
1145 }
1146 }
1147 SDL_DestroyAudioStream(stream);
1148 SDL_free(buf_out);
1149 signal_to_noise = 10 * SDL_log10(sum_squared_value / sum_squared_error); /* decibel */
1150 SDLTest_AssertCheck(ISFINITE(sum_squared_value), "Sum of squared target should be finite.");
1151 SDLTest_AssertCheck(ISFINITE(sum_squared_error), "Sum of squared error should be finite.");
1152 /* Infinity is theoretically possible when there is very little to no noise */
1153 SDLTest_AssertCheck(!ISNAN(signal_to_noise), "Signal-to-noise ratio should not be NaN.");
1154 SDLTest_AssertCheck(ISFINITE(max_error), "Maximum conversion error should be finite.");
1155 SDLTest_AssertCheck(signal_to_noise >= spec->signal_to_noise, "Conversion signal-to-noise ratio %f dB should be no less than %f dB.",
1156 signal_to_noise, spec->signal_to_noise);
1157 SDLTest_AssertCheck(max_error <= spec->max_error, "Maximum conversion error %f should be no more than %f.",
1158 max_error, spec->max_error);
1159
1160 if (++num_channels > max_channels) {
1161 num_channels = min_channels;
1162 ++spec_idx;
1163 }
1164 }
1165
1166 return TEST_COMPLETED;
1167}
1168
1169/**
1170 * Check accuracy converting between audio formats.
1171 *
1172 * \sa SDL_ConvertAudioSamples
1173 */
1174static int SDLCALL audio_convertAccuracy(void *arg)
1175{
1176 static SDL_AudioFormat formats[] = { SDL_AUDIO_S8, SDL_AUDIO_U8, SDL_AUDIO_S16, SDL_AUDIO_S32 };
1177 static const char* format_names[] = { "S8", "U8", "S16", "S32" };
1178
1179 int src_num = 65537 + 2048 + 48 + 256 + 100000;
1180 int src_len = src_num * sizeof(float);
1181 float* src_data = SDL_malloc(src_len);
1182 int i, j;
1183
1184 SDLTest_AssertCheck(src_data != NULL, "Expected source buffer to be created.");
1185 if (src_data == NULL) {
1186 return TEST_ABORTED;
1187 }
1188
1189 j = 0;
1190
1191 /* Generate a uniform range of floats between [-1.0, 1.0] */
1192 for (i = 0; i < 65537; ++i) {
1193 src_data[j++] = ((float)i - 32768.0f) / 32768.0f;
1194 }
1195
1196 /* Generate floats close to 1.0 */
1197 const float max_val = 16777216.0f;
1198
1199 for (i = 0; i < 1024; ++i) {
1200 float f = (max_val + (float)(512 - i)) / max_val;
1201 src_data[j++] = f;
1202 src_data[j++] = -f;
1203 }
1204
1205 for (i = 0; i < 24; ++i) {
1206 float f = (max_val + (float)(3u << i)) / max_val;
1207 src_data[j++] = f;
1208 src_data[j++] = -f;
1209 }
1210
1211 /* Generate floats far outside the [-1.0, 1.0] range */
1212 for (i = 0; i < 128; ++i) {
1213 float f = 2.0f + (float) i;
1214 src_data[j++] = f;
1215 src_data[j++] = -f;
1216 }
1217
1218 /* Fill the rest with random floats between [-1.0, 1.0] */
1219 for (i = 0; i < 100000; ++i) {
1220 src_data[j++] = SDLTest_RandomSint32() / 2147483648.0f;
1221 }
1222
1223 /* Shuffle the data for good measure */
1224 for (i = src_num - 1; i > 0; --i) {
1225 float f = src_data[i];
1226 j = SDLTest_RandomIntegerInRange(0, i);
1227 src_data[i] = src_data[j];
1228 src_data[j] = f;
1229 }
1230
1231 for (i = 0; i < SDL_arraysize(formats); ++i) {
1232 SDL_AudioSpec src_spec, tmp_spec;
1233 Uint64 convert_begin, convert_end;
1234 Uint8 *tmp_data, *dst_data;
1235 int tmp_len, dst_len;
1236 int ret;
1237
1238 SDL_zero(src_spec);
1239 SDL_zero(tmp_spec);
1240
1241 SDL_AudioFormat format = formats[i];
1242 const char* format_name = format_names[i];
1243
1244 /* Formats with > 23 bits can represent every value exactly */
1245 float min_delta = 1.0f;
1246 float max_delta = -1.0f;
1247
1248 /* Subtract 1 bit to account for sign */
1249 int bits = SDL_AUDIO_BITSIZE(format) - 1;
1250 float target_max_delta = (bits > 23) ? 0.0f : (1.0f / (float)(1 << bits));
1251 float target_min_delta = -target_max_delta;
1252
1253 src_spec.format = SDL_AUDIO_F32;
1254 src_spec.channels = 1;
1255 src_spec.freq = 44100;
1256
1257 tmp_spec.format = format;
1258 tmp_spec.channels = 1;
1259 tmp_spec.freq = 44100;
1260
1261 convert_begin = SDL_GetPerformanceCounter();
1262
1263 tmp_data = NULL;
1264 tmp_len = 0;
1265 ret = SDL_ConvertAudioSamples(&src_spec, (const Uint8*) src_data, src_len, &tmp_spec, &tmp_data, &tmp_len);
1266 SDLTest_AssertCheck(ret == true, "Expected SDL_ConvertAudioSamples(F32->%s) to succeed", format_name);
1267 if (!ret) {
1268 SDL_free(src_data);
1269 return TEST_ABORTED;
1270 }
1271
1272 dst_data = NULL;
1273 dst_len = 0;
1274 ret = SDL_ConvertAudioSamples(&tmp_spec, tmp_data, tmp_len, &src_spec, &dst_data, &dst_len);
1275 SDLTest_AssertCheck(ret == true, "Expected SDL_ConvertAudioSamples(%s->F32) to succeed", format_name);
1276 if (!ret) {
1277 SDL_free(tmp_data);
1278 SDL_free(src_data);
1279 return TEST_ABORTED;
1280 }
1281
1282 convert_end = SDL_GetPerformanceCounter();
1283 SDLTest_Log("Conversion via %s took %f seconds.", format_name, ((double)(convert_end - convert_begin)) / SDL_GetPerformanceFrequency());
1284
1285 SDL_free(tmp_data);
1286
1287 for (j = 0; j < src_num; ++j) {
1288 float x = src_data[j];
1289 float y = ((float*)dst_data)[j];
1290 float d = SDL_clamp(x, -1.0f, 1.0f) - y;
1291
1292 min_delta = SDL_min(min_delta, d);
1293 max_delta = SDL_max(max_delta, d);
1294 }
1295
1296 SDLTest_AssertCheck(min_delta >= target_min_delta, "%s has min delta of %+f, should be >= %+f", format_name, min_delta, target_min_delta);
1297 SDLTest_AssertCheck(max_delta <= target_max_delta, "%s has max delta of %+f, should be <= %+f", format_name, max_delta, target_max_delta);
1298
1299 SDL_free(dst_data);
1300 }
1301
1302 SDL_free(src_data);
1303
1304 return TEST_COMPLETED;
1305}
1306
1307/**
1308 * Check accuracy when switching between formats
1309 *
1310 * \sa SDL_SetAudioStreamFormat
1311 */
1312static int SDLCALL audio_formatChange(void *arg)
1313{
1314 int i;
1315 SDL_AudioSpec spec1, spec2, spec3;
1316 int frames_1, frames_2, frames_3;
1317 int length_1, length_2, length_3;
1318 int result = 0;
1319 int status = TEST_ABORTED;
1320 float* buffer_1 = NULL;
1321 float* buffer_2 = NULL;
1322 float* buffer_3 = NULL;
1323 SDL_AudioStream* stream = NULL;
1324 double max_error = 0;
1325 double sum_squared_error = 0;
1326 double sum_squared_value = 0;
1327 double signal_to_noise = 0;
1328 double target_max_error = 0.02;
1329 double target_signal_to_noise = 75.0;
1330 int sine_freq = 500;
1331
1332 SDL_zero(spec1);
1333 SDL_zero(spec2);
1334 SDL_zero(spec3);
1335
1336 spec1.format = SDL_AUDIO_F32;
1337 spec1.channels = 1;
1338 spec1.freq = 20000;
1339
1340 spec2.format = SDL_AUDIO_F32;
1341 spec2.channels = 1;
1342 spec2.freq = 40000;
1343
1344 spec3.format = SDL_AUDIO_F32;
1345 spec3.channels = 1;
1346 spec3.freq = 80000;
1347
1348 frames_1 = spec1.freq;
1349 frames_2 = spec2.freq;
1350 frames_3 = spec3.freq * 2;
1351
1352 length_1 = (int)(frames_1 * sizeof(*buffer_1));
1353 buffer_1 = (float*) SDL_malloc(length_1);
1354 if (!SDLTest_AssertCheck(buffer_1 != NULL, "Expected buffer_1 to be created.")) {
1355 goto cleanup;
1356 }
1357
1358 length_2 = (int)(frames_2 * sizeof(*buffer_2));
1359 buffer_2 = (float*) SDL_malloc(length_2);
1360 if (!SDLTest_AssertCheck(buffer_2 != NULL, "Expected buffer_2 to be created.")) {
1361 goto cleanup;
1362 }
1363
1364 length_3 = (int)(frames_3 * sizeof(*buffer_3));
1365 buffer_3 = (float*) SDL_malloc(length_3);
1366 if (!SDLTest_AssertCheck(buffer_3 != NULL, "Expected buffer_3 to be created.")) {
1367 goto cleanup;
1368 }
1369
1370 for (i = 0; i < frames_1; ++i) {
1371 buffer_1[i] = (float) sine_wave_sample(i, spec1.freq, sine_freq, 0.0f);
1372 }
1373
1374 for (i = 0; i < frames_2; ++i) {
1375 buffer_2[i] = (float) sine_wave_sample(i, spec2.freq, sine_freq, 0.0f);
1376 }
1377
1378 stream = SDL_CreateAudioStream(NULL, NULL);
1379 if (!SDLTest_AssertCheck(stream != NULL, "Expected SDL_CreateAudioStream to succeed")) {
1380 goto cleanup;
1381 }
1382
1383 result = SDL_SetAudioStreamFormat(stream, &spec1, &spec3);
1384 if (!SDLTest_AssertCheck(result == true, "Expected SDL_SetAudioStreamFormat(spec1, spec3) to succeed")) {
1385 goto cleanup;
1386 }
1387
1388 result = SDL_GetAudioStreamAvailable(stream);
1389 if (!SDLTest_AssertCheck(result == 0, "Expected SDL_GetAudioStreamAvailable return 0")) {
1390 goto cleanup;
1391 }
1392
1393 result = SDL_PutAudioStreamData(stream, buffer_1, length_1);
1394 if (!SDLTest_AssertCheck(result == true, "Expected SDL_PutAudioStreamData(buffer_1) to succeed")) {
1395 goto cleanup;
1396 }
1397
1398 result = SDL_FlushAudioStream(stream);
1399 if (!SDLTest_AssertCheck(result == true, "Expected SDL_FlushAudioStream to succeed")) {
1400 goto cleanup;
1401 }
1402
1403 result = SDL_SetAudioStreamFormat(stream, &spec2, &spec3);
1404 if (!SDLTest_AssertCheck(result == true, "Expected SDL_SetAudioStreamFormat(spec2, spec3) to succeed")) {
1405 goto cleanup;
1406 }
1407
1408 result = SDL_PutAudioStreamData(stream, buffer_2, length_2);
1409 if (!SDLTest_AssertCheck(result == true, "Expected SDL_PutAudioStreamData(buffer_1) to succeed")) {
1410 goto cleanup;
1411 }
1412
1413 result = SDL_FlushAudioStream(stream);
1414 if (!SDLTest_AssertCheck(result == true, "Expected SDL_FlushAudioStream to succeed")) {
1415 goto cleanup;
1416 }
1417
1418 result = SDL_GetAudioStreamAvailable(stream);
1419 if (!SDLTest_AssertCheck(result == length_3, "Expected SDL_GetAudioStreamAvailable to return %i, got %i", length_3, result)) {
1420 goto cleanup;
1421 }
1422
1423 result = SDL_GetAudioStreamData(stream, buffer_3, length_3);
1424 if (!SDLTest_AssertCheck(result == length_3, "Expected SDL_GetAudioStreamData to return %i, got %i", length_3, result)) {
1425 goto cleanup;
1426 }
1427
1428 result = SDL_GetAudioStreamAvailable(stream);
1429 if (!SDLTest_AssertCheck(result == 0, "Expected SDL_GetAudioStreamAvailable to return 0")) {
1430 goto cleanup;
1431 }
1432
1433 for (i = 0; i < frames_3; ++i) {
1434 const float output = buffer_3[i];
1435 const float target = (float) sine_wave_sample(i, spec3.freq, sine_freq, 0.0f);
1436 const double error = SDL_fabs(target - output);
1437 max_error = SDL_max(max_error, error);
1438 sum_squared_error += error * error;
1439 sum_squared_value += target * target;
1440 }
1441
1442 signal_to_noise = 10 * SDL_log10(sum_squared_value / sum_squared_error); /* decibel */
1443 SDLTest_AssertCheck(ISFINITE(sum_squared_value), "Sum of squared target should be finite.");
1444 SDLTest_AssertCheck(ISFINITE(sum_squared_error), "Sum of squared error should be finite.");
1445 /* Infinity is theoretically possible when there is very little to no noise */
1446 SDLTest_AssertCheck(!ISNAN(signal_to_noise), "Signal-to-noise ratio should not be NaN.");
1447 SDLTest_AssertCheck(ISFINITE(max_error), "Maximum conversion error should be finite.");
1448 SDLTest_AssertCheck(signal_to_noise >= target_signal_to_noise, "Conversion signal-to-noise ratio %f dB should be no less than %f dB.",
1449 signal_to_noise, target_signal_to_noise);
1450 SDLTest_AssertCheck(max_error <= target_max_error, "Maximum conversion error %f should be no more than %f.",
1451 max_error, target_max_error);
1452
1453 status = TEST_COMPLETED;
1454
1455cleanup:
1456 SDL_free(buffer_1);
1457 SDL_free(buffer_2);
1458 SDL_free(buffer_3);
1459 SDL_DestroyAudioStream(stream);
1460
1461 return status;
1462}
1463/* ================= Test Case References ================== */
1464
1465/* Audio test cases */
1466static const SDLTest_TestCaseReference audioTestGetAudioFormatName = {
1467 audio_getAudioFormatName, "audio_getAudioFormatName", "Call to SDL_GetAudioFormatName", TEST_ENABLED
1468};
1469
1470static const SDLTest_TestCaseReference audioTest1 = {
1471 audio_enumerateAndNameAudioDevices, "audio_enumerateAndNameAudioDevices", "Enumerate and name available audio devices (playback and recording)", TEST_ENABLED
1472};
1473
1474static const SDLTest_TestCaseReference audioTest2 = {
1475 audio_enumerateAndNameAudioDevicesNegativeTests, "audio_enumerateAndNameAudioDevicesNegativeTests", "Negative tests around enumeration and naming of audio devices.", TEST_ENABLED
1476};
1477
1478static const SDLTest_TestCaseReference audioTest3 = {
1479 audio_printAudioDrivers, "audio_printAudioDrivers", "Checks available audio driver names.", TEST_ENABLED
1480};
1481
1482static const SDLTest_TestCaseReference audioTest4 = {
1483 audio_printCurrentAudioDriver, "audio_printCurrentAudioDriver", "Checks current audio driver name with initialized audio.", TEST_ENABLED
1484};
1485
1486static const SDLTest_TestCaseReference audioTest5 = {
1487 audio_buildAudioStream, "audio_buildAudioStream", "Builds various audio conversion structures.", TEST_ENABLED
1488};
1489
1490static const SDLTest_TestCaseReference audioTest6 = {
1491 audio_buildAudioStreamNegative, "audio_buildAudioStreamNegative", "Checks calls with invalid input to SDL_CreateAudioStream", TEST_ENABLED
1492};
1493
1494static const SDLTest_TestCaseReference audioTest7 = {
1495 audio_getAudioStatus, "audio_getAudioStatus", "Checks current audio status.", TEST_ENABLED
1496};
1497
1498static const SDLTest_TestCaseReference audioTest8 = {
1499 audio_openCloseAndGetAudioStatus, "audio_openCloseAndGetAudioStatus", "Opens and closes audio device and get audio status.", TEST_ENABLED
1500};
1501
1502static const SDLTest_TestCaseReference audioTest9 = {
1503 audio_lockUnlockOpenAudioDevice, "audio_lockUnlockOpenAudioDevice", "Locks and unlocks an open audio device.", TEST_ENABLED
1504};
1505
1506static const SDLTest_TestCaseReference audioTest10 = {
1507 audio_convertAudio, "audio_convertAudio", "Convert audio using available formats.", TEST_ENABLED
1508};
1509
1510/* TODO: enable test when SDL_AudioDeviceConnected has been implemented. */
1511
1512static const SDLTest_TestCaseReference audioTest11 = {
1513 audio_openCloseAudioDeviceConnected, "audio_openCloseAudioDeviceConnected", "Opens and closes audio device and get connected status.", TEST_DISABLED
1514};
1515
1516static const SDLTest_TestCaseReference audioTest12 = {
1517 audio_quitInitAudioSubSystem, "audio_quitInitAudioSubSystem", "Quit and re-init audio subsystem.", TEST_ENABLED
1518};
1519
1520static const SDLTest_TestCaseReference audioTest13 = {
1521 audio_initQuitAudio, "audio_initQuitAudio", "Init and quit audio drivers directly.", TEST_ENABLED
1522};
1523
1524static const SDLTest_TestCaseReference audioTest14 = {
1525 audio_initOpenCloseQuitAudio, "audio_initOpenCloseQuitAudio", "Cycle through init, open, close and quit with various audio specs.", TEST_ENABLED
1526};
1527
1528static const SDLTest_TestCaseReference audioTest15 = {
1529 audio_pauseUnpauseAudio, "audio_pauseUnpauseAudio", "Pause and Unpause audio for various audio specs while testing callback.", TEST_ENABLED
1530};
1531
1532static const SDLTest_TestCaseReference audioTest16 = {
1533 audio_resampleLoss, "audio_resampleLoss", "Check signal-to-noise ratio and maximum error of audio resampling.", TEST_ENABLED
1534};
1535
1536static const SDLTest_TestCaseReference audioTest17 = {
1537 audio_convertAccuracy, "audio_convertAccuracy", "Check accuracy converting between audio formats.", TEST_ENABLED
1538};
1539
1540static const SDLTest_TestCaseReference audioTest18 = {
1541 audio_formatChange, "audio_formatChange", "Check handling of format changes.", TEST_ENABLED
1542};
1543
1544/* Sequence of Audio test cases */
1545static const SDLTest_TestCaseReference *audioTests[] = {
1546 &audioTestGetAudioFormatName,
1547 &audioTest1, &audioTest2, &audioTest3, &audioTest4, &audioTest5, &audioTest6,
1548 &audioTest7, &audioTest8, &audioTest9, &audioTest10, &audioTest11,
1549 &audioTest12, &audioTest13, &audioTest14, &audioTest15, &audioTest16,
1550 &audioTest17, &audioTest18, NULL
1551};
1552
1553/* Audio test suite (global) */
1554SDLTest_TestSuiteReference audioTestSuite = {
1555 "Audio",
1556 audioSetUp,
1557 audioTests,
1558 audioTearDown
1559};