summaryrefslogtreecommitdiff
path: root/src/contrib/SDL-3.2.20/test/testaudiostreamdynamicresample.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-08-30 16:53:58 -0700
committer3gg <3gg@shellblade.net>2025-08-30 16:53:58 -0700
commit6aaedb813fa11ba0679c3051bc2eb28646b9506c (patch)
tree34acbfc9840e02cb4753e6306ea7ce978bf8b58e /src/contrib/SDL-3.2.20/test/testaudiostreamdynamicresample.c
parent8f228ade99dd3d4c8da9b78ade1815c9adf85c8f (diff)
Update to SDL3
Diffstat (limited to 'src/contrib/SDL-3.2.20/test/testaudiostreamdynamicresample.c')
-rw-r--r--src/contrib/SDL-3.2.20/test/testaudiostreamdynamicresample.c441
1 files changed, 441 insertions, 0 deletions
diff --git a/src/contrib/SDL-3.2.20/test/testaudiostreamdynamicresample.c b/src/contrib/SDL-3.2.20/test/testaudiostreamdynamicresample.c
new file mode 100644
index 0000000..9ee2c84
--- /dev/null
+++ b/src/contrib/SDL-3.2.20/test/testaudiostreamdynamicresample.c
@@ -0,0 +1,441 @@
1/*
2 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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