diff options
author | 3gg <3gg@shellblade.net> | 2025-08-30 16:53:58 -0700 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2025-08-30 16:53:58 -0700 |
commit | 6aaedb813fa11ba0679c3051bc2eb28646b9506c (patch) | |
tree | 34acbfc9840e02cb4753e6306ea7ce978bf8b58e /src/contrib/SDL-3.2.20/test/testautomation_audio.c | |
parent | 8f228ade99dd3d4c8da9b78ade1815c9adf85c8f (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.c | 1559 |
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 | |||
22 | static 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 | |||
33 | static 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 */ | ||
43 | static int g_audio_testCallbackCounter; | ||
44 | |||
45 | /* Global accumulator for total callback length */ | ||
46 | static int g_audio_testCallbackLength; | ||
47 | |||
48 | /* Test callback function */ | ||
49 | static 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 | |||
57 | static 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 | */ | ||
67 | static 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 | */ | ||
85 | static 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 | */ | ||
148 | static 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 | */ | ||
238 | static 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 | */ | ||
364 | static 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 | */ | ||
403 | static 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 | */ | ||
414 | static 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 | */ | ||
444 | static 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 */ | ||
458 | static 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 | }; | ||
464 | static 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 | }; | ||
470 | static 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 | }; | ||
473 | static const char *g_invalidAudioFormatsVerbose[] = { | ||
474 | "SDL_AUDIO_UNKNOWN" | ||
475 | }; | ||
476 | static const int g_numAudioFormats = SDL_arraysize(g_audioFormats); | ||
477 | static const int g_numInvalidAudioFormats = SDL_arraysize(g_invalidAudioFormats); | ||
478 | static Uint8 g_audioChannels[] = { 1, 2, 4, 6 }; | ||
479 | static const int g_numAudioChannels = SDL_arraysize(g_audioChannels); | ||
480 | static int g_audioFrequencies[] = { 11025, 22050, 44100, 48000 }; | ||
481 | static const int g_numAudioFrequencies = SDL_arraysize(g_audioFrequencies); | ||
482 | |||
483 | /* Verify the audio formats are laid out as expected */ | ||
484 | SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_U8_FORMAT, SDL_AUDIO_U8 == SDL_AUDIO_BITSIZE(8)); | ||
485 | SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_S8_FORMAT, SDL_AUDIO_S8 == (SDL_AUDIO_BITSIZE(8) | SDL_AUDIO_MASK_SIGNED)); | ||
486 | SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_S16LE_FORMAT, SDL_AUDIO_S16LE == (SDL_AUDIO_BITSIZE(16) | SDL_AUDIO_MASK_SIGNED)); | ||
487 | SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_S16BE_FORMAT, SDL_AUDIO_S16BE == (SDL_AUDIO_S16LE | SDL_AUDIO_MASK_BIG_ENDIAN)); | ||
488 | SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_S32LE_FORMAT, SDL_AUDIO_S32LE == (SDL_AUDIO_BITSIZE(32) | SDL_AUDIO_MASK_SIGNED)); | ||
489 | SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_S32BE_FORMAT, SDL_AUDIO_S32BE == (SDL_AUDIO_S32LE | SDL_AUDIO_MASK_BIG_ENDIAN)); | ||
490 | SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_F32LE_FORMAT, SDL_AUDIO_F32LE == (SDL_AUDIO_BITSIZE(32) | SDL_AUDIO_MASK_FLOAT | SDL_AUDIO_MASK_SIGNED)); | ||
491 | SDL_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 | */ | ||
498 | static 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 | */ | ||
550 | static 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 | */ | ||
622 | static 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 | */ | ||
705 | static 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 | */ | ||
715 | static 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 | */ | ||
726 | static 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 | */ | ||
736 | static 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 | */ | ||
885 | static int SDLCALL audio_openCloseAudioDeviceConnected(void *arg) | ||
886 | { | ||
887 | return TEST_COMPLETED; /* not a thing in SDL3. */ | ||
888 | } | ||
889 | |||
890 | static 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 */ | ||
903 | static 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 */ | ||
932 | static 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 */ | ||
963 | static 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 | */ | ||
1032 | static 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 | */ | ||
1174 | static 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 | */ | ||
1312 | static 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 | |||
1455 | cleanup: | ||
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 */ | ||
1466 | static const SDLTest_TestCaseReference audioTestGetAudioFormatName = { | ||
1467 | audio_getAudioFormatName, "audio_getAudioFormatName", "Call to SDL_GetAudioFormatName", TEST_ENABLED | ||
1468 | }; | ||
1469 | |||
1470 | static const SDLTest_TestCaseReference audioTest1 = { | ||
1471 | audio_enumerateAndNameAudioDevices, "audio_enumerateAndNameAudioDevices", "Enumerate and name available audio devices (playback and recording)", TEST_ENABLED | ||
1472 | }; | ||
1473 | |||
1474 | static const SDLTest_TestCaseReference audioTest2 = { | ||
1475 | audio_enumerateAndNameAudioDevicesNegativeTests, "audio_enumerateAndNameAudioDevicesNegativeTests", "Negative tests around enumeration and naming of audio devices.", TEST_ENABLED | ||
1476 | }; | ||
1477 | |||
1478 | static const SDLTest_TestCaseReference audioTest3 = { | ||
1479 | audio_printAudioDrivers, "audio_printAudioDrivers", "Checks available audio driver names.", TEST_ENABLED | ||
1480 | }; | ||
1481 | |||
1482 | static const SDLTest_TestCaseReference audioTest4 = { | ||
1483 | audio_printCurrentAudioDriver, "audio_printCurrentAudioDriver", "Checks current audio driver name with initialized audio.", TEST_ENABLED | ||
1484 | }; | ||
1485 | |||
1486 | static const SDLTest_TestCaseReference audioTest5 = { | ||
1487 | audio_buildAudioStream, "audio_buildAudioStream", "Builds various audio conversion structures.", TEST_ENABLED | ||
1488 | }; | ||
1489 | |||
1490 | static const SDLTest_TestCaseReference audioTest6 = { | ||
1491 | audio_buildAudioStreamNegative, "audio_buildAudioStreamNegative", "Checks calls with invalid input to SDL_CreateAudioStream", TEST_ENABLED | ||
1492 | }; | ||
1493 | |||
1494 | static const SDLTest_TestCaseReference audioTest7 = { | ||
1495 | audio_getAudioStatus, "audio_getAudioStatus", "Checks current audio status.", TEST_ENABLED | ||
1496 | }; | ||
1497 | |||
1498 | static const SDLTest_TestCaseReference audioTest8 = { | ||
1499 | audio_openCloseAndGetAudioStatus, "audio_openCloseAndGetAudioStatus", "Opens and closes audio device and get audio status.", TEST_ENABLED | ||
1500 | }; | ||
1501 | |||
1502 | static const SDLTest_TestCaseReference audioTest9 = { | ||
1503 | audio_lockUnlockOpenAudioDevice, "audio_lockUnlockOpenAudioDevice", "Locks and unlocks an open audio device.", TEST_ENABLED | ||
1504 | }; | ||
1505 | |||
1506 | static 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 | |||
1512 | static const SDLTest_TestCaseReference audioTest11 = { | ||
1513 | audio_openCloseAudioDeviceConnected, "audio_openCloseAudioDeviceConnected", "Opens and closes audio device and get connected status.", TEST_DISABLED | ||
1514 | }; | ||
1515 | |||
1516 | static const SDLTest_TestCaseReference audioTest12 = { | ||
1517 | audio_quitInitAudioSubSystem, "audio_quitInitAudioSubSystem", "Quit and re-init audio subsystem.", TEST_ENABLED | ||
1518 | }; | ||
1519 | |||
1520 | static const SDLTest_TestCaseReference audioTest13 = { | ||
1521 | audio_initQuitAudio, "audio_initQuitAudio", "Init and quit audio drivers directly.", TEST_ENABLED | ||
1522 | }; | ||
1523 | |||
1524 | static const SDLTest_TestCaseReference audioTest14 = { | ||
1525 | audio_initOpenCloseQuitAudio, "audio_initOpenCloseQuitAudio", "Cycle through init, open, close and quit with various audio specs.", TEST_ENABLED | ||
1526 | }; | ||
1527 | |||
1528 | static const SDLTest_TestCaseReference audioTest15 = { | ||
1529 | audio_pauseUnpauseAudio, "audio_pauseUnpauseAudio", "Pause and Unpause audio for various audio specs while testing callback.", TEST_ENABLED | ||
1530 | }; | ||
1531 | |||
1532 | static const SDLTest_TestCaseReference audioTest16 = { | ||
1533 | audio_resampleLoss, "audio_resampleLoss", "Check signal-to-noise ratio and maximum error of audio resampling.", TEST_ENABLED | ||
1534 | }; | ||
1535 | |||
1536 | static const SDLTest_TestCaseReference audioTest17 = { | ||
1537 | audio_convertAccuracy, "audio_convertAccuracy", "Check accuracy converting between audio formats.", TEST_ENABLED | ||
1538 | }; | ||
1539 | |||
1540 | static const SDLTest_TestCaseReference audioTest18 = { | ||
1541 | audio_formatChange, "audio_formatChange", "Check handling of format changes.", TEST_ENABLED | ||
1542 | }; | ||
1543 | |||
1544 | /* Sequence of Audio test cases */ | ||
1545 | static 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) */ | ||
1554 | SDLTest_TestSuiteReference audioTestSuite = { | ||
1555 | "Audio", | ||
1556 | audioSetUp, | ||
1557 | audioTests, | ||
1558 | audioTearDown | ||
1559 | }; | ||