From 195c26a50aa50966ea80d0d408a412d4d1c5a34c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 24 Jul 2024 17:30:16 -0400 Subject: [PATCH] examples: Add a way to add datafiles to an example, and add audio/load-wav --- build-scripts/build-web-examples.pl | 19 ++++- examples/CMakeLists.txt | 19 +++-- examples/audio/03-load-wav/datafiles.txt | 1 + examples/audio/03-load-wav/load-wav.c | 104 +++++++++++++++++++++++ 4 files changed, 133 insertions(+), 10 deletions(-) create mode 100644 examples/audio/03-load-wav/datafiles.txt create mode 100644 examples/audio/03-load-wav/load-wav.c diff --git a/build-scripts/build-web-examples.pl b/build-scripts/build-web-examples.pl index 06eefd4ab2ba2..45cf96984ecca 100755 --- a/build-scripts/build-web-examples.pl +++ b/build-scripts/build-web-examples.pl @@ -80,14 +80,31 @@ sub handle_example_dir { } closedir($dh); + my $datafilestxtpath = "$examples_dir/$category/$example/datafiles.txt"; + my $datafiles_str = ''; + if ( -f $datafilestxtpath ) { + open (my $datafilesh, '<', $datafilestxtpath) or die("Couldn't opendir '$datafilestxtpath': $!\n"); + $spc = ''; + while (<$datafilesh>) { + chomp; + my $path = "$examples_dir/$category/$example/$_"; + my $fname = $_; + $fname =~ s/\A.*\///; + $datafiles_str .= "$spc--embed-file=$path\@/$fname"; + $spc = ' '; + } + close($datafilesh); + } + my $dst = "$output_dir/$category/$example"; print("Building $category/$example ...\n"); + do_mkdir($dst); # !!! FIXME: hardcoded SDL3 references, need to fix this for satellite libraries and SDL2. - do_system("EMSDK_QUIET=1 source '$emsdk_dir/emsdk_env.sh' && emcc -s USE_SDL=0 -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=1gb -s ASSERTIONS=0 -o '$dst/index.js' '-I$examples_dir/../include' $files_str '$compile_dir/libSDL3.a'") == 0 + do_system("EMSDK_QUIET=1 source '$emsdk_dir/emsdk_env.sh' && emcc -s USE_SDL=0 -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=1gb -s ASSERTIONS=0 -o '$dst/index.js' '-I$examples_dir/../include' $files_str '$compile_dir/libSDL3.a' $datafiles_str") == 0 or die("Failed to build $category/$example!\n"); my $highlight_cmd = "highlight '--outdir=$dst' --style-outfile=highlight.css --fragment --enclose-pre --stdout --syntax=c '--plug-in=$examples_dir/highlight-plugin.lua'"; diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8ac66d911f68c..fa189750c2651 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -70,7 +70,7 @@ if(WINDOWS_STORE) endif() macro(add_sdl_example_executable TARGET) - cmake_parse_arguments(AST "BUILD_DEPENDENT;NEEDS_RESOURCES;" "" "SOURCES" ${ARGN}) + cmake_parse_arguments(AST "BUILD_DEPENDENT" "" "SOURCES;DATAFILES" ${ARGN}) if(AST_UNPARSED_ARGUMENTS) message(FATAL_ERROR "Unknown argument(s): ${AST_UNPARSED_ARGUMENTS}") endif() @@ -100,8 +100,8 @@ macro(add_sdl_example_executable TARGET) "../test/uwp/splash-620x300.png" ) endif() - if(AST_NEEDS_RESOURCES) - list(APPEND EXTRA_SOURCES ${RESOURCE_FILES}) + if(AST_DATAFILES) + list(APPEND EXTRA_SOURCES ${DATAFILES}) endif() if(ANDROID) add_library(${TARGET} SHARED ${AST_SOURCES} ${EXTRA_SOURCES}) @@ -113,11 +113,11 @@ macro(add_sdl_example_executable TARGET) target_link_libraries(${TARGET} PRIVATE SDL3::${sdl_name_component}) list(APPEND SDL_EXAMPLE_EXECUTABLES ${TARGET}) - if(AST_NEEDS_RESOURCES) + if(AST_DATAFILES) if(PSP OR PS2) add_custom_command(TARGET ${TARGET} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS -E make_directory $/sdl-${TARGET} - COMMAND ${CMAKE_COMMAND} ARGS -E copy_if_different ${RESOURCE_FILES} $/sdl-${TARGET}) + COMMAND ${CMAKE_COMMAND} ARGS -E copy_if_different ${AST_DATAFILES} $/sdl-${TARGET}) elseif(WINDOWS_STORE) # MSVC does build the dependent targets (or POST_BUILD commands) when building an application # after starting to debug. By copying the resources in a custom target, the files can be copied afterwards. @@ -125,7 +125,7 @@ macro(add_sdl_example_executable TARGET) cmake_minimum_required(VERSION 3.19) add_custom_target(zzz-resources-copy-${TARGET} COMMAND ${CMAKE_COMMAND} -E make_directory "$/AppX" - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RESOURCE_FILES} "$/AppX" + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${AST_DATAFILES} "$/AppX" ) add_dependencies(${TARGET} zzz-resources-copy-${TARGET}) else() @@ -133,15 +133,15 @@ macro(add_sdl_example_executable TARGET) endif() if(APPLE) # Make sure resource files get installed into macOS/iOS .app bundles. - set_target_properties(${TARGET} PROPERTIES RESOURCE "${RESOURCE_FILES}") + set_target_properties(${TARGET} PROPERTIES RESOURCE "${AST_DATAFILES}") endif() if(EMSCRIPTEN) - foreach(res IN LISTS RESOURCE_FILES) + foreach(res IN LISTS AST_DATAFILES) get_filename_component(res_name "${res}" NAME) target_link_options(${TARGET} PRIVATE "SHELL:--embed-file ${res}@${res_name}") endforeach() endif() - set_property(TARGET ${TARGET} APPEND PROPERTY ADDITIONAL_CLEAN_FILES "$/$$/>") + set_property(TARGET ${TARGET} APPEND PROPERTY ADDITIONAL_CLEAN_FILES "$/$$/>") endif() if(WINDOWS) @@ -180,6 +180,7 @@ add_sdl_example_executable(renderer-clear SOURCES renderer/01-clear/renderer-cle add_sdl_example_executable(renderer-primitives SOURCES renderer/02-primitives/renderer-primitives.c) add_sdl_example_executable(audio-simple-playback SOURCES audio/01-simple-playback/simple-playback.c) add_sdl_example_executable(audio-simple-playback-callback SOURCES audio/02-simple-playback-callback/simple-playback-callback.c) +add_sdl_example_executable(audio-load-wav SOURCES audio/03-load-wav/load-wav.c DATAFILES ../test/sample.wav) if(PSP) diff --git a/examples/audio/03-load-wav/datafiles.txt b/examples/audio/03-load-wav/datafiles.txt new file mode 100644 index 0000000000000..a034029796ee5 --- /dev/null +++ b/examples/audio/03-load-wav/datafiles.txt @@ -0,0 +1 @@ +../../../test/sample.wav diff --git a/examples/audio/03-load-wav/load-wav.c b/examples/audio/03-load-wav/load-wav.c new file mode 100644 index 0000000000000..0a24a15ebba66 --- /dev/null +++ b/examples/audio/03-load-wav/load-wav.c @@ -0,0 +1,104 @@ +/* + * This example code creates an simple audio stream for playing sound, and + * loads a .wav file that is pushed through the stream in a loop. + * + * This code is public domain. Feel free to use it for any purpose! + * + * The .wav file is a sample from Will Provost's song, The Living Proof, + * used with permission. + * + * From the album The Living Proof + * Publisher: 5 Guys Named Will + * Copyright 1996 Will Provost + * https://itunes.apple.com/us/album/the-living-proof/id4153978 + * http://www.amazon.com/The-Living-Proof-Will-Provost/dp/B00004R8RH + */ + +#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ +#include +#include + +/* We will use this renderer to draw into this window every frame. */ +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; +static SDL_AudioStream *stream = NULL; +static Uint8 *wav_data = NULL; +static Uint32 wav_data_len = 0; + +/* This function runs once at startup. */ +int SDL_AppInit(void **appstate, int argc, char *argv[]) +{ + SDL_AudioSpec spec; + char *wav_path = NULL; + + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) == -1) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't initialize SDL!", SDL_GetError(), NULL); + return SDL_APP_FAILURE; + } + + /* we don't _need_ a window for audio-only things but it's good policy to have one. */ + if (SDL_CreateWindowAndRenderer("examples/audio/load-wav", 640, 480, 0, &window, &renderer) == -1) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window/renderer!", SDL_GetError(), NULL); + return SDL_APP_FAILURE; + } + + /* Load the .wav file from wherever the app is being run from. */ + SDL_asprintf(&wav_path, "%ssample.wav", SDL_GetBasePath()); /* allocate a string of the full file path */ + if (SDL_LoadWAV(wav_path, &spec, &wav_data, &wav_data_len) == -1) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't load .wav file!", SDL_GetError(), NULL); + return SDL_APP_FAILURE; + } + + SDL_free(wav_path); /* done with this string. */ + + /* Create our audio stream in the same format as the .wav file. It'll convert to what the audio hardware wants. */ + stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, NULL, NULL); + if (!stream) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create audio stream!", SDL_GetError(), window); + return SDL_APP_FAILURE; + } + + /* SDL_OpenAudioDeviceStream starts the device paused. You have to tell it to start! */ + SDL_ResumeAudioStreamDevice(stream); + + /* (this is a web browser requirement, not an SDL thing.) */ + SDL_Log("If you're running this in a web browser, you need to click the window before you'll hear anything."); + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ +int SDL_AppEvent(void *appstate, const SDL_Event *event) +{ + if (event->type == SDL_EVENT_QUIT) { + return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */ + } + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs once per frame, and is the heart of the program. */ +int SDL_AppIterate(void *appstate) +{ + /* see if we need to feed the audio stream more data yet. + We're being lazy here, but if there's less than the entire wav file left to play, + just shove a whole copy of it into the queue, so we always have _tons_ of + data queued for playback. */ + if (SDL_GetAudioStreamAvailable(stream) < wav_data_len) { + /* feed more data to the stream. It will queue at the end, and trickle out as the hardware needs more data. */ + SDL_PutAudioStreamData(stream, wav_data, wav_data_len); + } + + /* we're not doing anything with the renderer, so just blank it out. */ + SDL_RenderClear(renderer); + SDL_RenderPresent(renderer); + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs once at shutdown. */ +void SDL_AppQuit(void *appstate) +{ + SDL_free(wav_data); /* strictly speaking, this isn't necessary because the process is ending, but it's good policy. */ + /* SDL will clean up the window/renderer for us. */ +} +