SDL_mixer: examples: Added basic examples!

From 2326c7393cef840d2aec9074bbf1f98a60ff103b Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Sat, 24 Jan 2026 16:41:50 -0500
Subject: [PATCH] examples: Added basic examples!

Reference Issue #807.
---
 CMakeLists.txt                                |   8 +
 examples/CMakeLists.txt                       | 345 ++++++++++++++++++
 examples/README.md                            |  82 +++++
 examples/basics/01-load-and-play/README.txt   |   1 +
 .../basics/01-load-and-play/load-and-play.c   | 111 ++++++
 .../basics/02-play-with-options/README.txt    |   2 +
 .../02-play-with-options/play-with-options.c  | 126 +++++++
 .../basics/03-play-multiple-sounds/README.txt |   1 +
 .../play-multiple-sounds.c                    | 143 ++++++++
 examples/basics/04-metadata/README.txt        |   1 +
 examples/basics/04-metadata/metadata.c        | 145 ++++++++
 examples/basics/05-sinewave/README.txt        |   1 +
 examples/basics/05-sinewave/sinewave.c        | 106 ++++++
 examples/basics/06-seeking/README.txt         |   2 +
 examples/basics/06-seeking/seeking.c          | 144 ++++++++
 examples/highlight-plugin.lua                 | 100 +++++
 examples/music.mp3                            | Bin 0 -> 551040 bytes
 examples/sword.wav                            | Bin 0 -> 91358 bytes
 examples/template-category.html               |  43 +++
 examples/template-homepage.html               |  44 +++
 examples/template-placeholder.png             | Bin 0 -> 34957 bytes
 examples/template.c                           |  55 +++
 examples/template.css                         | 284 ++++++++++++++
 examples/template.html                        | 292 +++++++++++++++
 24 files changed, 2036 insertions(+)
 create mode 100644 examples/CMakeLists.txt
 create mode 100644 examples/README.md
 create mode 100644 examples/basics/01-load-and-play/README.txt
 create mode 100644 examples/basics/01-load-and-play/load-and-play.c
 create mode 100644 examples/basics/02-play-with-options/README.txt
 create mode 100644 examples/basics/02-play-with-options/play-with-options.c
 create mode 100644 examples/basics/03-play-multiple-sounds/README.txt
 create mode 100644 examples/basics/03-play-multiple-sounds/play-multiple-sounds.c
 create mode 100644 examples/basics/04-metadata/README.txt
 create mode 100644 examples/basics/04-metadata/metadata.c
 create mode 100644 examples/basics/05-sinewave/README.txt
 create mode 100644 examples/basics/05-sinewave/sinewave.c
 create mode 100644 examples/basics/06-seeking/README.txt
 create mode 100644 examples/basics/06-seeking/seeking.c
 create mode 100644 examples/highlight-plugin.lua
 create mode 100644 examples/music.mp3
 create mode 100644 examples/sword.wav
 create mode 100644 examples/template-category.html
 create mode 100644 examples/template-homepage.html
 create mode 100644 examples/template-placeholder.png
 create mode 100644 examples/template.c
 create mode 100644 examples/template.css
 create mode 100644 examples/template.html

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 17be40b9..672b1618 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -117,6 +117,7 @@ endif()
 
 option(SDLMIXER_TESTS "Build unit tests?" OFF)
 option(SDLMIXER_TESTS_INSTALL "Install unit tests?" OFF)
+option(SDLMIXER_EXAMPLES "Build the examples directory" OFF)
 
 # These are implemented in SDL_mixer itself, not using a third-party library.
 option(SDLMIXER_AIFF "Enable AIFF audio" ON)
@@ -1203,6 +1204,13 @@ if(SDLMIXER_TESTS)
   add_subdirectory(test)
 endif()
 
+##### Examples subproject (must appear after the install/uninstall targets) #####
+
+if(SDLMIXER_EXAMPLES)
+  set(HAVE_EXAMPLES ON)
+  add_subdirectory(examples)
+endif()
+
 set(available_deps)
 set(unavailable_deps)
 foreach(dep IN LISTS SDLMIXER_BACKENDS)
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 00000000..2c749b58
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,345 @@
+#
+# CMake script for building the SDL examples
+#
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../cmake")
+
+include(CheckIncludeFile)
+include(CheckStructHasMember)
+include(CMakePushCheckState)
+# !!! FIXME: @madebr, do we need this, or was this only needed in SDL itself?
+#include(sdlcompilers)
+
+set(SDLMIXER_EXAMPLES_LINK_SHARED On)  # !!! FIXME: save me, @madebr, this needs plumbing through the main cmake file.
+
+if(SDLMIXER_EXAMPLES_LINK_SHARED)
+    set(sdl_name_component SDL3-shared)
+    set(sdlmixer_name_component SDL3_mixer-shared)
+else()
+    set(sdl_name_component SDL3-static)
+    set(sdlmixer_name_component SDL3_mixer-static)
+endif()
+set(HAVE_EXAMPLES_LINK_SHARED "${SDLMIXER_EXAMPLES_LINK_SHARED}" PARENT_SCOPE)
+
+set(SDLMIXER_EXAMPLE_EXECUTABLES)
+
+if(CMAKE_RUNTIME_OUTPUT_DIRECTORY)
+    set(example_bin_dir "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
+    if(NOT IS_ABSOLUTE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
+        set(example_bin_dir "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
+    endif()
+else()
+    set(example_bin_dir "${CMAKE_CURRENT_BINARY_DIR}")
+endif()
+if(NOT CMAKE_VERSION VERSION_LESS 3.20)
+    get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+    set(example_bin_dir "${example_bin_dir}$<$<BOOL:${is_multi_config}>:/$<CONFIG>>")
+endif()
+
+file(GLOB RESOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.mp3 ${CMAKE_CURRENT_SOURCE_DIR}/*.wav)
+
+set(RESOURCE_FILE_NAMES)
+set(RESOURCE_FILES_BINDIR)
+foreach(resource_file IN LISTS RESOURCE_FILES)
+    get_filename_component(res_file_name ${resource_file} NAME)
+    list(APPEND RESOURCE_FILE_NAMES "${res_file_name}")
+    set(resource_file_bindir "${example_bin_dir}/${res_file_name}")
+    add_custom_command(OUTPUT "${resource_file_bindir}"
+        COMMAND "${CMAKE_COMMAND}" -E copy "${resource_file}" "${resource_file_bindir}"
+        DEPENDS "${resource_file}"
+    )
+    list(APPEND RESOURCE_FILES_BINDIR "${resource_file_bindir}")
+endforeach()
+add_custom_target(copy-sdlmixer-example-resources
+    DEPENDS "${RESOURCE_FILES_BINDIR}"
+)
+
+macro(add_sdlmixer_example_executable TARGET)
+    cmake_parse_arguments(AST "BUILD_DEPENDENT" "" "SOURCES;DATAFILES" ${ARGN})
+    if(AST_UNPARSED_ARGUMENTS)
+        message(FATAL_ERROR "Unknown argument(s): ${AST_UNPARSED_ARGUMENTS}")
+    endif()
+    if(NOT AST_SOURCES)
+        message(FATAL_ERROR "add_sdlmixer_example_executable needs at least one source")
+    endif()
+    if(ANDROID)
+        add_library(${TARGET} SHARED ${AST_SOURCES} ${AST_DATAFILES})
+    else()
+        add_executable(${TARGET} ${AST_SOURCES} ${AST_DATAFILES})
+    endif()
+
+    # !!! FIXME: @madebr, do we need this, or was this only needed in SDL itself?
+    #SDL_AddCommonCompilerFlags(${TARGET})
+
+    target_compile_definitions(${TARGET}
+        PRIVATE
+            $<TARGET_PROPERTY:${sdl3_mixer_target_name},COMPILE_DEFINITIONS>
+    )
+    sdl_add_warning_options(${TARGET} WARNING_AS_ERROR ${SDLMIXER_WERROR})
+
+    target_link_libraries(${TARGET} PRIVATE SDL3_mixer::SDL3_mixer SDL3::SDL3)
+
+    list(APPEND SDL_EXAMPLE_EXECUTABLES ${TARGET})
+    if(AST_DATAFILES)
+        if(PSP OR PS2)
+            add_custom_command(TARGET ${TARGET} POST_BUILD
+                COMMAND ${CMAKE_COMMAND} ARGS -E make_directory $<TARGET_FILE_DIR:${TARGET}>/sdl-${TARGET}
+                COMMAND ${CMAKE_COMMAND} ARGS -E copy_if_different ${AST_DATAFILES} $<TARGET_FILE_DIR:${TARGET}>/sdl-${TARGET}
+                WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+            )
+        elseif(NOT APPLE AND NOT N3DS)
+            add_dependencies(${TARGET} copy-sdlmixer-example-resources)
+        endif()
+        if(EMSCRIPTEN)
+            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 "$<TARGET_FILE_DIR:${TARGET}>/$<JOIN:${AST_DATAFILES},$<SEMICOLON>$<TARGET_FILE_DIR:${TARGET}>/>")
+    endif()
+
+    if(APPLE)
+        # Set Apple App ID / Bundle ID.  This is needed to launch apps on some Apple
+        # platforms (iOS, for example).
+        set_target_properties(${TARGET} PROPERTIES
+          RESOURCES "${AST_DATAFILES}"
+          MACOSX_BUNDLE TRUE
+          MACOSX_BUNDLE_GUI_IDENTIFIER "org.libsdl.${TARGET}"
+          MACOSX_BUNDLE_BUNDLE_VERSION "${SDL3_VERSION}"
+          MACOSX_BUNDLE_SHORT_VERSION_STRING "${SDL3_VERSION}"
+        )
+        set_property(SOURCE ${AST_DATAFILES} PROPERTY MACOSX_PACKAGE_LOCATION "Resources")
+    elseif(WINDOWS)
+        # CET support was added in VS 16.7
+        if(MSVC_VERSION GREATER 1926 AND CMAKE_GENERATOR_PLATFORM MATCHES "Win32|x64")
+            set_property(TARGET ${TARGET} APPEND_STRING PROPERTY LINK_FLAGS " -CETCOMPAT")
+        endif()
+    elseif(EMSCRIPTEN)
+        set_property(TARGET ${TARGET} PROPERTY SUFFIX ".html")
+        target_link_options(${TARGET} PRIVATE -sALLOW_MEMORY_GROWTH=1)
+    elseif(N3DS)
+        set(ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/romfs/${TARGET}")
+        file(MAKE_DIRECTORY "${ROMFS_DIR}")
+        file(COPY ${AST_DATAFILES} DESTINATION "${ROMFS_DIR}")
+        ctr_generate_smdh("${TARGET}.smdh"
+            NAME "SDL-${TARGET}"
+            DESCRIPTION "SDL3 example application"
+            AUTHOR "SDL3 Contributors"
+            ICON "${CMAKE_CURRENT_SOURCE_DIR}/../test/n3ds/logo48x48.png"
+        )
+        ctr_create_3dsx(
+            ${TARGET}
+            ROMFS "${ROMFS_DIR}"
+            SMDH "${TARGET}.smdh"
+        )
+    elseif(NGAGE)
+        string(MD5 TARGET_MD5 "${TARGET}")
+        string(SUBSTRING "${TARGET_MD5}" 0 8 TARGET_MD5_8)
+        target_link_options(${TARGET} PRIVATE "SHELL:-s UID3=0x${TARGET_MD5_8}")
+    endif()
+endmacro()
+
+add_sdlmixer_example_executable(basics-load-and-play SOURCES basics/01-load-and-play/load-and-play.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/music.mp3)
+add_sdlmixer_example_executable(basics-play-with-options SOURCES basics/02-play-with-options/play-with-options.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/music.mp3)
+add_sdlmixer_example_executable(basics-play-multiple-sounds SOURCES basics/03-play-multiple-sounds/play-multiple-sounds.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/music.mp3 ${CMAKE_CURRENT_SOURCE_DIR}/sword.wav)
+add_sdlmixer_example_executable(basics-metadata SOURCES basics/04-metadata/metadata.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/music.mp3)
+add_sdlmixer_example_executable(basics-sinewave SOURCES basics/05-sinewave/sinewave.c)
+add_sdlmixer_example_executable(basics-seeking SOURCES basics/06-seeking/seeking.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/music.mp3)
+
+# When you add an example, remember to add the Visual Studio project as well:
+# - Add a new example in examples/
+# - Run python VisualC/examples/generate.py
+# - Take note of the newly generated .vcxproj files
+# - Modify the .vcxproj files if necessary (adding content such as PNG or WAV files)
+# - Open VisualC/SDL.sln in Visual Studio or JetBrains Rider
+# - Locate the appropriate folder in the Solution Explorer
+# - Add the newly generated projects: Right click -> Add -> Existing project...
+# - Test if they work
+# - Save the SDL.sln solution
+
+if(PSP)
+    # Build EBOOT files if building for PSP
+    foreach(APP ${SDL_EXAMPLE_EXECUTABLES})
+        create_pbp_file(
+            TARGET          ${APP}
+            TITLE           SDL-${APP}
+            ICON_PATH       NULL
+            BACKGROUND_PATH NULL
+            PREVIEW_PATH    NULL
+            OUTPUT_DIR      $<TARGET_FILE_DIR:${APP}>/sdl-${APP}
+        )
+    endforeach()
+endif()
+
+if(RISCOS)
+    set(SDL_EXAMPLE_EXECUTABLES_AIF)
+    foreach(APP ${SDL_EXAMPLE_EXECUTABLES})
+        set_property(TARGET ${APP} APPEND_STRING PROPERTY LINK_FLAGS " -static")
+        add_custom_command(
+            OUTPUT ${APP},ff8
+            COMMAND elf2aif ${APP} ${APP},ff8
+            DEPENDS ${APP}
+        )
+        add_custom_target(${APP}-aif ALL DEPENDS ${APP},ff8)
+        list(APPEND SDL_EXAMPLE_EXECUTABLES_AIF ${CMAKE_CURRENT_BINARY_DIR}/${APP},ff8)
+    endforeach()
+endif()
+
+if(SDL_INSTALL_EXAMPLES)
+    if(RISCOS)
+        install(
+            FILES ${SDL_EXAMPLE_EXECUTABLES_AIF}
+            DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/installed-examples/SDL3
+        )
+    else()
+        install(
+            TARGETS ${SDL_EXAMPLE_EXECUTABLES}
+            DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/installed-examples/SDL3
+        )
+    endif()
+    if(MSVC)
+        foreach(example IN LISTS SDL_EXAMPLE_EXECUTABLES)
+            SDL_install_pdb(${example} "${CMAKE_INSTALL_LIBEXECDIR}/installed-examples/SDL3")
+        endforeach()
+    endif()
+    install(
+        FILES ${RESOURCE_FILES}
+        DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/installed-examples/SDL3
+    )
+endif()
+
+if(ANDROID AND TARGET SDL3::Jar)
+    list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../cmake/android")
+    find_package(SdlAndroid MODULE)
+    if(SdlAndroid_FOUND)
+        set(apks "")
+        set(packages "")
+
+        include(SdlAndroidFunctions)
+        sdl_create_android_debug_keystore(SDL_example-debug-keystore)
+        sdl_android_compile_resources(SDL_example-resources RESFOLDER ${CMAKE_CURRENT_SOURCE_DIR}/../test/android/res)
+        add_custom_target(sdl-example-apks)
+        foreach(EXAMPLE ${SDL_EXAMPLE_EXECUTABLES})
+            set(ANDROID_MANIFEST_APP_NAME "${EXAMPLE}")
+            set(ANDROID_MANIFEST_LABEL "${EXAMPLE}")
+            set(ANDROID_MANIFEST_LIB_NAME "$<TARGET_FILE_BASE_NAME:${EXAMPLE}>")
+            set(ANDROID_MANIFEST_PACKAGE "org.libsdl.sdl.example.${EXAMPLE}")
+            set(generated_manifest_path "${CMAKE_CURRENT_BINARY_DIR}/android/${EXAMPLE}-src/AndroidManifest.xml")
+            string(REPLACE "." "/" JAVA_PACKAGE_DIR "${ANDROID_MANIFEST_PACKAGE}")
+            set(GENERATED_SRC_FOLDER "${CMAKE_CURRENT_BINARY_DIR}/android/${EXAMPLE}-src")
+            set(GENERATED_RES_FOLDER "${GENERATED_SRC_FOLDER}/res")
+            set(JAVA_PACKAGE_DIR "${GENERATED_SRC_FOLDER}/${JAVA_PACKAGE_DIR}")
+            configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test/android/cmake/SDLEntryTestActivity.java.cmake "${JAVA_PACKAGE_DIR}/SDLEntryTestActivity.java" @ONLY)
+            configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test/android/cmake/SDLTestActivity.java.cmake "${JAVA_PACKAGE_DIR}/SDLTestActivity.java" @ONLY)
+            configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test/android/cmake/res/values/strings.xml.cmake android/res/values/strings-${EXAMPLE}.xml @ONLY)
+            configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test/android/cmake/res/xml/shortcuts.xml.cmake "${GENERATED_RES_FOLDER}/xml/shortcuts.xml" @ONLY)
+            configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test/android/cmake/AndroidManifest.xml.cmake "${generated_manifest_path}" @ONLY)
+            file(GENERATE
+                OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/android/${EXAMPLE}-$<CONFIG>/res/values/strings.xml"
+                INPUT "${CMAKE_CURRENT_BINARY_DIR}/android/res/values/strings-${EXAMPLE}.xml"
+            )
+
+            sdl_android_compile_resources(${EXAMPLE}-resources
+                RESOURCES
+                    "${CMAKE_CURRENT_BINARY_DIR}/android/${EXAMPLE}-$<CONFIG>/res/values/strings.xml"
+                    "${GENERATED_RES_FOLDER}/xml/shortcuts.xml"
+            )
+
+            sdl_android_link_resources(${EXAMPLE}-apk-linked
+                MANIFEST "${generated_manifest_path}"
+                PACKAGE ${ANDROID_MANIFEST_PACKAGE}
+                RES_TARGETS SDL_example-resources ${EXAMPLE}-resources
+                TARGET_SDK_VERSION 31
+            )
+
+            set(CMAKE_JAVA_COMPILE_FLAGS "-encoding;utf-8")
+            set(classes_path "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${EXAMPLE}-java.dir/classes")
+            # Some CMake versions have a slow `cmake -E make_directory` implementation
+            if(NOT IS_DIRECTORY "${classes_path}")
+                execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory "${classes_path}")
+            endif()
+            set(OUT_JAR "${CMAKE_CURRENT_BINARY_DIR}/${EXAMPLE}.jar")
+            add_custom_command(
+                OUTPUT "${OUT_JAR}"
+                COMMAND ${CMAKE_COMMAND} -E rm -rf "${classes_path}"
+                COMMAND ${CMAKE_COMMAND} -E make_directory "${classes_path}"
+                COMMAND ${Java_JAVAC_EXECUTABLE}
+                    -source 1.8 -target 1.8
+                    -bootclasspath "$<TARGET_PROPERTY:SDL3::Jar,JAR_FILE>"
+                    "${JAVA_PACKAGE_DIR}/SDLEntryTestActivity.java"
+                    "${JAVA_PACKAGE_DIR}/SDLTestActivity.java"
+                    $<TARGET_PROPERTY:${EXAMPLE}-apk-linked,JAVA_R>
+                    -cp "$<TARGET_PROPERTY:SDL3::Jar,JAR_FILE>:${SDL_ANDROID_PLATFORM_ANDROID_JAR}"
+                    -d "${classes_path}"
+                COMMAND ${Java_JAR_EXECUTABLE} cf "${OUT_JAR}" -C "${classes_path}" .
+                DEPENDS $<TARGET_PROPERTY:${EXAMPLE}-apk-linked,OUTPUTS> "$<TARGET_PROPERTY:SDL3::Jar,JAR_FILE>" "${JAVA_PACKAGE_DIR}/SDLTestActivity.java" "${JAVA_PACKAGE_DIR}/SDLEntryTestActivity.java"
+            )
+            add_custom_target(${EXAMPLE}-jar DEPENDS "${OUT_JAR}")
+            set_property(TARGET ${EXAMPLE}-jar PROPERTY OUTPUT "${OUT_JAR}")
+
+            set(dexworkdir "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${EXAMPLE}-dex.dir")
+            # Some CMake versions have a slow `cmake -E make_directory` implementation
+            if(NOT IS_DIRECTORY "${dexworkdir}")
+                execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${dexworkdir}")
+            endif()
+            set(classes_dex_base_name "classes.dex")
+            set(classes_dex "${dexworkdir}/${classes_dex_base_name}")
+            add_custom_command(
+                OUTPUT "${classes_dex}"
+                COMMAND SdlAndroid::d8
+                    $<TARGET_PROPERTY:${EXAMPLE}-jar,OUTPUT>
+                    $<TARGET_PROPERTY:SDL3::Jar,JAR_FILE>
+                    --lib "${SDL_ANDROID_PLATFORM_ANDROID_JAR}"
+                    --output "${dexworkdir}"
+                DEPENDS $<TARGET_PROPERTY:${EXAMPLE}-jar,OUTPUT> $<TARGET_PROPERTY:SDL3::Jar,JAR_FILE>
+            )
+            add_custom_target(${EXAMPLE}-dex DEPENDS "${classes_dex}")
+            set_property(TARGET ${EXAMPLE}-dex PROPERTY OUTPUT "${classes_dex}")
+            set_property(TARGET ${EXAMPLE}-dex PROPERTY OUTPUT_BASE_NAME "${classes_dex_base_name}")
+
+            sdl_add_to_apk_unaligned(${EXAMPLE}-unaligned-apk
+                APK_IN ${EXAMPLE}-apk-linked
+                OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/intermediates"
+                ASSETS ${RESOURCE_FILES}
+                NATIVE_LIBS SDL3::SDL3-shared ${EXAMPLE}
+                DEX ${EXAMPLE}-dex
+            )
+
+            sdl_apk_align(${EXAMPLE}-aligned-apk ${EXAMPLE}-unaligned-apk
+                OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/intermediates"
+            )
+            sdl_apk_sign(${EXAMPLE}-apk ${EXAMPLE}-aligned-apk
+                KEYSTORE SDL_example-debug-keystore
+            )
+            add_dependencies(sdl-example-apks ${EXAMPLE}-apk)
+
+            if(TARGET SdlAndroid::adb)
+                add_custom_target(install-${EXAMPLE}
+                    COMMAND "${CMAKE_COMMAND}" -DACTION=install "-DAPKS=$<TARGET_PROPERTY:${EXAMPLE}-apk,OUTPUT>" -P "${SDL3_SOURCE_DIR}/cmake/android/SdlAndroidScript.cmake"
+                    DEPENDS "${EXAMPLE}-apk"
+                )
+                add_custom_target(start-${EXAMPLE}
+                    COMMAND "${ADB_BIN}" shell am start-activity -S "${ANDROID_MANIFEST_PACKAGE}/.SDLTestActivity"
+                )
+                add_custom_target(build-install-start-${EXAMPLE}
+                    COMMAND "${CMAKE_COMMAND}" -DACTION=build-install-run "-DEXECUTABLES=${EXAMPLE}" "-DBUILD_FOLDER=${CMAKE_BINARY_DIR}" -P "${SDL3_SOURCE_DIR}/cmake/android/SdlAndroidScript.cmake"
+                )
+            endif()
+
+            list(APPEND packages "${ANDROID_MANIFEST_PACKAGE}")
+            list(APPEND install_targets install-${EXAMPLE})
+        endforeach()
+
+        if(TARGET SdlAndroid::adb)
+            add_custom_target(install-sdl-example-apks
+                DEPENDS ${install_targets}
+                VERBATIM
+            )
+            add_custom_target(uninstall-sdl-example-apks
+                COMMAND "${CMAKE_COMMAND}" "-DADB=$<TARGET_FILE:SdlAndroid::adb>" -DACTION=uninstall "-DPACKAGES=${packages}" -P "${SDL3_SOURCE_DIR}/cmake/android/SdlAndroidScript.cmake"
+                VERBATIM
+            )
+        endif()
+    endif()
+endif()
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 00000000..0f53860b
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,82 @@
+# Examples
+
+## What is this?
+
+In here are a collection of standalone SDL_mixer application examples. Unless
+otherwise stated, they should work on all supported platforms out of the box.
+If they don't [please file a bug to let us know](https://github.com/libsdl-org/SDL_mixer/issues/new).
+
+
+## What is this SDL_AppIterate thing?
+
+SDL can optionally build apps as a collection of callbacks instead of the
+usual program structure that starts and ends in a function called `main`.
+The examples use this format for two reasons.
+
+First, it allows the examples to work when built as web applications without
+a pile of ugly `#ifdef`s, and all of these examples are published on the web
+at [examples.libsdl.org](https://examples.libsdl.org/), so you can easily see
+them in action.
+
+Second, it's example code! The callbacks let us cleanly break the program up
+into the four logical pieces most apps care about:
+
+- Program startup
+- Event handling
+- What the program actually does in a single frame
+- Program shutdown
+
+A detailed technical explanation of these callbacks is in
+docs/README-main-functions.md (or view that page on the web on
+[the wiki](https://wiki.libsdl.org/SDL3/README/main-functions#main-callbacks-in-sdl3)).
+
+
+## I would like to build and run these examples myself.
+
+When you build SDL with CMake, you can add `-DSDLMIXER_EXAMPLES=On` to the
+CMake command line. When you build SDL_mixer, these examples will be built
+with it.
+
+But most of these can just be built as a single .c file, as long as you point
+your compiler at SDL3 and SDL3_mixer's headers and link against both of those
+libraries.
+
+
+## What is the license on the example code? Can I paste this into my project?
+
+All code in the examples directory is considered public domain! You can do
+anything you like with it, including copy/paste it into your closed-source
+project, sell it, and pretend you wrote it yourself. We do not require you to
+give us credit for this code (but we always appreciate if you do!).
+
+This is only true for the examples directory. The rest of SDL and SDL_mixer
+fall under the [zlib license](https://github.com/libsdl-org/SDL_mixer/blob/main/LICENSE.txt).
+
+
+## What is template.html and highlight-plugin.lua in this directory?
+
+This is what [examples.libsdl.org](https://examples.libsdl.org/) uses when
+generating the web versions of these example programs. You can ignore this,
+unless you are improving it, in which case we definitely would love to hear
+from you!
+
+
+## What is template.c in this directory?
+
+If writing new examples, this is the skeleton code we start from, to keep
+everything consistent. You can ignore it.
+
+
+## License for the audio files:
+
+- music.mp3:
+  Arcade Music Loop.wav by joshuaempyre ( https://www.empyreanma.com/welcome )
+  (We converted this to MP3 format.)
+  Original: https://freesound.org/s/251461/
+  License: https://creativecommons.org/licenses/by/3.0/
+
+- sword.wav:
+  sword04.wav by Erdie
+  Original: https://freesound.org/s/27858/
+  License: https://creativecommons.org/licenses/by/3.0/
+
diff --git a/examples/basics/01-load-and-play/README.txt b/examples/basics/01-load-and-play/README.txt
new file mode 100644
index 00000000..5845554b
--- /dev/null
+++ b/examples/basics/01-load-and-play/README.txt
@@ -0,0 +1 @@
+This example code creates a mixer, loads a single sound, and plays it once.
diff --git a/examples/basics/01-load-and-play/load-and-play.c b/examples/basics/01-load-and-play/load-and-play.c
new file mode 100644
index 00000000..32c6e9e7
--- /dev/null
+++ b/examples/basics/01-load-and-play/load-and-play.c
@@ -0,0 +1,111 @@
+/*
+ * This example code creates a mixer, loads a single sound, and plays it once.
+ *
+ * This code is public domain. Feel free to use it for any purpose!
+ */
+
+#define SDL_MAIN_USE_CALLBACKS 1  /* use the callbacks instead of main() */
+#include <SDL3/SDL.h>
+#include <SDL3/SDL_main.h>
+#include <SDL3_mixer/SDL_mixer.h>
+
+/* We will use this renderer to draw into this window every frame. */
+static SDL_Window *window = NULL;
+static SDL_Renderer *renderer = NULL;
+static MIX_Mixer *mixer = NULL;
+static MIX_Track *track = NULL;
+
+/* This function runs once at startup. */
+SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
+{
+    char *path = NULL;
+    MIX_Audio *audio = NULL;
+
+    SDL_SetAppMetadata("Example Load And Play", "1.0", "com.example.load-and-play");
+
+    /* this doesn't have to run very much, so give up tons of CPU time between iterations. Optional! */
+    SDL_SetHint(SDL_HINT_MAIN_CALLBACK_RATE, "5");
+
+    /* we don't need video, but we'll make a window for smooth operation. */
+    if (!SDL_Init(SDL_INIT_VIDEO)) {
+        SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
+        return SDL_APP_FAILURE;
+    }
+
+    if (!SDL_CreateWindowAndRenderer("examples/basic/load-and-play", 640, 480, SDL_WINDOW_RESIZABLE, &window, &renderer)) {
+        SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
+        return SDL_APP_FAILURE;
+    }
+
+    if (!MIX_Init()) {
+        SDL_Log("Couldn't init SDL_mixer library: %s", SDL_GetError());
+        return SDL_APP_FAILURE;
+    }
+
+    /* Create a mixer on the default audio device. Don't care about the specific audio format. */
+    mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL);
+    if (!mixer) {
+        SDL_Log("Couldn't create mixer on default device: %s", SDL_GetError());
+        return SDL_APP_FAILURE;
+    }
+
+    /* load a sound file */
+    SDL_asprintf(&path, "%smusic.mp3", SDL_GetBasePath());  /* allocate a string of the full file path */
+    audio = MIX_LoadAudio(mixer, path, false);
+    if (!audio) {
+        SDL_Log("Couldn't load %s: %s", path, SDL_GetError());
+        SDL_free(path);
+        return SDL_APP_FAILURE;
+    }
+
+    SDL_free(path);  /* done with this, the file is loaded. */
+
+    /* we need a track on the mixer to play the audio. Each track has audio assigned to it, and
+       all playing tracks are mixed together for the final output. */
+
+    track = MIX_CreateTrack(mixer);
+    if (!track) {
+        SDL_Log("Couldn't create a mixer track: %s", SDL_GetError());
+        return SDL_APP_FAILURE;
+    }
+    MIX_SetTrackAudio(track, audio);
+
+    /* start the audio playing! */
+    MIX_PlayTrack(track, 0);  /* no extra options this time, so a zero for the second argument. */
+
+    /* we don't save `audio`; SDL_mixer will clean it up for us during MIX_Quit(). */
+
+    return SDL_APP_CONTINUE;  /* carry on with the program! */
+}
+
+/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
+SDL_AppResult SDL_AppEvent(void *appstate, 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. */
+SDL_AppResult SDL_AppIterate(void *appstate)
+{
+    /* draw a blank video frame to keep the OS happy */
+    SDL_RenderClear(renderer);
+    SDL_RenderPresent(renderer);
+
+    /* when the track has finished playing, end the program. */
+    if (!MIX_TrackPlaying(track)) {
+        return SDL_APP_SUCCESS;
+    }
+
+    return SDL_APP_CONTINUE;  /* carry on with the program! */
+}
+
+/* This function runs once at shutdown. */
+void SDL_AppQuit(void *appstate, SDL_AppResult result)
+{
+    /* SDL will clean up the window/renderer for us, MIX_Quit() destroys any mixer objects we made. */
+    MIX_Quit();
+}
+
diff --git a/examples/basics/02-play-with-options/README.txt b/examples/basics/02-play-with-options/README.txt
new file mode 100644
index 00000000..8e81a529
--- /dev/null
+++ b/examples/basics/02-play-with-options/README.txt
@@ -0,0 +1,2 @@
+This example code creates a mixer, loads a single sound, and plays it, with
+several playback options (fade-in, loop, etc).
diff --git a/examples/basics/02-play-with-options/play-with-options.c b/examples/basics/02-play-with-options/play-with-options.c
new file mode 100644
index 00000000..69b4883c
--- /dev/null
+++ b/examples/basics/02-play-with-options/play-with-options.c
@@ -0,0 +1,126 @@
+/*
+ * This example code creates a mixer, loads a single sound, and plays it, with
+ * several playback options (fade-in, loop, etc).
+ *
+ * This code is public domain. Feel free to use it for any purpose!
+ */
+
+#define SDL_MAIN_USE_CALLBACKS 1  /* use the callbacks instead of main() */
+#include <SDL3/SDL.h>
+#include <SDL3/SDL_main.h>
+#include <SDL3_mixer/SDL_mixer.h>
+
+/* We will use this renderer to draw into this window every frame. */
+static SDL_Window *window = NULL;
+static SDL_Renderer *renderer = NULL;
+static MIX_Mixer *mixer = NULL;
+static MIX_Track *track = NULL;
+
+/* This function runs once at startup. */
+SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
+{
+    char *path = NULL;
+    MIX_Audio *audio = NULL;
+    SDL_PropertiesID options = 0;
+
+    SDL_SetAppMetadata("Example Play With Options", "1.0", "com.example.play-with-options");
+
+    /* this doesn't have to run very much, so give up tons of CPU time between iterations. Optional! */
+    SDL_SetHint(SDL_HINT_MAIN_CALLBACK_RATE, "5");
+
+    /* we don't need video, but we'll make a window for smooth operation. */
+    if (!SDL_Init(SDL_INIT_VIDEO)) {
+        SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
+        return SDL_APP_FAILURE;
+    }
+
+    if (!SDL_CreateWindowAndRenderer("examples/basic/play-with-options", 640, 480, SDL_WINDOW_RESIZABLE, &window, &renderer)) {
+        SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
+        return SDL_APP_FAILURE;
+    }
+
+    if (!MIX_Init()) {
+        SDL_Log("Couldn't init SDL_mixer library: %s", SDL_GetError());
+        return SDL_APP_FAILURE;
+    }
+
+    /* Create a mixer on the default audio device. Don't care about the specific audio format. */
+    mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL);
+    if (!mixer) {
+        SDL_Log("Couldn't create mixer on default device: %s", SDL_GetError());
+        return SDL_APP_FAILURE;
+    }
+
+    /* load a sound file */
+    SDL_asprintf(&path, "%smusic.mp3", SDL_GetBasePath());  /* allocate a string of the full file path */
+    audio = MIX_LoadAudio(mixer, path, false);
+    if (!audio) {
+        SDL_Log("Couldn't load %s: %s", path, SDL_GetError());
+        SDL_free(path);
+        return SDL_APP_FAILURE;
+    }
+
+    SDL_free(path);  /* done with this, the file is loaded. */
+
+    /* we need a track on the mixer to

(Patch may be truncated, please check the link at the top of this post.)