diff options
Diffstat (limited to 'contrib/SDL-3.2.8/test/testautomation_audio.c')
| -rw-r--r-- | contrib/SDL-3.2.8/test/testautomation_audio.c | 1559 |
1 files changed, 1559 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/test/testautomation_audio.c b/contrib/SDL-3.2.8/test/testautomation_audio.c new file mode 100644 index 0000000..7c141b3 --- /dev/null +++ b/contrib/SDL-3.2.8/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 | }; | ||
