Atlas - testaudiostreamdynamicresample.c
Home / ext / SDL / test Lines: 1 | Size: 12875 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 3 4 This software is provided 'as-is', without any express or implied 5 warranty. In no event will the authors be held liable for any damages 6 arising from the use of this software. 7 8 Permission is granted to anyone to use this software for any purpose, 9 including commercial applications, and to alter it and redistribute it 10 freely. 11*/ 12 13#include <SDL3/SDL.h> 14#include <SDL3/SDL_main.h> 15#include <SDL3/SDL_test.h> 16#include "testutils.h" 17 18#ifdef SDL_PLATFORM_EMSCRIPTEN 19#include <emscripten/emscripten.h> 20#endif 21 22#include <stdlib.h> 23 24#define SLIDER_WIDTH_PERC 500.f / 600.f 25#define SLIDER_HEIGHT_PERC 70.f / 480.f 26 27static int done; 28static SDLTest_CommonState *state; 29 30static SDL_AudioSpec spec; 31static SDL_AudioStream *stream; 32static Uint8 *audio_buf = NULL; 33static Uint32 audio_len = 0; 34 35static bool auto_loop = true; 36static bool auto_flush = false; 37 38static Uint64 last_get_callback = 0; 39static int last_get_amount_additional = 0; 40static int last_get_amount_total = 0; 41 42typedef struct Slider 43{ 44 SDL_FRect area; 45 bool changed; 46 char fmtlabel[64]; 47 float pos; 48 int flags; 49 float min; 50 float mid; 51 float max; 52 float value; 53} Slider; 54 55#define NUM_SLIDERS 3 56Slider sliders[NUM_SLIDERS]; 57static int active_slider = -1; 58 59static void init_slider(int index, const char* fmtlabel, int flags, float value, float min, float max) 60{ 61 Slider* slider = &sliders[index]; 62 63 slider->area.x = state->window_w * (1.f - SLIDER_WIDTH_PERC) / 2; 64 slider->area.y = state->window_h * (0.2f + (index * SLIDER_HEIGHT_PERC * 1.4f)); 65 slider->area.w = SLIDER_WIDTH_PERC * state->window_w; 66 slider->area.h = SLIDER_HEIGHT_PERC * state->window_h; 67 slider->changed = true; 68 SDL_strlcpy(slider->fmtlabel, fmtlabel, SDL_arraysize(slider->fmtlabel)); 69 slider->flags = flags; 70 slider->min = min; 71 slider->max = max; 72 slider->value = value; 73 74 if (slider->flags & 1) { 75 slider->pos = (value - slider->min + 0.5f) / (slider->max - slider->min + 1.0f); 76 } else { 77 slider->pos = 0.5f; 78 slider->mid = value; 79 } 80} 81 82static float lerp(float v0, float v1, float t) 83{ 84 return (1 - t) * v0 + t * v1; 85} 86 87static void draw_text(SDL_Renderer* renderer, int x, int y, const char* text) 88{ 89 SDL_SetRenderDrawColor(renderer, 0xFD, 0xF6, 0xE3, 0xFF); 90 SDLTest_DrawString(renderer, (float) x, (float) y, text); 91} 92 93static void draw_textf(SDL_Renderer* renderer, int x, int y, const char* fmt, ...) 94{ 95 char text[256]; 96 va_list ap; 97 98 va_start(ap, fmt); 99 SDL_vsnprintf(text, SDL_arraysize(text), fmt, ap); 100 va_end(ap); 101 102 draw_text(renderer, x, y, text); 103} 104 105static void queue_audio(void) 106{ 107 Uint8* new_data = NULL; 108 int new_len = 0; 109 bool result = true; 110 SDL_AudioSpec new_spec; 111 112 SDL_zero(new_spec); 113 new_spec.format = spec.format; 114 new_spec.channels = (int) sliders[2].value; 115 new_spec.freq = (int) sliders[1].value; 116 117 SDL_Log("Converting audio from %i to %i", spec.freq, new_spec.freq); 118 119 /* You shouldn't actually use SDL_ConvertAudioSamples like this (just put the data straight into the stream and let it handle conversion) */ 120 result = result && SDL_ConvertAudioSamples(&spec, audio_buf, audio_len, &new_spec, &new_data, &new_len); 121 result = result && SDL_SetAudioStreamFormat(stream, &new_spec, NULL); 122 result = result && SDL_PutAudioStreamData(stream, new_data, new_len); 123 124 if (auto_flush) { 125 result = result && SDL_FlushAudioStream(stream); 126 } 127 128 SDL_free(new_data); 129 130 if (result) { 131 SDL_Log("Queued audio"); 132 } else { 133 SDL_Log("Failed to queue audio: %s", SDL_GetError()); 134 } 135} 136 137static void skip_audio(float amount) 138{ 139 float speed; 140 SDL_AudioSpec dst_spec; 141 int num_bytes; 142 int result = 0; 143 void* buf = NULL; 144 145 SDL_LockAudioStream(stream); 146 147 speed = SDL_GetAudioStreamFrequencyRatio(stream); 148 SDL_GetAudioStreamFormat(stream, NULL, &dst_spec); 149 150 SDL_SetAudioStreamFrequencyRatio(stream, 100.0f); 151 152 num_bytes = (int)(SDL_AUDIO_FRAMESIZE(dst_spec) * dst_spec.freq * ((speed * amount) / 100.0f)); 153 buf = SDL_malloc(num_bytes); 154 155 if (buf) { 156 result = SDL_GetAudioStreamData(stream, buf, num_bytes); 157 SDL_free(buf); 158 } 159 160 SDL_SetAudioStreamFrequencyRatio(stream, speed); 161 162 SDL_UnlockAudioStream(stream); 163 164 if (result >= 0) { 165 SDL_Log("Skipped %.2f seconds", amount); 166 } else { 167 SDL_Log("Failed to skip: %s", SDL_GetError()); 168 } 169} 170 171static const char *AudioChansToStr(const int channels) 172{ 173 switch (channels) { 174 case 1: return "Mono"; 175 case 2: return "Stereo"; 176 case 3: return "2.1"; 177 case 4: return "Quad"; 178 case 5: return "4.1"; 179 case 6: return "5.1"; 180 case 7: return "6.1"; 181 case 8: return "7.1"; 182 default: break; 183 } 184 return "?"; 185} 186 187static void scale_mouse_coords(SDL_FPoint *p) 188{ 189 SDL_Window *window = SDL_GetMouseFocus(); 190 if (window) { 191 int w, p_w; 192 float scale; 193 SDL_GetWindowSize(window, &w, NULL); 194 SDL_GetWindowSizeInPixels(window, &p_w, NULL); 195 scale = (float)p_w / (float)w; 196 p->x *= scale; 197 p->y *= scale; 198 } 199} 200 201static void loop(void) 202{ 203 int i, j; 204 SDL_Event e; 205 SDL_FPoint p; 206 SDL_AudioSpec src_spec, dst_spec; 207 int queued_bytes = 0; 208 int available_bytes = 0; 209 float available_seconds = 0; 210 211 while (SDL_PollEvent(&e)) { 212 SDLTest_CommonEvent(state, &e, &done); 213#ifdef SDL_PLATFORM_EMSCRIPTEN 214 if (done) { 215 emscripten_cancel_main_loop(); 216 } 217#endif 218 if (e.type == SDL_EVENT_KEY_DOWN) { 219 SDL_Keycode key = e.key.key; 220 if (key == SDLK_Q) { 221 if (SDL_AudioDevicePaused(state->audio_id)) { 222 SDL_ResumeAudioDevice(state->audio_id); 223 } else { 224 SDL_PauseAudioDevice(state->audio_id); 225 } 226 } else if (key == SDLK_W) { 227 auto_loop = !auto_loop; 228 } else if (key == SDLK_E) { 229 auto_flush = !auto_flush; 230 } else if (key == SDLK_A) { 231 SDL_ClearAudioStream(stream); 232 SDL_Log("Cleared audio stream"); 233 } else if (key == SDLK_S) { 234 queue_audio(); 235 } else if (key == SDLK_D) { 236 float amount = 1.0f; 237 amount *= (e.key.mod & SDL_KMOD_CTRL) ? 10.0f : 1.0f; 238 amount *= (e.key.mod & SDL_KMOD_SHIFT) ? 10.0f : 1.0f; 239 skip_audio(amount); 240 } 241 } 242 } 243 244 if (SDL_GetMouseState(&p.x, &p.y) & SDL_BUTTON_LMASK) { 245 scale_mouse_coords(&p); 246 if (active_slider == -1) { 247 for (i = 0; i < NUM_SLIDERS; ++i) { 248 if (SDL_PointInRectFloat(&p, &sliders[i].area)) { 249 active_slider = i; 250 break; 251 } 252 } 253 } 254 } else { 255 active_slider = -1; 256 } 257 258 if (active_slider != -1) { 259 Slider* slider = &sliders[active_slider]; 260 261 float value = (p.x - slider->area.x) / slider->area.w; 262 value = SDL_clamp(value, 0.0f, 1.0f); 263 slider->pos = value; 264 265 if (slider->flags & 1) { 266 value = slider->min + (value * (slider->max - slider->min + 1.0f)); 267 value = SDL_clamp(value, slider->min, slider->max); 268 } else { 269 value = (value * 2.0f) - 1.0f; 270 value = (value >= 0) 271 ? lerp(slider->mid, slider->max, value) 272 : lerp(slider->mid, slider->min, -value); 273 } 274 275 if (value != slider->value) { 276 slider->value = value; 277 slider->changed = true; 278 } 279 } 280 281 if (sliders[0].changed) { 282 sliders[0].changed = false; 283 SDL_SetAudioStreamFrequencyRatio(stream, sliders[0].value); 284 } 285 286 if (SDL_GetAudioStreamFormat(stream, &src_spec, &dst_spec)) { 287 available_bytes = SDL_GetAudioStreamAvailable(stream); 288 available_seconds = (float)available_bytes / (float)(SDL_AUDIO_FRAMESIZE(dst_spec) * dst_spec.freq); 289 290 /* keep it looping. */ 291 if (auto_loop && (available_seconds < 10.0f)) { 292 queue_audio(); 293 } 294 } 295 296 queued_bytes = SDL_GetAudioStreamQueued(stream); 297 298 for (i = 0; i < state->num_windows; i++) { 299 int draw_y = 0; 300 SDL_Renderer* rend = state->renderers[i]; 301 302 SDL_SetRenderDrawColor(rend, 0x00, 0x2B, 0x36, 0xFF); 303 SDL_RenderClear(rend); 304 305 for (j = 0; j < NUM_SLIDERS; ++j) { 306 Slider* slider = &sliders[j]; 307 SDL_FRect area; 308 309 SDL_copyp(&area, &slider->area); 310 311 SDL_SetRenderDrawColor(rend, 0x07, 0x36, 0x42, 0xFF); 312 SDL_RenderFillRect(rend, &area); 313 314 area.w *= slider->pos; 315 316 SDL_SetRenderDrawColor(rend, 0x58, 0x6E, 0x75, 0xFF); 317 SDL_RenderFillRect(rend, &area); 318 319 draw_textf(rend, (int)slider->area.x, (int)slider->area.y, slider->fmtlabel, 320 (slider->flags & 2) ? ((float)(int)slider->value) : slider->value); 321 } 322 323 draw_textf(rend, 0, draw_y, "%7s, Loop: %3s, Flush: %3s", 324 SDL_AudioDevicePaused(state->audio_id) ? "Paused" : "Playing", auto_loop ? "On" : "Off", auto_flush ? "On" : "Off"); 325 draw_y += FONT_LINE_HEIGHT; 326 327 draw_textf(rend, 0, draw_y, "Available: %4.2f (%i bytes)", available_seconds, available_bytes); 328 draw_y += FONT_LINE_HEIGHT; 329 330 draw_textf(rend, 0, draw_y, "Queued: %i bytes", queued_bytes); 331 draw_y += FONT_LINE_HEIGHT; 332 333 SDL_LockAudioStream(stream); 334 335 draw_textf(rend, 0, draw_y, "Get Callback: %i/%i bytes, %2i ms ago", 336 last_get_amount_additional, last_get_amount_total, (int)(SDL_GetTicks() - last_get_callback)); 337 draw_y += FONT_LINE_HEIGHT; 338 339 SDL_UnlockAudioStream(stream); 340 341 draw_y = state->window_h - FONT_LINE_HEIGHT * 3; 342 343 draw_textf(rend, 0, draw_y, "Wav: %6s/%6s/%i", 344 SDL_GetAudioFormatName(spec.format), AudioChansToStr(spec.channels), spec.freq); 345 draw_y += FONT_LINE_HEIGHT; 346 347 draw_textf(rend, 0, draw_y, "Src: %6s/%6s/%i", 348 SDL_GetAudioFormatName(src_spec.format), AudioChansToStr(src_spec.channels), src_spec.freq); 349 draw_y += FONT_LINE_HEIGHT; 350 351 draw_textf(rend, 0, draw_y, "Dst: %6s/%6s/%i", 352 SDL_GetAudioFormatName(dst_spec.format), AudioChansToStr(dst_spec.channels), dst_spec.freq); 353 draw_y += FONT_LINE_HEIGHT; 354 355 SDL_RenderPresent(rend); 356 } 357} 358 359static void SDLCALL our_get_callback(void *userdata, SDL_AudioStream *strm, int additional_amount, int total_amount) 360{ 361 last_get_callback = SDL_GetTicks(); 362 last_get_amount_additional = additional_amount; 363 last_get_amount_total = total_amount; 364} 365 366int main(int argc, char *argv[]) 367{ 368 char *filename = NULL; 369 int i; 370 371 /* Initialize test framework */ 372 state = SDLTest_CommonCreateState(argv, SDL_INIT_AUDIO | SDL_INIT_VIDEO); 373 if (!state) { 374 return 1; 375 } 376 377 /* Parse commandline */ 378 for (i = 1; i < argc;) { 379 int consumed; 380 381 consumed = SDLTest_CommonArg(state, i); 382 if (!consumed) { 383 if (!filename) { 384 filename = argv[i]; 385 consumed = 1; 386 } 387 } 388 if (consumed <= 0) { 389 static const char *options[] = { "[sample.wav]", NULL }; 390 SDLTest_CommonLogUsage(state, argv[0], options); 391 exit(1); 392 } 393 394 i += consumed; 395 } 396 397 /* Load the SDL library */ 398 if (!SDLTest_CommonInit(state)) { 399 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError()); 400 return 1; 401 } 402 403 FONT_CHARACTER_SIZE = 16; 404 405 filename = GetResourceFilename(filename, "sample.wav"); 406 if (!SDL_LoadWAV(filename, &spec, &audio_buf, &audio_len)) { 407 SDL_Log("Failed to load '%s': %s", filename, SDL_GetError()); 408 SDL_free(filename); 409 SDL_Quit(); 410 return 1; 411 } 412 413 SDL_free(filename); 414 init_slider(0, "Speed: %3.2fx", 0x0, 1.0f, 0.2f, 5.0f); 415 init_slider(1, "Freq: %g", 0x2, (float)spec.freq, 4000.0f, 192000.0f); 416 init_slider(2, "Channels: %g", 0x3, (float)spec.channels, 1.0f, 8.0f); 417 418 for (i = 0; i < state->num_windows; i++) { 419 SDL_SetWindowTitle(state->windows[i], "Resampler Test"); 420 } 421 422 stream = SDL_CreateAudioStream(&spec, &spec); 423 SDL_SetAudioStreamGetCallback(stream, our_get_callback, NULL); 424 425 SDL_BindAudioStream(state->audio_id, stream); 426 427#ifdef SDL_PLATFORM_EMSCRIPTEN 428 emscripten_set_main_loop(loop, 0, 1); 429#else 430 while (!done) { 431 loop(); 432 } 433#endif 434 435 SDLTest_CleanupTextDrawing(); 436 SDL_DestroyAudioStream(stream); 437 SDL_free(audio_buf); 438 SDLTest_CommonQuit(state); 439 return 0; 440} 441 442[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.