Atlas - testaudio.c

Home / ext / SDL / test Lines: 1 | Size: 44041 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#define SDL_MAIN_USE_CALLBACKS 1 14#include <SDL3/SDL_test.h> 15#include <SDL3/SDL_test_common.h> 16#include <SDL3/SDL_main.h> 17#include "testutils.h" 18 19#define POOF_LIFETIME 250 20#define VISUALIZER_WIDTH 100 21#define VISUALIZER_HEIGHT 50 22 23typedef struct Texture 24{ 25 SDL_Texture *texture; 26 float w; 27 float h; 28} Texture; 29 30typedef enum ThingType 31{ 32 THING_NULL, 33 THING_PHYSDEV, 34 THING_PHYSDEV_RECORDING, 35 THING_LOGDEV, 36 THING_LOGDEV_RECORDING, 37 THING_TRASHCAN, 38 THING_STREAM, 39 THING_POOF, 40 THING_WAV 41} ThingType; 42 43 44typedef struct Thing Thing; 45 46struct Thing 47{ 48 ThingType what; 49 50 union { 51 struct { 52 SDL_AudioDeviceID devid; 53 bool recording; 54 SDL_AudioSpec spec; 55 char *name; 56 } physdev; 57 struct { 58 SDL_AudioDeviceID devid; 59 bool recording; 60 SDL_AudioSpec spec; 61 Thing *physdev; 62 bool visualizer_enabled; 63 bool visualizer_updated; 64 SDL_Texture *visualizer; 65 SDL_Mutex *postmix_lock; 66 float *postmix_buffer; 67 int postmix_buflen; 68 int postmix_allocated; 69 SDL_AudioSpec postmix_spec; 70 SDL_AtomicInt postmix_updated; 71 } logdev; 72 struct { 73 SDL_AudioSpec spec; 74 Uint8 *buf; 75 Uint32 buflen; 76 } wav; 77 struct { 78 float startw; 79 float starth; 80 float centerx; 81 float centery; 82 } poof; 83 struct { 84 SDL_AudioStream *stream; 85 int total_bytes; 86 Uint64 next_level_update; 87 Uint8 levels[5]; 88 } stream; 89 } data; 90 91 Thing *line_connected_to; 92 char *titlebar; 93 SDL_FRect rect; 94 float z; 95 Uint8 r, g, b, a; 96 float progress; 97 float meter; 98 float scale; 99 Uint64 createticks; 100 Texture *texture; 101 const ThingType *can_be_dropped_onto; 102 103 void (*ontick)(Thing *thing, Uint64 now); 104 void (*ondrag)(Thing *thing, int button, float x, float y); 105 void (*ondrop)(Thing *thing, int button, float x, float y); 106 void (*ondraw)(Thing *thing, SDL_Renderer *renderer); 107 void (*onmousewheel)(Thing *thing, float y); 108 109 Thing *prev; 110 Thing *next; 111}; 112 113 114static Uint64 app_ready_ticks = 0; 115static SDLTest_CommonState *state = NULL; 116 117static Thing *things = NULL; 118 119static Thing *mouseover_thing = NULL; 120static Thing *droppable_highlighted_thing = NULL; 121static Thing *dragging_thing = NULL; 122static int dragging_button = -1; 123static int dragging_button_real = -1; 124static bool ctrl_held = false; 125static bool alt_held = false; 126 127static Texture *physdev_texture = NULL; 128static Texture *logdev_texture = NULL; 129static Texture *audio_texture = NULL; 130static Texture *trashcan_texture = NULL; 131static Texture *soundboard_texture = NULL; 132static Texture *soundboard_levels_texture = NULL; 133 134 135static void SetTitleBar(const char *fmt, ...) 136{ 137 char *title = NULL; 138 va_list ap; 139 va_start(ap, fmt); 140 SDL_vasprintf(&title, fmt, ap); 141 va_end(ap); 142 143 SDL_SetWindowTitle(state->windows[0], title); 144 SDL_free(title); 145} 146 147static void SetDefaultTitleBar(void) 148{ 149 SetTitleBar("testaudio: %s", SDL_GetCurrentAudioDriver()); 150} 151 152static Thing *FindThingAtPoint(const float x, const float y) 153{ 154 const SDL_FPoint pt = { x, y }; 155 Thing *result = NULL; 156 Thing *i; 157 for (i = things; i; i = i->next) { 158 if ((i != dragging_thing) && SDL_PointInRectFloat(&pt, &i->rect)) { 159 result = i; /* keep going, though, because things drawn on top are later in the list. */ 160 } 161 } 162 return result; 163} 164 165static Thing *UpdateMouseOver(const float x, const float y) 166{ 167 Thing *thing; 168 169 if (dragging_thing) { 170 thing = dragging_thing; 171 } else { 172 thing = FindThingAtPoint(x, y); 173 } 174 175 mouseover_thing = thing; 176 177 if (!thing) { 178 SetDefaultTitleBar(); 179 } else if (thing->titlebar) { 180 SetTitleBar("%s", thing->titlebar); 181 } 182 183 return thing; 184} 185 186static Thing *CreateThing(ThingType what, float x, float y, float z, float w, float h, Texture *texture, const char *titlebar) 187{ 188 Thing *last = NULL; 189 Thing *i; 190 Thing *thing; 191 192 thing = (Thing *) SDL_calloc(1, sizeof (Thing)); 193 if (!thing) { 194 SDL_Log("Out of memory!"); 195 return NULL; 196 } 197 198 if ((w < 0) || (h < 0)) { 199 SDL_assert(texture != NULL); 200 if (w < 0) { 201 w = texture->w; 202 } 203 if (h < 0) { 204 h = texture->h; 205 } 206 } 207 208 thing->what = what; 209 thing->rect.x = x; 210 thing->rect.y = y; 211 thing->rect.w = w; 212 thing->rect.h = h; 213 thing->z = z; 214 thing->r = 255; 215 thing->g = 255; 216 thing->b = 255; 217 thing->a = 255; 218 thing->scale = 1.0f; 219 thing->createticks = SDL_GetTicks(); 220 thing->texture = texture; 221 thing->titlebar = titlebar ? SDL_strdup(titlebar) : NULL; /* if allocation fails, oh well. */ 222 223 /* insert in list by Z order (furthest from the "camera" first, so they get drawn over; negative Z is not drawn at all). */ 224 if (!things) { 225 things = thing; 226 return thing; 227 } 228 229 for (i = things; i; i = i->next) { 230 if (z > i->z) { /* insert here. */ 231 thing->next = i; 232 thing->prev = i->prev; 233 234 SDL_assert(i->prev == last); 235 236 if (i->prev) { 237 i->prev->next = thing; 238 } else { 239 SDL_assert(i == things); 240 things = thing; 241 } 242 i->prev = thing; 243 return thing; 244 } 245 last = i; 246 } 247 248 if (last) { 249 last->next = thing; 250 thing->prev = last; 251 } 252 return thing; 253} 254 255static void DestroyThing(Thing *thing) 256{ 257 if (!thing) { 258 return; 259 } 260 261 if (mouseover_thing == thing) { 262 mouseover_thing = NULL; 263 } 264 265 if (droppable_highlighted_thing == thing) { 266 droppable_highlighted_thing = NULL; 267 } 268 269 if (dragging_thing == thing) { 270 dragging_thing = NULL; 271 } 272 273 switch (thing->what) { 274 case THING_POOF: break; 275 case THING_NULL: break; 276 case THING_TRASHCAN: break; 277 278 case THING_LOGDEV: 279 case THING_LOGDEV_RECORDING: 280 SDL_CloseAudioDevice(thing->data.logdev.devid); 281 if (state->renderers[0] != NULL) { 282 SDL_DestroyTexture(thing->data.logdev.visualizer); 283 } 284 SDL_DestroyMutex(thing->data.logdev.postmix_lock); 285 SDL_free(thing->data.logdev.postmix_buffer); 286 break; 287 288 case THING_PHYSDEV: 289 case THING_PHYSDEV_RECORDING: 290 SDL_free(thing->data.physdev.name); 291 break; 292 293 case THING_WAV: 294 SDL_free(thing->data.wav.buf); 295 break; 296 297 case THING_STREAM: 298 SDL_DestroyAudioStream(thing->data.stream.stream); 299 break; 300 } 301 302 if (thing->prev) { 303 SDL_assert(thing != things); 304 thing->prev->next = thing->next; 305 } else { 306 SDL_assert(thing == things); 307 things = thing->next; 308 } 309 310 if (thing->next) { 311 thing->next->prev = thing->prev; 312 } 313 314 SDL_free(thing->titlebar); 315 SDL_free(thing); 316} 317 318static void DrawOneThing(SDL_Renderer *renderer, Thing *thing) 319{ 320 SDL_FRect dst; 321 SDL_memcpy(&dst, &thing->rect, sizeof (SDL_FRect)); 322 if (thing->scale != 1.0f) { 323 const float centerx = thing->rect.x + (thing->rect.w / 2); 324 const float centery = thing->rect.y + (thing->rect.h / 2); 325 const int w = thing->texture ? (int) thing->texture->w : 128; 326 const int h = thing->texture ? (int) thing->texture->h : 128; 327 dst.w = w * thing->scale; 328 dst.h = h * thing->scale; 329 dst.x = centerx - (dst.w / 2); 330 dst.y = centery - (dst.h / 2); 331 } 332 333 if (thing->texture) { 334 if (droppable_highlighted_thing == thing) { 335 SDL_SetRenderDrawColor(renderer, 255, 0, 255, 100); 336 SDL_RenderFillRect(renderer, &dst); 337 } 338 SDL_SetRenderDrawColor(renderer, thing->r, thing->g, thing->b, thing->a); 339 SDL_RenderTexture(renderer, thing->texture->texture, NULL, &dst); 340 } else { 341 SDL_SetRenderDrawColor(renderer, thing->r, thing->g, thing->b, thing->a); 342 SDL_RenderFillRect(renderer, &dst); 343 } 344 345 if (thing->ondraw) { 346 thing->ondraw(thing, renderer); 347 } 348 349 if (thing->progress > 0.0f) { 350 SDL_FRect r = { thing->rect.x, thing->rect.y + (thing->rect.h + 2.0f), 0.0f, 10.0f }; 351 r.w = thing->rect.w * ((thing->progress > 1.0f) ? 1.0f : thing->progress); 352 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 128); 353 SDL_RenderFillRect(renderer, &r); 354 } 355 356 if (thing->meter > 0.0f) { 357 SDL_FRect r; 358 r.w = 10.0f; 359 r.h = thing->rect.h * ((thing->meter > 1.0f) ? 1.0f : thing->meter); 360 r.x = thing->rect.x + thing->rect.w + 2.0f; 361 r.y = (thing->rect.y + thing->rect.h) - r.h; 362 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 128); 363 SDL_RenderFillRect(renderer, &r); 364 } 365} 366 367static void DrawThings(SDL_Renderer *renderer) 368{ 369 Thing *i; 370 371 /* draw connecting lines first, so they're behind everything else. */ 372 for (i = things; i && (i->z >= 0.0f); i = i->next) { 373 Thing *dst = i->line_connected_to; 374 if (dst) { 375 SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); 376 SDL_RenderLine(renderer, i->rect.x + (i->rect.w / 2), i->rect.y + (i->rect.h / 2), dst->rect.x + (dst->rect.w / 2), dst->rect.y + (dst->rect.h / 2)); 377 } 378 } 379 380 /* Draw the actual things. */ 381 for (i = things; i && (i->z >= 0.0f); i = i->next) { 382 if (i != dragging_thing) { 383 DrawOneThing(renderer, i); 384 } 385 } 386 387 if (dragging_thing) { 388 DrawOneThing(renderer, dragging_thing); /* draw last so it's always on top. */ 389 } 390} 391 392static void Draw(void) 393{ 394 SDL_Renderer *renderer = state->renderers[0]; 395 if (renderer) { /* might be NULL if we're shutting down. */ 396 SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); 397 SDL_SetRenderDrawColor(renderer, 64, 0, 64, 255); 398 SDL_RenderClear(renderer); 399 DrawThings(renderer); 400 SDL_RenderPresent(renderer); 401 } 402} 403 404static void RepositionRowOfThings(const ThingType what, const float y) 405{ 406 int total_things = 0; 407 float texw = 0.0f; 408 float texh = 0.0f; 409 Thing *i; 410 411 for (i = things; i; i = i->next) { 412 if (i->what == what) { 413 texw = i->rect.w; 414 texh = i->rect.h; 415 total_things++; 416 } 417 } 418 419 if (total_things > 0) { 420 int w, h; 421 SDL_GetWindowSize(state->windows[0], &w, &h); 422 const float spacing = w / ((float) total_things); 423 float x = (spacing - texw) / 2.0f; 424 for (i = things; i; i = i->next) { 425 if (i->what == what) { 426 i->rect.x = x; 427 i->rect.y = (y >= 0.0f) ? y : ((h + y) - texh); 428 x += spacing; 429 } 430 } 431 } 432} 433 434static const char *AudioChansToStr(const int channels) 435{ 436 switch (channels) { 437 case 1: return "mono"; 438 case 2: return "stereo"; 439 case 3: return "2.1"; 440 case 4: return "quad"; 441 case 5: return "4.1"; 442 case 6: return "5.1"; 443 case 7: return "6.1"; 444 case 8: return "7.1"; 445 default: break; 446 } 447 return "?"; 448} 449 450static void PoofThing_ondrag(Thing *thing, int button, float x, float y) 451{ 452 dragging_thing = NULL; /* refuse to be dragged. */ 453} 454 455static void PoofThing_ontick(Thing *thing, Uint64 now) 456{ 457 const int lifetime = POOF_LIFETIME; 458 const int elapsed = (int) (now - thing->createticks); 459 if (elapsed > lifetime) { 460 DestroyThing(thing); 461 } else { 462 const float pct = ((float) elapsed) / ((float) lifetime); 463 thing->a = (Uint8) (int) (255.0f - (pct * 255.0f)); 464 thing->scale = 1.0f - pct; /* shrink to nothing! */ 465 } 466} 467 468static Thing *CreatePoofThing(Thing *poofing_thing) 469{ 470 const float centerx = poofing_thing->rect.x + (poofing_thing->rect.w / 2); 471 const float centery = poofing_thing->rect.y + (poofing_thing->rect.h / 2); 472 const float z = poofing_thing->z; 473 Thing *thing = CreateThing(THING_POOF, poofing_thing->rect.x, poofing_thing->rect.y, z, poofing_thing->rect.w, poofing_thing->rect.h, poofing_thing->texture, NULL); 474 if (thing) { 475 thing->data.poof.startw = poofing_thing->rect.w; 476 thing->data.poof.starth = poofing_thing->rect.h; 477 thing->data.poof.centerx = centerx; 478 thing->data.poof.centery = centery; 479 thing->ontick = PoofThing_ontick; 480 thing->ondrag = PoofThing_ondrag; 481 } 482 return thing; 483} 484 485static void DestroyThingInPoof(Thing *thing) 486{ 487 if (thing) { 488 if (thing->what != THING_POOF) { 489 CreatePoofThing(thing); 490 } 491 DestroyThing(thing); 492 } 493} 494 495/* this poofs a thing and additionally poofs all things connected to the thing. */ 496static void TrashThing(Thing *thing) 497{ 498 Thing *i, *next; 499 for (i = things; i; i = next) { 500 next = i->next; 501 if (i->line_connected_to == thing) { 502 TrashThing(i); 503 next = things; /* start over in case this blew up the list. */ 504 } 505 } 506 DestroyThingInPoof(thing); 507} 508 509static void StreamThing_ontick(Thing *thing, Uint64 now) 510{ 511 if (!thing->line_connected_to) { 512 return; 513 } 514 515 /* are we playing? See if we're done, or update state. */ 516 if (thing->line_connected_to->what == THING_LOGDEV) { 517 const int available = SDL_GetAudioStreamAvailable(thing->data.stream.stream); 518 if (!available) { 519 DestroyThingInPoof(thing); 520 return; 521 } else { 522 thing->progress = 1.0f - (thing->data.stream.total_bytes ? (((float) (available)) / ((float) thing->data.stream.total_bytes)) : 0.0f); 523 } 524 } 525 526 if (thing->data.stream.next_level_update <= now) { 527 Uint64 perf = SDL_GetPerformanceCounter(); 528 int i; 529 for (i = 0; i < SDL_arraysize(thing->data.stream.levels); i++) { 530 thing->data.stream.levels[i] = (Uint8) (perf % 6); 531 perf >>= 3; 532 } 533 thing->data.stream.next_level_update += 150; 534 } 535} 536 537static void StreamThing_ondrag(Thing *thing, int button, float x, float y) 538{ 539 if (button == SDL_BUTTON_RIGHT) { /* this is kinda hacky, but use this to disconnect from a playing source. */ 540 if (thing->line_connected_to) { 541 SDL_UnbindAudioStream(thing->data.stream.stream); /* unbind from current device */ 542 if (thing->line_connected_to->what == THING_LOGDEV_RECORDING) { 543 SDL_FlushAudioStream(thing->data.stream.stream); 544 } 545 thing->line_connected_to = NULL; 546 } 547 } 548} 549 550static void StreamThing_ondrop(Thing *thing, int button, float x, float y) 551{ 552 if (droppable_highlighted_thing) { 553 if (droppable_highlighted_thing->what == THING_TRASHCAN) { 554 TrashThing(thing); 555 } else if (((droppable_highlighted_thing->what == THING_LOGDEV) || (droppable_highlighted_thing->what == THING_LOGDEV_RECORDING)) && (droppable_highlighted_thing != thing->line_connected_to)) { 556 /* connect to a logical device! */ 557 SDL_Log("Binding audio stream ('%s') to logical device %u", thing->titlebar, (unsigned int) droppable_highlighted_thing->data.logdev.devid); 558 if (thing->line_connected_to) { 559 SDL_UnbindAudioStream(thing->data.stream.stream); /* unbind from current device */ 560 if (thing->line_connected_to->what == THING_LOGDEV_RECORDING) { 561 SDL_FlushAudioStream(thing->data.stream.stream); 562 } 563 } 564 565 SDL_BindAudioStream(droppable_highlighted_thing->data.logdev.devid, thing->data.stream.stream); /* bind to new device! */ 566 thing->data.stream.total_bytes = SDL_GetAudioStreamAvailable(thing->data.stream.stream); 567 thing->progress = 0.0f; /* ontick will adjust this if we're on a playback device.*/ 568 thing->data.stream.next_level_update = SDL_GetTicks() + 100; 569 thing->line_connected_to = droppable_highlighted_thing; 570 } 571 } 572} 573 574static void StreamThing_onmousewheel(Thing *thing, float y) 575{ 576 thing->meter += y * 0.01f; 577 thing->meter = SDL_clamp(thing->meter, 0.0f, 1.0f); 578 SDL_SetAudioStreamGain(thing->data.stream.stream, thing->meter); 579} 580 581static void StreamThing_ondraw(Thing *thing, SDL_Renderer *renderer) 582{ 583 if (thing->line_connected_to) { /* are we playing? Update progress bar, and bounce the levels a little. */ 584 static const float xlocs[5] = { 18, 39, 59, 79, 99 }; 585 static const float ylocs[5] = { 49, 39, 29, 19, 10 }; 586 const float blockw = soundboard_levels_texture->w; 587 const float blockh = soundboard_levels_texture->h / 5.0f; 588 int i, j; 589 SDL_SetRenderDrawColor(renderer, thing->r, thing->g, thing->b, thing->a); 590 for (i = 0; i < SDL_arraysize(thing->data.stream.levels); i++) { 591 const int level = (int) thing->data.stream.levels[i]; 592 const float x = xlocs[i]; 593 for (j = 0; j < level; j++) { 594 const SDL_FRect src = { 0, soundboard_levels_texture->h - ((j+1) * blockh), blockw, blockh }; 595 const SDL_FRect dst = { thing->rect.x + x, thing->rect.y + ylocs[j], blockw, blockh }; 596 SDL_RenderTexture(renderer, soundboard_levels_texture->texture, &src, &dst); 597 } 598 } 599 } 600} 601 602static Thing *CreateStreamThing(const SDL_AudioSpec *spec, const Uint8 *buf, const Uint32 buflen, const char *fname, const float x, const float y) 603{ 604 static const ThingType can_be_dropped_onto[] = { THING_TRASHCAN, THING_LOGDEV, THING_LOGDEV_RECORDING, THING_NULL }; 605 Thing *thing = CreateThing(THING_STREAM, x, y, 0, -1, -1, soundboard_texture, fname); 606 if (thing) { 607 SDL_Log("Adding audio stream for %s", fname ? fname : "(null)"); 608 thing->data.stream.stream = SDL_CreateAudioStream(spec, spec); 609 if (buf && buflen) { 610 SDL_PutAudioStreamData(thing->data.stream.stream, buf, (int) buflen); 611 SDL_FlushAudioStream(thing->data.stream.stream); 612 thing->data.stream.total_bytes = SDL_GetAudioStreamAvailable(thing->data.stream.stream); 613 } 614 thing->ontick = StreamThing_ontick; 615 thing->ondrag = StreamThing_ondrag; 616 thing->ondrop = StreamThing_ondrop; 617 thing->ondraw = StreamThing_ondraw; 618 thing->onmousewheel = StreamThing_onmousewheel; 619 thing->can_be_dropped_onto = can_be_dropped_onto; 620 thing->meter = 1.0f; /* gain. */ 621 } 622 return thing; 623} 624 625static void WavThing_ondrag(Thing *thing, int button, float x, float y) 626{ 627 if (button == SDL_BUTTON_RIGHT) { /* drag out a new audio stream. */ 628 dragging_thing = CreateStreamThing(&thing->data.wav.spec, thing->data.wav.buf, thing->data.wav.buflen, thing->titlebar, x - (thing->rect.w / 2), y - (thing->rect.h / 2)); 629 } 630} 631 632static void WavThing_ondrop(Thing *thing, int button, float x, float y) 633{ 634 if (droppable_highlighted_thing) { 635 if (droppable_highlighted_thing->what == THING_TRASHCAN) { 636 TrashThing(thing); 637 } 638 } 639} 640 641static Thing *LoadWavThing(const char *fname, float x, float y) 642{ 643 Thing *thing = NULL; 644 char *path; 645 SDL_AudioSpec spec; 646 Uint8 *buf = NULL; 647 Uint32 buflen = 0; 648 649 path = GetNearbyFilename(fname); 650 if (path) { 651 fname = path; 652 } 653 654 if (SDL_LoadWAV(fname, &spec, &buf, &buflen)) { 655 static const ThingType can_be_dropped_onto[] = { THING_TRASHCAN, THING_NULL }; 656 char *titlebar = NULL; 657 const char *nodirs = SDL_strrchr(fname, '/'); 658 #ifdef SDL_PLATFORM_WINDOWS 659 const char *nodirs2 = SDL_strrchr(nodirs ? nodirs : fname, '\\'); 660 if (nodirs2) { 661 nodirs = nodirs2; 662 } 663 #endif 664 665 SDL_Log("Adding WAV file '%s'", fname); 666 667 if (nodirs) { 668 nodirs++; 669 } else { 670 nodirs = fname; 671 } 672 673 SDL_asprintf(&titlebar, "WAV file (\"%s\", %s, %s, %uHz)", nodirs, SDL_GetAudioFormatName(spec.format), AudioChansToStr(spec.channels), (unsigned int) spec.freq); 674 thing = CreateThing(THING_WAV, x - (audio_texture->w / 2), y - (audio_texture->h / 2), 5, -1, -1, audio_texture, titlebar); 675 if (thing) { 676 SDL_free(titlebar); 677 SDL_memcpy(&thing->data.wav.spec, &spec, sizeof (SDL_AudioSpec)); 678 thing->data.wav.buf = buf; 679 thing->data.wav.buflen = buflen; 680 thing->can_be_dropped_onto = can_be_dropped_onto; 681 thing->ondrag = WavThing_ondrag; 682 thing->ondrop = WavThing_ondrop; 683 } 684 } 685 686 SDL_free(path); 687 688 return thing; 689} 690 691static Thing *LoadStockWavThing(const char *fname) 692{ 693 char *path = GetNearbyFilename(fname); 694 Thing *thing = LoadWavThing(path ? path : fname, 0.0f, 0.0f); /* will reposition in a moment. */ 695 SDL_free(path); 696 return thing; 697} 698 699static void LoadStockWavThings(void) 700{ 701 LoadStockWavThing("sample.wav"); 702 RepositionRowOfThings(THING_WAV, -10.0f); 703} 704 705static void DestroyTexture(Texture *tex) 706{ 707 if (tex) { 708 if (state->renderers[0] != NULL) { /* if the renderer went away, this pointer is already bogus. */ 709 SDL_DestroyTexture(tex->texture); 710 } 711 SDL_free(tex); 712 } 713} 714 715static Texture *CreateTexture(const char *fname) 716{ 717 Texture *tex = (Texture *) SDL_calloc(1, sizeof (Texture)); 718 if (!tex) { 719 SDL_Log("Out of memory!"); 720 } else { 721 tex->texture = LoadTexture(state->renderers[0], fname, true); 722 if (!tex->texture) { 723 SDL_Log("Failed to load '%s': %s", fname, SDL_GetError()); 724 SDL_free(tex); 725 return NULL; 726 } 727 SDL_SetTextureBlendMode(tex->texture, SDL_BLENDMODE_BLEND); 728 tex->w = (float)tex->texture->w; 729 tex->h = (float)tex->texture->h; 730 } 731 return tex; 732} 733 734static Thing *CreateLogicalDeviceThing(Thing *parent, SDL_AudioDeviceID which, float x, float y); 735 736static void DeviceThing_ondrag(Thing *thing, int button, float x, float y) 737{ 738 if ((button == SDL_BUTTON_MIDDLE) && (thing->what == THING_LOGDEV_RECORDING)) { /* drag out a new stream. This is a UX mess. :/ */ 739 dragging_thing = CreateStreamThing(&thing->data.logdev.spec, NULL, 0, NULL, x, y); 740 if (dragging_thing) { 741 dragging_thing->data.stream.next_level_update = SDL_GetTicks() + 100; 742 SDL_BindAudioStream(thing->data.logdev.devid, dragging_thing->data.stream.stream); /* bind to new device! */ 743 dragging_thing->line_connected_to = thing; 744 } 745 } else if (button == SDL_BUTTON_RIGHT) { /* drag out a new logical device. */ 746 const SDL_AudioDeviceID which = ((thing->what == THING_LOGDEV) || (thing->what == THING_LOGDEV_RECORDING)) ? thing->data.logdev.devid : thing->data.physdev.devid; 747 const SDL_AudioDeviceID devid = SDL_OpenAudioDevice(which, NULL); 748 dragging_thing = devid ? CreateLogicalDeviceThing(thing, devid, x - (thing->rect.w / 2), y - (thing->rect.h / 2)) : NULL; 749 } 750} 751 752static void SetLogicalDeviceTitlebar(Thing *thing) 753{ 754 SDL_AudioSpec *spec = &thing->data.logdev.spec; 755 int frames = 0; 756 SDL_GetAudioDeviceFormat(thing->data.logdev.devid, spec, &frames); 757 SDL_free(thing->titlebar); 758 SDL_asprintf(&thing->titlebar, "Logical device #%u (%s, %s, %s, %uHz, %d frames)", (unsigned int) thing->data.logdev.devid, thing->data.logdev.recording ? "RECORDING" : "PLAYBACK", SDL_GetAudioFormatName(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames); 759} 760 761static void LogicalDeviceThing_ondrop(Thing *thing, int button, float x, float y) 762{ 763 if (droppable_highlighted_thing) { 764 if (droppable_highlighted_thing->what == THING_TRASHCAN) { 765 TrashThing(thing); 766 } 767 } 768} 769 770static void SDLCALL PostmixCallback(void *userdata, const SDL_AudioSpec *spec, float *buffer, int buflen) 771{ 772 Thing *thing = (Thing *) userdata; 773 774 SDL_LockMutex(thing->data.logdev.postmix_lock); 775 776 if (thing->data.logdev.postmix_allocated < buflen) { 777 void *ptr = SDL_realloc(thing->data.logdev.postmix_buffer, buflen); 778 if (!ptr) { 779 SDL_UnlockMutex(thing->data.logdev.postmix_lock); 780 return; /* oh well. */ 781 } 782 thing->data.logdev.postmix_buffer = (float *) ptr; 783 thing->data.logdev.postmix_allocated = buflen; 784 } 785 786 SDL_copyp(&thing->data.logdev.postmix_spec, spec); 787 SDL_memcpy(thing->data.logdev.postmix_buffer, buffer, buflen); 788 thing->data.logdev.postmix_buflen = buflen; 789 SDL_SetAtomicInt(&thing->data.logdev.postmix_updated, 1); 790 791 SDL_UnlockMutex(thing->data.logdev.postmix_lock); 792} 793 794static void UpdateVisualizer(SDL_Renderer *renderer, SDL_Texture *visualizer, const int channels, const float *buffer, const int buflen) 795{ 796 static const SDL_Color channel_colors[8] = { 797 { 255, 255, 255, 255 }, 798 { 255, 0, 0, 255 }, 799 { 0, 255, 0, 255 }, 800 { 0, 0, 255, 255 }, 801 { 255, 255, 0, 255 }, 802 { 0, 255, 255, 255 }, 803 { 255, 0, 255, 255 }, 804 { 127, 127, 127, 255 } 805 }; 806 807 SDL_SetRenderTarget(renderer, visualizer); 808 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); 809 SDL_RenderClear(renderer); 810 811 if (buffer && buflen) { 812 const int frames = (buflen / sizeof (float)) / channels; 813 const int skip = frames / (VISUALIZER_WIDTH * 2); 814 int i, j; 815 816 for (i = channels - 1; i >= 0; i--) { 817 const SDL_Color *color = &channel_colors[i % SDL_arraysize(channel_colors)]; 818 SDL_FPoint points[VISUALIZER_WIDTH + 2]; 819 float prevx = 0.0f; 820 int pointidx = 1; 821 822 points[0].x = 0.0f; 823 points[0].y = VISUALIZER_HEIGHT * 0.5f; 824 825 for (j = 0; j < (SDL_arraysize(points)-1); j++) { 826 const float val = buffer[((j * skip) * channels) + i]; 827 const float x = prevx + 2; 828 const float y = (VISUALIZER_HEIGHT * 0.5f) - (VISUALIZER_HEIGHT * (val * 0.5f)); 829 SDL_assert(pointidx < SDL_arraysize(points)); 830 points[pointidx].x = x; 831 points[pointidx].y = y; 832 pointidx++; 833 prevx = x; 834 } 835 836 SDL_SetRenderDrawColor(renderer, color->r, color->g, color->b, 255); 837 SDL_RenderLines(renderer, points, pointidx); 838 } 839 } 840 841 SDL_SetRenderTarget(renderer, NULL); 842} 843 844static void LogicalDeviceThing_ontick(Thing *thing, Uint64 now) 845{ 846 const bool ismousedover = (thing == mouseover_thing); 847 848 if (!thing->data.logdev.visualizer || !thing->data.logdev.postmix_lock) { /* need these to work, skip if they failed. */ 849 return; 850 } 851 852 if (thing->data.logdev.visualizer_enabled != ismousedover) { 853 thing->data.logdev.visualizer_enabled = ismousedover; 854 if (!ismousedover) { 855 SDL_SetAudioPostmixCallback(thing->data.logdev.devid, NULL, NULL); 856 } else { 857 if (thing->data.logdev.postmix_buffer) { 858 SDL_memset(thing->data.logdev.postmix_buffer, '\0', thing->data.logdev.postmix_buflen); 859 } 860 SDL_SetAtomicInt(&thing->data.logdev.postmix_updated, 1); /* so this will at least clear the texture later. */ 861 SDL_SetAudioPostmixCallback(thing->data.logdev.devid, PostmixCallback, thing); 862 } 863 } 864} 865 866static void LogicalDeviceThing_ondraw(Thing *thing, SDL_Renderer *renderer) 867{ 868 if (thing->data.logdev.visualizer_enabled) { 869 SDL_FRect dst; 870 dst.w = thing->rect.w; 871 dst.h = thing->rect.h; 872 dst.x = thing->rect.x + ((thing->rect.w - dst.w) / 2); 873 dst.y = thing->rect.y + ((thing->rect.h - dst.h) / 2); 874 875 if (SDL_GetAtomicInt(&thing->data.logdev.postmix_updated)) { 876 float *buffer; 877 int channels; 878 int buflen; 879 880 SDL_LockMutex(thing->data.logdev.postmix_lock); 881 channels = thing->data.logdev.postmix_spec.channels; 882 buflen = thing->data.logdev.postmix_buflen; 883 buffer = (float *) SDL_malloc(thing->data.logdev.postmix_buflen); 884 if (buffer) { 885 SDL_memcpy(buffer, thing->data.logdev.postmix_buffer, thing->data.logdev.postmix_buflen); 886 SDL_SetAtomicInt(&thing->data.logdev.postmix_updated, 0); 887 } 888 SDL_UnlockMutex(thing->data.logdev.postmix_lock); 889 890 UpdateVisualizer(renderer, thing->data.logdev.visualizer, channels, buffer, buflen); 891 SDL_free(buffer); 892 } 893 894 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 30); 895 SDL_RenderTexture(renderer, thing->data.logdev.visualizer, NULL, &dst); 896 } 897} 898 899static void LogicalDeviceThing_onmousewheel(Thing *thing, float y) 900{ 901 thing->meter += y * 0.01f; 902 thing->meter = SDL_clamp(thing->meter, 0.0f, 1.0f); 903 SDL_SetAudioDeviceGain(thing->data.logdev.devid, thing->meter); 904} 905 906static Thing *CreateLogicalDeviceThing(Thing *parent, SDL_AudioDeviceID which, float x, float y) 907{ 908 static const ThingType can_be_dropped_onto[] = { THING_TRASHCAN, THING_NULL }; 909 Thing *physthing = ((parent->what == THING_LOGDEV) || (parent->what == THING_LOGDEV_RECORDING)) ? parent->data.logdev.physdev : parent; 910 const bool recording = physthing->data.physdev.recording; 911 Thing *thing; 912 913 SDL_Log("Adding logical audio device %u", (unsigned int) which); 914 thing = CreateThing(recording ? THING_LOGDEV_RECORDING : THING_LOGDEV, x, y, 5, -1, -1, logdev_texture, NULL); 915 if (thing) { 916 thing->data.logdev.devid = which; 917 thing->data.logdev.recording = recording; 918 thing->data.logdev.physdev = physthing; 919 thing->data.logdev.visualizer = SDL_CreateTexture(state->renderers[0], SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, VISUALIZER_WIDTH, VISUALIZER_HEIGHT); 920 thing->data.logdev.postmix_lock = SDL_CreateMutex(); 921 if (thing->data.logdev.visualizer) { 922 SDL_SetTextureBlendMode(thing->data.logdev.visualizer, SDL_BLENDMODE_BLEND); 923 } 924 thing->line_connected_to = physthing; 925 thing->meter = 1.0f; 926 thing->ontick = LogicalDeviceThing_ontick; 927 thing->ondrag = DeviceThing_ondrag; 928 thing->ondrop = LogicalDeviceThing_ondrop; 929 thing->ondraw = LogicalDeviceThing_ondraw; 930 thing->onmousewheel = LogicalDeviceThing_onmousewheel; 931 thing->can_be_dropped_onto = can_be_dropped_onto; 932 933 SetLogicalDeviceTitlebar(thing); 934 } 935 return thing; 936} 937 938static void SetPhysicalDeviceTitlebar(Thing *thing) 939{ 940 int frames = 0; 941 SDL_AudioSpec *spec = &thing->data.physdev.spec; 942 SDL_GetAudioDeviceFormat(thing->data.physdev.devid, spec, &frames); 943 SDL_free(thing->titlebar); 944 if (thing->data.physdev.devid == SDL_AUDIO_DEVICE_DEFAULT_RECORDING) { 945 SDL_asprintf(&thing->titlebar, "Default system device (RECORDING, %s, %s, %uHz, %d frames)", SDL_GetAudioFormatName(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames); 946 } else if (thing->data.physdev.devid == SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) { 947 SDL_asprintf(&thing->titlebar, "Default system device (PLAYBACK, %s, %s, %uHz, %d frames)", SDL_GetAudioFormatName(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames); 948 } else { 949 SDL_asprintf(&thing->titlebar, "Physical device #%u (%s, \"%s\", %s, %s, %uHz, %d frames)", (unsigned int) thing->data.physdev.devid, thing->data.physdev.recording ? "RECORDING" : "PLAYBACK", thing->data.physdev.name, SDL_GetAudioFormatName(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames); 950 } 951} 952 953static void PhysicalDeviceThing_ondrop(Thing *thing, int button, float x, float y) 954{ 955 if (droppable_highlighted_thing) { 956 if (droppable_highlighted_thing->what == THING_TRASHCAN) { 957 TrashThing(thing); 958 } 959 } 960} 961 962static void PhysicalDeviceThing_ontick(Thing *thing, Uint64 now) 963{ 964 const int lifetime = POOF_LIFETIME; 965 const int elapsed = (int) (now - thing->createticks); 966 if (elapsed > lifetime) { 967 thing->scale = 1.0f; 968 thing->a = 255; 969 thing->ontick = NULL; /* no more ticking. */ 970 } else { 971 const float pct = ((float) elapsed) / ((float) lifetime); 972 thing->a = (Uint8) (int) (pct * 255.0f); 973 thing->scale = pct; /* grow to normal size */ 974 } 975} 976 977 978static Thing *CreatePhysicalDeviceThing(const SDL_AudioDeviceID which, const bool recording) 979{ 980 static const ThingType can_be_dropped_onto[] = { THING_TRASHCAN, THING_NULL }; 981 static float next_physdev_x = 0; 982 Thing *thing; 983 int winw, winh; 984 985 SDL_GetWindowSize(state->windows[0], &winw, &winh); 986 if (next_physdev_x > (winw-physdev_texture->w)) { 987 next_physdev_x = 0; 988 } 989 990 SDL_Log("Adding physical audio device %u", (unsigned int) which); 991 thing = CreateThing(recording ? THING_PHYSDEV_RECORDING : THING_PHYSDEV, next_physdev_x, 170, 5, -1, -1, physdev_texture, NULL); 992 if (thing) { 993 const char *name = SDL_GetAudioDeviceName(which); 994 thing->data.physdev.devid = which; 995 thing->data.physdev.recording = recording; 996 thing->data.physdev.name = name ? SDL_strdup(name) : NULL; 997 thing->ondrag = DeviceThing_ondrag; 998 thing->ondrop = PhysicalDeviceThing_ondrop; 999 thing->ontick = PhysicalDeviceThing_ontick; 1000 thing->can_be_dropped_onto = can_be_dropped_onto; 1001 1002 SetPhysicalDeviceTitlebar(thing); 1003 if (SDL_GetTicks() <= (app_ready_ticks + 2000)) { /* assume this is the initial batch if it happens in the first two seconds. */ 1004 RepositionRowOfThings(THING_PHYSDEV, 10.0f); /* don't rearrange them after the initial add. */ 1005 RepositionRowOfThings(THING_PHYSDEV_RECORDING, 170.0f); /* don't rearrange them after the initial add. */ 1006 next_physdev_x = 0.0f; 1007 } else { 1008 next_physdev_x += physdev_texture->w * 1.5f; 1009 } 1010 } 1011 1012 return thing; 1013} 1014 1015static Thing *CreateTrashcanThing(void) 1016{ 1017 int winw, winh; 1018 SDL_GetWindowSize(state->windows[0], &winw, &winh); 1019 return CreateThing(THING_TRASHCAN, winw - trashcan_texture->w, winh - trashcan_texture->h, 10, -1, -1, trashcan_texture, "Drag things here to remove them."); 1020} 1021 1022static Thing *CreateDefaultPhysicalDevice(const bool recording) 1023{ 1024 return CreatePhysicalDeviceThing(recording ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING : SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, recording); 1025} 1026 1027static void TickThings(void) 1028{ 1029 Thing *i; 1030 Thing *next; 1031 const Uint64 now = SDL_GetTicks(); 1032 for (i = things; i; i = next) { 1033 next = i->next; /* in case this deletes itself. */ 1034 if (i->ontick) { 1035 i->ontick(i, now); 1036 } 1037 } 1038} 1039 1040static void WindowResized(const int newwinw, const int newwinh) 1041{ 1042 Thing *i; 1043 const float neww = (float) newwinw; 1044 const float newh = (float) newwinh; 1045 const float oldw = (float) state->window_w; 1046 const float oldh = (float) state->window_h; 1047 for (i = things; i; i = i->next) { 1048 const float halfw = i->rect.w / 2.0f; 1049 const float halfh = i->rect.h / 2.0f; 1050 const float x = (i->rect.x + halfw) / oldw; 1051 const float y = (i->rect.y + halfh) / oldh; 1052 i->rect.x = (x * neww) - halfw; 1053 i->rect.y = (y * newh) - halfh; 1054 } 1055 state->window_w = newwinw; 1056 state->window_h = newwinh; 1057} 1058 1059SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) 1060{ 1061 int i; 1062 1063 char version[32]; /* use SDL's version number, since this test program is part of SDL's sources. */ 1064 SDL_snprintf(version, sizeof (version), "%d.%d.%d", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION); 1065 SDL_SetAppMetadata("SDL testaudio", version, "org.libsdl.testaudio"); 1066 1067 state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO | SDL_INIT_AUDIO); 1068 if (!state) { 1069 return SDL_APP_FAILURE; 1070 } 1071 1072 state->window_flags |= SDL_WINDOW_RESIZABLE; 1073 1074 for (i = 1; i < argc;) { 1075 int consumed = SDLTest_CommonArg(state, i); 1076 if (consumed == 0) { 1077 consumed = -1; 1078 /* add our own command lines here. */ 1079 } 1080 if (consumed < 0) { 1081 static const char *options[] = { 1082 /* add our own command lines here. */ 1083 /*"[--blend none|blend|add|mod|mul|sub]",*/ 1084 NULL 1085 }; 1086 SDLTest_CommonLogUsage(state, argv[0], options); 1087 return SDL_APP_FAILURE; 1088 } 1089 i += consumed; 1090 } 1091 1092 if (!SDLTest_CommonInit(state)) { 1093 return SDL_APP_FAILURE; 1094 } 1095 1096 if (state->audio_id) { 1097 SDL_CloseAudioDevice(state->audio_id); 1098 state->audio_id = 0; 1099 } 1100 1101 SetDefaultTitleBar(); 1102 1103 if ((physdev_texture = CreateTexture("physaudiodev.png")) == NULL) { return SDL_APP_FAILURE; } 1104 if ((logdev_texture = CreateTexture("logaudiodev.png")) == NULL) { return SDL_APP_FAILURE; } 1105 if ((audio_texture = CreateTexture("audiofile.png")) == NULL) { return SDL_APP_FAILURE; } 1106 if ((trashcan_texture = CreateTexture("trashcan.png")) == NULL) { return SDL_APP_FAILURE; } 1107 if ((soundboard_texture = CreateTexture("soundboard.png")) == NULL) { return SDL_APP_FAILURE; } 1108 if ((soundboard_levels_texture = CreateTexture("soundboard_levels.png")) == NULL) { return SDL_APP_FAILURE; } 1109 1110 LoadStockWavThings(); 1111 CreateTrashcanThing(); 1112 CreateDefaultPhysicalDevice(false); 1113 CreateDefaultPhysicalDevice(true); 1114 1115 return SDL_APP_CONTINUE; 1116} 1117 1118 1119static bool saw_event = false; 1120 1121SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) 1122{ 1123 Thing *thing = NULL; 1124 1125 saw_event = true; 1126 SDL_ConvertEventToRenderCoordinates(SDL_GetRenderer(SDL_GetWindowFromEvent(event)), event); 1127 1128 switch (event->type) { 1129 case SDL_EVENT_MOUSE_MOTION: 1130 thing = UpdateMouseOver(event->motion.x, event->motion.y); 1131 if ((dragging_button == -1) && event->motion.state) { 1132 if (event->motion.state & SDL_BUTTON_LMASK) { 1133 /* for people that don't have all three buttons... */ 1134 if (ctrl_held) { 1135 dragging_button = SDL_BUTTON_RIGHT; 1136 } else if (alt_held) { 1137 dragging_button = SDL_BUTTON_MIDDLE; 1138 } else { 1139 dragging_button = SDL_BUTTON_LEFT; 1140 } 1141 dragging_button_real = SDL_BUTTON_LEFT; 1142 } else if (event->motion.state & SDL_BUTTON_RMASK) { 1143 dragging_button = SDL_BUTTON_RIGHT; 1144 dragging_button_real = SDL_BUTTON_RIGHT; 1145 } else if (event->motion.state & SDL_BUTTON_MMASK) { 1146 dragging_button = SDL_BUTTON_MIDDLE; 1147 dragging_button_real = SDL_BUTTON_MIDDLE; 1148 } 1149 1150 if (dragging_button != -1) { 1151 dragging_thing = thing; 1152 if (thing && thing->ondrag) { 1153 thing->ondrag(thing, dragging_button, event->motion.x, event->motion.y); 1154 } 1155 } 1156 } 1157 1158 droppable_highlighted_thing = NULL; 1159 if (dragging_thing) { 1160 dragging_thing->rect.x = event->motion.x - (dragging_thing->rect.w / 2); 1161 dragging_thing->rect.y = event->motion.y - (dragging_thing->rect.h / 2); 1162 if (dragging_thing->can_be_dropped_onto) { 1163 thing = FindThingAtPoint(event->motion.x, event->motion.y); 1164 if (thing) { 1165 int i; 1166 for (i = 0; dragging_thing->can_be_dropped_onto[i]; i++) { 1167 if (dragging_thing->can_be_dropped_onto[i] == thing->what) { 1168 droppable_highlighted_thing = thing; 1169 break; 1170 } 1171 } 1172 } 1173 } 1174 } 1175 break; 1176 1177 case SDL_EVENT_MOUSE_BUTTON_DOWN: 1178 thing = UpdateMouseOver(event->button.x, event->button.y); 1179 break; 1180 1181 case SDL_EVENT_MOUSE_BUTTON_UP: 1182 if (dragging_button_real == event->button.button) { 1183 Thing *dropped_thing = dragging_thing; 1184 dragging_thing = NULL; 1185 dragging_button = -1; 1186 dragging_button_real = -1; 1187 if (dropped_thing && dropped_thing->ondrop) { 1188 dropped_thing->ondrop(dropped_thing, event->button.button, event->button.x, event->button.y); 1189 } 1190 droppable_highlighted_thing = NULL; 1191 } 1192 thing = UpdateMouseOver(event->button.x, event->button.y); 1193 break; 1194 1195 case SDL_EVENT_MOUSE_WHEEL: 1196 thing = UpdateMouseOver(event->wheel.mouse_x, event->wheel.mouse_y); 1197 if (thing && thing->onmousewheel) { 1198 thing->onmousewheel(thing, event->wheel.y * ((event->wheel.direction == SDL_MOUSEWHEEL_FLIPPED) ? -1.0f : 1.0f)); 1199 } 1200 break; 1201 1202 case SDL_EVENT_KEY_DOWN: 1203 case SDL_EVENT_KEY_UP: 1204 ctrl_held = ((event->key.mod & SDL_KMOD_CTRL) != 0); 1205 alt_held = ((event->key.mod & SDL_KMOD_ALT) != 0); 1206 break; 1207 1208 case SDL_EVENT_DROP_FILE: 1209 SDL_Log("Drop file! '%s'", event->drop.data); 1210 LoadWavThing(event->drop.data, event->drop.x, event->drop.y); 1211 /* SDL frees event->drop.data for you when you use SDL_AppEvent(). */ 1212 break; 1213 1214 case SDL_EVENT_WINDOW_RESIZED: 1215 WindowResized(event->window.data1, event->window.data2); 1216 break; 1217 1218 case SDL_EVENT_AUDIO_DEVICE_ADDED: 1219 CreatePhysicalDeviceThing(event->adevice.which, event->adevice.recording); 1220 break; 1221 1222 case SDL_EVENT_AUDIO_DEVICE_REMOVED: { 1223 const SDL_AudioDeviceID which = event->adevice.which; 1224 Thing *i, *next; 1225 SDL_Log("Removing audio device %u", (unsigned int) which); 1226 for (i = things; i; i = next) { 1227 next = i->next; 1228 if (((i->what == THING_PHYSDEV) || (i->what == THING_PHYSDEV_RECORDING)) && (i->data.physdev.devid == which)) { 1229 TrashThing(i); 1230 next = things; /* in case we mangled the list. */ 1231 } else if (((i->what == THING_LOGDEV) || (i->what == THING_LOGDEV_RECORDING)) && (i->data.logdev.devid == which)) { 1232 TrashThing(i); 1233 next = things; /* in case we mangled the list. */ 1234 } 1235 } 1236 break; 1237 } 1238 1239 default: break; 1240 } 1241 1242 return SDLTest_CommonEventMainCallbacks(state, event); 1243} 1244 1245SDL_AppResult SDL_AppIterate(void *appstate) 1246{ 1247 if (app_ready_ticks == 0) { 1248 app_ready_ticks = SDL_GetTicks(); 1249 } 1250 1251 TickThings(); 1252 Draw(); 1253 1254 if (saw_event) { 1255 saw_event = false; /* reset this so we know when SDL_AppEvent() runs again */ 1256 } else { 1257 SDL_Delay(10); 1258 } 1259 1260 return SDL_APP_CONTINUE; 1261} 1262 1263void SDL_AppQuit(void *appstate, SDL_AppResult result) 1264{ 1265 while (things) { 1266 DestroyThing(things); /* make sure all the audio devices are closed, etc. */ 1267 } 1268 1269 DestroyTexture(physdev_texture); 1270 DestroyTexture(logdev_texture); 1271 DestroyTexture(audio_texture); 1272 DestroyTexture(trashcan_texture); 1273 DestroyTexture(soundboard_texture); 1274 DestroyTexture(soundboard_levels_texture); 1275 SDLTest_CommonQuit(state); 1276} 1277 1278
[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.