diff --git a/external/sdl/SDL/.gitattributes b/external/sdl/SDL/.gitattributes deleted file mode 100644 index 56cee302..00000000 --- a/external/sdl/SDL/.gitattributes +++ /dev/null @@ -1,12 +0,0 @@ -*.c text -*.cpp text -*.h text -*.cmake text -*.py text -*.txt text -*.sh text -*.vcxproj text eol=crlf -*.sln text eol=crlf -*.filters text eol=crlf -*.appxmanifest text eol=crlf -*.pbxproj text diff --git a/external/sdl/SDL/.github/workflows/emscripten.yml b/external/sdl/SDL/.github/workflows/emscripten.yml index b74a5118..116b811a 100644 --- a/external/sdl/SDL/.github/workflows/emscripten.yml +++ b/external/sdl/SDL/.github/workflows/emscripten.yml @@ -25,7 +25,7 @@ jobs: -DSDL_WERROR=ON \ -DSDL_TESTS=ON \ -DSDL_INSTALL_TESTS=ON \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_INSTALL_PREFIX=prefix \ -GNinja - name: Build (CMake) @@ -42,11 +42,11 @@ jobs: cmake --install build/ - name: Package (CPack) run: | - cmake --build build/ --config Release --target package + cmake --build build/ --config Debug --target package - name: Verify CMake configuration files run: | emcmake cmake -S cmake/test -B cmake_config_build \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE=Debug \ -DSDL_VENDOR_INFO="Github Workflow" \ -DTEST_SHARED=FALSE \ -DCMAKE_PREFIX_PATH=${{ env.SDL3_DIR }} diff --git a/external/sdl/SDL/.github/workflows/main.yml b/external/sdl/SDL/.github/workflows/main.yml index 7f4fbab0..2a0a8e47 100644 --- a/external/sdl/SDL/.github/workflows/main.yml +++ b/external/sdl/SDL/.github/workflows/main.yml @@ -52,7 +52,7 @@ jobs: sudo apt-get update sudo apt-get install build-essential git \ pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \ - libaudio-dev libjack-dev libsndio-dev libsamplerate0-dev libx11-dev libxext-dev \ + libaudio-dev libjack-dev libsndio-dev libusb-1.0-0-dev libx11-dev libxext-dev \ libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libwayland-dev \ libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \ libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev fcitx-libs-dev @@ -111,7 +111,7 @@ jobs: ${{ matrix.platform.source_cmd }} set -eu export SDL_TESTS_QUICK=1 - ctest -VV --test-dir build/ + ctest -VV --test-dir build/ -j2 if test "${{ runner.os }}" = "Linux"; then # This should show us the SDL_REVISION strings build/libSDL3.so.0 | grep SDL- diff --git a/external/sdl/SDL/.github/workflows/msvc.yml b/external/sdl/SDL/.github/workflows/msvc.yml index a434a728..d6968cd8 100644 --- a/external/sdl/SDL/.github/workflows/msvc.yml +++ b/external/sdl/SDL/.github/workflows/msvc.yml @@ -61,7 +61,7 @@ jobs: if: "! contains(matrix.platform.name, 'ARM')" run: | $env:SDL_TESTS_QUICK=1 - ctest -VV --test-dir build/ -C Release + ctest -VV --test-dir build/ -C Release -j2 - name: Install (CMake) run: | echo "SDL3_DIR=$Env:GITHUB_WORKSPACE/prefix" >> $Env:GITHUB_ENV diff --git a/external/sdl/SDL/CMakeLists.txt b/external/sdl/SDL/CMakeLists.txt index d0457150..c244bc09 100644 --- a/external/sdl/SDL/CMakeLists.txt +++ b/external/sdl/SDL/CMakeLists.txt @@ -4,6 +4,9 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) message(FATAL_ERROR "Prevented in-tree build. Please create a build directory outside of the SDL source code and run \"cmake -S ${CMAKE_SOURCE_DIR} -B .\" from there") endif() +# MSVC runtime library flags are selected by an abstraction. +set(CMAKE_POLICY_DEFAULT_CMP0091 NEW) + # See docs/release_checklist.md project(SDL3 LANGUAGES C CXX VERSION "3.0.0") @@ -112,8 +115,8 @@ endif() # for systems without support. It's not currently enough to not use # pthread functions in a pthread-build; it won't start up on unsupported # browsers. As such, you have to explicitly enable it on Emscripten builds -# for the time being. This default with change to ON once this becomes -# commonly supported in browsers or the Emscripten teams makes a single +# for the time being. This default will change to ON once this becomes +# commonly supported in browsers or the Emscripten team makes a single # binary work everywhere. if (UNIX_OR_MAC_SYS AND NOT EMSCRIPTEN) set(SDL_PTHREADS_DEFAULT ON) @@ -137,14 +140,6 @@ else() set(SDL_HIDAPI_LIBUSB_AVAILABLE TRUE) endif() -# On the other hand, *BSD specifically uses libusb only, so we make a special -# case just for them. -if(FREEBSD OR NETBSD OR OPENBSD OR BSDI) - set(SDL_HIDAPI_LIBUSB_DEFAULT TRUE) -else() - set(SDL_HIDAPI_LIBUSB_DEFAULT FALSE) -endif() - set(SDL_ASSEMBLY_DEFAULT OFF) if(USE_CLANG OR USE_GCC OR USE_INTELCC OR MSVC_VERSION GREATER 1400) set(SDL_ASSEMBLY_DEFAULT ON) @@ -166,6 +161,9 @@ endif() if(MSVC) option(SDL_FORCE_STATIC_VCRT "Force /MT for static VC runtimes" OFF) if(SDL_FORCE_STATIC_VCRT) + if(NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() foreach(flag_var CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO) @@ -349,7 +347,8 @@ set_option(SDL_OFFSCREEN "Use offscreen video driver" ON) option_string(SDL_BACKGROUNDING_SIGNAL "number to use for magic backgrounding signal or 'OFF'" OFF) option_string(SDL_FOREGROUNDING_SIGNAL "number to use for magic foregrounding signal or 'OFF'" OFF) dep_option(SDL_HIDAPI "Enable the HIDAPI subsystem" ON "NOT VISIONOS" OFF) -dep_option(SDL_HIDAPI_LIBUSB "Use libusb for low level joystick drivers" ${SDL_HIDAPI_LIBUSB_DEFAULT} "SDL_HIDAPI;${SDL_HIDAPI_LIBUSB_AVAILABLE}" OFF) +dep_option(SDL_HIDAPI_LIBUSB "Use libusb for low level joystick drivers" ON SDL_HIDAPI_LIBUSB_AVAILABLE OFF) +dep_option(SDL_HIDAPI_LIBUSB_SHARED "Dynamically load libusb support" ON SDL_HIDAPI_LIBUSB OFF) dep_option(SDL_HIDAPI_JOYSTICK "Use HIDAPI for low level joystick drivers" ON SDL_HIDAPI OFF) dep_option(SDL_VIRTUAL_JOYSTICK "Enable the virtual-joystick driver" ON SDL_HIDAPI OFF) set_option(SDL_LIBUDEV "Enable libudev support" ON) @@ -3367,6 +3366,7 @@ if(NOT SDL_DISABLE_INSTALL) SDL_generate_manpages( SYMBOL "SDL_Init" WIKIHEADERS_PL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/build-scripts/wikiheaders.pl" + REVISION "${SDL_REVISION}" ) if(TARGET SDL3-javadoc) set(SDL_INSTALL_JAVADOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/javadoc" CACHE PATH "Path where to install SDL3 javadoc") diff --git a/external/sdl/SDL/README.md b/external/sdl/SDL/README.md index fac8663c..ce500fa5 100644 --- a/external/sdl/SDL/README.md +++ b/external/sdl/SDL/README.md @@ -10,7 +10,8 @@ emulators, and popular games including Valve's award winning catalog and many Humble Bundle games. More extensive documentation is available in the docs directory, starting -with README.md +with [README.md](docs/README.md). If you are migrating to SDL 3.0 from SDL 2.0, +the changes are extensively documented in [README-migration.md](docs/README-migration.md). Enjoy! diff --git a/external/sdl/SDL/VisualC-GDK/SDL/SDL.vcxproj b/external/sdl/SDL/VisualC-GDK/SDL/SDL.vcxproj index fd9d8455..0545d3c4 100644 --- a/external/sdl/SDL/VisualC-GDK/SDL/SDL.vcxproj +++ b/external/sdl/SDL/VisualC-GDK/SDL/SDL.vcxproj @@ -370,6 +370,8 @@ + + @@ -438,7 +440,6 @@ - $(IntDir)$(TargetName)_cpp.pch @@ -550,6 +551,8 @@ + + @@ -582,7 +585,7 @@ - + @@ -707,7 +710,6 @@ - diff --git a/external/sdl/SDL/VisualC-GDK/SDL/SDL.vcxproj.filters b/external/sdl/SDL/VisualC-GDK/SDL/SDL.vcxproj.filters index 81dee5cc..62f79c6b 100644 --- a/external/sdl/SDL/VisualC-GDK/SDL/SDL.vcxproj.filters +++ b/external/sdl/SDL/VisualC-GDK/SDL/SDL.vcxproj.filters @@ -31,7 +31,7 @@ {377061e4-3856-4f05-b916-0d3b360df0f6} - + {226a6643-1c65-4c7f-92aa-861313d974bb} @@ -396,7 +396,6 @@ API Headers - @@ -420,6 +419,12 @@ audio + + audio + + + audio + core\windows @@ -838,7 +843,6 @@ - @@ -856,6 +860,12 @@ audio + + audio + + + audio + audio @@ -916,8 +926,8 @@ file - - filesystem\windows + + filesystem\gdk haptic diff --git a/external/sdl/SDL/VisualC-WinRT/SDL-UWP.vcxproj b/external/sdl/SDL/VisualC-WinRT/SDL-UWP.vcxproj index 27b374d9..d0e60385 100644 --- a/external/sdl/SDL/VisualC-WinRT/SDL-UWP.vcxproj +++ b/external/sdl/SDL/VisualC-WinRT/SDL-UWP.vcxproj @@ -94,6 +94,11 @@ +<<<<<<< HEAD +======= + + +>>>>>>> 644725478f4de0f074a6834e8423ac36dce3974f @@ -147,7 +152,10 @@ +<<<<<<< HEAD +======= +>>>>>>> 644725478f4de0f074a6834e8423ac36dce3974f @@ -194,6 +202,11 @@ +<<<<<<< HEAD +======= + + +>>>>>>> 644725478f4de0f074a6834e8423ac36dce3974f @@ -388,7 +401,10 @@ +<<<<<<< HEAD +======= +>>>>>>> 644725478f4de0f074a6834e8423ac36dce3974f diff --git a/external/sdl/SDL/VisualC-WinRT/SDL-UWP.vcxproj.filters b/external/sdl/SDL/VisualC-WinRT/SDL-UWP.vcxproj.filters index 11a13d65..fc2449c8 100644 --- a/external/sdl/SDL/VisualC-WinRT/SDL-UWP.vcxproj.filters +++ b/external/sdl/SDL/VisualC-WinRT/SDL-UWP.vcxproj.filters @@ -183,6 +183,15 @@ Source Files +<<<<<<< HEAD +======= + + Source Files + + + Source Files + +>>>>>>> 644725478f4de0f074a6834e8423ac36dce3974f Source Files @@ -408,9 +417,12 @@ Source Files +<<<<<<< HEAD Source Files +======= +>>>>>>> 644725478f4de0f074a6834e8423ac36dce3974f Source Files @@ -474,6 +486,15 @@ Source Files +<<<<<<< HEAD +======= + + Source Files + + + Source Files + +>>>>>>> 644725478f4de0f074a6834e8423ac36dce3974f Source Files @@ -786,9 +807,12 @@ Source Files +<<<<<<< HEAD Source Files +======= +>>>>>>> 644725478f4de0f074a6834e8423ac36dce3974f Source Files diff --git a/external/sdl/SDL/VisualC/SDL/SDL.vcxproj b/external/sdl/SDL/VisualC/SDL/SDL.vcxproj index 0cea5060..e3554040 100644 --- a/external/sdl/SDL/VisualC/SDL/SDL.vcxproj +++ b/external/sdl/SDL/VisualC/SDL/SDL.vcxproj @@ -319,6 +319,8 @@ + + @@ -385,7 +387,6 @@ - Create @@ -476,6 +477,8 @@ + + @@ -585,7 +588,6 @@ - diff --git a/external/sdl/SDL/VisualC/SDL/SDL.vcxproj.filters b/external/sdl/SDL/VisualC/SDL/SDL.vcxproj.filters index 63fd04c0..dbf4e543 100644 --- a/external/sdl/SDL/VisualC/SDL/SDL.vcxproj.filters +++ b/external/sdl/SDL/VisualC/SDL/SDL.vcxproj.filters @@ -387,7 +387,6 @@ API Headers - @@ -411,6 +410,12 @@ audio + + audio + + + audio + core\windows @@ -817,7 +822,6 @@ - @@ -835,6 +839,12 @@ audio + + audio + + + audio + audio diff --git a/external/sdl/SDL/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/external/sdl/SDL/Xcode/SDL/SDL.xcodeproj/project.pbxproj index 5fd66058..90e2ccb9 100644 --- a/external/sdl/SDL/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/external/sdl/SDL/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -63,7 +63,6 @@ A75FDBB823E4CBC700529352 /* ReadMe.txt in Resources */ = {isa = PBXBuildFile; fileRef = F59C710300D5CB5801000001 /* ReadMe.txt */; }; A75FDBC523EA380300529352 /* SDL_hidapi_rumble.h in Headers */ = {isa = PBXBuildFile; fileRef = A75FDBC323EA380300529352 /* SDL_hidapi_rumble.h */; }; A75FDBCE23EA380300529352 /* SDL_hidapi_rumble.c in Sources */ = {isa = PBXBuildFile; fileRef = A75FDBC423EA380300529352 /* SDL_hidapi_rumble.c */; }; - A7D8A94523E2514000DCD162 /* SDL_dataqueue.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A57023E2513D00DCD162 /* SDL_dataqueue.h */; }; A7D8A94B23E2514000DCD162 /* SDL.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A57123E2513D00DCD162 /* SDL.c */; }; A7D8A95123E2514000DCD162 /* SDL_spinlock.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A57323E2513D00DCD162 /* SDL_spinlock.c */; }; A7D8A95723E2514000DCD162 /* SDL_atomic.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A57423E2513D00DCD162 /* SDL_atomic.c */; }; @@ -315,7 +314,6 @@ A7D8BAFD23E2514500DCD162 /* s_floor.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A92523E2514000DCD162 /* s_floor.c */; }; A7D8BB0323E2514500DCD162 /* math_libm.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A92623E2514000DCD162 /* math_libm.h */; }; A7D8BB0923E2514500DCD162 /* k_tan.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A92723E2514000DCD162 /* k_tan.c */; }; - A7D8BB0F23E2514500DCD162 /* SDL_dataqueue.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A92823E2514000DCD162 /* SDL_dataqueue.c */; }; A7D8BB1523E2514500DCD162 /* SDL_mouse.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A92A23E2514000DCD162 /* SDL_mouse.c */; }; A7D8BB1B23E2514500DCD162 /* SDL_mouse_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A92B23E2514000DCD162 /* SDL_mouse_c.h */; }; A7D8BB2123E2514500DCD162 /* scancodes_windows.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A92C23E2514000DCD162 /* scancodes_windows.h */; }; @@ -371,6 +369,12 @@ F31A92C828D4CB39003BFD6A /* SDL_offscreenopengles.h in Headers */ = {isa = PBXBuildFile; fileRef = F31A92C628D4CB39003BFD6A /* SDL_offscreenopengles.h */; }; F31A92D228D4CB39003BFD6A /* SDL_offscreenopengles.c in Sources */ = {isa = PBXBuildFile; fileRef = F31A92C728D4CB39003BFD6A /* SDL_offscreenopengles.c */; }; F32305FF28939F6400E66D30 /* SDL_hidapi_combined.c in Sources */ = {isa = PBXBuildFile; fileRef = F32305FE28939F6400E66D30 /* SDL_hidapi_combined.c */; }; + F32DDACF2AB795A30041EAA5 /* SDL_audio_channel_converters.h in Headers */ = {isa = PBXBuildFile; fileRef = F32DDAC92AB795A30041EAA5 /* SDL_audio_channel_converters.h */; }; + F32DDAD02AB795A30041EAA5 /* SDL_audioresample.h in Headers */ = {isa = PBXBuildFile; fileRef = F32DDACA2AB795A30041EAA5 /* SDL_audioresample.h */; }; + F32DDAD12AB795A30041EAA5 /* SDL_audioqueue.c in Sources */ = {isa = PBXBuildFile; fileRef = F32DDACB2AB795A30041EAA5 /* SDL_audioqueue.c */; }; + F32DDAD22AB795A30041EAA5 /* SDL_audio_resampler_filter.h in Headers */ = {isa = PBXBuildFile; fileRef = F32DDACC2AB795A30041EAA5 /* SDL_audio_resampler_filter.h */; }; + F32DDAD32AB795A30041EAA5 /* SDL_audioqueue.h in Headers */ = {isa = PBXBuildFile; fileRef = F32DDACD2AB795A30041EAA5 /* SDL_audioqueue.h */; }; + F32DDAD42AB795A30041EAA5 /* SDL_audioresample.c in Sources */ = {isa = PBXBuildFile; fileRef = F32DDACE2AB795A30041EAA5 /* SDL_audioresample.c */; }; F34B9895291DEFF500AAC96E /* SDL_hidapi_steam.c in Sources */ = {isa = PBXBuildFile; fileRef = A75FDAAC23E2795C00529352 /* SDL_hidapi_steam.c */; }; F36C7AD1294BA009004D61C3 /* SDL_runapp.c in Sources */ = {isa = PBXBuildFile; fileRef = F36C7AD0294BA009004D61C3 /* SDL_runapp.c */; }; F376F6552559B4E300CFC0BC /* SDL_hidapi.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A81423E2513F00DCD162 /* SDL_hidapi.c */; }; @@ -531,7 +535,6 @@ A75FDBA723E4CB6F00529352 /* LICENSE.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE.txt; sourceTree = ""; }; A75FDBC323EA380300529352 /* SDL_hidapi_rumble.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_hidapi_rumble.h; sourceTree = ""; }; A75FDBC423EA380300529352 /* SDL_hidapi_rumble.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_rumble.c; sourceTree = ""; }; - A7D8A57023E2513D00DCD162 /* SDL_dataqueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dataqueue.h; sourceTree = ""; }; A7D8A57123E2513D00DCD162 /* SDL.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL.c; sourceTree = ""; }; A7D8A57323E2513D00DCD162 /* SDL_spinlock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_spinlock.c; sourceTree = ""; }; A7D8A57423E2513D00DCD162 /* SDL_atomic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_atomic.c; sourceTree = ""; }; @@ -812,7 +815,6 @@ A7D8A92523E2514000DCD162 /* s_floor.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = s_floor.c; sourceTree = ""; }; A7D8A92623E2514000DCD162 /* math_libm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = math_libm.h; sourceTree = ""; }; A7D8A92723E2514000DCD162 /* k_tan.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = k_tan.c; sourceTree = ""; }; - A7D8A92823E2514000DCD162 /* SDL_dataqueue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_dataqueue.c; sourceTree = ""; }; A7D8A92A23E2514000DCD162 /* SDL_mouse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_mouse.c; sourceTree = ""; }; A7D8A92B23E2514000DCD162 /* SDL_mouse_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_mouse_c.h; sourceTree = ""; }; A7D8A92C23E2514000DCD162 /* scancodes_windows.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scancodes_windows.h; sourceTree = ""; }; @@ -843,6 +845,12 @@ F31A92C628D4CB39003BFD6A /* SDL_offscreenopengles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_offscreenopengles.h; sourceTree = ""; }; F31A92C728D4CB39003BFD6A /* SDL_offscreenopengles.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_offscreenopengles.c; sourceTree = ""; }; F32305FE28939F6400E66D30 /* SDL_hidapi_combined.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_combined.c; sourceTree = ""; }; + F32DDAC92AB795A30041EAA5 /* SDL_audio_channel_converters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_audio_channel_converters.h; sourceTree = ""; }; + F32DDACA2AB795A30041EAA5 /* SDL_audioresample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_audioresample.h; sourceTree = ""; }; + F32DDACB2AB795A30041EAA5 /* SDL_audioqueue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_audioqueue.c; sourceTree = ""; }; + F32DDACC2AB795A30041EAA5 /* SDL_audio_resampler_filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_audio_resampler_filter.h; sourceTree = ""; }; + F32DDACD2AB795A30041EAA5 /* SDL_audioqueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_audioqueue.h; sourceTree = ""; }; + F32DDACE2AB795A30041EAA5 /* SDL_audioresample.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_audioresample.c; sourceTree = ""; }; F36C7AD0294BA009004D61C3 /* SDL_runapp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_runapp.c; sourceTree = ""; }; F376F6182559B29300CFC0BC /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.1.sdk/System/Library/Frameworks/OpenGLES.framework; sourceTree = DEVELOPER_DIR; }; F376F61A2559B2AF00CFC0BC /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/iOSSupport/System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; @@ -1108,8 +1116,6 @@ A7D8A5EB23E2513D00DCD162 /* video */, A7D8A7F523E2513F00DCD162 /* SDL_assert_c.h */, A7D8A94423E2514000DCD162 /* SDL_assert.c */, - A7D8A92823E2514000DCD162 /* SDL_dataqueue.c */, - A7D8A57023E2513D00DCD162 /* SDL_dataqueue.h */, A7D8A57523E2513D00DCD162 /* SDL_error_c.h */, A7D8A8BF23E2513F00DCD162 /* SDL_error.c */, F382071C284F362F004DD584 /* SDL_guid.c */, @@ -1789,10 +1795,16 @@ A7D8A8AF23E2513F00DCD162 /* disk */, A7D8A87023E2513F00DCD162 /* dummy */, A7D8A87323E2513F00DCD162 /* SDL_audio_c.h */, + F32DDAC92AB795A30041EAA5 /* SDL_audio_channel_converters.h */, + F32DDACC2AB795A30041EAA5 /* SDL_audio_resampler_filter.h */, A7D8A8B823E2513F00DCD162 /* SDL_audio.c */, A7D8A8A123E2513F00DCD162 /* SDL_audiocvt.c */, A7D8A87723E2513F00DCD162 /* SDL_audiodev_c.h */, A7D8A88F23E2513F00DCD162 /* SDL_audiodev.c */, + F32DDACB2AB795A30041EAA5 /* SDL_audioqueue.c */, + F32DDACD2AB795A30041EAA5 /* SDL_audioqueue.h */, + F32DDACE2AB795A30041EAA5 /* SDL_audioresample.c */, + F32DDACA2AB795A30041EAA5 /* SDL_audioresample.h */, A7D8A8A023E2513F00DCD162 /* SDL_audiotypecvt.c */, A7D8A86523E2513F00DCD162 /* SDL_mixer.c */, A7D8A89F23E2513F00DCD162 /* SDL_sysaudio.h */, @@ -2064,13 +2076,13 @@ A7D8AF0023E2514100DCD162 /* SDL_cocoavideo.h in Headers */, A7D8AEE823E2514100DCD162 /* SDL_cocoavulkan.h in Headers */, A7D8AEFA23E2514100DCD162 /* SDL_cocoawindow.h in Headers */, + F32DDACF2AB795A30041EAA5 /* SDL_audio_channel_converters.h in Headers */, F3F7D9D12933074E00816151 /* SDL_copying.h in Headers */, A7D8B8CC23E2514400DCD162 /* SDL_coreaudio.h in Headers */, A7D8A96F23E2514000DCD162 /* SDL_coremotionsensor.h in Headers */, F3F7D9B92933074E00816151 /* SDL_cpuinfo.h in Headers */, F3990E062A788303000D8759 /* SDL_hidapi_ios.h in Headers */, A7D8B98023E2514400DCD162 /* SDL_d3dmath.h in Headers */, - A7D8A94523E2514000DCD162 /* SDL_dataqueue.h in Headers */, A7D8B8A223E2514400DCD162 /* SDL_diskaudio.h in Headers */, A7D8BB3F23E2514500DCD162 /* SDL_displayevents_c.h in Headers */, A7D8BA1923E2514400DCD162 /* SDL_draw.h in Headers */, @@ -2080,6 +2092,7 @@ A7D8B79423E2514400DCD162 /* SDL_dummyaudio.h in Headers */, A7D8A96323E2514000DCD162 /* SDL_dummysensor.h in Headers */, A7D8AB0A23E2514100DCD162 /* SDL_dynapi.h in Headers */, + F32DDAD02AB795A30041EAA5 /* SDL_audioresample.h in Headers */, A7D8AB1023E2514100DCD162 /* SDL_dynapi_overrides.h in Headers */, A7D8AB1C23E2514100DCD162 /* SDL_dynapi_procs.h in Headers */, F3F7D9252933074E00816151 /* SDL_egl.h in Headers */, @@ -2143,6 +2156,7 @@ F3F7D9392933074E00816151 /* SDL_opengles2_gl2ext.h in Headers */, F3F7D9692933074E00816151 /* SDL_opengles2_gl2platform.h in Headers */, F3F7D9092933074E00816151 /* SDL_opengles2_khrplatform.h in Headers */, + F32DDAD22AB795A30041EAA5 /* SDL_audio_resampler_filter.h in Headers */, F3F7D9192933074E00816151 /* SDL_pixels.h in Headers */, A7D8B2C023E2514200DCD162 /* SDL_pixels_c.h in Headers */, F3F7D8F12933074E00816151 /* SDL_platform.h in Headers */, @@ -2249,6 +2263,7 @@ A7D8B28423E2514200DCD162 /* vulkan_macos.h in Headers */, A7D8B29623E2514200DCD162 /* vulkan_mir.h in Headers */, A7D8B25A23E2514200DCD162 /* vulkan_vi.h in Headers */, + F32DDAD32AB795A30041EAA5 /* SDL_audioqueue.h in Headers */, A7D8B27823E2514200DCD162 /* vulkan_wayland.h in Headers */, A7D8B27E23E2514200DCD162 /* vulkan_win32.h in Headers */, A7D8B29023E2514200DCD162 /* vulkan_xcb.h in Headers */, @@ -2385,6 +2400,7 @@ A7D8B41C23E2514300DCD162 /* SDL_systls.c in Sources */, 9846B07C287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */, A7D8BBD923E2574800DCD162 /* SDL_uikitmessagebox.m in Sources */, + F32DDAD42AB795A30041EAA5 /* SDL_audioresample.c in Sources */, A7D8AD2923E2514100DCD162 /* SDL_vulkan_utils.c in Sources */, A7D8A95123E2514000DCD162 /* SDL_spinlock.c in Sources */, F34B9895291DEFF500AAC96E /* SDL_hidapi_steam.c in Sources */, @@ -2464,6 +2480,7 @@ A7D8AEE223E2514100DCD162 /* SDL_cocoashape.m in Sources */, A7D8BBD323E2574800DCD162 /* SDL_uikitappdelegate.m in Sources */, A7D8AEB823E2514100DCD162 /* SDL_cocoamouse.m in Sources */, + F32DDAD12AB795A30041EAA5 /* SDL_audioqueue.c in Sources */, A7D8B8E423E2514400DCD162 /* SDL_error.c in Sources */, A7D8AD6823E2514100DCD162 /* SDL_blit.c in Sources */, A7D8B5BD23E2514300DCD162 /* SDL_rwops.c in Sources */, @@ -2552,7 +2569,6 @@ A7D8BBDB23E2574800DCD162 /* SDL_uikitmetalview.m in Sources */, A7D8BB1523E2514500DCD162 /* SDL_mouse.c in Sources */, A7D8BAD923E2514500DCD162 /* e_rem_pio2.c in Sources */, - A7D8BB0F23E2514500DCD162 /* SDL_dataqueue.c in Sources */, F395C19C2569C68F00942BFF /* SDL_iokitjoystick.c in Sources */, A7D8B4B223E2514300DCD162 /* SDL_sysjoystick.c in Sources */, A7D8B3E023E2514300DCD162 /* SDL_cpuinfo.c in Sources */, diff --git a/external/sdl/SDL/android-project/app/build.gradle b/external/sdl/SDL/android-project/app/build.gradle index f59f482c..a158bbff 100644 --- a/external/sdl/SDL/android-project/app/build.gradle +++ b/external/sdl/SDL/android-project/app/build.gradle @@ -8,23 +8,23 @@ else { } android { - compileSdkVersion 31 + if (buildAsApplication) { + namespace "org.libsdl.app" + } + compileSdkVersion 34 defaultConfig { - if (buildAsApplication) { - applicationId "org.libsdl.app" - } - minSdkVersion 16 - targetSdkVersion 31 + minSdkVersion 19 + targetSdkVersion 34 versionCode 1 versionName "1.0" externalNativeBuild { ndkBuild { - arguments "APP_PLATFORM=android-16" + arguments "APP_PLATFORM=android-19" // abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' abiFilters 'arm64-v8a' } cmake { - arguments "-DANDROID_APP_PLATFORM=android-16", "-DANDROID_STL=c++_static" + arguments "-DANDROID_APP_PLATFORM=android-19", "-DANDROID_STL=c++_static" // abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' abiFilters 'arm64-v8a' } @@ -54,10 +54,10 @@ android { } } - lintOptions { + lint { abortOnError false } - + if (buildAsLibrary) { libraryVariants.all { variant -> variant.outputs.each { output -> diff --git a/external/sdl/SDL/android-project/app/src/main/AndroidManifest.xml b/external/sdl/SDL/android-project/app/src/main/AndroidManifest.xml index d997afe4..e0d03341 100644 --- a/external/sdl/SDL/android-project/app/src/main/AndroidManifest.xml +++ b/external/sdl/SDL/android-project/app/src/main/AndroidManifest.xml @@ -3,7 +3,6 @@ com.gamemaker.game --> diff --git a/external/sdl/SDL/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/external/sdl/SDL/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java index 30a3fe1a..a80b4941 100644 --- a/external/sdl/SDL/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/external/sdl/SDL/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -1416,23 +1416,6 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh } } - if ((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - if (isTextInputEvent(event)) { - if (ic != null) { - ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1); - } else { - SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1); - } - } - onNativeKeyDown(keyCode); - return true; - } else if (event.getAction() == KeyEvent.ACTION_UP) { - onNativeKeyUp(keyCode); - return true; - } - } - if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) { // on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses // they are ignored here because sending them as mouse input to SDL is messy @@ -1447,6 +1430,21 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh } } + if (event.getAction() == KeyEvent.ACTION_DOWN) { + if (isTextInputEvent(event)) { + if (ic != null) { + ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1); + } else { + SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1); + } + } + onNativeKeyDown(keyCode); + return true; + } else if (event.getAction() == KeyEvent.ACTION_UP) { + onNativeKeyUp(keyCode); + return true; + } + return false; } diff --git a/external/sdl/SDL/android-project/build.gradle b/external/sdl/SDL/android-project/build.gradle index 6f629c8a..2c911c6f 100644 --- a/external/sdl/SDL/android-project/build.gradle +++ b/external/sdl/SDL/android-project/build.gradle @@ -6,7 +6,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:7.0.3' + classpath 'com.android.tools.build:gradle:8.1.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/external/sdl/SDL/android-project/gradle/wrapper/gradle-wrapper.properties b/external/sdl/SDL/android-project/gradle/wrapper/gradle-wrapper.properties index ada5af48..5b9d7599 100644 --- a/external/sdl/SDL/android-project/gradle/wrapper/gradle-wrapper.properties +++ b/external/sdl/SDL/android-project/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Thu Nov 11 18:20:34 PST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/external/sdl/SDL/build-scripts/SDL_migration.cocci b/external/sdl/SDL/build-scripts/SDL_migration.cocci index a2243cff..df9acc00 100644 --- a/external/sdl/SDL/build-scripts/SDL_migration.cocci +++ b/external/sdl/SDL/build-scripts/SDL_migration.cocci @@ -1069,11 +1069,6 @@ typedef SDL_GameControllerButton, SDL_GamepadButton; - SDL_GameControllerButton + SDL_GamepadButton @@ -typedef SDL_GameControllerButtonBind, SDL_GamepadBinding; -@@ -- SDL_GameControllerButtonBind -+ SDL_GamepadBinding -@@ @@ - SDL_GameControllerClose + SDL_CloseGamepad @@ -1115,16 +1110,6 @@ typedef SDL_GameControllerButtonBind, SDL_GamepadBinding; (...) @@ @@ -- SDL_GameControllerGetBindForAxis -+ SDL_GetGamepadBindForAxis - (...) -@@ -@@ -- SDL_GameControllerGetBindForButton -+ SDL_GetGamepadBindForButton - (...) -@@ -@@ - SDL_GameControllerGetButton + SDL_GetGamepadButton (...) @@ -2591,51 +2576,51 @@ typedef SDL_cond, SDL_Condition; @@ @@ - AUDIO_F32 -+ SDL_AUDIO_F32 ++ SDL_AUDIO_F32LE @@ @@ - AUDIO_F32LSB -+ SDL_AUDIO_F32LSB ++ SDL_AUDIO_F32LE @@ @@ - AUDIO_F32MSB -+ SDL_AUDIO_F32MSB ++ SDL_AUDIO_F32BE @@ @@ - AUDIO_F32SYS -+ SDL_AUDIO_F32SYS ++ SDL_AUDIO_F32 @@ @@ - AUDIO_S16 -+ SDL_AUDIO_S16 ++ SDL_AUDIO_S16LE @@ @@ - AUDIO_S16LSB -+ SDL_AUDIO_S16LSB ++ SDL_AUDIO_S16LE @@ @@ - AUDIO_S16MSB -+ SDL_AUDIO_S16MSB ++ SDL_AUDIO_S16BE @@ @@ - AUDIO_S16SYS -+ SDL_AUDIO_S16SYS ++ SDL_AUDIO_S16 @@ @@ - AUDIO_S32 -+ SDL_AUDIO_S32 ++ SDL_AUDIO_S32LE @@ @@ - AUDIO_S32LSB -+ SDL_AUDIO_S32LSB ++ SDL_AUDIO_S32LE @@ @@ - AUDIO_S32MSB -+ SDL_AUDIO_S32MSB ++ SDL_AUDIO_S32BE @@ @@ - AUDIO_S32SYS -+ SDL_AUDIO_S32SYS ++ SDL_AUDIO_S32 @@ @@ - AUDIO_S8 diff --git a/external/sdl/SDL/build-scripts/add-source-to-projects.pl b/external/sdl/SDL/build-scripts/add-source-to-projects.pl new file mode 100755 index 00000000..4530e92d --- /dev/null +++ b/external/sdl/SDL/build-scripts/add-source-to-projects.pl @@ -0,0 +1,599 @@ +#!/usr/bin/perl -w + +# Add source files and headers to Xcode and Visual Studio projects. +# THIS IS NOT ROBUST, THIS IS JUST RYAN AVOIDING RUNNING BETWEEN +# THREE COMPUTERS AND A BUNCH OF DEVELOPMENT ENVIRONMENTS TO ADD +# A STUPID FILE TO THE BUILD. + + +use warnings; +use strict; +use File::Basename; + + +my %xcode_references = (); +sub generate_xcode_id { + my @chars = ('0'..'9', 'A'..'F'); + my $str; + + do { + my $len = 16; + $str = '0000'; # start and end with '0000' so we know we added it. + while ($len--) { + $str .= $chars[rand @chars] + }; + $str .= '0000'; # start and end with '0000' so we know we added it. + } while (defined($xcode_references{$str})); + + $xcode_references{$str} = 1; # so future calls can't generate this one. + + return $str; +} + +sub process_xcode { + my $addpath = shift; + my $pbxprojfname = shift; + my $lineno; + + %xcode_references = (); + + my $addfname = basename($addpath); + my $addext = ''; + if ($addfname =~ /\.(.*?)\Z/) { + $addext = $1; + } + + my $is_public_header = ($addpath =~ /\Ainclude\/SDL3\//) ? 1 : 0; + my $filerefpath = $is_public_header ? "SDL3/$addfname" : $addfname; + + my $srcs_or_headers = ''; + my $addfiletype = ''; + + if ($addext eq 'c') { + $srcs_or_headers = 'Sources'; + $addfiletype = 'sourcecode.c.c'; + } elsif ($addext eq 'm') { + $srcs_or_headers = 'Sources'; + $addfiletype = 'sourcecode.c.objc'; + } elsif ($addext eq 'h') { + $srcs_or_headers = 'Headers'; + $addfiletype = 'sourcecode.c.h'; + } else { + die("Unexpected file extension '$addext'\n"); + } + + my $fh; + + open $fh, '<', $pbxprojfname or die("Failed to open '$pbxprojfname': $!\n"); + chomp(my @pbxproj = <$fh>); + close($fh); + + # build a table of all ids, in case we duplicate one by some miracle. + $lineno = 0; + foreach (@pbxproj) { + $lineno++; + + # like "F3676F582A7885080091160D /* SDL3.dmg */ = {" + if (/\A\t\t([A-F0-9]{24}) \/\* (.*?) \*\/ \= \{\Z/) { + $xcode_references{$1} = $2; + } + } + + # build out of a tree of PBXGroup items. + my %pbxgroups = (); + my $thispbxgroup; + my $pbxgroup_children; + my $pbxgroup_state = 0; + my $pubheaders_group_hash = ''; + my $libsrc_group_hash = ''; + $lineno = 0; + foreach (@pbxproj) { + $lineno++; + if ($pbxgroup_state == 0) { + $pbxgroup_state++ if /\A\/\* Begin PBXGroup section \*\/\Z/; + } elsif ($pbxgroup_state == 1) { + # like "F3676F582A7885080091160D /* SDL3.dmg */ = {" + if (/\A\t\t([A-F0-9]{24}) \/\* (.*?) \*\/ \= \{\Z/) { + my %newhash = (); + $pbxgroups{$1} = \%newhash; + $thispbxgroup = \%newhash; + $pubheaders_group_hash = $1 if $2 eq 'Public Headers'; + $libsrc_group_hash = $1 if $2 eq 'Library Source'; + $pbxgroup_state++; + } elsif (/\A\/\* End PBXGroup section \*\/\Z/) { + last; + } else { + die("Expected pbxgroup obj on '$pbxprojfname' line $lineno\n"); + } + } elsif ($pbxgroup_state == 2) { + if (/\A\t\t\tisa \= PBXGroup;\Z/) { + $pbxgroup_state++; + } else { + die("Expected pbxgroup obj's isa field on '$pbxprojfname' line $lineno\n"); + } + } elsif ($pbxgroup_state == 3) { + if (/\A\t\t\tchildren \= \(\Z/) { + my %newhash = (); + $$thispbxgroup{'children'} = \%newhash; + $pbxgroup_children = \%newhash; + $pbxgroup_state++; + } else { + die("Expected pbxgroup obj's children field on '$pbxprojfname' line $lineno\n"); + } + } elsif ($pbxgroup_state == 4) { + if (/\A\t\t\t\t([A-F0-9]{24}) \/\* (.*?) \*\/,\Z/) { + $$pbxgroup_children{$1} = $2; + } elsif (/\A\t\t\t\);\Z/) { + $pbxgroup_state++; + } else { + die("Expected pbxgroup obj's children element on '$pbxprojfname' line $lineno\n"); + } + } elsif ($pbxgroup_state == 5) { + if (/\A\t\t\t(.*?) \= (.*?);\Z/) { + $$thispbxgroup{$1} = $2; + } elsif (/\A\t\t\};\Z/) { + $pbxgroup_state = 1; + } else { + die("Expected pbxgroup obj field on '$pbxprojfname' line $lineno\n"); + } + } else { + die("bug in this script."); + } + } + + die("Didn't see PBXGroup section in '$pbxprojfname'. Bug?\n") if $pbxgroup_state == 0; + die("Didn't see Public Headers PBXGroup in '$pbxprojfname'. Bug?\n") if $pubheaders_group_hash eq ''; + die("Didn't see Library Source PBXGroup in '$pbxprojfname'. Bug?\n") if $libsrc_group_hash eq ''; + + # Some debug log dumping... + if (0) { + foreach (keys %pbxgroups) { + my $k = $_; + my $g = $pbxgroups{$k}; + print("$_:\n"); + foreach (keys %$g) { + print(" $_:\n"); + if ($_ eq 'children') { + my $kids = $$g{$_}; + foreach (keys %$kids) { + print(" $_ -> " . $$kids{$_} . "\n"); + } + } else { + print(' ' . $$g{$_} . "\n"); + } + } + print("\n"); + } + } + + # Get some unique IDs for our new thing. + my $fileref = generate_xcode_id(); + my $buildfileref = generate_xcode_id(); + + + # Figure out what group to insert this into (or what groups to make) + my $add_to_group_fileref = $fileref; + my $add_to_group_addfname = $addfname; + my $newgrptext = ''; + my $grphash = ''; + if ($is_public_header) { + $grphash = $pubheaders_group_hash; # done! + } else { + $grphash = $libsrc_group_hash; + my @splitpath = split(/\//, dirname($addpath)); + if ($splitpath[0] eq 'src') { + shift @splitpath; + } + while (my $elem = shift(@splitpath)) { + my $g = $pbxgroups{$grphash}; + my $kids = $$g{'children'}; + my $found = 0; + foreach (keys %$kids) { + my $hash = $_; + my $fname = $$kids{$hash}; + if (uc($fname) eq uc($elem)) { + $grphash = $hash; + $found = 1; + last; + } + } + unshift(@splitpath, $elem), last if (not $found); + } + + if (@splitpath) { # still elements? We need to build groups. + my $newgroupref = generate_xcode_id(); + + $add_to_group_fileref = $newgroupref; + $add_to_group_addfname = $splitpath[0]; + + while (my $elem = shift(@splitpath)) { + my $lastelem = @splitpath ? 0 : 1; + my $childhash = $lastelem ? $fileref : generate_xcode_id(); + my $childpath = $lastelem ? $addfname : $splitpath[0]; + $newgrptext .= "\t\t$newgroupref /* $elem */ = {\n"; + $newgrptext .= "\t\t\tisa = PBXGroup;\n"; + $newgrptext .= "\t\t\tchildren = (\n"; + $newgrptext .= "\t\t\t\t$childhash /* $childpath */,\n"; + $newgrptext .= "\t\t\t);\n"; + $newgrptext .= "\t\t\tpath = $elem;\n"; + $newgrptext .= "\t\t\tsourceTree = \"\";\n"; + $newgrptext .= "\t\t};\n"; + $newgroupref = $childhash; + } + } + } + + my $tmpfname = "$pbxprojfname.tmp"; + open $fh, '>', $tmpfname or die("Failed to open '$tmpfname': $!\n"); + + my $add_to_this_group = 0; + $pbxgroup_state = 0; + $lineno = 0; + foreach (@pbxproj) { + $lineno++; + if ($pbxgroup_state == 0) { + # Drop in new references at the end of their sections... + if (/\A\/\* End PBXBuildFile section \*\/\Z/) { + print $fh "\t\t$buildfileref /* $addfname in $srcs_or_headers */ = {isa = PBXBuildFile; fileRef = $fileref /* $addfname */;"; + if ($is_public_header) { + print $fh " settings = {ATTRIBUTES = (Public, ); };"; + } + print $fh " };\n"; + } elsif (/\A\/\* End PBXFileReference section \*\/\Z/) { + print $fh "\t\t$fileref /* $addfname */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = $addfiletype; name = $addfname; path = $filerefpath; sourceTree = \"\"; };\n"; + } elsif (/\A\/\* Begin PBXGroup section \*\/\Z/) { + $pbxgroup_state = 1; + } elsif (/\A\/\* Begin PBXSourcesBuildPhase section \*\/\Z/) { + $pbxgroup_state = 5; + } + } elsif ($pbxgroup_state == 1) { + if (/\A\t\t([A-F0-9]{24}) \/\* (.*?) \*\/ \= \{\Z/) { + $pbxgroup_state++; + $add_to_this_group = $1 eq $grphash; + } elsif (/\A\/\* End PBXGroup section \*\/\Z/) { + print $fh $newgrptext; + $pbxgroup_state = 0; + } + } elsif ($pbxgroup_state == 2) { + if (/\A\t\t\tchildren \= \(\Z/) { + $pbxgroup_state++; + } + } elsif ($pbxgroup_state == 3) { + if (/\A\t\t\t\);\Z/) { + if ($add_to_this_group) { + print $fh "\t\t\t\t$add_to_group_fileref /* $add_to_group_addfname */,\n"; + } + $pbxgroup_state++; + } + } elsif ($pbxgroup_state == 4) { + if (/\A\t\t\};\Z/) { + $add_to_this_group = 0; + } + $pbxgroup_state = 1; + } elsif ($pbxgroup_state == 5) { + if (/\A\t\t\t\);\Z/) { + if ($srcs_or_headers eq 'Sources') { + print $fh "\t\t\t\t$buildfileref /* $addfname in $srcs_or_headers */,\n"; + } + $pbxgroup_state = 0; + } + } + + print $fh "$_\n"; + } + + close($fh); + rename($tmpfname, $pbxprojfname); +} + +my %visualc_references = (); +sub generate_visualc_id { # these are just standard Windows GUIDs. + my @chars = ('0'..'9', 'a'..'f'); + my $str; + + do { + my $len = 24; + $str = '0000'; # start and end with '0000' so we know we added it. + while ($len--) { + $str .= $chars[rand @chars] + }; + $str .= '0000'; # start and end with '0000' so we know we added it. + $str =~ s/\A(........)(....)(....)(............)\Z/$1-$2-$3-$4/; # add dashes in the appropriate places. + } while (defined($visualc_references{$str})); + + $visualc_references{$str} = 1; # so future calls can't generate this one. + + return $str; +} + + +sub process_visualstudio { + my $addpath = shift; + my $vcxprojfname = shift; + my $lineno; + + %visualc_references = (); + + my $is_public_header = ($addpath =~ /\Ainclude\/SDL3\//) ? 1 : 0; + + my $addfname = basename($addpath); + my $addext = ''; + if ($addfname =~ /\.(.*?)\Z/) { + $addext = $1; + } + + my $isheader = 0; + if ($addext eq 'c') { + $isheader = 0; + } elsif ($addext eq 'm') { + return; # don't add Objective-C files to Visual Studio projects! + } elsif ($addext eq 'h') { + $isheader = 1; + } else { + die("Unexpected file extension '$addext'\n"); + } + + my $fh; + + open $fh, '<', $vcxprojfname or die("Failed to open '$vcxprojfname': $!\n"); + chomp(my @vcxproj = <$fh>); + close($fh); + + my $vcxgroup_state; + + my $rawaddvcxpath = "$addpath"; + $rawaddvcxpath =~ s/\//\\/g; + + # Figure out relative path from vcxproj file... + my $addvcxpath = ''; + my @subdirs = split(/\//, $vcxprojfname); + pop @subdirs; + foreach (@subdirs) { + $addvcxpath .= "..\\"; + } + $addvcxpath .= $rawaddvcxpath; + + my $prevname = undef; + + my $tmpfname; + + $tmpfname = "$vcxprojfname.tmp"; + open $fh, '>', $tmpfname or die("Failed to open '$tmpfname': $!\n"); + + my $added = 0; + + $added = 0; + $vcxgroup_state = 0; + $prevname = undef; + $lineno = 0; + foreach (@vcxproj) { + $lineno++; + if ($vcxgroup_state == 0) { + if (/\A \\Z/) { + $vcxgroup_state = 1; + $prevname = undef; + } + } elsif ($vcxgroup_state == 1) { + if (/\A \\Z/) { + $vcxgroup_state = 0; + $prevname = undef; + } + } + + # Don't do elsif, we need to check this line again. + if ($vcxgroup_state == 2) { + if (/\A \Z/) { + my $nextname = $1; + if ((not $added) && (((not defined $prevname) || (uc($prevname) lt uc($addvcxpath))) && (uc($nextname) gt uc($addvcxpath)))) { + print $fh " \n"; + $vcxgroup_state = 0; + $added = 1; + } + $prevname = $nextname; + } elsif (/\A \<\/ItemGroup\>\Z/) { + if ((not $added) && ((not defined $prevname) || (uc($prevname) lt uc($addvcxpath)))) { + print $fh " \n"; + $vcxgroup_state = 0; + $added = 1; + } + } + } elsif ($vcxgroup_state == 3) { + if (/\A \Z/) { + my $nextname = $1; + if ((not $added) && (((not defined $prevname) || (uc($prevname) lt uc($addvcxpath))) && (uc($nextname) gt uc($addvcxpath)))) { + print $fh " \n"; + $vcxgroup_state = 0; + $added = 1; + } + $prevname = $nextname; + } elsif (/\A \<\/ItemGroup\>\Z/) { + if ((not $added) && ((not defined $prevname) || (uc($prevname) lt uc($addvcxpath)))) { + print $fh " \n"; + $vcxgroup_state = 0; + $added = 1; + } + } + } + + print $fh "$_\n"; + } + + close($fh); + rename($tmpfname, $vcxprojfname); + + my $vcxfiltersfname = "$vcxprojfname.filters"; + open $fh, '<', $vcxfiltersfname or die("Failed to open '$vcxfiltersfname': $!\n"); + chomp(my @vcxfilters = <$fh>); + close($fh); + + my $newgrptext = ''; + my $filter = ''; + if ($is_public_header) { + $filter = 'API Headers'; + } else { + $filter = lc(dirname($addpath)); + $filter =~ s/\Asrc\///; # there's no filter for the base "src/" dir, where SDL.c and friends live. + $filter =~ s/\//\\/g; + + if ($filter ne '') { + # see if the filter already exists, otherwise add it. + my %existing_filters = (); + my $current_filter = ''; + my $found = 0; + foreach (@vcxfilters) { + # These lines happen to be unique, so we don't have to parse down to find this section. + if (/\A \\Z/) { + $current_filter = lc($1); + if ($current_filter eq $filter) { + $found = 1; + } + } elsif (/\A \\{(.*?)\}\<\/UniqueIdentifier\>\Z/) { + $visualc_references{$1} = $current_filter; # gather up existing GUIDs to avoid duplicates. + $existing_filters{$current_filter} = $1; + } + } + + if (not $found) { # didn't find it? We need to build filters. + my $subpath = ''; + my @splitpath = split(/\\/, $filter); + while (my $elem = shift(@splitpath)) { + $subpath .= "\\" if ($subpath ne ''); + $subpath .= $elem; + if (not $existing_filters{$subpath}) { + my $newgroupref = generate_visualc_id(); + $newgrptext .= " \n"; + $newgrptext .= " {$newgroupref}\n"; + $newgrptext .= " \n" + } + } + } + } + } + + $tmpfname = "$vcxfiltersfname.tmp"; + open $fh, '>', $tmpfname or die("Failed to open '$tmpfname': $!\n"); + + $added = 0; + $vcxgroup_state = 0; + $prevname = undef; + $lineno = 0; + foreach (@vcxfilters) { + $lineno++; + + # We cheat here, because these lines are unique, we don't have to fully parse this file. + if ($vcxgroup_state == 0) { + if (/\A \\Z/) { + if ($newgrptext ne '') { + $vcxgroup_state = 1; + $prevname = undef; + } + } elsif (/\A \\Z/) { + print $fh $newgrptext; + $newgrptext = ''; + $vcxgroup_state = 0; + } + } elsif ($vcxgroup_state == 2) { + if (/\A \n"; + print $fh " $filter\n"; + print $fh " \n"; + } else { + print $fh " />\n"; + } + $added = 1; + } + $prevname = $nextname; + } elsif (/\A \<\/ItemGroup\>\Z/) { + if ((not $added) && ((not defined $prevname) || (uc($prevname) lt uc($addvcxpath)))) { + print $fh " \n"; + print $fh " $filter\n"; + print $fh " \n"; + } else { + print $fh " />\n"; + } + $added = 1; + } + $vcxgroup_state = 0; + } + } elsif ($vcxgroup_state == 3) { + if (/\A \n"; + print $fh " $filter\n"; + print $fh " \n"; + } else { + print $fh " />\n"; + } + $added = 1; + } + $prevname = $nextname; + } elsif (/\A \<\/ItemGroup\>\Z/) { + if ((not $added) && ((not defined $prevname) || (uc($prevname) lt uc($addvcxpath)))) { + print $fh " \n"; + print $fh " $filter\n"; + print $fh " \n"; + } else { + print $fh " />\n"; + } + $added = 1; + } + $vcxgroup_state = 0; + } + } + + print $fh "$_\n"; + } + + close($fh); + rename($tmpfname, $vcxfiltersfname); +} + + +# Mainline! + +chdir(dirname($0)); # assumed to be in build-scripts +chdir('..'); # head to root of source tree. + +foreach (@ARGV) { + s/\A\.\///; # Turn "./path/to/file.txt" into "path/to/file.txt" + my $arg = $_; + process_xcode($arg, 'Xcode/SDL/SDL.xcodeproj/project.pbxproj'); + process_visualstudio($arg, 'VisualC/SDL/SDL.vcxproj'); + process_visualstudio($arg, 'VisualC-GDK/SDL/SDL.vcxproj'); + process_visualstudio($arg, 'VisualC-WinRT/SDL-UWP.vcxproj'); +} + +print("Done. Please run `git diff` and make sure this looks okay!\n"); + +exit(0); + diff --git a/external/sdl/SDL/build-scripts/gen_audio_resampler_filter.c b/external/sdl/SDL/build-scripts/gen_audio_resampler_filter.c index fe4e5388..19991bd9 100644 --- a/external/sdl/SDL/build-scripts/gen_audio_resampler_filter.c +++ b/external/sdl/SDL/build-scripts/gen_audio_resampler_filter.c @@ -41,23 +41,28 @@ gcc -o genfilter build-scripts/gen_audio_resampler_filter.c -lm && ./genfilter > #include #include +#ifndef M_PI + #define M_PI 3.14159265358979323846 +#endif + #define RESAMPLER_ZERO_CROSSINGS 5 #define RESAMPLER_BITS_PER_SAMPLE 16 -#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)) -#define RESAMPLER_FILTER_SIZE ((RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS) + 1) +#define RESAMPLER_BITS_PER_ZERO_CROSSING ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1) +#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING) +#define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS) /* This is a "modified" bessel function, so you can't use POSIX j0() */ static double bessel(const double x) { const double xdiv2 = x / 2.0; - double i0 = 1.0f; - double f = 1.0f; + double i0 = 1.0; + double f = 1.0; int i = 1; while (1) { const double diff = pow(xdiv2, i * 2) / pow(f, 2); - if (diff < 1.0e-21f) { + if (diff < 1.0e-21) { break; } i0 += diff; @@ -70,30 +75,21 @@ bessel(const double x) /* build kaiser table with cardinal sine applied to it, and array of differences between elements. */ static void -kaiser_and_sinc(float *table, float *diffs, const int tablelen, const double beta) +kaiser_and_sinc(double *table, const int tablelen, const double beta) { - const int lenm1 = tablelen - 1; - const int lenm1div2 = lenm1 / 2; const double bessel_beta = bessel(beta); int i; - table[0] = 1.0f; - for (i = 1; i < tablelen; i++) { - const double kaiser = bessel(beta * sqrt(1.0 - pow(((i - lenm1) / 2.0) / lenm1div2, 2.0))) / bessel_beta; - table[tablelen - i] = (float) kaiser; - } + table[0] = 1.0; for (i = 1; i < tablelen; i++) { - const float x = (((float) i) / ((float) RESAMPLER_SAMPLES_PER_ZERO_CROSSING)) * ((float) M_PI); - table[i] *= sinf(x) / x; - diffs[i - 1] = table[i] - table[i - 1]; + const double kaiser = bessel(beta * sqrt(1.0 - pow((double)i / (double)(tablelen), 2.0))) / bessel_beta; + const double x = (((double) i) / ((double) RESAMPLER_SAMPLES_PER_ZERO_CROSSING)) * M_PI; + table[i] = kaiser * (sin(x) / x); } - diffs[lenm1] = 0.0f; } - -static float ResamplerFilter[RESAMPLER_FILTER_SIZE]; -static float ResamplerFilterDifference[RESAMPLER_FILTER_SIZE]; +static double ResamplerFilter[RESAMPLER_FILTER_SIZE]; static void PrepareResampleFilter(void) @@ -101,12 +97,12 @@ PrepareResampleFilter(void) /* if dB > 50, beta=(0.1102 * (dB - 8.7)), according to Matlab. */ const double dB = 80.0; const double beta = 0.1102 * (dB - 8.7); - kaiser_and_sinc(ResamplerFilter, ResamplerFilterDifference, RESAMPLER_FILTER_SIZE, beta); + kaiser_and_sinc(ResamplerFilter, RESAMPLER_FILTER_SIZE, beta); } int main(void) { - int i; + int i, j; PrepareResampleFilter(); @@ -136,22 +132,16 @@ int main(void) "\n" "#define RESAMPLER_ZERO_CROSSINGS %d\n" "#define RESAMPLER_BITS_PER_SAMPLE %d\n" - "#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1))\n" - "#define RESAMPLER_FILTER_SIZE ((RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS) + 1)\n" + "#define RESAMPLER_BITS_PER_ZERO_CROSSING ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)\n" + "#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING)\n" + "#define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS)\n" "\n", RESAMPLER_ZERO_CROSSINGS, RESAMPLER_BITS_PER_SAMPLE ); - printf("static const float ResamplerFilter[RESAMPLER_FILTER_SIZE] = {\n"); - printf(" %.9ff", ResamplerFilter[0]); - for (i = 0; i < RESAMPLER_FILTER_SIZE-1; i++) { - printf("%s%.9ff", ((i % 5) == 4) ? ",\n " : ", ", ResamplerFilter[i+1]); - } - printf("\n};\n\n"); - - printf("static const float ResamplerFilterDifference[RESAMPLER_FILTER_SIZE] = {\n"); - printf(" %.9ff", ResamplerFilterDifference[0]); - for (i = 0; i < RESAMPLER_FILTER_SIZE-1; i++) { - printf("%s%.9ff", ((i % 5) == 4) ? ",\n " : ", ", ResamplerFilterDifference[i+1]); + printf("static const float ResamplerFilter[RESAMPLER_FILTER_SIZE] = {"); + for (i = 0; i < RESAMPLER_FILTER_SIZE; i++) { + j = (i % RESAMPLER_ZERO_CROSSINGS) * RESAMPLER_SAMPLES_PER_ZERO_CROSSING + (i / RESAMPLER_ZERO_CROSSINGS); + printf("%s%12.9ff,", (i % RESAMPLER_ZERO_CROSSINGS) ? "" : "\n ", ResamplerFilter[j]); } printf("\n};\n\n"); diff --git a/external/sdl/SDL/build-scripts/wikiheaders.pl b/external/sdl/SDL/build-scripts/wikiheaders.pl index bb09faa8..c2bb8edb 100755 --- a/external/sdl/SDL/build-scripts/wikiheaders.pl +++ b/external/sdl/SDL/build-scripts/wikiheaders.pl @@ -31,6 +31,7 @@ my $optionsfname = undef; my $wikipreamble = undef; my $changeformat = undef; my $manpath = undef; +my $gitrev = undef; foreach (@ARGV) { $warn_about_missing = 1, next if $_ eq '--warn-about-missing'; @@ -47,6 +48,9 @@ foreach (@ARGV) { } elsif (/\A--manpath=(.*)\Z/) { $manpath = $1; next; + } elsif (/\A--rev=(.*)\Z/) { + $gitrev = $1; + next; } $srcpath = $_, next if not defined $srcpath; $wikipath = $_, next if not defined $wikipath; @@ -788,6 +792,45 @@ while (my $d = readdir(DH)) { } closedir(DH); +delete $wikifuncs{"Undocumented"}; + +{ + my $path = "$wikipath/Undocumented.md"; + open(FH, '>', $path) or die("Can't open '$path': $!\n"); + + print FH "# Undocumented\n\n"; + + print FH "## Functions defined in the headers, but not in the wiki\n\n"; + my $header_only_func = 0; + foreach (sort keys %headerfuncs) { + my $fn = $_; + if (not defined $wikifuncs{$fn}) { + print FH "- [$fn]($fn)\n"; + $header_only_func = 1; + } + } + if (!$header_only_func) { + print FH "(none)\n"; + } + print FH "\n"; + + print FH "## Functions defined in the wiki, but not in the headers\n\n"; + + my $wiki_only_func = 0; + foreach (sort keys %wikifuncs) { + my $fn = $_; + if (not defined $headerfuncs{$fn}) { + print FH "- [$fn]($fn)\n"; + $wiki_only_func = 1; + } + } + if (!$wiki_only_func) { + print FH "(none)\n"; + } + print FH "\n"; + + close(FH); +} if ($warn_about_missing) { foreach (keys %wikifuncs) { @@ -1437,8 +1480,10 @@ if ($copy_direction == 1) { # --copy-to-headers close(FH); } - my $gitrev = `cd "$srcpath" ; git rev-list HEAD~..`; - chomp($gitrev); + if (!$gitrev) { + $gitrev = `cd "$srcpath" ; git rev-list HEAD~..`; + chomp($gitrev); + } # !!! FIXME open(FH, '<', "$srcpath/$versionfname") or die("Can't open '$srcpath/$versionfname': $!\n"); diff --git a/external/sdl/SDL/cmake/sdl3.pc.in b/external/sdl/SDL/cmake/sdl3.pc.in index 5069c90d..bfc2afff 100644 --- a/external/sdl/SDL/cmake/sdl3.pc.in +++ b/external/sdl/SDL/cmake/sdl3.pc.in @@ -10,4 +10,4 @@ Version: @PROJECT_VERSION@ Requires.private: @SDL_PC_PRIVATE_REQUIRES@ Conflicts: Libs: -L${libdir} @SDL_RLD_FLAGS@ @SDL_PC_LIBS@ @SDL_PC_SECTION_LIBS_PRIVATE@ @SDL_PC_STATIC_LIBS@ -Cflags: -I${includedir} -I${includedir}/SDL3 @SDL_PC_CFLAGS@ +Cflags: -I${includedir} @SDL_PC_CFLAGS@ diff --git a/external/sdl/SDL/cmake/sdlchecks.cmake b/external/sdl/SDL/cmake/sdlchecks.cmake index f12b7e3e..478c5691 100644 --- a/external/sdl/SDL/cmake/sdlchecks.cmake +++ b/external/sdl/SDL/cmake/sdlchecks.cmake @@ -1019,8 +1019,8 @@ endmacro() # Check for HIDAPI support macro(CheckHIDAPI) - set(HAVE_HIDAPI TRUE) if(SDL_HIDAPI) + set(HAVE_HIDAPI ON) if(SDL_HIDAPI_LIBUSB) set(HAVE_LIBUSB FALSE) @@ -1029,25 +1029,27 @@ macro(CheckHIDAPI) if(PC_LIBUSB_FOUND) cmake_push_check_state() list(APPEND CMAKE_REQUIRED_INCLUDES ${PC_LIBUSB_INCLUDE_DIRS}) - check_include_file(libusb.h HAVE_LIBUSB_H) + list(APPEND CMAKE_REQUIRED_LIBRARIES PkgConfig::PC_LIBUSB) + check_c_source_compiles(" + #include + #include + int main(int argc, char **argv) { + libusb_close(NULL); + return 0; + }" HAVE_LIBUSB_H) cmake_pop_check_state() if(HAVE_LIBUSB_H) set(HAVE_LIBUSB TRUE) - if(HIDAPI_ONLY_LIBUSB) - sdl_link_dependency(hidapi LIBS PkgConfig::PC_LIBUSB PKG_CONFIG_PREFIX PC_LIBUSB PKG_CONFIG_SPECS ${LibUSB_PKG_CONFIG_SPEC}) - else() - # libusb is loaded dynamically, so don't add link to it - FindLibraryAndSONAME("usb-1.0" LIBDIRS ${PC_LIBUSB_LIBRARY_DIRS}) - if(USB_1.0_LIB) - set(SDL_LIBUSB_DYNAMIC "\"${USB_1.0_LIB_SONAME}\"") - endif() + FindLibraryAndSONAME("usb-1.0" LIBDIRS ${PC_LIBUSB_LIBRARY_DIRS}) + if(SDL_HIDAPI_LIBUSB_SHARED AND USB_1.0_LIB_SONAME) + set(HAVE_HIDAPI_LIBUSB_SHARED ON) + set(SDL_LIBUSB_DYNAMIC "\"${USB_1.0_LIB_SONAME}\"") sdl_link_dependency(hidapi INCLUDES $) + else() + sdl_link_dependency(hidapi LIBS PkgConfig::PC_LIBUSB PKG_CONFIG_PREFIX PC_LIBUSB PKG_CONFIG_SPECS ${LibUSB_PKG_CONFIG_SPEC}) endif() endif() endif() - if(HIDAPI_ONLY_LIBUSB AND NOT HAVE_LIBUSB) - set(HAVE_HIDAPI FALSE) - endif() set(HAVE_HIDAPI_LIBUSB ${HAVE_LIBUSB}) endif() diff --git a/external/sdl/SDL/cmake/sdlmanpages.cmake b/external/sdl/SDL/cmake/sdlmanpages.cmake index 0049de1f..cc706dd7 100644 --- a/external/sdl/SDL/cmake/sdlmanpages.cmake +++ b/external/sdl/SDL/cmake/sdlmanpages.cmake @@ -2,7 +2,9 @@ include(CMakeParseArguments) include(GNUInstallDirs) function(SDL_generate_manpages) - cmake_parse_arguments(ARG "" "RESULT_VARIABLE;NAME;BUILD_DOCDIR;HEADERS_DIR;SOURCE_DIR;SYMBOL;OPTION_FILE;WIKIHEADERS_PL_PATH" "" ${ARGN}) + cmake_parse_arguments(ARG "" "RESULT_VARIABLE;NAME;BUILD_DOCDIR;HEADERS_DIR;SOURCE_DIR;SYMBOL;OPTION_FILE;WIKIHEADERS_PL_PATH;REVISION" "" ${ARGN}) + + set(wikiheaders_extra_args) if(NOT ARG_NAME) set(ARG_NAME "${PROJECT_NAME}") @@ -25,6 +27,10 @@ function(SDL_generate_manpages) message(FATAL_ERROR "Missing required SYMBOL argument") endif() + if(ARG_REVISION) + list(APPEND wikiheaders_extra_args "--rev=${ARG_REVISION}") + endif() + if(NOT ARG_BUILD_DOCDIR) set(ARG_BUILD_DOCDIR "${CMAKE_CURRENT_BINARY_DIR}/docs") endif() @@ -40,13 +46,13 @@ function(SDL_generate_manpages) add_custom_command( OUTPUT "${BUILD_WIKIDIR}/${ARG_SYMBOL}.md" COMMAND "${CMAKE_COMMAND}" -E make_directory "${BUILD_WIKIDIR}" - COMMAND "${PERL_EXECUTABLE}" "${ARG_WIKIHEADERS_PL_PATH}" "${ARG_SOURCE_DIR}" "${BUILD_WIKIDIR}" "--options=${ARG_OPTION_FILE}" --copy-to-wiki + COMMAND "${PERL_EXECUTABLE}" "${ARG_WIKIHEADERS_PL_PATH}" "${ARG_SOURCE_DIR}" "${BUILD_WIKIDIR}" "--options=${ARG_OPTION_FILE}" --copy-to-wiki ${wikiheaders_extra_args} DEPENDS ${HEADER_FILES} "${ARG_WIKIHEADERS_PL_PATH}" "${ARG_OPTION_FILE}" COMMENT "Generating ${ARG_NAME} wiki markdown files" ) add_custom_command( OUTPUT "${BUILD_MANDIR}/man3/${ARG_SYMBOL}.3" - COMMAND "${PERL_EXECUTABLE}" "${ARG_WIKIHEADERS_PL_PATH}" "${ARG_SOURCE_DIR}" "${BUILD_WIKIDIR}" "--options=${ARG_OPTION_FILE}" "--manpath=${BUILD_MANDIR}" --copy-to-manpages + COMMAND "${PERL_EXECUTABLE}" "${ARG_WIKIHEADERS_PL_PATH}" "${ARG_SOURCE_DIR}" "${BUILD_WIKIDIR}" "--options=${ARG_OPTION_FILE}" "--manpath=${BUILD_MANDIR}" --copy-to-manpages ${wikiheaders_extra_args} DEPENDS "${BUILD_WIKIDIR}/${ARG_SYMBOL}.md" "${ARG_WIKIHEADERS_PL_PATH}" "${ARG_OPTION_FILE}" COMMENT "Generating ${ARG_NAME} man pages" ) diff --git a/external/sdl/SDL/cmake/xxd.py b/external/sdl/SDL/cmake/xxd.py index 5254cd1f..678946ae 100755 --- a/external/sdl/SDL/cmake/xxd.py +++ b/external/sdl/SDL/cmake/xxd.py @@ -17,7 +17,7 @@ def main(): binary_data = args.input.open("rb").read() - with args.output.open("w") as fout: + with args.output.open("w", newline="\n") as fout: fout.write("unsigned char {}[] = {{\n".format(varname)) bytes_written = 0 while bytes_written < len(binary_data): diff --git a/external/sdl/SDL/docs/README-android.md b/external/sdl/SDL/docs/README-android.md index 75a8f60d..2eaee31b 100644 --- a/external/sdl/SDL/docs/README-android.md +++ b/external/sdl/SDL/docs/README-android.md @@ -10,13 +10,13 @@ The rest of this README covers the Android gradle style build process. Requirements ================================================================================ -Android SDK (version 31 or later) +Android SDK (version 34 or later) https://developer.android.com/sdk/index.html Android NDK r15c or later https://developer.android.com/tools/sdk/ndk/index.html -Minimum API level supported by SDL: 16 (Android 4.1) +Minimum API level supported by SDL: 19 (Android 4.4) How the port works @@ -435,13 +435,13 @@ The Tegra Graphics Debugger is available from NVidia here: https://developer.nvidia.com/tegra-graphics-debugger -Why is API level 16 the minimum required? +Why is API level 19 the minimum required? ================================================================================ -The latest NDK toolchain doesn't support targeting earlier than API level 16. -As of this writing, according to https://developer.android.com/about/dashboards/index.html -about 99% of the Android devices accessing Google Play support API level 16 or -higher (January 2018). +The latest NDK toolchain doesn't support targeting earlier than API level 19. +As of this writing, according to https://www.composables.com/tools/distribution-chart +about 99.7% of the Android devices accessing Google Play support API level 19 or +higher (August 2023). A note regarding the use of the "dirty rectangles" rendering technique diff --git a/external/sdl/SDL/docs/README-emscripten.md b/external/sdl/SDL/docs/README-emscripten.md index 5c56984e..5826d847 100644 --- a/external/sdl/SDL/docs/README-emscripten.md +++ b/external/sdl/SDL/docs/README-emscripten.md @@ -1,27 +1,187 @@ # Emscripten -(This documentation is not very robust; we will update and expand this later.) +## The state of things -## A quick note about audio +(As of September 2023, but things move quickly and we don't update this +document often.) + +In modern times, all the browsers you probably care about (Chrome, Firefox, +Edge, and Safari, on Windows, macOS, Linux, iOS and Android), support some +reasonable base configurations: + +- WebAssembly (don't bother with asm.js any more) +- WebGL (which will look like OpenGL ES 2 or 3 to your app). +- Threads (see caveats, though!) +- Game controllers +- Autoupdating (so you can assume they have a recent version of the browser) + +All this to say we're at the point where you don't have to make a lot of +concessions to get even a fairly complex SDL-based game up and running. + + +## RTFM + +This document is a quick rundown of some high-level details. The +documentation at [emscripten.org](https://emscripten.org/) is vast +and extremely detailed for a wide variety of topics, and you should at +least skim through it at some point. + + +## Porting your app to Emscripten + +Many many things just need some simple adjustments and they'll compile +like any other C/C++ code, as long as SDL was handling the platform-specific +work for your program. + +First, you probably need this in at least one of your source files: + +```c +#ifdef __EMSCRIPTEN__ +#include +#endif +``` + +Second: assembly language code has to go. Replace it with C. You can even use +[x86 SIMD intrinsic functions in Emscripten](https://emscripten.org/docs/porting/simd.html)! + +Third: Middleware has to go. If you have a third-party library you link +against, you either need an Emscripten port of it, or the source code to it +to compile yourself, or you need to remove it. + +Fourth: You still start in a function called main(), but you need to get out of +it and into a function that gets called repeatedly, and returns quickly, +called a mainloop. + +Somewhere in your program, you probably have something that looks like a more +complicated version of this: + +```c +void main(void) +{ + initialize_the_game(); + while (game_is_still_running) { + check_for_new_input(); + think_about_stuff(); + draw_the_next_frame(); + } + deinitialize_the_game(); +} +``` + +This will not work on Emscripten, because the main thread needs to be free +to do stuff and can't sit in this loop forever. So Emscripten lets you set up +a [mainloop](https://emscripten.org/docs/porting/emscripten-runtime-environment.html#browser-main-loop). + +```c +static void mainloop(void) /* this will run often, possibly at the monitor's refresh rate */ +{ + if (!game_is_still_running) { + deinitialize_the_game(); + #ifdef __EMSCRIPTEN__ + emscripten_cancel_main_loop(); /* this should "kill" the app. */ + #else + exit(0); + #endif + } + + check_for_new_input(); + think_about_stuff(); + draw_the_next_frame(); +} + +void main(void) +{ + initialize_the_game(); + #ifdef __EMSCRIPTEN__ + emscripten_set_main_loop(mainloop, 0, 1); + #else + while (1) { mainloop(); } + #endif +} +``` + +Basically, `emscripten_set_main_loop(mainloop, 0, 1);` says "run +`mainloop` over and over until I end the program." The function will +run, and return, freeing the main thread for other tasks, and then +run again when it's time. The `1` parameter does some magic to make +your main() function end immediately; this is useful because you +don't want any shutdown code that might be sitting below this code +to actually run if main() were to continue on, since we're just +getting started. + +There's a lot of little details that are beyond the scope of this +document, but that's the biggest intial set of hurdles to porting +your app to the web. + + +## Do you need threads? + +If you plan to use threads, they work on all major browsers now. HOWEVER, +they bring with them a lot of careful considerations. Rendering _must_ +be done on the main thread. This is a general guideline for many +platforms, but a hard requirement on the web. + +Many other things also must happen on the main thread; often times SDL +and Emscripten make efforts to "proxy" work to the main thread that +must be there, but you have to be careful (and read more detailed +documentation than this for the finer points). + +Even when using threads, your main thread needs to set an Emscripten +mainloop that runs quickly and returns, or things will fail to work +correctly. + +You should definitely read [Emscripten's pthreads docs](https://emscripten.org/docs/porting/pthreads.html) +for all the finer points. Mostly SDL's thread API will work as expected, +but is built on pthreads, so it shares the same little incompatibilities +that are documented there, such as where you can use a mutex, and when +a thread will start running, etc. + + +IMPORTANT: You have to decide to either build something that uses +threads or something that doesn't; you can't have one build +that works everywhere. This is an Emscripten (or maybe WebAssembly? +Or just web browsers in general?) limitation. If you aren't using +threads, it's easier to not enable them at all, at build time. + +If you use threads, you _have to_ run from a web server that has +[COOP/COEP headers set correctly](https://web.dev/why-coop-coep/) +or your program will fail to start at all. + +If building with threads, `__EMSCRIPTEN_PTHREADS__` will be defined +for checking with the C preprocessor, so you can build something +different depending on what sort of build you're compiling. + + +## Audio + +Audio works as expected at the API level, but not exactly like other +platforms. + +You'll only see a single default audio device. Audio capture also works; +if the browser pops up a prompt to ask for permission to access the +microphone, the SDL_OpenAudioDevice call will succeed and start producing +silence at a regular interval. Once the user approves the request, real +audio data will flow. If the user denies it, the app is not informed and +will just continue to receive silence. Modern web browsers will not permit web pages to produce sound before the -user has interacted with them; this is for several reasons, not the least -of which being that no one likes when a random browser tab suddenly starts -making noise and the user has to scramble to figure out which and silence -it. +user has interacted with them (clicked or tapped on them, usually); this is +for several reasons, not the least of which being that no one likes when a +random browser tab suddenly starts making noise and the user has to scramble +to figure out which and silence it. -To solve this, most browsers will refuse to let a web app use the audio -subsystem at all before the user has interacted with (clicked on) the page -in a meaningful way. SDL-based apps also have to deal with this problem; if -the user hasn't interacted with the page, SDL_OpenAudioDevice will fail. +SDL will allow you to open the audio device for playback in this +circumstance, and your audio callback will fire, but SDL will throw the audio +data away until the user interacts with the page. This helps apps that depend +on the audio callback to make progress, and also keeps audio playback in sync +once the app is finally allowed to make noise. -There are two reasonable ways to deal with this: if you are writing some -sort of media player thing, where the user expects there to be a volume -control when you mouseover the canvas, just default that control to a muted -state; if the user clicks on the control to unmute it, on this first click, -open the audio device. This allows the media to play at start, the user can -reasonably opt-in to listening, and you never get access denied to the audio -device. +There are two reasonable ways to deal with the silence at the app level: +if you are writing some sort of media player thing, where the user expects +there to be a volume control when you mouseover the canvas, just default +that control to a muted state; if the user clicks on the control to unmute +it, on this first click, open the audio device. This allows the media to +play at start, and the user can reasonably opt-in to listening. Many games do not have this sort of UI, and are more rigid about starting audio along with everything else at the start of the process. For these, your @@ -36,41 +196,170 @@ Please see the discussion at https://github.com/libsdl-org/SDL/issues/6385 for some Javascript code to steal for this approach. +## Rendering + +If you use SDL's 2D render API, it will use GLES2 internally, which +Emscripten will turn into WebGL calls. You can also use OpenGL ES 2 +directly by creating a GL context and drawing into it. + +Calling SDL_RenderPresent (or SDL_GL_SwapWindow) will not actually +present anything on the screen until your return from your mainloop +function. + + ## Building SDL/emscripten +First: do you _really_ need to build SDL from source? + +If you aren't developing SDL itself, have a desire to mess with its source +code, or need something on the bleeding edge, don't build SDL. Just use +Emscripten's packaged version! + +Compile and link your app with `-sUSE_SDL=2` and it'll use a build of +SDL packaged with Emscripten. This comes from the same source code and +fixes the Emscripten project makes to SDL are generally merged into SDL's +revision control, so often this is much easier for app developers. + +`-sUSE_SDL=1` will select Emscripten's JavaScript reimplementation of SDL +1.2 instead; if you need SDL 1.2, this might be fine, but we generally +recommend you don't use SDL 1.2 in modern times. + + +If you want to build SDL, though... + SDL currently requires at least Emscripten 3.1.35 to build. Newer versions are likely to work, as well. Build: - $ mkdir build - $ cd build - $ emcmake cmake .. - $ emmake make +This works on Linux/Unix and macOS. Please send comments about Windows. -Or with cmake: +Make sure you've [installed emsdk](https://emscripten.org/docs/getting_started/downloads.html) +first, and run `source emsdk_env.sh` at the command line so it finds the +tools. - $ mkdir build - $ cd build - $ emcmake cmake .. - $ emmake make +(These cmake options might be overkill, but this has worked for me.) -To build one of the tests: +```bash +mkdir build +cd build +emcmake cmake .. +# you can also do `emcmake cmake -G Ninja ..` and then use `ninja` instead of this command. +emmake make -j4 +``` - $ cd test/ - $ emcc -O2 --js-opts 0 -g4 testdraw.c -I../include ../build/.libs/libSDL3.a ../build/libSDL3_test.a -o a.html +If you want to build with thread support, something like this works: -Uses GLES2 renderer or software +```bash +mkdir build +cd build +emcmake cmake -DSDL_THREADS=On .. +# you can also do `emcmake cmake -G Ninja ..` and then use `ninja` instead of this command. +emmake make -j4 +``` -Some other SDL3 libraries can be easily built (assuming SDL3 is installed somewhere): +To build the tests, add `-DSDL_TESTS=On` to the `emcmake cmake` command line. -SDL_mixer (http://www.libsdl.org/projects/SDL_mixer/): - $ emcmake cmake .. - build as usual... +## Building your app + +You need to compile with `emcc` instead of `gcc` or `clang` or whatever, but +mostly it uses the same command line arguments as Clang. + +Link against the SDL/build/libSDL3.a file you generated by building SDL, +link with `-sUSE_SDL=2` to use Emscripten's prepackaged SDL2 build. + +Usually you would produce a binary like this: + +```bash +gcc -o mygame mygame.c # or whatever +``` + +But for Emscripten, you want to output something else: + +```bash +emcc -o index.html mygame.c +``` + +This will produce several files...support Javascript and WebAssembly (.wasm) +files. The `-o index.html` will produce a simple HTML page that loads and +runs your app. You will (probably) eventually want to replace or customize +that file and do `-o index.js` instead to just build the code pieces. + +If you're working on a program of any serious size, you'll likely need to +link with `-sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=1gb` to get access +to more memory. If using pthreads, you'll need the `-sMAXIMUM_MEMORY=1gb` +or the app will fail to start on iOS browsers, but this might be a bug that +goes away in the future. + + +## Data files + +Your game probably has data files. Here's how to access them. + +Filesystem access works like a Unix filesystem; you have a single directory +tree, possibly interpolated from several mounted locations, no drive letters, +'/' for a path separator. You can access them with standard file APIs like +open() or fopen() or SDL_RWops. You can read or write from the filesystem. + +By default, you probably have a "MEMFS" filesystem (all files are stored in +memory, but access to them is immediate and doesn't need to block). There are +other options, like "IDBFS" (files are stored in a local database, so they +don't need to be in RAM all the time and they can persist between runs of the +program, but access is not synchronous). You can mix and match these file +systems, mounting a MEMFS filesystem at one place and idbfs elsewhere, etc, +but that's beyond the scope of this document. Please refer to Emscripten's +[page on the topic](https://emscripten.org/docs/porting/files/file_systems_overview.html) +for more info. + +The _easiest_ (but not the best) way to get at your data files is to embed +them in the app itself. Emscripten's linker has support for automating this. + +```bash +emcc -o index.html loopwave.c --embed-file=../test/sample.wav@/sounds/sample.wav +``` + +This will pack ../test/sample.wav in your app, and make it available at +"/sounds/sample.wav" at runtime. Emscripten makes sure this data is available +before your main() function runs, and since it's in MEMFS, you can just +read it like you do on other platforms. `--embed-file` can also accept a +directory to pack an entire tree, and you can specify the argument multiple +times to pack unrelated things into the final installation. + +Note that this is absolutely the best approach if you have a few small +files to include and shouldn't worry about the issue further. However, if you +have hundreds of megabytes and/or thousands of files, this is not so great, +since the user will download it all every time they load your page, and it +all has to live in memory at runtime. + +[Emscripten's documentation on the matter](https://emscripten.org/docs/porting/files/packaging_files.html) +gives other options and details, and is worth a read. + + +## Debugging + +Debugging web apps is a mixed bag. You should compile and link with +`-gsource-map`, which embeds a ton of source-level debugging information into +the build, and make sure _the app source code is available on the web server_, +which is often a scary proposition for various reasons. + +When you debug from the browser's tools and hit a breakpoint, you can step +through the actual C/C++ source code, though, which can be nice. + +If you try debugging in Firefox and it doesn't work well for no apparent +reason, try Chrome, and vice-versa. These tools are still relatively new, +and improving all the time. + +SDL_Log() (or even plain old printf) will write to the Javascript console, +and honestly I find printf-style debugging to be easier than setting up a build +for proper debugging, so use whatever tools work best for you. + + +## Questions? + +Please give us feedback on this document at [the SDL bug tracker](https://github.com/libsdl-org/SDL/issues). +If something is wrong or unclear, we want to know! + -SDL_gfx (http://cms.ferzkopp.net/index.php/software/13-sdl-gfx): - $ emcmake cmake .. - build as usual... diff --git a/external/sdl/SDL/docs/README-gdk.md b/external/sdl/SDL/docs/README-gdk.md index a785e653..fefe1635 100644 --- a/external/sdl/SDL/docs/README-gdk.md +++ b/external/sdl/SDL/docs/README-gdk.md @@ -29,6 +29,12 @@ The Windows GDK port supports the full set of Win32 APIs, renderers, controllers * Global task queue callbacks are dispatched during `SDL_PumpEvents` (which is also called internally if using `SDL_PollEvent`). * You can get the handle of the global task queue through `SDL_GDKGetTaskQueue`, if needed. When done with the queue, be sure to use `XTaskQueueCloseHandle` to decrement the reference count (otherwise it will cause a resource leak). +* Single-player games have some additional features available: + * Call `SDL_GDKGetDefaultUser` to get the default XUserHandle pointer. + * `SDL_GetPrefPath` still works, but only for single-player titles. + +These functions mostly wrap around async APIs, and thus should be treated as synchronous alternatives. Also note that the single-player functions return on any OS errors, so be sure to validate the return values! + * What doesn't work: * Compilation with anything other than through the included Visual C++ solution file diff --git a/external/sdl/SDL/docs/README-migration.md b/external/sdl/SDL/docs/README-migration.md index 21ac569f..c4dd52b5 100644 --- a/external/sdl/SDL/docs/README-migration.md +++ b/external/sdl/SDL/docs/README-migration.md @@ -4,6 +4,8 @@ This guide provides useful information for migrating applications from SDL 2.0 t Details on API changes are organized by SDL 2.0 header below. +The file with your main() function should include , as that is no longer included in SDL.h. + Many functions and symbols have been renamed. We have provided a handy Python script [rename_symbols.py](https://github.com/libsdl-org/SDL/blob/main/build-scripts/rename_symbols.py) to rename SDL2 functions to their SDL3 counterparts: ```sh rename_symbols.py --all-symbols source_code_path @@ -11,14 +13,11 @@ rename_symbols.py --all-symbols source_code_path It's also possible to apply a semantic patch to migrate more easily to SDL3: [SDL_migration.cocci](https://github.com/libsdl-org/SDL/blob/main/build-scripts/SDL_migration.cocci) - SDL headers should now be included as `#include `. Typically that's the only header you'll need in your application unless you are using OpenGL or Vulkan functionality. We have provided a handy Python script [rename_headers.py](https://github.com/libsdl-org/SDL/blob/main/build-scripts/rename_headers.py) to rename SDL2 headers to their SDL3 counterparts: ```sh rename_headers.py source_code_path ``` -The file with your main() function should also include , see below in the SDL_main.h section. - CMake users should use this snippet to include SDL support in their project: ``` find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3) @@ -39,10 +38,7 @@ LDFLAGS += $(shell pkg-config sdl3 --libs) The SDL3test library has been renamed SDL3_test. -There is no SDLmain library anymore, it's now header-only, see below in the SDL_main.h section. - - -begin_code.h and close_code.h in the public headers have been renamed to SDL_begin_code.h and SDL_close_code.h. These aren't meant to be included directly by applications, but if your application did, please update your `#include` lines. +The SDLmain library has been removed, it's been entirely replaced by SDL_main.h. The vi format comments have been removed from source code. Vim users can use the [editorconfig plugin](https://github.com/editorconfig/editorconfig-vim) to automatically set tab spacing for the SDL coding style. @@ -53,15 +49,15 @@ The following structures have been renamed: ## SDL_audio.h -The audio subsystem in SDL3 is dramatically different than SDL2. The primary way to play audio is no longer an audio callback; instead you bind SDL_AudioStreams to devices. +The audio subsystem in SDL3 is dramatically different than SDL2. The primary way to play audio is no longer an audio callback; instead you bind SDL_AudioStreams to devices; however, there is still a callback method available if needed. The SDL 1.2 audio compatibility API has also been removed, as it was a simplified version of the audio callback interface. SDL3 will not implicitly initialize the audio subsystem on your behalf if you open a device without doing so. Please explicitly call SDL_Init(SDL_INIT_AUDIO) at some point. -If your app depends on the callback method, there is a similar approach you can take. But first, this is the new approach: +SDL3's audio subsystem offers an enormous amount of power over SDL2, but if you just want a simple migration of your existing code, you can ignore most of it. The simplest migration path from SDL2 looks something like this: -In SDL2, you might have done something like this to play audio: +In SDL2, you might have done something like this to play audio... ```c void SDLCALL MyAudioCallback(void *userdata, Uint8 * stream, int len) @@ -82,20 +78,7 @@ In SDL2, you might have done something like this to play audio: SDL_PauseAudioDevice(my_audio_device, 0); ``` -in SDL3: - -```c - /* ...somewhere near startup... */ - SDL_AudioSpec spec = { SDL_AUDIO_S16, 2, 44100 }; - SDL_AudioDeviceID my_audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec); - SDL_AudioSteam *stream = SDL_CreateAndBindAudioStream(my_audio_device, &spec); - - /* ...in your main loop... */ - /* calculate a little more audio into `buf`, add it to `stream` */ - SDL_PutAudioStreamData(stream, buf, buflen); -``` - -If you absolutely require the callback method, SDL_AudioStreams can use a callback whenever more data is to be read from them, which can be used to simulate SDL2 semantics: +...in SDL3, you can do this... ```c void SDLCALL MyAudioCallback(SDL_AudioStream *stream, int len, void *userdata) @@ -105,19 +88,32 @@ If you absolutely require the callback method, SDL_AudioStreams can use a callba } /* ...somewhere near startup... */ - SDL_AudioSpec spec = { SDL_AUDIO_S16, 2, 44100 }; - SDL_AudioDeviceID my_audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec); - SDL_AudioSteam *stream = SDL_CreateAndBindAudioStream(my_audio_device, &spec); - SDL_SetAudioStreamGetCallback(stream, MyAudioCallback); - - /* MyAudioCallback will be called whenever the device requests more audio data. */ + const SDL_AudioSpec spec = { SDL_AUDIO_S16, 2, 44100 }; + SDL_AudioStream *stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec, MyAudioCallback, &my_audio_callback_user_data); + SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(stream)); ``` +If you used SDL_QueueAudio instead of a callback in SDL2, this is also straightforward. + +```c + /* ...somewhere near startup... */ + const SDL_AudioSpec spec = { SDL_AUDIO_S16, 2, 44100 }; + SDL_AudioStream *stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec, NULL, NULL); + SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(stream)); + + /* ...in your main loop... */ + /* calculate a little more audio into `buf`, add it to `stream` */ + SDL_PutAudioStreamData(stream, buf, buflen); + +``` + +...these same migration examples apply to audio capture, just using SDL_GetAudioStreamData instead of SDL_PutAudioStreamData. + SDL_AudioInit() and SDL_AudioQuit() have been removed. Instead you can call SDL_InitSubSystem() and SDL_QuitSubSystem() with SDL_INIT_AUDIO, which will properly refcount the subsystems. You can choose a specific audio driver using SDL_AUDIO_DRIVER hint. The `SDL_AUDIO_ALLOW_*` symbols have been removed; now one may request the format they desire from the audio device, but ultimately SDL_AudioStream will manage the difference. One can use SDL_GetAudioDeviceFormat() to see what the final format is, if any "allowed" changes should be accomodated by the app. -SDL_AudioDeviceID now represents both an open audio device's handle (a "logical" device) and the instance ID that the hardware owns as long as it exists on the system (a "physical" device). The separation between device instances and device indexes is gone. +SDL_AudioDeviceID now represents both an open audio device's handle (a "logical" device) and the instance ID that the hardware owns as long as it exists on the system (a "physical" device). The separation between device instances and device indexes is gone, and logical and physical devices are almost entirely interchangeable at the API level. Devices are opened by physical device instance ID, and a new logical instance ID is generated by the open operation; This allows any device to be opened multiple times, possibly by unrelated pieces of code. SDL will manage the logical devices to provide a single stream of audio to the physical device behind the scenes. @@ -125,13 +121,13 @@ Devices are not opened by an arbitrary string name anymore, but by device instan Many functions that would accept a device index and an `iscapture` parameter now just take an SDL_AudioDeviceID, as they are unique across all devices, instead of separate indices into output and capture device lists. -Rather than iterating over audio devices using a device index, there is a new function, SDL_GetAudioDevices(), to get the current list of devices, and new functions to get information about devices from their instance ID: +Rather than iterating over audio devices using a device index, there are new functions, SDL_GetAudioOutputDevices() and SDL_GetAudioCaptureDevices(), to get the current list of devices, and new functions to get information about devices from their instance ID: ```c { if (SDL_InitSubSystem(SDL_INIT_AUDIO) == 0) { int i, num_devices; - SDL_AudioDeviceID *devices = SDL_GetAudioDevices(/*iscapture=*/SDL_FALSE, &num_devices); + SDL_AudioDeviceID *devices = SDL_GetAudioOutputDevices(&num_devices); if (devices) { for (i = 0; i < num_devices; ++i) { SDL_AudioDeviceID instance_id = devices[i]; @@ -152,7 +148,7 @@ SDL_PauseAudioDevice() no longer takes a second argument; it always pauses the d Audio devices, opened by SDL_OpenAudioDevice(), no longer start in a paused state, as they don't begin processing audio until a stream is bound. -SDL_GetAudioDeviceStatus() has been removed; there is now SDL_IsAudioDevicePaused(). +SDL_GetAudioDeviceStatus() has been removed; there is now SDL_AudioDevicePaused(). SDL_QueueAudio(), SDL_DequeueAudio, and SDL_ClearQueuedAudio and SDL_GetQueuedAudioSize() have been removed; an SDL_AudioStream bound to a device provides the exact same functionality. @@ -259,18 +255,18 @@ The following functions have been removed: * SDL_GetQueuedAudioSize() The following symbols have been renamed: -* AUDIO_F32 => SDL_AUDIO_F32 -* AUDIO_F32LSB => SDL_AUDIO_F32LSB -* AUDIO_F32MSB => SDL_AUDIO_F32MSB -* AUDIO_F32SYS => SDL_AUDIO_F32SYS -* AUDIO_S16 => SDL_AUDIO_S16 -* AUDIO_S16LSB => SDL_AUDIO_S16LSB -* AUDIO_S16MSB => SDL_AUDIO_S16MSB -* AUDIO_S16SYS => SDL_AUDIO_S16SYS -* AUDIO_S32 => SDL_AUDIO_S32 -* AUDIO_S32LSB => SDL_AUDIO_S32LSB -* AUDIO_S32MSB => SDL_AUDIO_S32MSB -* AUDIO_S32SYS => SDL_AUDIO_S32SYS +* AUDIO_F32 => SDL_AUDIO_F32LE +* AUDIO_F32LSB => SDL_AUDIO_F32LE +* AUDIO_F32MSB => SDL_AUDIO_F32BE +* AUDIO_F32SYS => SDL_AUDIO_F32 +* AUDIO_S16 => SDL_AUDIO_S16LE +* AUDIO_S16LSB => SDL_AUDIO_S16LE +* AUDIO_S16MSB => SDL_AUDIO_S16BE +* AUDIO_S16SYS => SDL_AUDIO_S16 +* AUDIO_S32 => SDL_AUDIO_S32LE +* AUDIO_S32LSB => SDL_AUDIO_S32LE +* AUDIO_S32MSB => SDL_AUDIO_S32BE +* AUDIO_S32SYS => SDL_AUDIO_S32 * AUDIO_S8 => SDL_AUDIO_S8 * AUDIO_U8 => SDL_AUDIO_U8 @@ -378,8 +374,6 @@ The SDL_EVENT_GAMEPAD_ADDED event now provides the joystick instance ID in the w The functions SDL_GetGamepads(), SDL_GetGamepadInstanceName(), SDL_GetGamepadInstancePath(), SDL_GetGamepadInstancePlayerIndex(), SDL_GetGamepadInstanceGUID(), SDL_GetGamepadInstanceVendor(), SDL_GetGamepadInstanceProduct(), SDL_GetGamepadInstanceProductVersion(), and SDL_GetGamepadInstanceType() have been added to directly query the list of available gamepads. -The gamepad binding structure has been removed in favor of exchanging bindings in text format. - SDL_GameControllerGetSensorDataWithTimestamp() has been removed. If you want timestamps for the sensor data, you should use the sensor_timestamp member of SDL_EVENT_GAMEPAD_SENSOR_UPDATE events. SDL_CONTROLLER_TYPE_VIRTUAL has been removed, so virtual controllers can emulate other gamepad types. If you need to know whether a controller is virtual, you can use SDL_IsJoystickVirtual(). @@ -417,7 +411,6 @@ The following enums have been renamed: The following structures have been renamed: * SDL_GameController => SDL_Gamepad -* SDL_GameControllerButtonBind => SDL_GamepadBinding The following functions have been renamed: * SDL_GameControllerAddMapping() => SDL_AddGamepadMapping() @@ -431,8 +424,6 @@ The following functions have been renamed: * SDL_GameControllerGetAttached() => SDL_GamepadConnected() * SDL_GameControllerGetAxis() => SDL_GetGamepadAxis() * SDL_GameControllerGetAxisFromString() => SDL_GetGamepadAxisFromString() -* SDL_GameControllerGetBindForAxis() => SDL_GetGamepadBindForAxis() -* SDL_GameControllerGetBindForButton() => SDL_GetGamepadBindForButton() * SDL_GameControllerGetButton() => SDL_GetGamepadButton() * SDL_GameControllerGetButtonFromString() => SDL_GetGamepadButtonFromString() * SDL_GameControllerGetFirmwareVersion() => SDL_GetGamepadFirmwareVersion() @@ -475,8 +466,8 @@ The following functions have been renamed: The following functions have been removed: * SDL_GameControllerEventState() - replaced with SDL_SetGamepadEventsEnabled() and SDL_GamepadEventsEnabled() -* SDL_GameControllerGetBindForAxis() -* SDL_GameControllerGetBindForButton() +* SDL_GameControllerGetBindForAxis() - replaced with SDL_GetGamepadBindings() +* SDL_GameControllerGetBindForButton() - replaced with SDL_GetGamepadBindings() * SDL_GameControllerMappingForDeviceIndex() - replaced with SDL_GetGamepadInstanceMapping() * SDL_GameControllerNameForIndex() - replaced with SDL_GetGamepadInstanceName() * SDL_GameControllerPathForIndex() - replaced with SDL_GetGamepadInstancePath() @@ -699,27 +690,6 @@ Instead SDL_main.h is now a header-only library **and not included by SDL.h anym Using it is really simple: Just `#include ` in the source file with your standard `int main(int argc, char* argv[])` function. -The rest happens automatically: If your target platform needs the SDL_main functionality, -your main function will be renamed to SDL_main (with a macro, just like in SDL2), -and the real main-function will be implemented by inline code from SDL_main.h - and if your target -platform doesn't need it, nothing happens. -Like in SDL2, if you want to handle the platform-specific main yourself instead of using the SDL_main magic, -you can `#define SDL_MAIN_HANDLED` before `#include ` - don't forget to call SDL_SetMainReady() - -If you need SDL_main.h in another source file (that doesn't implement main()), you also need to -`#define SDL_MAIN_HANDLED` there, to avoid that multiple main functions are generated by SDL_main.h - -There is currently one platform where this approach doesn't always work: WinRT. -It requires WinMain to be implemented in a C++ source file that's compiled with `/ZW`. If your main -is implemented in plain C, or you can't use `/ZW` on that file, you can add another .cpp -source file that just contains `#include ` and compile that with `/ZW` - but keep -in mind that the source file with your standard main also needs that include! -See [README-winrt.md](./README-winrt.md) for more details. - -Furthermore, the different SDL_*RunApp() functions (SDL_WinRtRunApp, SDL_GDKRunApp, SDL_UIKitRunApp) -have been unified into just `int SDL_RunApp(int argc, char* argv[], void * reserved)` (which is also -used by additional platforms that didn't have a SDL_RunApp-like function before). - ## SDL_metal.h SDL_Metal_GetDrawableSize() has been removed. SDL_GetWindowSizeInPixels() can be used in its place. diff --git a/external/sdl/SDL/docs/README-wayland.md b/external/sdl/SDL/docs/README-wayland.md index 7aefd192..2bab54f2 100644 --- a/external/sdl/SDL/docs/README-wayland.md +++ b/external/sdl/SDL/docs/README-wayland.md @@ -34,3 +34,12 @@ encounter limitations or behavior that is different from other windowing systems ### Warping the global mouse cursor position via ```SDL_WarpMouseGlobal()``` doesn't work - For security reasons, Wayland does not allow warping the global mouse cursor position. + +### The application icon can't be set via ```SDL_SetWindowIcon()``` + +- Wayland doesn't support programmatically setting the application icon. To provide a custom icon for your application, + you must create an associated desktop entry file, aka a `.desktop` file, that points to the icon image. Please see the + [Desktop Entry Specification](https://specifications.freedesktop.org/desktop-entry-spec/latest/) for more information + on the format of this file. Note that if your application manually sets the application ID via the `SDL_APP_ID` hint + string, the desktop entry file name should match the application ID. For example, if your application ID is set + to `org.my_org.sdl_app`, the desktop entry file should be named `org.my_org.sdl_app.desktop`. diff --git a/external/sdl/SDL/docs/README-windows.md b/external/sdl/SDL/docs/README-windows.md index cd29c1f9..1e6d59c4 100644 --- a/external/sdl/SDL/docs/README-windows.md +++ b/external/sdl/SDL/docs/README-windows.md @@ -56,3 +56,11 @@ it change the value of `SDL_VIDEO_VULKAN` to 0 in `SDL_config_windows.h`. You must install the [Vulkan SDK](https://www.lunarg.com/vulkan-sdk/) in order to use Vulkan graphics in your application. +## Transparent Window Support + +SDL uses the Desktop Window Manager (DWM) to create transparent windows. DWM is +always enabled from Windows 8 and above. Windows 7 only enables DWM with Aero Glass +theme. + +However, it cannot be guaranteed to work on all hardware configurations (an example +is hybrid GPU systems, such as NVIDIA Optimus laptops). diff --git a/external/sdl/SDL/include/SDL3/SDL_assert.h b/external/sdl/SDL/include/SDL3/SDL_assert.h index 12eb6356..e0addc2e 100644 --- a/external/sdl/SDL/include/SDL3/SDL_assert.h +++ b/external/sdl/SDL/include/SDL3/SDL_assert.h @@ -64,6 +64,8 @@ assert can have unique static variables associated with it. #define SDL_TriggerBreakpoint() __builtin_debugtrap() #elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "int $3\n\t" ) +#elif (defined(__GNUC__) || defined(__clang__)) && defined(__riscv) + #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "ebreak\n\t" ) #elif ( defined(__APPLE__) && (defined(__arm64__) || defined(__aarch64__)) ) /* this might work on other ARM targets, but this is a known quantity... */ #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "brk #22\n\t" ) #elif defined(__APPLE__) && defined(__arm__) diff --git a/external/sdl/SDL/include/SDL3/SDL_audio.h b/external/sdl/SDL/include/SDL3/SDL_audio.h index fbef6236..9b873791 100644 --- a/external/sdl/SDL/include/SDL3/SDL_audio.h +++ b/external/sdl/SDL/include/SDL3/SDL_audio.h @@ -80,15 +80,16 @@ typedef Uint16 SDL_AudioFormat; /* @{ */ #define SDL_AUDIO_MASK_BITSIZE (0xFF) -#define SDL_AUDIO_MASK_DATATYPE (1<<8) -#define SDL_AUDIO_MASK_ENDIAN (1<<12) +#define SDL_AUDIO_MASK_FLOAT (1<<8) +#define SDL_AUDIO_MASK_BIG_ENDIAN (1<<12) #define SDL_AUDIO_MASK_SIGNED (1<<15) -#define SDL_AUDIO_BITSIZE(x) (x & SDL_AUDIO_MASK_BITSIZE) -#define SDL_AUDIO_ISFLOAT(x) (x & SDL_AUDIO_MASK_DATATYPE) -#define SDL_AUDIO_ISBIGENDIAN(x) (x & SDL_AUDIO_MASK_ENDIAN) -#define SDL_AUDIO_ISSIGNED(x) (x & SDL_AUDIO_MASK_SIGNED) -#define SDL_AUDIO_ISINT(x) (!SDL_AUDIO_ISFLOAT(x)) +#define SDL_AUDIO_BITSIZE(x) ((x) & SDL_AUDIO_MASK_BITSIZE) +#define SDL_AUDIO_BYTESIZE(x) (SDL_AUDIO_BITSIZE(x) / 8) +#define SDL_AUDIO_ISFLOAT(x) ((x) & SDL_AUDIO_MASK_FLOAT) +#define SDL_AUDIO_ISBIGENDIAN(x) ((x) & SDL_AUDIO_MASK_BIG_ENDIAN) #define SDL_AUDIO_ISLITTLEENDIAN(x) (!SDL_AUDIO_ISBIGENDIAN(x)) +#define SDL_AUDIO_ISSIGNED(x) ((x) & SDL_AUDIO_MASK_SIGNED) +#define SDL_AUDIO_ISINT(x) (!SDL_AUDIO_ISFLOAT(x)) #define SDL_AUDIO_ISUNSIGNED(x) (!SDL_AUDIO_ISSIGNED(x)) /** @@ -99,27 +100,24 @@ typedef Uint16 SDL_AudioFormat; /* @{ */ #define SDL_AUDIO_U8 0x0008 /**< Unsigned 8-bit samples */ #define SDL_AUDIO_S8 0x8008 /**< Signed 8-bit samples */ -#define SDL_AUDIO_S16LSB 0x8010 /**< Signed 16-bit samples */ -#define SDL_AUDIO_S16MSB 0x9010 /**< As above, but big-endian byte order */ -#define SDL_AUDIO_S16 SDL_AUDIO_S16LSB +#define SDL_AUDIO_S16LE 0x8010 /**< Signed 16-bit samples */ +#define SDL_AUDIO_S16BE 0x9010 /**< As above, but big-endian byte order */ /* @} */ /** * \name int32 support */ /* @{ */ -#define SDL_AUDIO_S32LSB 0x8020 /**< 32-bit integer samples */ -#define SDL_AUDIO_S32MSB 0x9020 /**< As above, but big-endian byte order */ -#define SDL_AUDIO_S32 SDL_AUDIO_S32LSB +#define SDL_AUDIO_S32LE 0x8020 /**< 32-bit integer samples */ +#define SDL_AUDIO_S32BE 0x9020 /**< As above, but big-endian byte order */ /* @} */ /** * \name float32 support */ /* @{ */ -#define SDL_AUDIO_F32LSB 0x8120 /**< 32-bit floating point samples */ -#define SDL_AUDIO_F32MSB 0x9120 /**< As above, but big-endian byte order */ -#define SDL_AUDIO_F32 SDL_AUDIO_F32LSB +#define SDL_AUDIO_F32LE 0x8120 /**< 32-bit floating point samples */ +#define SDL_AUDIO_F32BE 0x9120 /**< As above, but big-endian byte order */ /* @} */ /** @@ -127,13 +125,13 @@ typedef Uint16 SDL_AudioFormat; */ /* @{ */ #if SDL_BYTEORDER == SDL_LIL_ENDIAN -#define SDL_AUDIO_S16SYS SDL_AUDIO_S16LSB -#define SDL_AUDIO_S32SYS SDL_AUDIO_S32LSB -#define SDL_AUDIO_F32SYS SDL_AUDIO_F32LSB +#define SDL_AUDIO_S16 SDL_AUDIO_S16LE +#define SDL_AUDIO_S32 SDL_AUDIO_S32LE +#define SDL_AUDIO_F32 SDL_AUDIO_F32LE #else -#define SDL_AUDIO_S16SYS SDL_AUDIO_S16MSB -#define SDL_AUDIO_S32SYS SDL_AUDIO_S32MSB -#define SDL_AUDIO_F32SYS SDL_AUDIO_F32MSB +#define SDL_AUDIO_S16 SDL_AUDIO_S16BE +#define SDL_AUDIO_S32 SDL_AUDIO_S32BE +#define SDL_AUDIO_F32 SDL_AUDIO_F32BE #endif /* @} */ @@ -154,6 +152,9 @@ typedef struct SDL_AudioSpec int freq; /**< sample rate: sample frames per second */ } SDL_AudioSpec; +/* Calculate the size of each audio frame (in bytes) */ +#define SDL_AUDIO_FRAMESIZE(x) (SDL_AUDIO_BYTESIZE((x).format) * (x).channels) + /* SDL_AudioStream is an audio conversion interface. - It can handle resampling data in chunks without generating artifacts, when it doesn't have the complete buffer available. @@ -308,7 +309,8 @@ extern DECLSPEC SDL_AudioDeviceID *SDLCALL SDL_GetAudioCaptureDevices(int *count * * \since This function is available since SDL 3.0.0. * - * \sa SDL_GetNumAudioDevices + * \sa SDL_GetAudioOutputDevices + * \sa SDL_GetAudioCaptureDevices * \sa SDL_GetDefaultAudioInfo */ extern DECLSPEC char *SDLCALL SDL_GetAudioDeviceName(SDL_AudioDeviceID devid); @@ -325,8 +327,20 @@ extern DECLSPEC char *SDLCALL SDL_GetAudioDeviceName(SDL_AudioDeviceID devid); * reasonable recommendation before opening the system-recommended default * device. * + * You can also use this to request the current device buffer size. This is + * specified in sample frames and represents the amount of data SDL will feed + * to the physical hardware in each chunk. This can be converted to + * milliseconds of audio with the following equation: + * + * `ms = (int) ((((Sint64) frames) * 1000) / spec.freq);` + * + * Buffer size is only important if you need low-level control over the audio + * playback timing. Most apps do not need this. + * * \param devid the instance ID of the device to query. * \param spec On return, will be filled with device details. + * \param sample_frames Pointer to store device buffer size, in sample frames. + * Can be NULL. * \returns 0 on success or a negative error code on failure; call * SDL_GetError() for more information. * @@ -334,7 +348,7 @@ extern DECLSPEC char *SDLCALL SDL_GetAudioDeviceName(SDL_AudioDeviceID devid); * * \since This function is available since SDL 3.0.0. */ -extern DECLSPEC int SDLCALL SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec); +extern DECLSPEC int SDLCALL SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec, int *sample_frames); /** @@ -348,9 +362,9 @@ extern DECLSPEC int SDLCALL SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SD * An opened audio device starts out with no audio streams bound. To start * audio playing, bind a stream and supply audio data to it. Unlike SDL2, * there is no audio callback; you only bind audio streams and make sure they - * have data flowing into them (although, as an optional feature, each audio - * stream may have its own callback, which can be used to simulate SDL2's - * semantics). + * have data flowing into them (however, you can simulate SDL2's semantics + * fairly closely by using SDL_OpenAudioDeviceStream instead of this + * function). * * If you don't care about opening a specific device, pass a `devid` of either * `SDL_AUDIO_DEVICE_DEFAULT_OUTPUT` or `SDL_AUDIO_DEVICE_DEFAULT_CAPTURE`. In @@ -439,7 +453,7 @@ extern DECLSPEC SDL_AudioDeviceID SDLCALL SDL_OpenAudioDevice(SDL_AudioDeviceID * \since This function is available since SDL 3.0.0. * * \sa SDL_ResumeAudioDevice - * \sa SDL_IsAudioDevicePaused + * \sa SDL_AudioDevicePaused */ extern DECLSPEC int SDLCALL SDL_PauseAudioDevice(SDL_AudioDeviceID dev); @@ -466,8 +480,8 @@ extern DECLSPEC int SDLCALL SDL_PauseAudioDevice(SDL_AudioDeviceID dev); * * \since This function is available since SDL 3.0.0. * - * \sa SDL_ResumeAudioDevice - * \sa SDL_IsAudioDevicePaused + * \sa SDL_AudioDevicePaused + * \sa SDL_PauseAudioDevice */ extern DECLSPEC int SDLCALL SDL_ResumeAudioDevice(SDL_AudioDeviceID dev); @@ -490,9 +504,8 @@ extern DECLSPEC int SDLCALL SDL_ResumeAudioDevice(SDL_AudioDeviceID dev); * * \sa SDL_PauseAudioDevice * \sa SDL_ResumeAudioDevice - * \sa SDL_IsAudioDevicePaused */ -extern DECLSPEC SDL_bool SDLCALL SDL_IsAudioDevicePaused(SDL_AudioDeviceID dev); +extern DECLSPEC SDL_bool SDLCALL SDL_AudioDevicePaused(SDL_AudioDeviceID dev); /** * Close a previously-opened audio device. @@ -549,7 +562,7 @@ extern DECLSPEC void SDLCALL SDL_CloseAudioDevice(SDL_AudioDeviceID devid); * \sa SDL_BindAudioStreams * \sa SDL_UnbindAudioStreams * \sa SDL_UnbindAudioStream - * \sa SDL_GetAudioStreamBinding + * \sa SDL_GetAudioStreamDevice */ extern DECLSPEC int SDLCALL SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int num_streams); @@ -571,7 +584,7 @@ extern DECLSPEC int SDLCALL SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_Au * \sa SDL_BindAudioStreams * \sa SDL_UnbindAudioStreams * \sa SDL_UnbindAudioStream - * \sa SDL_GetAudioStreamBinding + * \sa SDL_GetAudioStreamDevice */ extern DECLSPEC int SDLCALL SDL_BindAudioStream(SDL_AudioDeviceID devid, SDL_AudioStream *stream); @@ -595,7 +608,7 @@ extern DECLSPEC int SDLCALL SDL_BindAudioStream(SDL_AudioDeviceID devid, SDL_Aud * \sa SDL_BindAudioStreams * \sa SDL_BindAudioStream * \sa SDL_UnbindAudioStream - * \sa SDL_GetAudioStreamBinding + * \sa SDL_GetAudioStreamDevice */ extern DECLSPEC void SDLCALL SDL_UnbindAudioStreams(SDL_AudioStream **streams, int num_streams); @@ -614,7 +627,7 @@ extern DECLSPEC void SDLCALL SDL_UnbindAudioStreams(SDL_AudioStream **streams, i * \sa SDL_BindAudioStream * \sa SDL_BindAudioStreams * \sa SDL_UnbindAudioStreams - * \sa SDL_GetAudioStreamBinding + * \sa SDL_GetAudioStreamDevice */ extern DECLSPEC void SDLCALL SDL_UnbindAudioStream(SDL_AudioStream *stream); @@ -638,7 +651,7 @@ extern DECLSPEC void SDLCALL SDL_UnbindAudioStream(SDL_AudioStream *stream); * \sa SDL_UnbindAudioStream * \sa SDL_UnbindAudioStreams */ -extern DECLSPEC SDL_AudioDeviceID SDLCALL SDL_GetAudioStreamBinding(SDL_AudioStream *stream); +extern DECLSPEC SDL_AudioDeviceID SDLCALL SDL_GetAudioStreamDevice(SDL_AudioStream *stream); /** @@ -703,11 +716,54 @@ extern DECLSPEC int SDLCALL SDL_GetAudioStreamFormat(SDL_AudioStream *stream, * \sa SDL_PutAudioStreamData * \sa SDL_GetAudioStreamData * \sa SDL_GetAudioStreamAvailable + * \sa SDL_SetAudioStreamFrequencyRatio */ extern DECLSPEC int SDLCALL SDL_SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_spec, const SDL_AudioSpec *dst_spec); +/** + * Get the frequency ratio of an audio stream. + * + * \param stream the SDL_AudioStream to query. + * \returns the frequency ratio of the stream, or 0.0 on error + * + * \threadsafety It is safe to call this function from any thread, as it holds + * a stream-specific mutex while running. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_SetAudioStreamFrequencyRatio + */ +extern DECLSPEC float SDLCALL SDL_GetAudioStreamFrequencyRatio(SDL_AudioStream *stream); + +/** + * Change the frequency ratio of an audio stream. + * + * The frequency ratio is used to adjust the rate at which input data is + * consumed. Changing this effectively modifies the speed and pitch of the + * audio. A value greater than 1.0 will play the audio faster, and at a higher + * pitch. A value less than 1.0 will play the audio slower, and at a lower + * pitch. + * + * This is applied during SDL_GetAudioStreamData, and can be continuously + * changed to create various effects. + * + * \param stream The stream the frequency ratio is being changed + * \param ratio The frequency ratio. 1.0 is normal speed. Must be between 0.01 + * and 100. + * \returns 0 on success, or -1 on error. + * + * \threadsafety It is safe to call this function from any thread, as it holds + * a stream-specific mutex while running. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_GetAudioStreamFrequencyRatio + * \sa SDL_SetAudioStreamFormat + */ +extern DECLSPEC int SDLCALL SDL_SetAudioStreamFrequencyRatio(SDL_AudioStream *stream, float ratio); + /** * Add data to be converted/resampled to the stream. * @@ -802,6 +858,40 @@ extern DECLSPEC int SDLCALL SDL_GetAudioStreamData(SDL_AudioStream *stream, void */ extern DECLSPEC int SDLCALL SDL_GetAudioStreamAvailable(SDL_AudioStream *stream); + +/** + * Get the number of bytes currently queued. + * + * Note that audio streams can change their input format at any time, even if + * there is still data queued in a different format, so the returned byte + * count will not necessarily match the number of _sample frames_ available. + * Users of this API should be aware of format changes they make when feeding + * a stream and plan accordingly. + * + * Queued data is not converted until it is consumed by + * SDL_GetAudioStreamData, so this value should be representative of the exact + * data that was put into the stream. + * + * If the stream has so much data that it would overflow an int, the return + * value is clamped to a maximum value, but no queued data is lost; if there + * are gigabytes of data queued, the app might need to read some of it with + * SDL_GetAudioStreamData before this function's return value is no longer + * clamped. + * + * \param stream The audio stream to query + * \returns the number of bytes queued. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_PutAudioStreamData + * \sa SDL_GetAudioStreamData + * \sa SDL_ClearAudioStream + */ +extern DECLSPEC int SDLCALL SDL_GetAudioStreamQueued(SDL_AudioStream *stream); + + /** * Tell the stream that you're done sending data, and anything being buffered * should be converted/resampled and made available immediately. @@ -909,13 +999,22 @@ extern DECLSPEC int SDLCALL SDL_UnlockAudioStream(SDL_AudioStream *stream); * before your callback is called, so your callback does not need to * manage the lock explicitly. * + * Two values are offered here: one is the amount of additional data needed + * to satisfy the immediate request (which might be zero if the stream + * already has enough data queued) and the other is the total amount + * being requested. In a Get call triggering a Put callback, these + * values can be different. In a Put call triggering a Get callback, + * these values are always the same. + * + * Byte counts might be slightly overestimated due to buffering or + * resampling, and may change from call to call. + * * \param stream The SDL audio stream associated with this callback. - * \param approx_request The _approximate_ amout of data, in bytes, that is requested. - * This might be slightly overestimated due to buffering or - * resampling, and may change from call to call anyhow. + * \param additional_amount The amount of data, in bytes, that is needed right now. + * \param total_amount The total amount of data requested, in bytes, that is requested or available. * \param userdata An opaque pointer provided by the app for their personal use. */ -typedef void (SDLCALL *SDL_AudioStreamRequestCallback)(SDL_AudioStream *stream, int approx_request, void *userdata); +typedef void (SDLCALL *SDL_AudioStreamCallback)(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount); /** * Set a callback that runs when data is requested from an audio stream. @@ -960,7 +1059,7 @@ typedef void (SDLCALL *SDL_AudioStreamRequestCallback)(SDL_AudioStream *stream, * * \sa SDL_SetAudioStreamPutCallback */ -extern DECLSPEC int SDLCALL SDL_SetAudioStreamGetCallback(SDL_AudioStream *stream, SDL_AudioStreamRequestCallback callback, void *userdata); +extern DECLSPEC int SDLCALL SDL_SetAudioStreamGetCallback(SDL_AudioStream *stream, SDL_AudioStreamCallback callback, void *userdata); /** * Set a callback that runs when data is added to an audio stream. @@ -1008,7 +1107,7 @@ extern DECLSPEC int SDLCALL SDL_SetAudioStreamGetCallback(SDL_AudioStream *strea * * \sa SDL_SetAudioStreamGetCallback */ -extern DECLSPEC int SDLCALL SDL_SetAudioStreamPutCallback(SDL_AudioStream *stream, SDL_AudioStreamRequestCallback callback, void *userdata); +extern DECLSPEC int SDLCALL SDL_SetAudioStreamPutCallback(SDL_AudioStream *stream, SDL_AudioStreamCallback callback, void *userdata); /** @@ -1031,32 +1130,125 @@ extern DECLSPEC void SDLCALL SDL_DestroyAudioStream(SDL_AudioStream *stream); /** - * Convenience function to create and bind an audio stream in one step. + * Convenience function for straightforward audio init for the common case. * - * This manages the creation of an audio stream, and setting its format - * correctly to match both the app and the audio device's needs. This is - * optional, but slightly less cumbersome to set up for a common use case. + * If all your app intends to do is provide a single source of PCM audio, this + * function allows you to do all your audio setup in a single call. + * + * This is intended to be a clean means to migrate apps from SDL2. + * + * This function will open an audio device, create a stream and bind it. + * Unlike other methods of setup, the audio device will be closed when this + * stream is destroyed, so the app can treat the returned SDL_AudioStream as + * the only object needed to manage audio playback. + * + * Also unlike other functions, the audio device begins paused. This is to map + * more closely to SDL2-style behavior, and since there is no extra step here + * to bind a stream to begin audio flowing. The audio device should be resumed + * with SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(stream)); + * + * This function works with both playback and capture devices. * * The `spec` parameter represents the app's side of the audio stream. That * is, for recording audio, this will be the output format, and for playing - * audio, this will be the input format. This function will set the other side - * of the audio stream to the device's format. + * audio, this will be the input format. * - * \param devid an audio device to bind a stream to. This must be an opened - * device, and can not be zero. - * \param spec the audio stream's input format - * \returns a bound audio stream on success, ready to use. NULL on error; call - * SDL_GetError() for more information. + * If you don't care about opening a specific audio device, you can (and + * probably _should_), use SDL_AUDIO_DEVICE_DEFAULT_OUTPUT for playback and + * SDL_AUDIO_DEVICE_DEFAULT_CAPTURE for recording. + * + * One can optionally provide a callback function; if NULL, the app is + * expected to queue audio data for playback (or unqueue audio data if + * capturing). Otherwise, the callback will begin to fire once the device is + * unpaused. + * + * \param devid an audio device to open, or SDL_AUDIO_DEVICE_DEFAULT_OUTPUT or + * SDL_AUDIO_DEVICE_DEFAULT_CAPTURE. + * \param spec the audio stream's data format. Required. + * \param callback A callback where the app will provide new data for + * playback, or receive new data for capture. Can be NULL, in + * which case the app will need to call SDL_PutAudioStreamData + * or SDL_GetAudioStreamData as necessary. + * \param userdata App-controlled pointer passed to callback. Can be NULL. + * Ignored if callback is NULL. + * \returns an audio stream on success, ready to use. NULL on error; call + * SDL_GetError() for more information. When done with this stream, + * call SDL_DestroyAudioStream to free resources and close the + * device. * * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_BindAudioStreams - * \sa SDL_UnbindAudioStreams - * \sa SDL_UnbindAudioStream + * \sa SDL_GetAudioStreamDevice + * \sa SDL_ResumeAudioDevice */ -extern DECLSPEC SDL_AudioStream *SDLCALL SDL_CreateAndBindAudioStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec); +extern DECLSPEC SDL_AudioStream *SDLCALL SDL_OpenAudioDeviceStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec, SDL_AudioStreamCallback callback, void *userdata); + + +/** + * A callback that fires when data is about to be fed to an audio device. + * + * This is useful for accessing the final mix, perhaps for writing a + * visualizer or applying a final effect to the audio data before playback. + * + * \sa SDL_SetAudioDevicePostmixCallback + */ +typedef void (SDLCALL *SDL_AudioPostmixCallback)(void *userdata, const SDL_AudioSpec *spec, float *buffer, int buflen); + +/** + * Set a callback that fires when data is about to be fed to an audio device. + * + * This is useful for accessing the final mix, perhaps for writing a + * visualizer or applying a final effect to the audio data before playback. + * + * The buffer is the final mix of all bound audio streams on an opened device; + * this callback will fire regularly for any device that is both opened and + * unpaused. If there is no new data to mix, either because no streams are + * bound to the device or all the streams are empty, this callback will still + * fire with the entire buffer set to silence. + * + * This callback is allowed to make changes to the data; the contents of the + * buffer after this call is what is ultimately passed along to the hardware. + * + * The callback is always provided the data in float format (values from -1.0f + * to 1.0f), but the number of channels or sample rate may be different than + * the format the app requested when opening the device; SDL might have had to + * manage a conversion behind the scenes, or the playback might have jumped to + * new physical hardware when a system default changed, etc. These details may + * change between calls. Accordingly, the size of the buffer might change + * between calls as well. + * + * This callback can run at any time, and from any thread; if you need to + * serialize access to your app's data, you should provide and use a mutex or + * other synchronization device. + * + * All of this to say: there are specific needs this callback can fulfill, but + * it is not the simplest interface. Apps should generally provide audio in + * their preferred format through an SDL_AudioStream and let SDL handle the + * difference. + * + * This function is extremely time-sensitive; the callback should do the least + * amount of work possible and return as quickly as it can. The longer the + * callback runs, the higher the risk of audio dropouts or other problems. + * + * This function will block until the audio device is in between iterations, + * so any existing callback that might be running will finish before this + * function sets the new callback and returns. + * + * Setting a NULL callback function disables any previously-set callback. + * + * \param devid The ID of an opened audio device. + * \param callback A callback function to be called. Can be NULL. + * \param userdata App-controlled pointer passed to callback. Can be NULL. + * \returns zero on success, -1 on error; call SDL_GetError() for more + * information. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC int SDLCALL SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallback callback, void *userdata); /** diff --git a/external/sdl/SDL/include/SDL3/SDL_events.h b/external/sdl/SDL/include/SDL3/SDL_events.h index f21e2516..a3c5cced 100644 --- a/external/sdl/SDL/include/SDL3/SDL_events.h +++ b/external/sdl/SDL/include/SDL3/SDL_events.h @@ -185,8 +185,9 @@ typedef enum SDL_EVENT_DROP_POSITION, /**< Position while moving over the window */ /* Audio hotplug events */ - SDL_EVENT_AUDIO_DEVICE_ADDED = 0x1100, /**< A new audio device is available */ - SDL_EVENT_AUDIO_DEVICE_REMOVED, /**< An audio device has been removed. */ + SDL_EVENT_AUDIO_DEVICE_ADDED = 0x1100, /**< A new audio device is available */ + SDL_EVENT_AUDIO_DEVICE_REMOVED, /**< An audio device has been removed. */ + SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED, /**< An audio device's format has been changed by the system. */ /* Sensor events */ SDL_EVENT_SENSOR_UPDATE = 0x1200, /**< A sensor was updated */ @@ -491,9 +492,9 @@ typedef struct SDL_GamepadSensorEvent */ typedef struct SDL_AudioDeviceEvent { - Uint32 type; /**< ::SDL_EVENT_AUDIO_DEVICE_ADDED, or ::SDL_EVENT_AUDIO_DEVICE_REMOVED */ + Uint32 type; /**< ::SDL_EVENT_AUDIO_DEVICE_ADDED, or ::SDL_EVENT_AUDIO_DEVICE_REMOVED, or ::SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED */ Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ - SDL_AudioDeviceID which; /**< SDL_AudioDeviceID for the device being added or removed */ + SDL_AudioDeviceID which; /**< SDL_AudioDeviceID for the device being added or removed or changing */ Uint8 iscapture; /**< zero if an output device, non-zero if a capture device. */ Uint8 padding1; Uint8 padding2; diff --git a/external/sdl/SDL/include/SDL3/SDL_gamepad.h b/external/sdl/SDL/include/SDL3/SDL_gamepad.h index 484aee49..b9c31e8d 100644 --- a/external/sdl/SDL/include/SDL3/SDL_gamepad.h +++ b/external/sdl/SDL/include/SDL3/SDL_gamepad.h @@ -125,6 +125,53 @@ typedef enum SDL_GAMEPAD_AXIS_MAX } SDL_GamepadAxis; +typedef enum +{ + SDL_GAMEPAD_BINDTYPE_NONE = 0, + SDL_GAMEPAD_BINDTYPE_BUTTON, + SDL_GAMEPAD_BINDTYPE_AXIS, + SDL_GAMEPAD_BINDTYPE_HAT +} SDL_GamepadBindingType; + +typedef struct +{ + SDL_GamepadBindingType inputType; + union + { + int button; + + struct + { + int axis; + int axis_min; + int axis_max; + } axis; + + struct + { + int hat; + int hat_mask; + } hat; + + } input; + + SDL_GamepadBindingType outputType; + union + { + SDL_GamepadButton button; + + struct + { + SDL_GamepadAxis axis; + int axis_min; + int axis_max; + } axis; + + } output; + +} SDL_GamepadBinding; + + /** * Add support for gamepads that SDL is unaware of or change the binding of an * existing gamepad. @@ -736,6 +783,19 @@ extern DECLSPEC void SDLCALL SDL_SetGamepadEventsEnabled(SDL_bool enabled); */ extern DECLSPEC SDL_bool SDLCALL SDL_GamepadEventsEnabled(void); +/** + * Get the SDL joystick layer bindings for a gamepad + * + * \param gamepad a gamepad + * \param count a pointer filled in with the number of bindings returned + * \returns a NULL terminated array of pointers to bindings which should be + * freed with SDL_free(), or NULL on error; call SDL_GetError() for + * more details. + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC SDL_GamepadBinding **SDLCALL SDL_GetGamepadBindings(SDL_Gamepad *gamepad, int *count); + /** * Manually pump gamepad updates if not using the loop. * diff --git a/external/sdl/SDL/include/SDL3/SDL_hints.h b/external/sdl/SDL/include/SDL3/SDL_hints.h index f57a165f..01193b0c 100644 --- a/external/sdl/SDL/include/SDL3/SDL_hints.h +++ b/external/sdl/SDL/include/SDL3/SDL_hints.h @@ -1488,6 +1488,17 @@ extern "C" { */ #define SDL_HINT_RENDER_VSYNC "SDL_RENDER_VSYNC" +/** + * \brief A variable controlling whether the Metal render driver select low power device over default one + * + * This variable can be set to the following values: + * "0" - Use the prefered OS device + * "1" - Select a low power one + * + * By default the prefered OS device is used. + */ +#define SDL_HINT_RENDER_METAL_PREFER_LOW_POWER_DEVICE "SDL_RENDER_METAL_PREFER_LOW_POWER_DEVICE" + /** * \brief A variable controlling if VSYNC is automatically disable if doesn't reach the enough FPS * @@ -2499,6 +2510,26 @@ extern "C" { */ #define SDL_HINT_GDK_TEXTINPUT_MAX_LENGTH "SDL_GDK_TEXTINPUT_MAX_LENGTH" +/** + * Set the next device open's buffer size. + * + * This hint is an integer > 0, that represents the size of the device's + * buffer in sample frames (stereo audio data in 16-bit format is 4 bytes + * per sample frame, for example). + * + * SDL3 generally decides this value on behalf of the app, but if for some + * reason the app needs to dictate this (because they want either lower + * latency or higher throughput AND ARE WILLING TO DEAL WITH what that + * might require of the app), they can specify it. + * + * SDL will try to accomodate this value, but there is no promise you'll + * get the buffer size requested. Many platforms won't honor this request + * at all, or might adjust it. + * + * This hint is checked when opening an audio device and can be changed + * between calls. + */ +#define SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES "SDL_AUDIO_DEVICE_SAMPLE_FRAMES" /** * \brief An enumeration of hint priorities diff --git a/external/sdl/SDL/include/SDL3/SDL_oldnames.h b/external/sdl/SDL/include/SDL3/SDL_oldnames.h index e58ec3a4..e5b5c9b5 100644 --- a/external/sdl/SDL/include/SDL3/SDL_oldnames.h +++ b/external/sdl/SDL/include/SDL3/SDL_oldnames.h @@ -43,18 +43,18 @@ #define SDL_atomic_t SDL_AtomicInt /* ##SDL_audio.h */ -#define AUDIO_F32 SDL_AUDIO_F32 -#define AUDIO_F32LSB SDL_AUDIO_F32LSB -#define AUDIO_F32MSB SDL_AUDIO_F32MSB -#define AUDIO_F32SYS SDL_AUDIO_F32SYS -#define AUDIO_S16 SDL_AUDIO_S16 -#define AUDIO_S16LSB SDL_AUDIO_S16LSB -#define AUDIO_S16MSB SDL_AUDIO_S16MSB -#define AUDIO_S16SYS SDL_AUDIO_S16SYS -#define AUDIO_S32 SDL_AUDIO_S32 -#define AUDIO_S32LSB SDL_AUDIO_S32LSB -#define AUDIO_S32MSB SDL_AUDIO_S32MSB -#define AUDIO_S32SYS SDL_AUDIO_S32SYS +#define AUDIO_F32 SDL_AUDIO_F32LE +#define AUDIO_F32LSB SDL_AUDIO_F32LE +#define AUDIO_F32MSB SDL_AUDIO_F32BE +#define AUDIO_F32SYS SDL_AUDIO_F32 +#define AUDIO_S16 SDL_AUDIO_S16LE +#define AUDIO_S16LSB SDL_AUDIO_S16LE +#define AUDIO_S16MSB SDL_AUDIO_S16BE +#define AUDIO_S16SYS SDL_AUDIO_S16 +#define AUDIO_S32 SDL_AUDIO_S32LE +#define AUDIO_S32LSB SDL_AUDIO_S32LE +#define AUDIO_S32MSB SDL_AUDIO_S32BE +#define AUDIO_S32SYS SDL_AUDIO_S32 #define AUDIO_S8 SDL_AUDIO_S8 #define AUDIO_U8 SDL_AUDIO_U8 #define SDL_AudioStreamAvailable SDL_GetAudioStreamAvailable @@ -202,7 +202,6 @@ #define SDL_GameControllerAxis SDL_GamepadAxis #define SDL_GameControllerBindType SDL_GamepadBindingType #define SDL_GameControllerButton SDL_GamepadButton -#define SDL_GameControllerButtonBind SDL_GamepadBinding #define SDL_GameControllerClose SDL_CloseGamepad #define SDL_GameControllerFromInstanceID SDL_GetGamepadFromInstanceID #define SDL_GameControllerFromPlayerIndex SDL_GetGamepadFromPlayerIndex @@ -211,8 +210,6 @@ #define SDL_GameControllerGetAttached SDL_GamepadConnected #define SDL_GameControllerGetAxis SDL_GetGamepadAxis #define SDL_GameControllerGetAxisFromString SDL_GetGamepadAxisFromString -#define SDL_GameControllerGetBindForAxis SDL_GetGamepadBindForAxis -#define SDL_GameControllerGetBindForButton SDL_GetGamepadBindForButton #define SDL_GameControllerGetButton SDL_GetGamepadButton #define SDL_GameControllerGetButtonFromString SDL_GetGamepadButtonFromString #define SDL_GameControllerGetFirmwareVersion SDL_GetGamepadFirmwareVersion @@ -494,18 +491,18 @@ #elif !defined(SDL_DISABLE_OLD_NAMES) /* ##SDL_audio.h */ -#define AUDIO_F32 AUDIO_F32_renamed_SDL_AUDIO_F32 -#define AUDIO_F32LSB AUDIO_F32LSB_renamed_SDL_AUDIO_F32LSB -#define AUDIO_F32MSB AUDIO_F32MSB_renamed_SDL_AUDIO_F32MSB -#define AUDIO_F32SYS AUDIO_F32SYS_renamed_SDL_AUDIO_F32SYS -#define AUDIO_S16 AUDIO_S16_renamed_SDL_AUDIO_S16 -#define AUDIO_S16LSB AUDIO_S16LSB_renamed_SDL_AUDIO_S16LSB -#define AUDIO_S16MSB AUDIO_S16MSB_renamed_SDL_AUDIO_S16MSB -#define AUDIO_S16SYS AUDIO_S16SYS_renamed_SDL_AUDIO_S16SYS -#define AUDIO_S32 AUDIO_S32_renamed_SDL_AUDIO_S32 -#define AUDIO_S32LSB AUDIO_S32LSB_renamed_SDL_AUDIO_S32LSB -#define AUDIO_S32MSB AUDIO_S32MSB_renamed_SDL_AUDIO_S32MSB -#define AUDIO_S32SYS AUDIO_S32SYS_renamed_SDL_AUDIO_S32SYS +#define AUDIO_F32 AUDIO_F32_renamed_SDL_AUDIO_F32LE +#define AUDIO_F32LSB AUDIO_F32LSB_renamed_SDL_AUDIO_F32LE +#define AUDIO_F32MSB AUDIO_F32MSB_renamed_SDL_AUDIO_F32BE +#define AUDIO_F32SYS AUDIO_F32SYS_renamed_SDL_AUDIO_F32 +#define AUDIO_S16 AUDIO_S16_renamed_SDL_AUDIO_S16LE +#define AUDIO_S16LSB AUDIO_S16LSB_renamed_SDL_AUDIO_S16LE +#define AUDIO_S16MSB AUDIO_S16MSB_renamed_SDL_AUDIO_S16BE +#define AUDIO_S16SYS AUDIO_S16SYS_renamed_SDL_AUDIO_S16 +#define AUDIO_S32 AUDIO_S32_renamed_SDL_AUDIO_S32LE +#define AUDIO_S32LSB AUDIO_S32LSB_renamed_SDL_AUDIO_S32LE +#define AUDIO_S32MSB AUDIO_S32MSB_renamed_SDL_AUDIO_S32BE +#define AUDIO_S32SYS AUDIO_S32SYS_renamed_SDL_AUDIO_S32 #define AUDIO_S8 AUDIO_S8_renamed_SDL_AUDIO_S8 #define AUDIO_U8 AUDIO_U8_renamed_SDL_AUDIO_U8 #define SDL_AudioStreamAvailable SDL_AudioStreamAvailable_renamed_SDL_GetAudioStreamAvailable @@ -653,7 +650,6 @@ #define SDL_GameControllerAxis SDL_GameControllerAxis_renamed_SDL_GamepadAxis #define SDL_GameControllerBindType SDL_GameControllerBindType_renamed_SDL_GamepadBindingType #define SDL_GameControllerButton SDL_GameControllerButton_renamed_SDL_GamepadButton -#define SDL_GameControllerButtonBind SDL_GameControllerButtonBind_renamed_SDL_GamepadBinding #define SDL_GameControllerClose SDL_GameControllerClose_renamed_SDL_CloseGamepad #define SDL_GameControllerFromInstanceID SDL_GameControllerFromInstanceID_renamed_SDL_GetGamepadFromInstanceID #define SDL_GameControllerFromPlayerIndex SDL_GameControllerFromPlayerIndex_renamed_SDL_GetGamepadFromPlayerIndex @@ -662,8 +658,6 @@ #define SDL_GameControllerGetAttached SDL_GameControllerGetAttached_renamed_SDL_GamepadConnected #define SDL_GameControllerGetAxis SDL_GameControllerGetAxis_renamed_SDL_GetGamepadAxis #define SDL_GameControllerGetAxisFromString SDL_GameControllerGetAxisFromString_renamed_SDL_GetGamepadAxisFromString -#define SDL_GameControllerGetBindForAxis SDL_GameControllerGetBindForAxis_renamed_SDL_GetGamepadBindForAxis -#define SDL_GameControllerGetBindForButton SDL_GameControllerGetBindForButton_renamed_SDL_GetGamepadBindForButton #define SDL_GameControllerGetButton SDL_GameControllerGetButton_renamed_SDL_GetGamepadButton #define SDL_GameControllerGetButtonFromString SDL_GameControllerGetButtonFromString_renamed_SDL_GetGamepadButtonFromString #define SDL_GameControllerGetFirmwareVersion SDL_GameControllerGetFirmwareVersion_renamed_SDL_GetGamepadFirmwareVersion diff --git a/external/sdl/SDL/include/SDL3/SDL_render.h b/external/sdl/SDL/include/SDL3/SDL_render.h index d33b99bb..d38484a1 100644 --- a/external/sdl/SDL/include/SDL3/SDL_render.h +++ b/external/sdl/SDL/include/SDL3/SDL_render.h @@ -42,7 +42,7 @@ * of the many good 3D engines. * * These functions must be called from the main thread. - * See this bug for details: http://bugzilla.libsdl.org/show_bug.cgi?id=1995 + * See this bug for details: https://github.com/libsdl-org/SDL/issues/986 */ #ifndef SDL_render_h_ diff --git a/external/sdl/SDL/include/SDL3/SDL_rwops.h b/external/sdl/SDL/include/SDL3/SDL_rwops.h index 27e1f316..6f6c3c01 100644 --- a/external/sdl/SDL/include/SDL3/SDL_rwops.h +++ b/external/sdl/SDL/include/SDL3/SDL_rwops.h @@ -954,7 +954,22 @@ extern DECLSPEC SDL_bool SDLCALL SDL_WriteS64LE(SDL_RWops *dst, Sint64 value); */ extern DECLSPEC SDL_bool SDLCALL SDL_WriteU64BE(SDL_RWops *dst, Uint64 value); -extern DECLSPEC SDL_bool SDLCALL SDL_WriteU64BE(SDL_RWops *dst, Uint64 value); +/** + * Use this function to write 64 bits in native format to an SDL_RWops as + * big-endian data. + * + * SDL byteswaps the data only if necessary, so the application always + * specifies native format, and the data written will be in big-endian format. + * + * \param dst the stream to which data will be written + * \param value the data to be written, in native format + * \returns SDL_TRUE on successful write, SDL_FALSE on failure; call + * SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_WriteS64BE(SDL_RWops *dst, Sint64 value); + /* @} *//* Write endian functions */ /* Ends C function definitions when using C++ */ diff --git a/external/sdl/SDL/include/SDL3/SDL_surface.h b/external/sdl/SDL/include/SDL3/SDL_surface.h index 77483247..bdba4d07 100644 --- a/external/sdl/SDL/include/SDL3/SDL_surface.h +++ b/external/sdl/SDL/include/SDL3/SDL_surface.h @@ -778,15 +778,16 @@ extern DECLSPEC int SDLCALL SDL_FillSurfaceRects * \param srcrect the SDL_Rect structure representing the rectangle to be * copied, or NULL to copy the entire surface * \param dst the SDL_Surface structure that is the blit target - * \param dstrect the SDL_Rect structure representing the target rectangle in - * the destination surface, filled with the actual rectangle - * used after clipping + * \param dstrect the SDL_Rect structure representing the x and y position in + * the destination surface. On input the width and height are + * ignored (taken from srcrect), and on output this is filled + * in with the actual rectangle used after clipping. * \returns 0 on success or a negative error code on failure; call * SDL_GetError() for more information. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_BlitSurface + * \sa SDL_BlitSurfaceScaled */ extern DECLSPEC int SDLCALL SDL_BlitSurface (SDL_Surface *src, const SDL_Rect *srcrect, @@ -815,7 +816,6 @@ extern DECLSPEC int SDLCALL SDL_BlitSurfaceUnchecked (SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect); - /** * Perform a fast, low quality, stretch blit between two surfaces of the same * format. @@ -872,6 +872,8 @@ extern DECLSPEC int SDLCALL SDL_SoftStretchLinear(SDL_Surface *src, * SDL_GetError() for more information. * * \since This function is available since SDL 3.0.0. + * + * \sa SDL_BlitSurface */ extern DECLSPEC int SDLCALL SDL_BlitSurfaceScaled (SDL_Surface *src, const SDL_Rect *srcrect, diff --git a/external/sdl/SDL/include/SDL3/SDL_system.h b/external/sdl/SDL/include/SDL3/SDL_system.h index b934066f..8b9539b9 100644 --- a/external/sdl/SDL/include/SDL3/SDL_system.h +++ b/external/sdl/SDL/include/SDL3/SDL_system.h @@ -582,7 +582,16 @@ extern DECLSPEC SDL_WinRT_DeviceFamily SDLCALL SDL_WinRTGetDeviceFamily(); */ extern DECLSPEC SDL_bool SDLCALL SDL_IsTablet(void); -/* Functions used by iOS application delegates to notify SDL about state changes */ +/* Functions used by iOS app delegates to notify SDL about state changes. + * + * These functions allow iOS apps that have their own event handling to hook + * into SDL to generate SDL events. These map directly to iOS-specific + * events, but since they don't do anything iOS-specific internally, they + * are available on all platforms, in case they might be useful for some + * specific paradigm. Most apps do not need to use these directly; SDL's + * internal event code will handle all this for windows created by + * SDL_CreateWindow! + */ /* * \since This function is available since SDL 3.0.0. @@ -623,7 +632,8 @@ extern DECLSPEC void SDLCALL SDL_OnApplicationDidChangeStatusBarOrientation(void /* Functions used only by GDK */ #ifdef __GDK__ -typedef struct XTaskQueueObject * XTaskQueueHandle; +typedef struct XTaskQueueObject *XTaskQueueHandle; +typedef struct XUser *XUserHandle; /** * Gets a reference to the global async task queue handle for GDK, @@ -641,6 +651,20 @@ typedef struct XTaskQueueObject * XTaskQueueHandle; */ extern DECLSPEC int SDLCALL SDL_GDKGetTaskQueue(XTaskQueueHandle * outTaskQueue); +/** + * Gets a reference to the default user handle for GDK. + * + * This is effectively a synchronous version of XUserAddAsync, which always + * prefers the default user and allows a sign-in UI. + * + * \param outUserHandle a pointer to be filled in with the default user + * handle. + * \returns 0 if success, -1 if any error occurs. + * + * \since This function is available since SDL 2.28.0. + */ +extern DECLSPEC int SDLCALL SDL_GDKGetDefaultUser(XUserHandle * outUserHandle); + #endif /* Ends C function definitions when using C++ */ diff --git a/external/sdl/SDL/include/SDL3/SDL_test_font.h b/external/sdl/SDL/include/SDL3/SDL_test_font.h index c5146fca..fd6194aa 100644 --- a/external/sdl/SDL/include/SDL3/SDL_test_font.h +++ b/external/sdl/SDL/include/SDL3/SDL_test_font.h @@ -38,7 +38,8 @@ extern "C" { /* Function prototypes */ -#define FONT_CHARACTER_SIZE 8 +extern int FONT_CHARACTER_SIZE; + #define FONT_LINE_HEIGHT (FONT_CHARACTER_SIZE + 2) /** diff --git a/external/sdl/SDL/include/SDL3/SDL_test_memory.h b/external/sdl/SDL/include/SDL3/SDL_test_memory.h index 1b3f7d39..f2f12881 100644 --- a/external/sdl/SDL/include/SDL3/SDL_test_memory.h +++ b/external/sdl/SDL/include/SDL3/SDL_test_memory.h @@ -42,7 +42,14 @@ extern "C" { * * \note This should be called before any other SDL functions for complete tracking coverage */ -int SDLTest_TrackAllocations(void); +void SDLTest_TrackAllocations(void); + +/** + * \brief Fill allocations with random data + * + * \note This implicitly calls SDLTest_TrackAllocations() + */ +void SDLTest_RandFillAllocations(); /** * \brief Print a log of any outstanding allocations diff --git a/external/sdl/SDL/include/SDL3/SDL_video.h b/external/sdl/SDL/include/SDL3/SDL_video.h index aadc5c22..dd018de1 100644 --- a/external/sdl/SDL/include/SDL3/SDL_video.h +++ b/external/sdl/SDL/include/SDL3/SDL_video.h @@ -132,7 +132,7 @@ typedef enum SDL_WINDOW_FULLSCREEN = 0x00000001, /**< window is in fullscreen mode */ SDL_WINDOW_OPENGL = 0x00000002, /**< window usable with OpenGL context */ SDL_WINDOW_OCCLUDED = 0x00000004, /**< window is occluded */ - SDL_WINDOW_HIDDEN = 0x00000008, /**< window is not visible */ + SDL_WINDOW_HIDDEN = 0x00000008, /**< window is neither mapped onto the desktop nor shown in the taskbar/dock/window list; SDL_ShowWindow() is required for it to become visible */ SDL_WINDOW_BORDERLESS = 0x00000010, /**< no window decoration */ SDL_WINDOW_RESIZABLE = 0x00000020, /**< window can be resized */ SDL_WINDOW_MINIMIZED = 0x00000040, /**< window is minimized */ @@ -150,7 +150,8 @@ typedef enum SDL_WINDOW_KEYBOARD_GRABBED = 0x00100000, /**< window has grabbed keyboard input */ SDL_WINDOW_VULKAN = 0x10000000, /**< window usable for Vulkan surface */ SDL_WINDOW_METAL = 0x20000000, /**< window usable for Metal view */ - SDL_WINDOW_TRANSPARENT = 0x40000000 /**< window with transparent buffer */ + SDL_WINDOW_TRANSPARENT = 0x40000000, /**< window with transparent buffer */ + SDL_WINDOW_NOT_FOCUSABLE = 0x80000000, /**< window should not be focusable */ } SDL_WindowFlags; @@ -1676,6 +1677,20 @@ extern DECLSPEC int SDLCALL SDL_SetWindowModalFor(SDL_Window *modal_window, SDL_ */ extern DECLSPEC int SDLCALL SDL_SetWindowInputFocus(SDL_Window *window); +/** + * Set whether the window may have input focus. + * + * \param window the window to set focusable state + * \param focusable SDL_TRUE to allow input focus, SDL_FALSE to not allow + * input focus + * \returns 0 on success or a negative error code on failure; call + * SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC int SDLCALL SDL_SetWindowFocusable(SDL_Window *window, SDL_bool focusable); + + /** * Display the system-level window menu. * diff --git a/external/sdl/SDL/src/SDL_assert.c b/external/sdl/SDL/src/SDL_assert.c index acde7f4a..ba1c194d 100644 --- a/external/sdl/SDL/src/SDL_assert.c +++ b/external/sdl/SDL/src/SDL_assert.c @@ -34,7 +34,15 @@ #endif #ifdef __EMSCRIPTEN__ -#include + #include + /* older Emscriptens don't have this, but we need to for wasm64 compatibility. */ + #ifndef MAIN_THREAD_EM_ASM_PTR + #ifdef __wasm64__ + #error You need to upgrade your Emscripten compiler to support wasm64 + #else + #define MAIN_THREAD_EM_ASM_PTR MAIN_THREAD_EM_ASM_INT + #endif + #endif #endif /* The size of the stack buffer to use for rendering assert messages. */ @@ -211,7 +219,7 @@ static SDL_AssertState SDLCALL SDL_PromptAssertion(const SDL_AssertData *data, v } /* Leave fullscreen mode, if possible (scary!) */ - window = SDL_GetFocusWindow(); + window = SDL_GetToplevelForKeyboardFocus(); if (window) { if (window->fullscreen_exclusive) { SDL_MinimizeWindow(window); @@ -243,7 +251,7 @@ static SDL_AssertState SDLCALL SDL_PromptAssertion(const SDL_AssertData *data, v for (;;) { SDL_bool okay = SDL_TRUE; /* *INDENT-OFF* */ /* clang-format off */ - char *buf = (char *) EM_ASM_INT({ + char *buf = (char *) MAIN_THREAD_EM_ASM_PTR({ var str = UTF8ToString($0) + '\n\n' + 'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :'; diff --git a/external/sdl/SDL/src/SDL_dataqueue.c b/external/sdl/SDL/src/SDL_dataqueue.c deleted file mode 100644 index 4b3b79c9..00000000 --- a/external/sdl/SDL/src/SDL_dataqueue.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2023 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#include "./SDL_dataqueue.h" - -typedef struct SDL_DataQueuePacket -{ - size_t datalen; /* bytes currently in use in this packet. */ - size_t startpos; /* bytes currently consumed in this packet. */ - struct SDL_DataQueuePacket *next; /* next item in linked list. */ - Uint8 data[SDL_VARIABLE_LENGTH_ARRAY]; /* packet data */ -} SDL_DataQueuePacket; - -struct SDL_DataQueue -{ - SDL_Mutex *lock; - SDL_DataQueuePacket *head; /* device fed from here. */ - SDL_DataQueuePacket *tail; /* queue fills to here. */ - SDL_DataQueuePacket *pool; /* these are unused packets. */ - size_t packet_size; /* size of new packets */ - size_t queued_bytes; /* number of bytes of data in the queue. */ -}; - -static void SDL_FreeDataQueueList(SDL_DataQueuePacket *packet) -{ - while (packet) { - SDL_DataQueuePacket *next = packet->next; - SDL_free(packet); - packet = next; - } -} - -SDL_DataQueue *SDL_CreateDataQueue(const size_t _packetlen, const size_t initialslack) -{ - SDL_DataQueue *queue = (SDL_DataQueue *)SDL_calloc(1, sizeof(SDL_DataQueue)); - - if (queue == NULL) { - SDL_OutOfMemory(); - } else { - const size_t packetlen = _packetlen ? _packetlen : 1024; - const size_t wantpackets = (initialslack + (packetlen - 1)) / packetlen; - size_t i; - - queue->packet_size = packetlen; - - queue->lock = SDL_CreateMutex(); - if (!queue->lock) { - SDL_free(queue); - return NULL; - } - - for (i = 0; i < wantpackets; i++) { - SDL_DataQueuePacket *packet = (SDL_DataQueuePacket *)SDL_malloc(sizeof(SDL_DataQueuePacket) + packetlen); - if (packet) { /* don't care if this fails, we'll deal later. */ - packet->datalen = 0; - packet->startpos = 0; - packet->next = queue->pool; - queue->pool = packet; - } - } - } - - return queue; -} - -void SDL_DestroyDataQueue(SDL_DataQueue *queue) -{ - if (queue) { - SDL_FreeDataQueueList(queue->head); - SDL_FreeDataQueueList(queue->pool); - SDL_DestroyMutex(queue->lock); - SDL_free(queue); - } -} - -void SDL_ClearDataQueue(SDL_DataQueue *queue, const size_t slack) -{ - const size_t packet_size = queue ? queue->packet_size : 1; - const size_t slackpackets = (slack + (packet_size - 1)) / packet_size; - SDL_DataQueuePacket *packet; - SDL_DataQueuePacket *prev = NULL; - size_t i; - - if (queue == NULL) { - return; - } - - SDL_LockMutex(queue->lock); - - packet = queue->head; - - /* merge the available pool and the current queue into one list. */ - if (packet) { - queue->tail->next = queue->pool; - } else { - packet = queue->pool; - } - - /* Remove the queued packets from the device. */ - queue->tail = NULL; - queue->head = NULL; - queue->queued_bytes = 0; - queue->pool = packet; - - /* Optionally keep some slack in the pool to reduce memory allocation pressure. */ - for (i = 0; packet && (i < slackpackets); i++) { - prev = packet; - packet = packet->next; - } - - if (prev) { - prev->next = NULL; - } else { - queue->pool = NULL; - } - - SDL_UnlockMutex(queue->lock); - - SDL_FreeDataQueueList(packet); /* free extra packets */ -} - -/* You must hold queue->lock before calling this! */ -static SDL_DataQueuePacket *AllocateDataQueuePacket(SDL_DataQueue *queue) -{ - SDL_DataQueuePacket *packet; - - SDL_assert(queue != NULL); - - packet = queue->pool; - if (packet != NULL) { - /* we have one available in the pool. */ - queue->pool = packet->next; - } else { - /* Have to allocate a new one! */ - packet = (SDL_DataQueuePacket *)SDL_malloc(sizeof(SDL_DataQueuePacket) + queue->packet_size); - if (packet == NULL) { - return NULL; - } - } - - packet->datalen = 0; - packet->startpos = 0; - packet->next = NULL; - - SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0)); - if (queue->tail == NULL) { - queue->head = packet; - } else { - queue->tail->next = packet; - } - queue->tail = packet; - return packet; -} - -int SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *_data, const size_t _len) -{ - size_t len = _len; - const Uint8 *data = (const Uint8 *)_data; - const size_t packet_size = queue ? queue->packet_size : 0; - SDL_DataQueuePacket *orighead; - SDL_DataQueuePacket *origtail; - size_t origlen; - size_t datalen; - - if (queue == NULL) { - return SDL_InvalidParamError("queue"); - } - - SDL_LockMutex(queue->lock); - - orighead = queue->head; - origtail = queue->tail; - origlen = origtail ? origtail->datalen : 0; - - while (len > 0) { - SDL_DataQueuePacket *packet = queue->tail; - SDL_assert(packet == NULL || (packet->datalen <= packet_size)); - if (packet == NULL || (packet->datalen >= packet_size)) { - /* tail packet missing or completely full; we need a new packet. */ - packet = AllocateDataQueuePacket(queue); - if (packet == NULL) { - /* uhoh, reset so we've queued nothing new, free what we can. */ - if (origtail == NULL) { - packet = queue->head; /* whole queue. */ - } else { - packet = origtail->next; /* what we added to existing queue. */ - origtail->next = NULL; - origtail->datalen = origlen; - } - queue->head = orighead; - queue->tail = origtail; - queue->pool = NULL; - - SDL_UnlockMutex(queue->lock); - SDL_FreeDataQueueList(packet); /* give back what we can. */ - return SDL_OutOfMemory(); - } - } - - datalen = SDL_min(len, packet_size - packet->datalen); - SDL_memcpy(packet->data + packet->datalen, data, datalen); - data += datalen; - len -= datalen; - packet->datalen += datalen; - queue->queued_bytes += datalen; - } - - SDL_UnlockMutex(queue->lock); - - return 0; -} - -size_t SDL_PeekIntoDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len) -{ - size_t len = _len; - Uint8 *buf = (Uint8 *)_buf; - Uint8 *ptr = buf; - SDL_DataQueuePacket *packet; - - if (queue == NULL) { - return 0; - } - - SDL_LockMutex(queue->lock); - - for (packet = queue->head; len && packet; packet = packet->next) { - const size_t avail = packet->datalen - packet->startpos; - const size_t cpy = SDL_min(len, avail); - SDL_assert(queue->queued_bytes >= avail); - - SDL_memcpy(ptr, packet->data + packet->startpos, cpy); - ptr += cpy; - len -= cpy; - } - - SDL_UnlockMutex(queue->lock); - - return (size_t)(ptr - buf); -} - -size_t SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len) -{ - size_t len = _len; - Uint8 *buf = (Uint8 *)_buf; - Uint8 *ptr = buf; - SDL_DataQueuePacket *packet; - - if (queue == NULL) { - return 0; - } - - SDL_LockMutex(queue->lock); - - while ((len > 0) && ((packet = queue->head) != NULL)) { - const size_t avail = packet->datalen - packet->startpos; - const size_t cpy = SDL_min(len, avail); - SDL_assert(queue->queued_bytes >= avail); - - SDL_memcpy(ptr, packet->data + packet->startpos, cpy); - packet->startpos += cpy; - ptr += cpy; - queue->queued_bytes -= cpy; - len -= cpy; - - if (packet->startpos == packet->datalen) { /* packet is done, put it in the pool. */ - queue->head = packet->next; - SDL_assert((packet->next != NULL) || (packet == queue->tail)); - packet->next = queue->pool; - queue->pool = packet; - } - } - - SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0)); - - if (queue->head == NULL) { - queue->tail = NULL; /* in case we drained the queue entirely. */ - } - - SDL_UnlockMutex(queue->lock); - - return (size_t)(ptr - buf); -} - -size_t SDL_GetDataQueueSize(SDL_DataQueue *queue) -{ - size_t retval = 0; - if (queue) { - SDL_LockMutex(queue->lock); - retval = queue->queued_bytes; - SDL_UnlockMutex(queue->lock); - } - return retval; -} - -SDL_Mutex *SDL_GetDataQueueMutex(SDL_DataQueue *queue) -{ - return queue ? queue->lock : NULL; -} - diff --git a/external/sdl/SDL/src/SDL_dataqueue.h b/external/sdl/SDL/src/SDL_dataqueue.h deleted file mode 100644 index c9ea8305..00000000 --- a/external/sdl/SDL/src/SDL_dataqueue.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2023 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#ifndef SDL_dataqueue_h_ -#define SDL_dataqueue_h_ - -/* this is not (currently) a public API. But maybe it should be! */ - -struct SDL_DataQueue; -typedef struct SDL_DataQueue SDL_DataQueue; - -SDL_DataQueue *SDL_CreateDataQueue(const size_t packetlen, const size_t initialslack); -void SDL_DestroyDataQueue(SDL_DataQueue *queue); -void SDL_ClearDataQueue(SDL_DataQueue *queue, const size_t slack); -int SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *data, const size_t len); -size_t SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *buf, const size_t len); -size_t SDL_PeekIntoDataQueue(SDL_DataQueue *queue, void *buf, const size_t len); -size_t SDL_GetDataQueueSize(SDL_DataQueue *queue); -SDL_Mutex *SDL_GetDataQueueMutex(SDL_DataQueue *queue); /* don't destroy this, obviously. */ - -#endif /* SDL_dataqueue_h_ */ diff --git a/external/sdl/SDL/src/audio/SDL_audio.c b/external/sdl/SDL/src/audio/SDL_audio.c index c4162738..cfd169ad 100644 --- a/external/sdl/SDL/src/audio/SDL_audio.c +++ b/external/sdl/SDL/src/audio/SDL_audio.c @@ -116,6 +116,106 @@ const char *SDL_GetCurrentAudioDriver(void) return current_audio.name; } +static int GetDefaultSampleFramesFromFreq(const int freq) +{ + const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES); + if (hint) { + const int val = SDL_atoi(hint); + if (val > 0) { + return val; + } + } + + if (freq <= 22050) { + return 512; + } else if (freq <= 48000) { + return 1024; + } else if (freq <= 96000) { + return 2048; + } else { + return 4096; + } +} + +void OnAudioStreamCreated(SDL_AudioStream *stream) +{ + SDL_assert(SDL_GetCurrentAudioDriver() != NULL); + SDL_assert(stream != NULL); + + // this isn't really part of the "device list" but it's a convenient lock to use here. + SDL_LockRWLockForWriting(current_audio.device_list_lock); + if (current_audio.existing_streams) { + current_audio.existing_streams->prev = stream; + } + stream->prev = NULL; + stream->next = current_audio.existing_streams; + current_audio.existing_streams = stream; + SDL_UnlockRWLock(current_audio.device_list_lock); +} + +void OnAudioStreamDestroy(SDL_AudioStream *stream) +{ + SDL_assert(SDL_GetCurrentAudioDriver() != NULL); + SDL_assert(stream != NULL); + + // this isn't really part of the "device list" but it's a convenient lock to use here. + SDL_LockRWLockForWriting(current_audio.device_list_lock); + if (stream->prev) { + stream->prev->next = stream->next; + } + if (stream->next) { + stream->next->prev = stream->prev; + } + if (stream == current_audio.existing_streams) { + current_audio.existing_streams = stream->next; + } + SDL_UnlockRWLock(current_audio.device_list_lock); +} + + +// should hold logdev's physical device's lock before calling. +static void UpdateAudioStreamFormatsLogical(SDL_LogicalAudioDevice *logdev) +{ + const SDL_bool iscapture = logdev->physical_device->iscapture; + SDL_AudioSpec spec; + SDL_copyp(&spec, &logdev->physical_device->spec); + if (logdev->postmix != NULL) { + spec.format = SDL_AUDIO_F32; + } + + for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) { + // set the proper end of the stream to the device's format. + // SDL_SetAudioStreamFormat does a ton of validation just to memcpy an audiospec. + SDL_LockMutex(stream->lock); + SDL_copyp(iscapture ? &stream->src_spec : &stream->dst_spec, &spec); + SDL_UnlockMutex(stream->lock); + } +} + +// should hold device->lock before calling. +static void UpdateAudioStreamFormatsPhysical(SDL_AudioDevice *device) +{ + for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) { + UpdateAudioStreamFormatsLogical(logdev); + } +} + + +// device should be locked when calling this. +static SDL_bool AudioDeviceCanUseSimpleCopy(SDL_AudioDevice *device) +{ + SDL_assert(device != NULL); + return ( + device->logical_devices && // there's a logical device + !device->logical_devices->next && // there's only _ONE_ logical device + !device->logical_devices->postmix && // there isn't a postmix callback + !SDL_AtomicGet(&device->logical_devices->paused) && // it isn't paused + device->logical_devices->bound_streams && // there's a bound stream + !device->logical_devices->bound_streams->next_binding // there's only _ONE_ bound stream. + ) ? SDL_TRUE : SDL_FALSE; +} + + // device management and hotplug... @@ -176,6 +276,8 @@ static void DestroyLogicalAudioDevice(SDL_LogicalAudioDevice *logdev) SDL_UnlockMutex(stream->lock); } + logdev->physical_device->simple_copy = AudioDeviceCanUseSimpleCopy(logdev->physical_device); + SDL_free(logdev); } @@ -208,7 +310,10 @@ static SDL_AudioDevice *CreatePhysicalAudioDevice(const char *name, SDL_bool isc { SDL_assert(name != NULL); - if (SDL_AtomicGet(¤t_audio.shutting_down)) { + SDL_LockRWLockForReading(current_audio.device_list_lock); + const int shutting_down = SDL_AtomicGet(¤t_audio.shutting_down); + SDL_UnlockRWLock(current_audio.device_list_lock); + if (shutting_down) { return NULL; // we're shutting down, don't add any devices that are hotplugged at the last possible moment. } @@ -236,8 +341,9 @@ static SDL_AudioDevice *CreatePhysicalAudioDevice(const char *name, SDL_bool isc SDL_AtomicSet(&device->condemned, 0); SDL_AtomicSet(&device->zombie, 0); device->iscapture = iscapture; - SDL_memcpy(&device->spec, spec, sizeof (SDL_AudioSpec)); - SDL_memcpy(&device->default_spec, spec, sizeof (SDL_AudioSpec)); + SDL_copyp(&device->spec, spec); + SDL_copyp(&device->default_spec, spec); + device->sample_frames = GetDefaultSampleFramesFromFreq(device->spec.freq); device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format); device->handle = handle; device->prev = NULL; @@ -336,7 +442,7 @@ void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device) SDL_LogicalAudioDevice *next = NULL; for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = next) { next = logdev->next; - if (!logdev->is_default) { // if opened as a default, leave it on the zombie device for later migration. + if (!logdev->opened_as_default) { // if opened as a default, leave it on the zombie device for later migration. DisconnectLogicalAudioDevice(logdev); } } @@ -412,7 +518,7 @@ void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device) static void SDL_AudioThreadDeinit_Default(SDL_AudioDevice *device) { /* no-op. */ } static void SDL_AudioWaitDevice_Default(SDL_AudioDevice *device) { /* no-op. */ } -static void SDL_AudioPlayDevice_Default(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { /* no-op. */ } +static int SDL_AudioPlayDevice_Default(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { return 0; /* no-op. */ } static void SDL_AudioWaitCaptureDevice_Default(SDL_AudioDevice *device) { /* no-op. */ } static void SDL_AudioFlushCapture_Default(SDL_AudioDevice *device) { /* no-op. */ } static void SDL_AudioCloseDevice_Default(SDL_AudioDevice *device) { /* no-op. */ } @@ -493,6 +599,7 @@ int SDL_InitAudio(const char *driver_name) } SDL_ChooseAudioConverters(); + SDL_SetupAudioResampler(); SDL_RWLock *device_list_lock = SDL_CreateRWLock(); // create this early, so if it fails we don't have to tear down the whole audio subsystem. if (!device_list_lock) { @@ -614,6 +721,11 @@ void SDL_QuitAudio(void) return; } + // Destroy any audio streams that still exist... + while (current_audio.existing_streams != NULL) { + SDL_DestroyAudioStream(current_audio.existing_streams); + } + // merge device lists so we don't have to duplicate work below. SDL_LockRWLockForWriting(current_audio.device_list_lock); SDL_AtomicSet(¤t_audio.shutting_down, 1); @@ -633,7 +745,7 @@ void SDL_QuitAudio(void) SDL_AtomicSet(¤t_audio.output_device_count, 0); SDL_AtomicSet(¤t_audio.capture_device_count, 0); SDL_UnlockRWLock(current_audio.device_list_lock); - + // mark all devices for shutdown so all threads can begin to terminate. for (SDL_AudioDevice *i = devices; i != NULL; i = i->next) { SDL_AtomicSet(&i->shutdown, 1); @@ -676,6 +788,14 @@ void SDL_AudioThreadFinalize(SDL_AudioDevice *device) SDL_AtomicSet(&device->thread_alive, 0); } +static void MixFloat32Audio(float *dst, const float *src, const int buffer_size) +{ + if (SDL_MixAudioFormat((Uint8 *) dst, (const Uint8 *) src, SDL_AUDIO_F32, buffer_size, SDL_MIX_MAXVOLUME) < 0) { + SDL_assert(!"This shouldn't happen."); + } +} + + // Output device thread. This is split into chunks, so backends that need to control this directly can use the pieces they need without duplicating effort. void SDL_OutputAudioThreadSetup(SDL_AudioDevice *device) @@ -697,41 +817,90 @@ SDL_bool SDL_OutputAudioThreadIterate(SDL_AudioDevice *device) SDL_bool retval = SDL_TRUE; int buffer_size = device->buffer_size; - Uint8 *mix_buffer = current_audio.impl.GetDeviceBuf(device, &buffer_size); - if (!mix_buffer) { + Uint8 *device_buffer = current_audio.impl.GetDeviceBuf(device, &buffer_size); + if (!device_buffer) { retval = SDL_FALSE; } else { SDL_assert(buffer_size <= device->buffer_size); // you can ask for less, but not more. - SDL_memset(mix_buffer, device->silence_value, buffer_size); // start with silence. + SDL_assert(AudioDeviceCanUseSimpleCopy(device) == device->simple_copy); // make sure this hasn't gotten out of sync. - for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) { - if (SDL_AtomicGet(&logdev->paused)) { - continue; // paused? Skip this logical device. + // can we do a basic copy without silencing/mixing the buffer? This is an extremely likely scenario, so we special-case it. + if (device->simple_copy) { + SDL_LogicalAudioDevice *logdev = device->logical_devices; + SDL_AudioStream *stream = logdev->bound_streams; + + // We should have updated this elsewhere if the format changed! + SDL_assert(AUDIO_SPECS_EQUAL(stream->dst_spec, device->spec)); + + const int br = SDL_GetAudioStreamData(stream, device_buffer, buffer_size); + if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow. + retval = SDL_FALSE; + SDL_memset(device_buffer, device->silence_value, buffer_size); // just supply silence to the device before we die. + } else if (br < buffer_size) { + SDL_memset(device_buffer + br, device->silence_value, buffer_size - br); // silence whatever we didn't write to. } + } else { // need to actually mix (or silence the buffer) + float *final_mix_buffer = (float *) ((device->spec.format == SDL_AUDIO_F32) ? device_buffer : device->mix_buffer); + const int needed_samples = buffer_size / SDL_AUDIO_BYTESIZE(device->spec.format); + const int work_buffer_size = needed_samples * sizeof (float); + SDL_AudioSpec outspec; - for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) { - /* this will hold a lock on `stream` while getting. We don't explicitly lock the streams - for iterating here because the binding linked list can only change while the device lock is held. - (we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind - the same stream to different devices at the same time, though.) */ - const int br = SDL_GetAudioStreamData(stream, device->work_buffer, buffer_size); - if (br < 0) { - // oh crud, we probably ran out of memory. This is possibly an overreaction to kill the audio device, but it's likely the whole thing is going down in a moment anyhow. - retval = SDL_FALSE; - break; - } else if (br > 0) { // it's okay if we get less than requested, we mix what we have. - // !!! FIXME: this needs to mix to float32 or int32, so we don't clip. - if (SDL_MixAudioFormat(mix_buffer, device->work_buffer, device->spec.format, br, SDL_MIX_MAXVOLUME) < 0) { // !!! FIXME: allow streams to specify gain? - SDL_assert(!"We probably ended up with some totally unexpected audio format here"); - retval = SDL_FALSE; // uh...? + SDL_assert(work_buffer_size <= device->work_buffer_size); + + outspec.format = SDL_AUDIO_F32; + outspec.channels = device->spec.channels; + outspec.freq = device->spec.freq; + + SDL_memset(final_mix_buffer, '\0', work_buffer_size); // start with silence. + + for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) { + if (SDL_AtomicGet(&logdev->paused)) { + continue; // paused? Skip this logical device. + } + + const SDL_AudioPostmixCallback postmix = logdev->postmix; + float *mix_buffer = final_mix_buffer; + if (postmix) { + mix_buffer = device->postmix_buffer; + SDL_memset(mix_buffer, '\0', work_buffer_size); // start with silence. + } + + for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) { + // We should have updated this elsewhere if the format changed! + SDL_assert(AUDIO_SPECS_EQUAL(stream->dst_spec, outspec)); + + /* this will hold a lock on `stream` while getting. We don't explicitly lock the streams + for iterating here because the binding linked list can only change while the device lock is held. + (we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind + the same stream to different devices at the same time, though.) */ + const int br = SDL_GetAudioStreamData(stream, device->work_buffer, work_buffer_size); + if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow. + retval = SDL_FALSE; break; + } else if (br > 0) { // it's okay if we get less than requested, we mix what we have. + MixFloat32Audio(mix_buffer, (float *) device->work_buffer, br); } } + + if (postmix) { + SDL_assert(mix_buffer == device->postmix_buffer); + postmix(logdev->postmix_userdata, &outspec, mix_buffer, work_buffer_size); + MixFloat32Audio(final_mix_buffer, mix_buffer, work_buffer_size); + } + } + + if (((Uint8 *) final_mix_buffer) != device_buffer) { + // !!! FIXME: we can't promise the device buf is aligned/padded for SIMD. + //ConvertAudio(needed_samples * device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, device_buffer, device->spec.format, device->spec.channels, device->work_buffer); + ConvertAudio(needed_samples / device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, device->work_buffer, device->spec.format, device->spec.channels, NULL); + SDL_memcpy(device_buffer, device->work_buffer, buffer_size); } } - // !!! FIXME: have PlayDevice return a value and do disconnects in here with it. - current_audio.impl.PlayDevice(device, mix_buffer, buffer_size); // this SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitDevice! + // PlayDevice SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitDevice instead! + if (current_audio.impl.PlayDevice(device, device_buffer, buffer_size) < 0) { + retval = SDL_FALSE; + } } SDL_UnlockMutex(device->lock); @@ -746,9 +915,9 @@ SDL_bool SDL_OutputAudioThreadIterate(SDL_AudioDevice *device) void SDL_OutputAudioThreadShutdown(SDL_AudioDevice *device) { SDL_assert(!device->iscapture); - const int samples = (device->buffer_size / (SDL_AUDIO_BITSIZE(device->spec.format) / 8)) / device->spec.channels; + const int frames = device->buffer_size / SDL_AUDIO_FRAMESIZE(device->spec); // Wait for the audio to drain. !!! FIXME: don't bother waiting if device is lost. - SDL_Delay(((samples * 1000) / device->spec.freq) * 2); + SDL_Delay(((frames * 1000) / device->spec.freq) * 2); current_audio.impl.ThreadDeinit(device); SDL_AudioThreadFinalize(device); } @@ -791,21 +960,42 @@ SDL_bool SDL_CaptureAudioThreadIterate(SDL_AudioDevice *device) current_audio.impl.FlushCapture(device); // nothing wants data, dump anything pending. } else { // this SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitCaptureDevice! - const int rc = current_audio.impl.CaptureFromDevice(device, device->work_buffer, device->buffer_size); - if (rc < 0) { // uhoh, device failed for some reason! + int br = current_audio.impl.CaptureFromDevice(device, device->work_buffer, device->buffer_size); + if (br < 0) { // uhoh, device failed for some reason! retval = SDL_FALSE; - } else if (rc > 0) { // queue the new data to each bound stream. + } else if (br > 0) { // queue the new data to each bound stream. for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) { if (SDL_AtomicGet(&logdev->paused)) { continue; // paused? Skip this logical device. } + void *output_buffer = device->work_buffer; + + // I don't know why someone would want a postmix on a capture device, but we offer it for API consistency. + if (logdev->postmix) { + // move to float format. + SDL_AudioSpec outspec; + outspec.format = SDL_AUDIO_F32; + outspec.channels = device->spec.channels; + outspec.freq = device->spec.freq; + output_buffer = device->postmix_buffer; + const int frames = br / SDL_AUDIO_FRAMESIZE(device->spec); + br = frames * SDL_AUDIO_FRAMESIZE(outspec); + ConvertAudio(frames, device->work_buffer, device->spec.format, outspec.channels, device->postmix_buffer, SDL_AUDIO_F32, outspec.channels, NULL); + logdev->postmix(logdev->postmix_userdata, &outspec, device->postmix_buffer, br); + } + for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) { + // We should have updated this elsewhere if the format changed! + SDL_assert(stream->src_spec.format == (logdev->postmix ? SDL_AUDIO_F32 : device->spec.format)); + SDL_assert(stream->src_spec.channels == device->spec.channels); + SDL_assert(stream->src_spec.freq == device->spec.freq); + /* this will hold a lock on `stream` while putting. We don't explicitly lock the streams for iterating here because the binding linked list can only change while the device lock is held. (we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind the same stream to different devices at the same time, though.) */ - if (SDL_PutAudioStreamData(stream, device->work_buffer, rc) < 0) { + if (SDL_PutAudioStreamData(stream, output_buffer, br) < 0) { // oh crud, we probably ran out of memory. This is possibly an overreaction to kill the audio device, but it's likely the whole thing is going down in a moment anyhow. retval = SDL_FALSE; break; @@ -1036,22 +1226,22 @@ char *SDL_GetAudioDeviceName(SDL_AudioDeviceID devid) return retval; } -int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec) +int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec, int *sample_frames) { if (!spec) { return SDL_InvalidParamError("spec"); } - SDL_bool is_default = SDL_FALSE; + SDL_bool wants_default = SDL_FALSE; if (devid == SDL_AUDIO_DEVICE_DEFAULT_OUTPUT) { devid = current_audio.default_output_device_id; - is_default = SDL_TRUE; + wants_default = SDL_TRUE; } else if (devid == SDL_AUDIO_DEVICE_DEFAULT_CAPTURE) { devid = current_audio.default_capture_device_id; - is_default = SDL_TRUE; + wants_default = SDL_TRUE; } - if ((devid == 0) && is_default) { + if ((devid == 0) && wants_default) { return SDL_SetError("No default audio device available"); } @@ -1060,7 +1250,10 @@ int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec) return -1; } - SDL_memcpy(spec, &device->spec, sizeof (SDL_AudioSpec)); + SDL_copyp(spec, &device->spec); + if (sample_frames) { + *sample_frames = device->sample_frames; + } SDL_UnlockMutex(device->lock); return 0; @@ -1080,18 +1273,22 @@ static void ClosePhysicalAudioDevice(SDL_AudioDevice *device) SDL_AtomicSet(&device->thread_alive, 0); } - if (device->is_opened) { + if (device->currently_opened) { current_audio.impl.CloseDevice(device); // if ProvidesOwnCallbackThread, this must join on any existing device thread before returning! - device->is_opened = SDL_FALSE; + device->currently_opened = SDL_FALSE; device->hidden = NULL; // just in case. } - if (device->work_buffer) { - SDL_aligned_free(device->work_buffer); - device->work_buffer = NULL; - } + SDL_aligned_free(device->work_buffer); + device->work_buffer = NULL; - SDL_memcpy(&device->spec, &device->default_spec, sizeof (SDL_AudioSpec)); + SDL_aligned_free(device->mix_buffer); + device->mix_buffer = NULL; + + SDL_aligned_free(device->postmix_buffer); + device->postmix_buffer = NULL; + + SDL_copyp(&device->spec, &device->default_spec); device->sample_frames = 0; device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format); SDL_AtomicSet(&device->shutdown, 0); // ready to go again. @@ -1121,16 +1318,14 @@ static SDL_AudioFormat ParseAudioFormatString(const char *string) #define CHECK_FMT_STRING(x) if (SDL_strcmp(string, #x) == 0) { return SDL_AUDIO_##x; } CHECK_FMT_STRING(U8); CHECK_FMT_STRING(S8); - CHECK_FMT_STRING(S16LSB); - CHECK_FMT_STRING(S16MSB); + CHECK_FMT_STRING(S16LE); + CHECK_FMT_STRING(S16BE); CHECK_FMT_STRING(S16); - CHECK_FMT_STRING(S32LSB); - CHECK_FMT_STRING(S32MSB); - CHECK_FMT_STRING(S32SYS); + CHECK_FMT_STRING(S32LE); + CHECK_FMT_STRING(S32BE); CHECK_FMT_STRING(S32); - CHECK_FMT_STRING(F32LSB); - CHECK_FMT_STRING(F32MSB); - CHECK_FMT_STRING(F32SYS); + CHECK_FMT_STRING(F32LE); + CHECK_FMT_STRING(F32BE); CHECK_FMT_STRING(F32); #undef CHECK_FMT_STRING } @@ -1168,15 +1363,12 @@ static void PrepareAudioFormat(SDL_bool iscapture, SDL_AudioSpec *spec) } } -static int GetDefaultSampleFramesFromFreq(int freq) -{ - return SDL_powerof2((freq / 1000) * 46); // Pick the closest power-of-two to ~46 ms at desired frequency -} - void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device) { device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format); - device->buffer_size = device->sample_frames * (SDL_AUDIO_BITSIZE(device->spec.format) / 8) * device->spec.channels; + device->buffer_size = device->sample_frames * SDL_AUDIO_FRAMESIZE(device->spec); + device->work_buffer_size = device->sample_frames * sizeof (float) * device->spec.channels; + device->work_buffer_size = SDL_max(device->buffer_size, device->work_buffer_size); // just in case we end up with a 64-bit audio format at some point. } char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen) @@ -1189,7 +1381,7 @@ char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen) // this expects the device lock to be held. static int OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec *inspec) { - SDL_assert(!device->is_opened); + SDL_assert(!device->currently_opened); SDL_assert(device->logical_devices == NULL); // Just pretend to open a zombie device. It can still collect logical devices on the assumption they will all migrate when the default device is officially changed. @@ -1198,7 +1390,7 @@ static int OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec } SDL_AudioSpec spec; - SDL_memcpy(&spec, inspec ? inspec : &device->default_spec, sizeof (SDL_AudioSpec)); + SDL_copyp(&spec, inspec ? inspec : &device->default_spec); PrepareAudioFormat(device->iscapture, &spec); /* We allow the device format to change if it's better than the current settings (by various definitions of "better"). This prevents @@ -1211,7 +1403,7 @@ static int OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec device->sample_frames = GetDefaultSampleFramesFromFreq(device->spec.freq); SDL_UpdatedAudioDeviceFormat(device); // start this off sane. - device->is_opened = SDL_TRUE; // mark this true even if impl.OpenDevice fails, so we know to clean up. + device->currently_opened = SDL_TRUE; // mark this true even if impl.OpenDevice fails, so we know to clean up. if (current_audio.impl.OpenDevice(device) < 0) { ClosePhysicalAudioDevice(device); // clean up anything the backend left half-initialized. return -1; @@ -1220,12 +1412,20 @@ static int OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec SDL_UpdatedAudioDeviceFormat(device); // in case the backend changed things and forgot to call this. // Allocate a scratch audio buffer - device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->buffer_size); + device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size); if (device->work_buffer == NULL) { ClosePhysicalAudioDevice(device); return SDL_OutOfMemory(); } + if (device->spec.format != SDL_AUDIO_F32) { + device->mix_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size); + if (device->mix_buffer == NULL) { + ClosePhysicalAudioDevice(device); + return SDL_OutOfMemory(); + } + } + // Start the audio thread if necessary SDL_AtomicSet(&device->thread_alive, 1); if (!current_audio.impl.ProvidesOwnCallbackThread) { @@ -1251,16 +1451,16 @@ SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSp return 0; } - SDL_bool is_default = SDL_FALSE; + SDL_bool wants_default = SDL_FALSE; if (devid == SDL_AUDIO_DEVICE_DEFAULT_OUTPUT) { devid = current_audio.default_output_device_id; - is_default = SDL_TRUE; + wants_default = SDL_TRUE; } else if (devid == SDL_AUDIO_DEVICE_DEFAULT_CAPTURE) { devid = current_audio.default_capture_device_id; - is_default = SDL_TRUE; + wants_default = SDL_TRUE; } - if ((devid == 0) && is_default) { + if ((devid == 0) && wants_default) { SDL_SetError("No default audio device available"); return 0; } @@ -1273,7 +1473,7 @@ SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSp } else { SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid); // this locks the physical device, too. if (logdev) { - is_default = logdev->is_default; // was the original logical device meant to be a default? Make this one, too. + wants_default = logdev->opened_as_default; // was the original logical device meant to be a default? Make this one, too. device = logdev->physical_device; } } @@ -1282,23 +1482,24 @@ SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSp if (device) { SDL_LogicalAudioDevice *logdev = NULL; - if (!is_default && SDL_AtomicGet(&device->zombie)) { + if (!wants_default && SDL_AtomicGet(&device->zombie)) { // uhoh, this device is undead, and just waiting for a new default device to be declared so it can hand off to it. Refuse explicit opens. SDL_SetError("Device was already lost and can't accept new opens"); } else if ((logdev = (SDL_LogicalAudioDevice *) SDL_calloc(1, sizeof (SDL_LogicalAudioDevice))) == NULL) { SDL_OutOfMemory(); - } else if (!device->is_opened && OpenPhysicalAudioDevice(device, spec) == -1) { // first thing using this physical device? Open at the OS level... + } else if (!device->currently_opened && OpenPhysicalAudioDevice(device, spec) == -1) { // first thing using this physical device? Open at the OS level... SDL_free(logdev); } else { SDL_AtomicSet(&logdev->paused, 0); retval = logdev->instance_id = assign_audio_device_instance_id(device->iscapture, /*islogical=*/SDL_TRUE); logdev->physical_device = device; - logdev->is_default = is_default; + logdev->opened_as_default = wants_default; logdev->next = device->logical_devices; if (device->logical_devices) { device->logical_devices->prev = logdev; } device->logical_devices = logdev; + device->simple_copy = AudioDeviceCanUseSimpleCopy(device); } SDL_UnlockMutex(device->lock); } @@ -1313,6 +1514,7 @@ static int SetLogicalAudioDevicePauseState(SDL_AudioDeviceID devid, int value) return -1; // ObtainLogicalAudioDevice will have set an error. } SDL_AtomicSet(&logdev->paused, value); + logdev->physical_device->simple_copy = AudioDeviceCanUseSimpleCopy(logdev->physical_device); SDL_UnlockMutex(logdev->physical_device->lock); return 0; } @@ -1327,7 +1529,7 @@ int SDLCALL SDL_ResumeAudioDevice(SDL_AudioDeviceID devid) return SetLogicalAudioDevicePauseState(devid, 0); } -SDL_bool SDL_IsAudioDevicePaused(SDL_AudioDeviceID devid) +SDL_bool SDL_AudioDevicePaused(SDL_AudioDeviceID devid) { SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid); SDL_bool retval = SDL_FALSE; @@ -1340,6 +1542,31 @@ SDL_bool SDL_IsAudioDevicePaused(SDL_AudioDeviceID devid) return retval; } +int SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallback callback, void *userdata) +{ + SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid); + int retval = 0; + if (logdev) { + SDL_AudioDevice *device = logdev->physical_device; + if (callback && !device->postmix_buffer) { + device->postmix_buffer = (float *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size); + if (device->mix_buffer == NULL) { + retval = SDL_OutOfMemory(); + } + } + + if (retval == 0) { + logdev->postmix = callback; + logdev->postmix_userdata = userdata; + } + + UpdateAudioStreamFormatsLogical(logdev); + device->simple_copy = AudioDeviceCanUseSimpleCopy(device); + + SDL_UnlockMutex(device->lock); + } + return retval; +} int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int num_streams) { @@ -1356,8 +1583,14 @@ int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int return SDL_SetError("Audio streams are bound to device ids from SDL_OpenAudioDevice, not raw physical devices"); } else if ((logdev = ObtainLogicalAudioDevice(devid)) == NULL) { return -1; // ObtainLogicalAudioDevice set the error message. + } else if (logdev->simplified) { + SDL_UnlockMutex(logdev->physical_device->lock); + return SDL_SetError("Cannot change stream bindings on device opened with SDL_OpenAudioDeviceStream"); } + // !!! FIXME: We'll set the device's side's format below, but maybe we should refuse to bind a stream if the app's side doesn't have a format set yet. + // !!! FIXME: Actually, why do we allow there to be an invalid format, again? + // make sure start of list is sane. SDL_assert(!logdev->bound_streams || (logdev->bound_streams->prev_binding == NULL)); @@ -1388,18 +1621,8 @@ int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int if (retval == 0) { // Now that everything is verified, chain everything together. - const SDL_bool iscapture = device->iscapture; for (int i = 0; i < num_streams; i++) { SDL_AudioStream *stream = streams[i]; - SDL_AudioSpec src_spec, dst_spec; - - // set the proper end of the stream to the device's format. - SDL_GetAudioStreamFormat(stream, &src_spec, &dst_spec); - if (iscapture) { - SDL_SetAudioStreamFormat(stream, &device->spec, &dst_spec); - } else { - SDL_SetAudioStreamFormat(stream, &src_spec, &device->spec); - } stream->bound_device = logdev; stream->prev_binding = NULL; @@ -1411,8 +1634,12 @@ int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int SDL_UnlockMutex(stream->lock); } + + UpdateAudioStreamFormatsLogical(logdev); } + device->simple_copy = AudioDeviceCanUseSimpleCopy(device); + SDL_UnlockMutex(device->lock); return retval; @@ -1459,7 +1686,8 @@ void SDL_UnbindAudioStreams(SDL_AudioStream **streams, int num_streams) // everything is locked, start unbinding streams. for (int i = 0; i < num_streams; i++) { SDL_AudioStream *stream = streams[i]; - if (stream && stream->bound_device) { + // don't allow unbinding from "simplified" devices (opened with SDL_OpenAudioDeviceStream). Just ignore them. + if (stream && stream->bound_device && !stream->bound_device->simplified) { if (stream->bound_device->bound_streams == stream) { SDL_assert(stream->prev_binding == NULL); stream->bound_device->bound_streams = stream->next_binding; @@ -1482,6 +1710,7 @@ void SDL_UnbindAudioStreams(SDL_AudioStream **streams, int num_streams) stream->bound_device = NULL; SDL_UnlockMutex(stream->lock); if (logdev) { + logdev->physical_device->simple_copy = AudioDeviceCanUseSimpleCopy(logdev->physical_device); SDL_UnlockMutex(logdev->physical_device->lock); } } @@ -1493,7 +1722,7 @@ void SDL_UnbindAudioStream(SDL_AudioStream *stream) SDL_UnbindAudioStreams(&stream, 1); } -SDL_AudioDeviceID SDL_GetAudioStreamBinding(SDL_AudioStream *stream) +SDL_AudioDeviceID SDL_GetAudioStreamDevice(SDL_AudioStream *stream) { SDL_AudioDeviceID retval = 0; if (stream) { @@ -1506,45 +1735,71 @@ SDL_AudioDeviceID SDL_GetAudioStreamBinding(SDL_AudioStream *stream) return retval; } -SDL_AudioStream *SDL_CreateAndBindAudioStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec) +SDL_AudioStream *SDL_OpenAudioDeviceStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec, SDL_AudioStreamCallback callback, void *userdata) { - const SDL_bool islogical = (devid & (1<<1)) ? SDL_FALSE : SDL_TRUE; - if (!islogical) { - SDL_SetError("Audio streams are bound to device ids from SDL_OpenAudioDevice, not raw physical devices"); - return NULL; + SDL_AudioDeviceID logdevid = SDL_OpenAudioDevice(devid, spec); + if (!logdevid) { + return NULL; // error string should already be set. } + SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(logdevid); + if (logdev == NULL) { // this shouldn't happen, but just in case. + SDL_CloseAudioDevice(logdevid); + return NULL; // error string should already be set. + } + + SDL_AudioDevice *physdevice = logdev->physical_device; + SDL_assert(physdevice != NULL); + + SDL_AtomicSet(&logdev->paused, 1); // start the device paused, to match SDL2. + physdevice->simple_copy = AudioDeviceCanUseSimpleCopy(physdevice); + + SDL_UnlockMutex(physdevice->lock); // we don't need to hold the lock for any of this. + const SDL_bool iscapture = physdevice->iscapture; + SDL_AudioStream *stream = NULL; - SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid); - if (logdev) { - SDL_AudioDevice *device = logdev->physical_device; - if (device->iscapture) { - stream = SDL_CreateAudioStream(&device->spec, spec); - } else { - stream = SDL_CreateAudioStream(spec, &device->spec); - } - - if (stream) { - if (SDL_BindAudioStream(devid, stream) == -1) { - SDL_DestroyAudioStream(stream); - stream = NULL; - } - } - SDL_UnlockMutex(device->lock); + if (iscapture) { + stream = SDL_CreateAudioStream(&physdevice->spec, spec); + } else { + stream = SDL_CreateAudioStream(spec, &physdevice->spec); } - return stream; + + if (!stream) { + SDL_CloseAudioDevice(logdevid); + return NULL; // error string should already be set. + } + if (SDL_BindAudioStream(logdevid, stream) == -1) { + SDL_DestroyAudioStream(stream); + SDL_CloseAudioDevice(logdevid); + return NULL; // error string should already be set. + } + + logdev->simplified = SDL_TRUE; // forbid further binding changes on this logical device. + stream->simplified = SDL_TRUE; // so we know to close the audio device when this is destroyed. + + if (callback) { + int rc; + if (iscapture) { + rc = SDL_SetAudioStreamPutCallback(stream, callback, userdata); + } else { + rc = SDL_SetAudioStreamGetCallback(stream, callback, userdata); + } + SDL_assert(rc == 0); // should only fail if stream==NULL atm. + } + + return stream; // ready to rock. } #define NUM_FORMATS 8 static const SDL_AudioFormat format_list[NUM_FORMATS][NUM_FORMATS + 1] = { - { SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, 0 }, - { SDL_AUDIO_S8, SDL_AUDIO_U8, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, 0 }, - { SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 }, - { SDL_AUDIO_S16MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 }, - { SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 }, - { SDL_AUDIO_S32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 }, - { SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 }, - { SDL_AUDIO_F32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 }, + { SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, SDL_AUDIO_F32LE, SDL_AUDIO_F32BE, 0 }, + { SDL_AUDIO_S8, SDL_AUDIO_U8, SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, SDL_AUDIO_F32LE, SDL_AUDIO_F32BE, 0 }, + { SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, SDL_AUDIO_F32LE, SDL_AUDIO_F32BE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 }, + { SDL_AUDIO_S16BE, SDL_AUDIO_S16LE, SDL_AUDIO_S32BE, SDL_AUDIO_S32LE, SDL_AUDIO_F32BE, SDL_AUDIO_F32LE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 }, + { SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, SDL_AUDIO_F32LE, SDL_AUDIO_F32BE, SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 }, + { SDL_AUDIO_S32BE, SDL_AUDIO_S32LE, SDL_AUDIO_F32BE, SDL_AUDIO_F32LE, SDL_AUDIO_S16BE, SDL_AUDIO_S16LE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 }, + { SDL_AUDIO_F32LE, SDL_AUDIO_F32BE, SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 }, + { SDL_AUDIO_F32BE, SDL_AUDIO_F32LE, SDL_AUDIO_S32BE, SDL_AUDIO_S32LE, SDL_AUDIO_S16BE, SDL_AUDIO_S16LE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 }, }; const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format) @@ -1597,8 +1852,9 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) SDL_AudioSpec spec; SDL_bool needs_migration = SDL_FALSE; SDL_zero(spec); + for (SDL_LogicalAudioDevice *logdev = current_default_device->logical_devices; logdev != NULL; logdev = logdev->next) { - if (logdev->is_default) { + if (logdev->opened_as_default) { needs_migration = SDL_TRUE; for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) { const SDL_AudioSpec *streamspec = iscapture ? &stream->dst_spec : &stream->src_spec; @@ -1624,19 +1880,16 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) } if (needs_migration) { + const SDL_bool spec_changed = !AUDIO_SPECS_EQUAL(current_default_device->spec, new_default_device->spec); + const SDL_bool post_fmt_event = (spec_changed && SDL_EventEnabled(SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED)) ? SDL_TRUE : SDL_FALSE; SDL_LogicalAudioDevice *next = NULL; for (SDL_LogicalAudioDevice *logdev = current_default_device->logical_devices; logdev != NULL; logdev = next) { next = logdev->next; - if (!logdev->is_default) { + if (!logdev->opened_as_default) { continue; // not opened as a default, leave it on the current physical device. } - // make sure all our streams are targeting the new device's format. - for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) { - SDL_SetAudioStreamFormat(stream, iscapture ? &new_default_device->spec : NULL, iscapture ? NULL : &new_default_device->spec); - } - // now migrate the logical device. if (logdev->next) { logdev->next->prev = logdev->prev; @@ -1652,8 +1905,25 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) logdev->prev = NULL; logdev->next = new_default_device->logical_devices; new_default_device->logical_devices = logdev; + + // make sure all our streams are targeting the new device's format. + UpdateAudioStreamFormatsLogical(logdev); + + // Post an event for each logical device we moved. + if (post_fmt_event) { + SDL_Event event; + SDL_zero(event); + event.type = SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED; + event.common.timestamp = 0; + event.adevice.iscapture = iscapture ? 1 : 0; + event.adevice.which = logdev->instance_id; + SDL_PushEvent(&event); + } } + current_default_device->simple_copy = AudioDeviceCanUseSimpleCopy(current_default_device); + new_default_device->simple_copy = AudioDeviceCanUseSimpleCopy(new_default_device); + if (current_default_device->logical_devices == NULL) { // nothing left on the current physical device, close it. // !!! FIXME: we _need_ to release this lock, but doing so can cause a race condition if someone opens a device while we're closing it. SDL_UnlockMutex(current_default_device->lock); // can't hold the lock or the audio thread will deadlock while we WaitThread it. @@ -1675,31 +1945,56 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) int SDL_AudioDeviceFormatChangedAlreadyLocked(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames) { + const int orig_work_buffer_size = device->work_buffer_size; + + if (AUDIO_SPECS_EQUAL(device->spec, *newspec) && new_sample_frames == device->sample_frames) { + return 0; // we're already in that format. + } + + SDL_copyp(&device->spec, newspec); + UpdateAudioStreamFormatsPhysical(device); + SDL_bool kill_device = SDL_FALSE; - const int orig_buffer_size = device->buffer_size; - const SDL_bool iscapture = device->iscapture; + device->sample_frames = new_sample_frames; + SDL_UpdatedAudioDeviceFormat(device); + if (device->work_buffer && (device->work_buffer_size > orig_work_buffer_size)) { + SDL_aligned_free(device->work_buffer); + device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size); + if (!device->work_buffer) { + kill_device = SDL_TRUE; + } - if ((device->spec.format != newspec->format) || (device->spec.channels != newspec->channels) || (device->spec.freq != newspec->freq)) { - SDL_memcpy(&device->spec, newspec, sizeof (*newspec)); - for (SDL_LogicalAudioDevice *logdev = device->logical_devices; !kill_device && (logdev != NULL); logdev = logdev->next) { - for (SDL_AudioStream *stream = logdev->bound_streams; !kill_device && (stream != NULL); stream = stream->next_binding) { - if (SDL_SetAudioStreamFormat(stream, iscapture ? &device->spec : NULL, iscapture ? NULL : &device->spec) == -1) { - kill_device = SDL_TRUE; - } + if (device->postmix_buffer) { + SDL_aligned_free(device->postmix_buffer); + device->postmix_buffer = (float *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size); + if (!device->postmix_buffer) { + kill_device = SDL_TRUE; + } + } + + SDL_aligned_free(device->mix_buffer); + device->mix_buffer = NULL; + if (device->spec.format != SDL_AUDIO_F32) { + device->mix_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size); + if (!device->mix_buffer) { + kill_device = SDL_TRUE; } } } - if (!kill_device) { - device->sample_frames = new_sample_frames; - SDL_UpdatedAudioDeviceFormat(device); - if (device->work_buffer && (device->buffer_size > orig_buffer_size)) { - SDL_aligned_free(device->work_buffer); - device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->buffer_size); - if (!device->work_buffer) { - kill_device = SDL_TRUE; - } + // Post an event for the physical device, and each logical device on this physical device. + if (!kill_device && SDL_EventEnabled(SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED)) { + SDL_Event event; + SDL_zero(event); + event.type = SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED; + event.common.timestamp = 0; + event.adevice.iscapture = device->iscapture ? 1 : 0; + event.adevice.which = device->instance_id; + SDL_PushEvent(&event); + for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) { + event.adevice.which = logdev->instance_id; + SDL_PushEvent(&event); } } diff --git a/external/sdl/SDL/src/audio/SDL_audio_resampler_filter.h b/external/sdl/SDL/src/audio/SDL_audio_resampler_filter.h index 8e6c8871..1ea9c33d 100644 --- a/external/sdl/SDL/src/audio/SDL_audio_resampler_filter.h +++ b/external/sdl/SDL/src/audio/SDL_audio_resampler_filter.h @@ -21,1039 +21,524 @@ /* DO NOT EDIT, THIS FILE WAS GENERATED BY build-scripts/gen_audio_resampler_filter.c */ -#define RESAMPLER_ZERO_CROSSINGS 5 -#define RESAMPLER_BITS_PER_SAMPLE 16 -#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)) -#define RESAMPLER_FILTER_SIZE ((RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS) + 1) +#define RESAMPLER_ZERO_CROSSINGS 5 +#define RESAMPLER_BITS_PER_SAMPLE 16 +#define RESAMPLER_BITS_PER_ZERO_CROSSING ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1) +#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING) +#define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS) static const float ResamplerFilter[RESAMPLER_FILTER_SIZE] = { - 1.000000000f, 0.999993682f, 0.999974370f, 0.999941289f, 0.999894559f, - 0.999834180f, 0.999760151f, 0.999672413f, 0.999571025f, 0.999455988f, - 0.999327302f, 0.999184966f, 0.999028981f, 0.998859286f, 0.998676121f, - 0.998479128f, 0.998268604f, 0.998044431f, 0.997806668f, 0.997555375f, - 0.997290313f, 0.997011721f, 0.996719599f, 0.996413827f, 0.996094406f, - 0.995761573f, 0.995415151f, 0.995055199f, 0.994681656f, 0.994294584f, - 0.993894041f, 0.993480027f, 0.993052363f, 0.992611289f, 0.992156804f, - 0.991688788f, 0.991207361f, 0.990712583f, 0.990204275f, 0.989682615f, - 0.989147604f, 0.988599122f, 0.988037288f, 0.987462223f, 0.986873686f, - 0.986271918f, 0.985656738f, 0.985028386f, 0.984386861f, 0.983731806f, - 0.983063757f, 0.982382476f, 0.981687963f, 0.980980217f, 0.980259418f, - 0.979525447f, 0.978778243f, 0.978018105f, 0.977244914f, 0.976458490f, - 0.975659251f, 0.974846840f, 0.974021494f, 0.973183155f, 0.972331941f, - 0.971467674f, 0.970590651f, 0.969700634f, 0.968797863f, 0.967882276f, - 0.966953754f, 0.966012537f, 0.965058565f, 0.964091897f, 0.963112533f, - 0.962120414f, 0.961115718f, 0.960098326f, 0.959068418f, 0.958025992f, - 0.956970990f, 0.955903471f, 0.954823375f, 0.953730941f, 0.952626109f, - 0.951508820f, 0.950379193f, 0.949237227f, 0.948082924f, 0.946916521f, - 0.945737660f, 0.944546640f, 0.943343520f, 0.942128360f, 0.940900803f, - 0.939661384f, 0.938409984f, 0.937146366f, 0.935870886f, 0.934583426f, - 0.933284163f, 0.931972861f, 0.930649877f, 0.929314911f, 0.927968264f, - 0.926609993f, 0.925239921f, 0.923858225f, 0.922464967f, 0.921060085f, - 0.919643581f, 0.918215632f, 0.916776240f, 0.915325403f, 0.913863063f, - 0.912389517f, 0.910904646f, 0.909408391f, 0.907901049f, 0.906382322f, - 0.904852629f, 0.903311789f, 0.901759744f, 0.900196850f, 0.898622811f, - 0.897037864f, 0.895442069f, 0.893835366f, 0.892217875f, 0.890589595f, - 0.888950586f, 0.887300909f, 0.885640502f, 0.883969545f, 0.882288039f, - 0.880595982f, 0.878893554f, 0.877180517f, 0.875457227f, 0.873723626f, - 0.871979713f, 0.870225549f, 0.868461132f, 0.866686642f, 0.864902079f, - 0.863107383f, 0.861302733f, 0.859488070f, 0.857663572f, 0.855829120f, - 0.853984952f, 0.852130949f, 0.850267172f, 0.848393857f, 0.846510828f, - 0.844618261f, 0.842716157f, 0.840804636f, 0.838883698f, 0.836953342f, - 0.835013688f, 0.833064795f, 0.831106603f, 0.829139292f, 0.827162862f, - 0.825177312f, 0.823182821f, 0.821179330f, 0.819167018f, 0.817145705f, - 0.815115690f, 0.813076973f, 0.811029494f, 0.808973312f, 0.806908667f, - 0.804835379f, 0.802753687f, 0.800663531f, 0.798565030f, 0.796458125f, - 0.794343054f, 0.792219698f, 0.790088236f, 0.787948668f, 0.785801113f, - 0.783645451f, 0.781481922f, 0.779310465f, 0.777131259f, 0.774944246f, - 0.772749543f, 0.770547271f, 0.768337309f, 0.766119778f, 0.763894856f, - 0.761662483f, 0.759422719f, 0.757175624f, 0.754921377f, 0.752659857f, - 0.750391185f, 0.748115480f, 0.745832801f, 0.743543088f, 0.741246462f, - 0.738943100f, 0.736632884f, 0.734315932f, 0.731992364f, 0.729662120f, - 0.727325320f, 0.724982083f, 0.722632408f, 0.720276356f, 0.717914045f, - 0.715545416f, 0.713170588f, 0.710789680f, 0.708402693f, 0.706009746f, - 0.703610778f, 0.701205969f, 0.698795319f, 0.696378946f, 0.693956852f, - 0.691529095f, 0.689095736f, 0.686656833f, 0.684212506f, 0.681762815f, - 0.679307759f, 0.676847458f, 0.674381971f, 0.671911240f, 0.669435501f, - 0.666954637f, 0.664468944f, 0.661978245f, 0.659482718f, 0.656982541f, - 0.654477477f, 0.651967824f, 0.649453580f, 0.646934807f, 0.644411504f, - 0.641883910f, 0.639351964f, 0.636815608f, 0.634275198f, 0.631730616f, - 0.629181862f, 0.626629114f, 0.624072373f, 0.621511757f, 0.618947387f, - 0.616379082f, 0.613807201f, 0.611231565f, 0.608652413f, 0.606069744f, - 0.603483617f, 0.600894034f, 0.598301172f, 0.595705032f, 0.593105674f, - 0.590503156f, 0.587897599f, 0.585289061f, 0.582677484f, 0.580063045f, - 0.577445805f, 0.574825764f, 0.572203040f, 0.569577694f, 0.566949785f, - 0.564319372f, 0.561686456f, 0.559051216f, 0.556413651f, 0.553773820f, - 0.551131785f, 0.548487663f, 0.545841396f, 0.543193221f, 0.540543079f, - 0.537890971f, 0.535237134f, 0.532581568f, 0.529924273f, 0.527265370f, - 0.524604917f, 0.521942914f, 0.519279540f, 0.516614795f, 0.513948679f, - 0.511281490f, 0.508612931f, 0.505943298f, 0.503272593f, 0.500600994f, - 0.497928351f, 0.495254934f, 0.492580622f, 0.489905566f, 0.487229884f, - 0.484553576f, 0.481876701f, 0.479199320f, 0.476521552f, 0.473843396f, - 0.471164882f, 0.468486160f, 0.465807229f, 0.463128209f, 0.460449100f, - 0.457770079f, 0.455091029f, 0.452412128f, 0.449733406f, 0.447054923f, - 0.444376737f, 0.441698909f, 0.439021617f, 0.436344713f, 0.433668375f, - 0.430992693f, 0.428317666f, 0.425643355f, 0.422969848f, 0.420297176f, - 0.417625397f, 0.414954633f, 0.412284881f, 0.409616232f, 0.406948745f, - 0.404282451f, 0.401617438f, 0.398953736f, 0.396291435f, 0.393630594f, - 0.390971363f, 0.388313591f, 0.385657459f, 0.383003026f, 0.380350292f, - 0.377699375f, 0.375050306f, 0.372403204f, 0.369758040f, 0.367114902f, - 0.364473879f, 0.361834973f, 0.359198302f, 0.356563985f, 0.353931844f, - 0.351302117f, 0.348674864f, 0.346050024f, 0.343427807f, 0.340808123f, - 0.338191152f, 0.335576862f, 0.332965344f, 0.330356658f, 0.327750862f, - 0.325147986f, 0.322548091f, 0.319951355f, 0.317357630f, 0.314767033f, - 0.312179655f, 0.309595555f, 0.307014734f, 0.304437339f, 0.301863313f, - 0.299292803f, 0.296725839f, 0.294162393f, 0.291602641f, 0.289046586f, - 0.286494225f, 0.283945769f, 0.281401068f, 0.278860301f, 0.276323408f, - 0.273790598f, 0.271261781f, 0.268737078f, 0.266216546f, 0.263700217f, - 0.261188149f, 0.258680373f, 0.256176978f, 0.253677934f, 0.251183391f, - 0.248693451f, 0.246207923f, 0.243727028f, 0.241250798f, 0.238779247f, - 0.236312449f, 0.233850464f, 0.231393293f, 0.228941023f, 0.226493701f, - 0.224051371f, 0.221614078f, 0.219181836f, 0.216754735f, 0.214332923f, - 0.211916193f, 0.209504753f, 0.207098618f, 0.204697832f, 0.202302471f, - 0.199912533f, 0.197528124f, 0.195149198f, 0.192775860f, 0.190408155f, - 0.188046113f, 0.185689807f, 0.183339253f, 0.180994540f, 0.178655609f, - 0.176322535f, 0.173995405f, 0.171674222f, 0.169359058f, 0.167049929f, - 0.164746925f, 0.162450001f, 0.160159275f, 0.157874763f, 0.155596510f, - 0.153324530f, 0.151058972f, 0.148799688f, 0.146546826f, 0.144300416f, - 0.142060474f, 0.139827073f, 0.137600228f, 0.135380000f, 0.133166388f, - 0.130959451f, 0.128759250f, 0.126565769f, 0.124379098f, 0.122199245f, - 0.120026320f, 0.117860198f, 0.115701027f, 0.113548823f, 0.111403592f, - 0.109265402f, 0.107134290f, 0.105010264f, 0.102893390f, 0.100783676f, - 0.098681159f, 0.096585885f, 0.094497882f, 0.092417173f, 0.090343870f, - 0.088277847f, 0.086219221f, 0.084168032f, 0.082124293f, 0.080088042f, - 0.078059308f, 0.076038122f, 0.074024513f, 0.072018512f, 0.070020139f, - 0.068029441f, 0.066046432f, 0.064071149f, 0.062103685f, 0.060143925f, - 0.058191977f, 0.056247860f, 0.054311592f, 0.052383222f, 0.050462760f, - 0.048550237f, 0.046645675f, 0.044749107f, 0.042860553f, 0.040980037f, - 0.039107583f, 0.037243221f, 0.035387039f, 0.033538923f, 0.031698968f, - 0.029867193f, 0.028043624f, 0.026228283f, 0.024421191f, 0.022622371f, - 0.020831848f, 0.019049639f, 0.017275762f, 0.015510243f, 0.013753103f, - 0.012004361f, 0.010264101f, 0.008532210f, 0.006808777f, 0.005093819f, - 0.003387353f, 0.001689399f, -0.000000024f, -0.001680900f, -0.003353210f, - -0.005016938f, -0.006672067f, -0.008318579f, -0.009956459f, -0.011585629f, - -0.013206195f, -0.014818084f, -0.016421281f, -0.018015765f, -0.019601526f, - -0.021178551f, -0.022746822f, -0.024306327f, -0.025857056f, -0.027398987f, - -0.028932119f, -0.030456433f, -0.031971917f, -0.033478502f, -0.034976289f, - -0.036465216f, -0.037945267f, -0.039416436f, -0.040878706f, -0.042332072f, - -0.043776520f, -0.045212042f, -0.046638630f, -0.048056271f, -0.049464963f, - -0.050864697f, -0.052255459f, -0.053637192f, -0.055009995f, -0.056373812f, - -0.057728622f, -0.059074435f, -0.060411237f, -0.061739020f, -0.063057788f, - -0.064367518f, -0.065668218f, -0.066959888f, -0.068242513f, -0.069516085f, - -0.070780613f, -0.072036028f, -0.073282443f, -0.074519798f, -0.075748093f, - -0.076967306f, -0.078177467f, -0.079378553f, -0.080570564f, -0.081753515f, - -0.082927369f, -0.084092163f, -0.085247882f, -0.086394526f, -0.087532081f, - -0.088660523f, -0.089779936f, -0.090890266f, -0.091991536f, -0.093083732f, - -0.094166860f, -0.095240898f, -0.096305899f, -0.097361818f, -0.098408684f, - -0.099446490f, -0.100475237f, -0.101494931f, -0.102505587f, -0.103507154f, - -0.104499720f, -0.105483264f, -0.106457770f, -0.107423261f, -0.108379729f, - -0.109327182f, -0.110265635f, -0.111195073f, -0.112115517f, -0.113026999f, - -0.113929473f, -0.114823014f, -0.115707509f, -0.116583094f, -0.117449738f, - -0.118307441f, -0.119156219f, -0.119996056f, -0.120826989f, -0.121649019f, - -0.122462146f, -0.123266406f, -0.124061778f, -0.124848284f, -0.125625938f, - -0.126394749f, -0.127154693f, -0.127905846f, -0.128648207f, -0.129381746f, - -0.130106509f, -0.130822510f, -0.131529734f, -0.132228225f, -0.132917970f, - -0.133599013f, -0.134271339f, -0.134934962f, -0.135589913f, -0.136236191f, - -0.136873797f, -0.137502789f, -0.138123170f, -0.138734937f, -0.139338136f, - -0.139932722f, -0.140518770f, -0.141096294f, -0.141665265f, -0.142225727f, - -0.142777696f, -0.143321201f, -0.143856242f, -0.144382849f, -0.144900993f, - -0.145410761f, -0.145912141f, -0.146405146f, -0.146889821f, -0.147366136f, - -0.147834152f, -0.148293883f, -0.148745313f, -0.149188504f, -0.149623469f, - -0.150050193f, -0.150468737f, -0.150879115f, -0.151281342f, -0.151675433f, - -0.152061403f, -0.152439296f, -0.152809098f, -0.153170869f, -0.153524607f, - -0.153870359f, -0.154208109f, -0.154537901f, -0.154859766f, -0.155173704f, - -0.155479759f, -0.155777946f, -0.156068295f, -0.156350806f, -0.156625539f, - -0.156892478f, -0.157151699f, -0.157403171f, -0.157646939f, -0.157883018f, - -0.158111468f, -0.158332288f, -0.158545509f, -0.158751160f, -0.158949256f, - -0.159139827f, -0.159322888f, -0.159498483f, -0.159666643f, -0.159827381f, - -0.159980699f, -0.160126686f, -0.160265297f, -0.160396621f, -0.160520658f, - -0.160637438f, -0.160746962f, -0.160849288f, -0.160944447f, -0.161032468f, - -0.161113337f, -0.161187142f, -0.161253884f, -0.161313564f, -0.161366254f, - -0.161411941f, -0.161450699f, -0.161482543f, -0.161507472f, -0.161525562f, - -0.161536828f, -0.161541268f, -0.161538944f, -0.161529869f, -0.161514089f, - -0.161491618f, -0.161462501f, -0.161426738f, -0.161384419f, -0.161335513f, - -0.161280081f, -0.161218151f, -0.161149755f, -0.161074907f, -0.160993651f, - -0.160906032f, -0.160812065f, -0.160711765f, -0.160605207f, -0.160492390f, - -0.160373345f, -0.160248131f, -0.160116762f, -0.159979254f, -0.159835666f, - -0.159686014f, -0.159530357f, -0.159368694f, -0.159201056f, -0.159027517f, - -0.158848062f, -0.158662766f, -0.158471599f, -0.158274680f, -0.158071980f, - -0.157863557f, -0.157649443f, -0.157429650f, -0.157204241f, -0.156973228f, - -0.156736642f, -0.156494558f, -0.156246960f, -0.155993909f, -0.155735433f, - -0.155471563f, -0.155202329f, -0.154927775f, -0.154647917f, -0.154362813f, - -0.154072478f, -0.153776988f, -0.153476328f, -0.153170541f, -0.152859688f, - -0.152543753f, -0.152222827f, -0.151896924f, -0.151566073f, -0.151230305f, - -0.150889665f, -0.150544196f, -0.150193915f, -0.149838865f, -0.149479061f, - -0.149114594f, -0.148745432f, -0.148371637f, -0.147993281f, -0.147610337f, - -0.147222877f, -0.146830946f, -0.146434546f, -0.146033719f, -0.145628527f, - -0.145218968f, -0.144805118f, -0.144386992f, -0.143964618f, -0.143538073f, - -0.143107325f, -0.142672464f, -0.142233476f, -0.141790435f, -0.141343385f, - -0.140892327f, -0.140437320f, -0.139978394f, -0.139515579f, -0.139048934f, - -0.138578460f, -0.138104215f, -0.137626216f, -0.137144551f, -0.136659175f, - -0.136170194f, -0.135677606f, -0.135181442f, -0.134681761f, -0.134178594f, - -0.133671984f, -0.133161947f, -0.132648528f, -0.132131740f, -0.131611660f, - -0.131088302f, -0.130561695f, -0.130031943f, -0.129498973f, -0.128962860f, - -0.128423676f, -0.127881393f, -0.127336115f, -0.126787826f, -0.126236603f, - -0.125682443f, -0.125125393f, -0.124565504f, -0.124002807f, -0.123437323f, - -0.122869089f, -0.122298159f, -0.121724561f, -0.121148311f, -0.120569460f, - -0.119988061f, -0.119404130f, -0.118817687f, -0.118228793f, -0.117637470f, - -0.117043748f, -0.116447672f, -0.115849286f, -0.115248613f, -0.114645667f, - -0.114040568f, -0.113433234f, -0.112823755f, -0.112212166f, -0.111598492f, - -0.110982776f, -0.110365056f, -0.109745361f, -0.109123722f, -0.108500175f, - -0.107874751f, -0.107247494f, -0.106618427f, -0.105987601f, -0.105355024f, - -0.104720749f, -0.104084812f, -0.103447236f, -0.102808066f, -0.102167316f, - -0.101525046f, -0.100881256f, -0.100236014f, -0.099589340f, -0.098941252f, - -0.098291807f, -0.097641021f, -0.096988983f, -0.096335620f, -0.095681041f, - -0.095025249f, -0.094368286f, -0.093710184f, -0.093050972f, -0.092390701f, - -0.091729380f, -0.091067046f, -0.090403758f, -0.089739501f, -0.089074343f, - -0.088408306f, -0.087741412f, -0.087073699f, -0.086405218f, -0.085735977f, - -0.085065998f, -0.084395349f, -0.083724037f, -0.083052084f, -0.082379535f, - -0.081706434f, -0.081032783f, -0.080358639f, -0.079684012f, -0.079008937f, - -0.078333504f, -0.077657640f, -0.076981425f, -0.076304883f, -0.075628042f, - -0.074950956f, -0.074273616f, -0.073596083f, -0.072918370f, -0.072240517f, - -0.071562558f, -0.070884496f, -0.070206381f, -0.069528244f, -0.068850100f, - -0.068171993f, -0.067493953f, -0.066815972f, -0.066138126f, -0.065460421f, - -0.064782880f, -0.064105548f, -0.063428439f, -0.062751584f, -0.062075011f, - -0.061398748f, -0.060722828f, -0.060047265f, -0.059372153f, -0.058697399f, - -0.058023106f, -0.057349272f, -0.056675948f, -0.056003150f, -0.055330887f, - -0.054659221f, -0.053988155f, -0.053317714f, -0.052647941f, -0.051978838f, - -0.051310450f, -0.050642796f, -0.049975898f, -0.049309790f, -0.048644483f, - -0.047980014f, -0.047316402f, -0.046653673f, -0.045991853f, -0.045330971f, - -0.044671040f, -0.044012092f, -0.043354150f, -0.042697236f, -0.042041373f, - -0.041386627f, -0.040732939f, -0.040080376f, -0.039428953f, -0.038778704f, - -0.038129643f, -0.037481792f, -0.036835186f, -0.036189832f, -0.035545766f, - -0.034902997f, -0.034261558f, -0.033621464f, -0.032982741f, -0.032345407f, - -0.031709481f, -0.031074993f, -0.030441960f, -0.029810399f, -0.029180335f, - -0.028551787f, -0.027924776f, -0.027299322f, -0.026675448f, -0.026053172f, - -0.025432510f, -0.024813488f, -0.024196124f, -0.023580479f, -0.022966485f, - -0.022354206f, -0.021743659f, -0.021134868f, -0.020527845f, -0.019922616f, - -0.019319192f, -0.018717598f, -0.018117845f, -0.017519956f, -0.016923945f, - -0.016329836f, -0.015737642f, -0.015147380f, -0.014559067f, -0.013972721f, - -0.013388360f, -0.012805998f, -0.012225654f, -0.011647343f, -0.011071082f, - -0.010496887f, -0.009924773f, -0.009354758f, -0.008786854f, -0.008221081f, - -0.007657450f, -0.007096022f, -0.006536725f, -0.005979617f, -0.005424712f, - -0.004872025f, -0.004321571f, -0.003773364f, -0.003227417f, -0.002683746f, - -0.002142363f, -0.001603282f, -0.001066516f, -0.000532080f, 0.000000015f, - 0.000529755f, 0.001057127f, 0.001582118f, 0.002104717f, 0.002624909f, - 0.003142685f, 0.003658031f, 0.004170935f, 0.004681387f, 0.005189373f, - 0.005694883f, 0.006197905f, 0.006698429f, 0.007196404f, 0.007691898f, - 0.008184860f, 0.008675281f, 0.009163151f, 0.009648458f, 0.010131192f, - 0.010611345f, 0.011088905f, 0.011563865f, 0.012036213f, 0.012505942f, - 0.012973041f, 0.013437501f, 0.013899315f, 0.014358475f, 0.014814967f, - 0.015268789f, 0.015719930f, 0.016168380f, 0.016614137f, 0.017057184f, - 0.017497523f, 0.017935142f, 0.018370032f, 0.018802188f, 0.019231608f, - 0.019658273f, 0.020082152f, 0.020503307f, 0.020921690f, 0.021337299f, - 0.021750130f, 0.022160176f, 0.022567427f, 0.022971880f, 0.023373535f, - 0.023772376f, 0.024168408f, 0.024561619f, 0.024952007f, 0.025339566f, - 0.025724288f, 0.026106175f, 0.026485221f, 0.026861422f, 0.027234772f, - 0.027605265f, 0.027972901f, 0.028337676f, 0.028699588f, 0.029058624f, - 0.029414795f, 0.029768089f, 0.030118505f, 0.030466037f, 0.030810660f, - 0.031152423f, 0.031491302f, 0.031827286f, 0.032160383f, 0.032490581f, - 0.032817882f, 0.033142287f, 0.033463787f, 0.033782389f, 0.034098089f, - 0.034410883f, 0.034720775f, 0.035027757f, 0.035331838f, 0.035633009f, - 0.035931274f, 0.036226626f, 0.036519073f, 0.036808614f, 0.037095241f, - 0.037378959f, 0.037659772f, 0.037937686f, 0.038212679f, 0.038484775f, - 0.038753960f, 0.039020218f, 0.039283592f, 0.039544068f, 0.039801639f, - 0.040056318f, 0.040308096f, 0.040556971f, 0.040802956f, 0.041046046f, - 0.041286245f, 0.041523557f, 0.041757982f, 0.041989524f, 0.042218179f, - 0.042443957f, 0.042666860f, 0.042886890f, 0.043104045f, 0.043318339f, - 0.043529764f, 0.043738328f, 0.043944035f, 0.044146888f, 0.044346895f, - 0.044544052f, 0.044738363f, 0.044929843f, 0.045118481f, 0.045304272f, - 0.045487255f, 0.045667417f, 0.045844764f, 0.046019293f, 0.046191018f, - 0.046359941f, 0.046526067f, 0.046689399f, 0.046849940f, 0.047007702f, - 0.047162689f, 0.047314901f, 0.047464348f, 0.047611035f, 0.047754966f, - 0.047896147f, 0.048034586f, 0.048170291f, 0.048303265f, 0.048433512f, - 0.048561048f, 0.048685864f, 0.048807982f, 0.048927400f, 0.049044125f, - 0.049158167f, 0.049269531f, 0.049378205f, 0.049484238f, 0.049587611f, - 0.049688339f, 0.049786422f, 0.049881872f, 0.049974691f, 0.050064899f, - 0.050152492f, 0.050237484f, 0.050319877f, 0.050399683f, 0.050476916f, - 0.050551571f, 0.050623666f, 0.050693203f, 0.050760195f, 0.050824653f, - 0.050886583f, 0.050945986f, 0.051002879f, 0.051057268f, 0.051109165f, - 0.051158577f, 0.051205512f, 0.051249981f, 0.051291991f, 0.051331542f, - 0.051368665f, 0.051403359f, 0.051435627f, 0.051465489f, 0.051492948f, - 0.051518012f, 0.051540703f, 0.051561009f, 0.051578961f, 0.051594563f, - 0.051607810f, 0.051618744f, 0.051627338f, 0.051633626f, 0.051637612f, - 0.051639307f, 0.051638719f, 0.051635865f, 0.051630747f, 0.051623378f, - 0.051613763f, 0.051601931f, 0.051587880f, 0.051571615f, 0.051553156f, - 0.051532514f, 0.051509693f, 0.051484708f, 0.051457576f, 0.051428299f, - 0.051396891f, 0.051363368f, 0.051327739f, 0.051290002f, 0.051250193f, - 0.051208302f, 0.051164351f, 0.051118344f, 0.051070303f, 0.051020239f, - 0.050968144f, 0.050914053f, 0.050857969f, 0.050799899f, 0.050739862f, - 0.050677869f, 0.050613929f, 0.050548054f, 0.050480254f, 0.050410546f, - 0.050338943f, 0.050265454f, 0.050190084f, 0.050112855f, 0.050033774f, - 0.049952857f, 0.049870111f, 0.049785554f, 0.049699202f, 0.049611051f, - 0.049521122f, 0.049429435f, 0.049335998f, 0.049240820f, 0.049143907f, - 0.049045283f, 0.048944961f, 0.048842940f, 0.048739251f, 0.048633892f, - 0.048526883f, 0.048418235f, 0.048307955f, 0.048196062f, 0.048082568f, - 0.047967490f, 0.047850832f, 0.047732603f, 0.047612831f, 0.047491513f, - 0.047368675f, 0.047244325f, 0.047118478f, 0.046991132f, 0.046862319f, - 0.046732042f, 0.046600312f, 0.046467155f, 0.046332560f, 0.046196572f, - 0.046059173f, 0.045920394f, 0.045780238f, 0.045638733f, 0.045495875f, - 0.045351684f, 0.045206167f, 0.045059349f, 0.044911236f, 0.044761848f, - 0.044611182f, 0.044459261f, 0.044306096f, 0.044151701f, 0.043996092f, - 0.043839272f, 0.043681268f, 0.043522079f, 0.043361723f, 0.043200217f, - 0.043037571f, 0.042873796f, 0.042708915f, 0.042542927f, 0.042375844f, - 0.042207699f, 0.042038482f, 0.041868217f, 0.041696910f, 0.041524585f, - 0.041351244f, 0.041176908f, 0.041001581f, 0.040825289f, 0.040648032f, - 0.040469825f, 0.040290687f, 0.040110629f, 0.039929654f, 0.039747790f, - 0.039565038f, 0.039381415f, 0.039196935f, 0.039011609f, 0.038825445f, - 0.038638465f, 0.038450673f, 0.038262092f, 0.038072724f, 0.037882581f, - 0.037691690f, 0.037500042f, 0.037307668f, 0.037114572f, 0.036920771f, - 0.036726266f, 0.036531083f, 0.036335230f, 0.036138717f, 0.035941560f, - 0.035743766f, 0.035545345f, 0.035346344f, 0.035146721f, 0.034946512f, - 0.034745730f, 0.034544386f, 0.034342494f, 0.034140065f, 0.033937111f, - 0.033733644f, 0.033529676f, 0.033325221f, 0.033120286f, 0.032914892f, - 0.032709036f, 0.032502741f, 0.032296017f, 0.032088876f, 0.031881329f, - 0.031673383f, 0.031465057f, 0.031256359f, 0.031047301f, 0.030837893f, - 0.030628148f, 0.030418083f, 0.030207697f, 0.029997014f, 0.029786035f, - 0.029574780f, 0.029363252f, 0.029151469f, 0.028939439f, 0.028727176f, - 0.028514685f, 0.028301982f, 0.028089074f, 0.027875982f, 0.027662706f, - 0.027449260f, 0.027235655f, 0.027021904f, 0.026808018f, 0.026594002f, - 0.026379872f, 0.026165638f, 0.025951311f, 0.025736896f, 0.025522409f, - 0.025307864f, 0.025093259f, 0.024878617f, 0.024663944f, 0.024449248f, - 0.024234539f, 0.024019832f, 0.023805158f, 0.023590479f, 0.023375830f, - 0.023161218f, 0.022946658f, 0.022732155f, 0.022517720f, 0.022303363f, - 0.022089096f, 0.021874927f, 0.021660868f, 0.021446921f, 0.021233102f, - 0.021019425f, 0.020805888f, 0.020592507f, 0.020379292f, 0.020166250f, - 0.019953389f, 0.019740723f, 0.019528257f, 0.019316001f, 0.019103965f, - 0.018892156f, 0.018680587f, 0.018469261f, 0.018258194f, 0.018047387f, - 0.017836852f, 0.017626595f, 0.017416634f, 0.017206967f, 0.016997607f, - 0.016788563f, 0.016579840f, 0.016371451f, 0.016163401f, 0.015955698f, - 0.015748354f, 0.015541371f, 0.015334761f, 0.015128531f, 0.014922690f, - 0.014717245f, 0.014512201f, 0.014307571f, 0.014103360f, 0.013899575f, - 0.013696224f, 0.013493315f, 0.013290856f, 0.013088853f, 0.012887316f, - 0.012686247f, 0.012485661f, 0.012285583f, 0.012085971f, 0.011886863f, - 0.011688258f, 0.011490168f, 0.011292599f, 0.011095556f, 0.010899050f, - 0.010703083f, 0.010507663f, 0.010312798f, 0.010118493f, 0.009924755f, - 0.009731592f, 0.009539006f, 0.009347008f, 0.009155600f, 0.008964794f, - 0.008774590f, 0.008584999f, 0.008396022f, 0.008207668f, 0.008019943f, - 0.007832851f, 0.007646400f, 0.007460595f, 0.007275441f, 0.007090943f, - 0.006907108f, 0.006723941f, 0.006541446f, 0.006359630f, 0.006178498f, - 0.005998055f, 0.005818305f, 0.005639255f, 0.005460909f, 0.005283272f, - 0.005106349f, 0.004930146f, 0.004754666f, 0.004579914f, 0.004405896f, - 0.004232615f, 0.004060077f, 0.003888285f, 0.003717245f, 0.003546960f, - 0.003377435f, 0.003208674f, 0.003040682f, 0.002873462f, 0.002707019f, - 0.002541356f, 0.002376478f, 0.002212389f, 0.002049116f, 0.001886615f, - 0.001724914f, 0.001564016f, 0.001403926f, 0.001244646f, 0.001086180f, - 0.000928532f, 0.000771705f, 0.000615702f, 0.000460527f, 0.000306183f, - 0.000152673f, -0.000000001f, -0.000151834f, -0.000302824f, -0.000452968f, - -0.000602263f, -0.000750707f, -0.000898296f, -0.001045028f, -0.001190900f, - -0.001335910f, -0.001480055f, -0.001623332f, -0.001765741f, -0.001907276f, - -0.002047938f, -0.002187723f, -0.002326628f, -0.002464653f, -0.002601795f, - -0.002738052f, -0.002873422f, -0.003007903f, -0.003141493f, -0.003274190f, - -0.003405993f, -0.003536900f, -0.003666909f, -0.003796018f, -0.003924227f, - -0.004051533f, -0.004177936f, -0.004303433f, -0.004428024f, -0.004551707f, - -0.004674482f, -0.004796346f, -0.004917298f, -0.005037338f, -0.005156465f, - -0.005274678f, -0.005391975f, -0.005508356f, -0.005623801f, -0.005738347f, - -0.005851975f, -0.005964684f, -0.006076474f, -0.006187343f, -0.006297291f, - -0.006406318f, -0.006514424f, -0.006621608f, -0.006727869f, -0.006833209f, - -0.006937624f, -0.007041118f, -0.007143687f, -0.007245334f, -0.007346058f, - -0.007445859f, -0.007544736f, -0.007642691f, -0.007739722f, -0.007835831f, - -0.007931018f, -0.008025283f, -0.008118626f, -0.008211047f, -0.008302549f, - -0.008393129f, -0.008482790f, -0.008571531f, -0.008659353f, -0.008746257f, - -0.008832245f, -0.008917316f, -0.009001471f, -0.009084711f, -0.009167036f, - -0.009248449f, -0.009328948f, -0.009408538f, -0.009487216f, -0.009564986f, - -0.009641849f, -0.009717803f, -0.009792852f, -0.009866998f, -0.009940239f, - -0.010012579f, -0.010084019f, -0.010154560f, -0.010224204f, -0.010292951f, - -0.010360803f, -0.010427763f, -0.010493831f, -0.010558996f, -0.010623286f, - -0.010686691f, -0.010749210f, -0.010810846f, -0.010871602f, -0.010931478f, - -0.010990476f, -0.011048601f, -0.011105850f, -0.011162230f, -0.011217739f, - -0.011272381f, -0.011326157f, -0.011379071f, -0.011431124f, -0.011482318f, - -0.011532655f, -0.011582140f, -0.011630770f, -0.011678550f, -0.011725485f, - -0.011771576f, -0.011816822f, -0.011861229f, -0.011904799f, -0.011947533f, - -0.011989436f, -0.012030509f, -0.012070752f, -0.012110173f, -0.012148771f, - -0.012186551f, -0.012223513f, -0.012259661f, -0.012295000f, -0.012329529f, - -0.012363252f, -0.012396174f, -0.012428297f, -0.012459621f, -0.012490152f, - -0.012519893f, -0.012548844f, -0.012577013f, -0.012604399f, -0.012631006f, - -0.012656839f, -0.012681897f, -0.012706185f, -0.012729709f, -0.012752469f, - -0.012774469f, -0.012795713f, -0.012816204f, -0.012835944f, -0.012854930f, - -0.012873179f, -0.012890689f, -0.012907463f, -0.012923501f, -0.012938812f, - -0.012953395f, -0.012967254f, -0.012980393f, -0.012992817f, -0.013004529f, - -0.013015532f, -0.013025828f, -0.013035421f, -0.013044316f, -0.013052518f, - -0.013060026f, -0.013066847f, -0.013072984f, -0.013078442f, -0.013083221f, - -0.013087329f, -0.013090767f, -0.013093537f, -0.013095647f, -0.013097100f, - -0.013097897f, -0.013098043f, -0.013097542f, -0.013096399f, -0.013094617f, - -0.013092197f, -0.013089147f, -0.013085471f, -0.013081170f, -0.013076247f, - -0.013070711f, -0.013064560f, -0.013057803f, -0.013050442f, -0.013042479f, - -0.013033919f, -0.013024768f, -0.013015027f, -0.013004702f, -0.012993797f, - -0.012982314f, -0.012970260f, -0.012957636f, -0.012944449f, -0.012930701f, - -0.012916395f, -0.012901539f, -0.012886133f, -0.012870182f, -0.012853689f, - -0.012836664f, -0.012819104f, -0.012801019f, -0.012782406f, -0.012763276f, - -0.012743629f, -0.012723471f, -0.012702805f, -0.012681637f, -0.012659966f, - -0.012637801f, -0.012615148f, -0.012592005f, -0.012568381f, -0.012544277f, - -0.012519698f, -0.012494650f, -0.012469133f, -0.012443157f, -0.012416720f, - -0.012389832f, -0.012362492f, -0.012334707f, -0.012306482f, -0.012277817f, - -0.012248720f, -0.012219194f, -0.012189244f, -0.012158873f, -0.012128084f, - -0.012096884f, -0.012065274f, -0.012033261f, -0.012000849f, -0.011968041f, - -0.011934839f, -0.011901250f, -0.011867279f, -0.011832927f, -0.011798202f, - -0.011763104f, -0.011727640f, -0.011691812f, -0.011655626f, -0.011619086f, - -0.011582195f, -0.011544957f, -0.011507377f, -0.011469459f, -0.011431207f, - -0.011392625f, -0.011353718f, -0.011314487f, -0.011274938f, -0.011235079f, - -0.011194907f, -0.011154431f, -0.011113651f, -0.011072575f, -0.011031206f, - -0.010989547f, -0.010947601f, -0.010905375f, -0.010862871f, -0.010820094f, - -0.010777046f, -0.010733733f, -0.010690158f, -0.010646327f, -0.010602240f, - -0.010557905f, -0.010513324f, -0.010468500f, -0.010423439f, -0.010378144f, - -0.010332618f, -0.010286866f, -0.010240891f, -0.010194698f, -0.010148291f, - -0.010101672f, -0.010054846f, -0.010007816f, -0.009960588f, -0.009913164f, - -0.009865548f, -0.009817744f, -0.009769755f, -0.009721586f, -0.009673241f, - -0.009624721f, -0.009576033f, -0.009527179f, -0.009478163f, -0.009428989f, - -0.009379660f, -0.009330180f, -0.009280552f, -0.009230781f, -0.009180869f, - -0.009130822f, -0.009080641f, -0.009030331f, -0.008979894f, -0.008929336f, - -0.008878659f, -0.008827867f, -0.008776963f, -0.008725950f, -0.008674833f, - -0.008623619f, -0.008572303f, -0.008520893f, -0.008469390f, -0.008417801f, - -0.008366127f, -0.008314372f, -0.008262540f, -0.008210634f, -0.008158657f, - -0.008106613f, -0.008054503f, -0.008002332f, -0.007950104f, -0.007897821f, - -0.007845488f, -0.007793106f, -0.007740679f, -0.007688211f, -0.007635703f, - -0.007583159f, -0.007530585f, -0.007477981f, -0.007425350f, -0.007372697f, - -0.007320023f, -0.007267332f, -0.007214627f, -0.007161912f, -0.007109188f, - -0.007056459f, -0.007003729f, -0.006951000f, -0.006898273f, -0.006845553f, - -0.006792843f, -0.006740147f, -0.006687465f, -0.006634800f, -0.006582157f, - -0.006529537f, -0.006476944f, -0.006424380f, -0.006371849f, -0.006319351f, - -0.006266891f, -0.006214471f, -0.006162094f, -0.006109761f, -0.006057477f, - -0.006005243f, -0.005953063f, -0.005900938f, -0.005848871f, -0.005796865f, - -0.005744927f, -0.005693051f, -0.005641241f, -0.005589503f, -0.005537837f, - -0.005486248f, -0.005434736f, -0.005383304f, -0.005331955f, -0.005280690f, - -0.005229514f, -0.005178426f, -0.005127430f, -0.005076529f, -0.005025723f, - -0.004975016f, -0.004924410f, -0.004873907f, -0.004823509f, -0.004773218f, - -0.004723036f, -0.004672966f, -0.004623009f, -0.004573168f, -0.004523444f, - -0.004473840f, -0.004424357f, -0.004374999f, -0.004325765f, -0.004276660f, - -0.004227683f, -0.004178838f, -0.004130126f, -0.004081549f, -0.004033110f, - -0.003984808f, -0.003936647f, -0.003888629f, -0.003840754f, -0.003793026f, - -0.003745445f, -0.003698014f, -0.003650733f, -0.003603605f, -0.003556631f, - -0.003509813f, -0.003463153f, -0.003416652f, -0.003370312f, -0.003324133f, - -0.003278119f, -0.003232269f, -0.003186587f, -0.003141073f, -0.003095729f, - -0.003050560f, -0.003005560f, -0.002960733f, -0.002916081f, -0.002871606f, - -0.002827310f, -0.002783192f, -0.002739255f, -0.002695500f, -0.002651928f, - -0.002608541f, -0.002565339f, -0.002522324f, -0.002479498f, -0.002436860f, - -0.002394413f, -0.002352157f, -0.002310094f, -0.002268225f, -0.002226550f, - -0.002185071f, -0.002143790f, -0.002102705f, -0.002061821f, -0.002021135f, - -0.001980651f, -0.001940368f, -0.001900289f, -0.001860412f, -0.001820740f, - -0.001781274f, -0.001742014f, -0.001702961f, -0.001664116f, -0.001625479f, - -0.001587052f, -0.001548836f, -0.001510830f, -0.001473036f, -0.001435455f, - -0.001398086f, -0.001360932f, -0.001323992f, -0.001287267f, -0.001250757f, - -0.001214464f, -0.001178389f, -0.001142530f, -0.001106890f, -0.001071468f, - -0.001036266f, -0.001001283f, -0.000966520f, -0.000931978f, -0.000897657f, - -0.000863557f, -0.000829685f, -0.000796029f, -0.000762597f, -0.000729387f, - -0.000696401f, -0.000663639f, -0.000631101f, -0.000598788f, -0.000566699f, - -0.000534836f, -0.000503197f, -0.000471784f, -0.000440597f, -0.000409636f, - -0.000378901f, -0.000348393f, -0.000318111f, -0.000288056f, -0.000258228f, - -0.000228626f, -0.000199252f, -0.000170105f, -0.000141186f, -0.000112493f, - -0.000084028f, -0.000055791f, -0.000027781f, 0.000000002f, 0.000027557f, - 0.000054884f, 0.000081984f, 0.000108857f, 0.000135502f, 0.000161920f, - 0.000188111f, 0.000214075f, 0.000239812f, 0.000265322f, 0.000290606f, - 0.000315663f, 0.000340493f, 0.000365097f, 0.000389476f, 0.000413628f, - 0.000437555f, 0.000461256f, 0.000484733f, 0.000507984f, 0.000531011f, - 0.000553814f, 0.000576393f, 0.000598748f, 0.000620879f, 0.000642788f, - 0.000664473f, 0.000685933f, 0.000707174f, 0.000728194f, 0.000748993f, - 0.000769570f, 0.000789927f, 0.000810064f, 0.000829982f, 0.000849680f, - 0.000869160f, 0.000888421f, 0.000907465f, 0.000926291f, 0.000944901f, - 0.000963294f, 0.000981472f, 0.000999434f, 0.001017181f, 0.001034715f, - 0.001052034f, 0.001069141f, 0.001086035f, 0.001102717f, 0.001119188f, - 0.001135448f, 0.001151499f, 0.001167339f, 0.001182971f, 0.001198395f, - 0.001213611f, 0.001228620f, 0.001243423f, 0.001258021f, 0.001272413f, - 0.001286602f, 0.001300586f, 0.001314368f, 0.001327948f, 0.001341327f, - 0.001354505f, 0.001367483f, 0.001380263f, 0.001392843f, 0.001405226f, - 0.001417413f, 0.001429403f, 0.001441198f, 0.001452798f, 0.001464205f, - 0.001475418f, 0.001486440f, 0.001497271f, 0.001507911f, 0.001518361f, - 0.001528623f, 0.001538696f, 0.001548581f, 0.001558281f, 0.001567796f, - 0.001577127f, 0.001586274f, 0.001595238f, 0.001604020f, 0.001612622f, - 0.001621043f, 0.001629285f, 0.001637349f, 0.001645236f, 0.001652946f, - 0.001660481f, 0.001667841f, 0.001675027f, 0.001682041f, 0.001688883f, - 0.001695554f, 0.001702055f, 0.001708387f, 0.001714552f, 0.001720549f, - 0.001726380f, 0.001732047f, 0.001737549f, 0.001742888f, 0.001748065f, - 0.001753081f, 0.001757936f, 0.001762632f, 0.001767171f, 0.001771552f, - 0.001775777f, 0.001779847f, 0.001783762f, 0.001787525f, 0.001791135f, - 0.001794595f, 0.001797904f, 0.001801064f, 0.001804076f, 0.001806941f, - 0.001809660f, 0.001812234f, 0.001814664f, 0.001816952f, 0.001819097f, - 0.001821102f, 0.001822967f, 0.001824693f, 0.001826282f, 0.001827733f, - 0.001829050f, 0.001830231f, 0.001831278f, 0.001832194f, 0.001832979f, - 0.001833633f, 0.001834158f, 0.001834554f, 0.001834824f, 0.001834967f, - 0.001834986f, 0.001834880f, 0.001834651f, 0.001834301f, 0.001833830f, - 0.001833239f, 0.001832529f, 0.001831702f, 0.001830758f, 0.001829698f, - 0.001828525f, 0.001827237f, 0.001825838f, 0.001824327f, 0.001822706f, - 0.001820976f, 0.001819138f, 0.001817193f, 0.001815141f, 0.001812985f, - 0.001810725f, 0.001808363f, 0.001805898f, 0.001803333f, 0.001800668f, - 0.001797904f, 0.001795043f, 0.001792086f, 0.001789033f, 0.001785886f, - 0.001782645f, 0.001779312f, 0.001775887f, 0.001772373f, 0.001768769f, - 0.001765078f, 0.001761299f, 0.001757434f, 0.001753483f, 0.001749449f, - 0.001745332f, 0.001741133f, 0.001736853f, 0.001732493f, 0.001728054f, - 0.001723537f, 0.001718944f, 0.001714273f, 0.001709529f, 0.001704710f, - 0.001699819f, 0.001694857f, 0.001689823f, 0.001684719f, 0.001679546f, - 0.001674306f, 0.001668998f, 0.001663625f, 0.001658187f, 0.001652685f, - 0.001647120f, 0.001641493f, 0.001635804f, 0.001630056f, 0.001624249f, - 0.001618383f, 0.001612461f, 0.001606481f, 0.001600448f, 0.001594359f, - 0.001588217f, 0.001582022f, 0.001575776f, 0.001569480f, 0.001563134f, - 0.001556739f, 0.001550296f, 0.001543806f, 0.001537270f, 0.001530689f, - 0.001524064f, 0.001517395f, 0.001510684f, 0.001503932f, 0.001497139f, - 0.001490306f, 0.001483435f, 0.001476525f, 0.001469578f, 0.001462596f, - 0.001455577f, 0.001448524f, 0.001441438f, 0.001434318f, 0.001427167f, - 0.001419984f, 0.001412771f, 0.001405529f, 0.001398258f, 0.001390959f, - 0.001383633f, 0.001376281f, 0.001368903f, 0.001361501f, 0.001354076f, - 0.001346627f, 0.001339155f, 0.001331663f, 0.001324150f, 0.001316617f, - 0.001309064f, 0.001301494f, 0.001293906f, 0.001286301f, 0.001278680f, - 0.001271044f, 0.001263393f, 0.001255728f, 0.001248050f, 0.001240360f, - 0.001232658f, 0.001224945f, 0.001217221f, 0.001209488f, 0.001201747f, - 0.001193997f, 0.001186240f, 0.001178476f, 0.001170706f, 0.001162930f, - 0.001155150f, 0.001147365f, 0.001139577f, 0.001131787f, 0.001123994f, - 0.001116200f, 0.001108405f, 0.001100609f, 0.001092814f, 0.001085020f, - 0.001077228f, 0.001069438f, 0.001061651f, 0.001053867f, 0.001046087f, - 0.001038312f, 0.001030542f, 0.001022778f, 0.001015020f, 0.001007270f, - 0.000999526f, 0.000991791f, 0.000984065f, 0.000976347f, 0.000968640f, - 0.000960942f, 0.000953256f, 0.000945580f, 0.000937917f, 0.000930266f, - 0.000922628f, 0.000915002f, 0.000907391f, 0.000899794f, 0.000892212f, - 0.000884646f, 0.000877095f, 0.000869560f, 0.000862042f, 0.000854541f, - 0.000847058f, 0.000839593f, 0.000832147f, 0.000824719f, 0.000817311f, - 0.000809923f, 0.000802555f, 0.000795208f, 0.000787881f, 0.000780577f, - 0.000773294f, 0.000766033f, 0.000758795f, 0.000751581f, 0.000744389f, - 0.000737222f, 0.000730079f, 0.000722960f, 0.000715866f, 0.000708798f, - 0.000701755f, 0.000694738f, 0.000687748f, 0.000680784f, 0.000673847f, - 0.000666937f, 0.000660055f, 0.000653202f, 0.000646376f, 0.000639579f, - 0.000632810f, 0.000626071f, 0.000619362f, 0.000612682f, 0.000606032f, - 0.000599412f, 0.000592823f, 0.000586265f, 0.000579738f, 0.000573242f, - 0.000566777f, 0.000560345f, 0.000553944f, 0.000547577f, 0.000541241f, - 0.000534938f, 0.000528668f, 0.000522432f, 0.000516228f, 0.000510059f, - 0.000503923f, 0.000497821f, 0.000491753f, 0.000485720f, 0.000479721f, - 0.000473757f, 0.000467828f, 0.000461934f, 0.000456075f, 0.000450252f, - 0.000444464f, 0.000438712f, 0.000432996f, 0.000427316f, 0.000421672f, - 0.000416064f, 0.000410493f, 0.000404958f, 0.000399460f, 0.000393998f, - 0.000388574f, 0.000383186f, 0.000377836f, 0.000372523f, 0.000367247f, - 0.000362008f, 0.000356807f, 0.000351643f, 0.000346517f, 0.000341429f, - 0.000336378f, 0.000331366f, 0.000326391f, 0.000321454f, 0.000316555f, - 0.000311695f, 0.000306872f, 0.000302088f, 0.000297341f, 0.000292633f, - 0.000287963f, 0.000283332f, 0.000278739f, 0.000274184f, 0.000269667f, - 0.000265189f, 0.000260749f, 0.000256348f, 0.000251985f, 0.000247661f, - 0.000243374f, 0.000239126f, 0.000234917f, 0.000230745f, 0.000226613f, - 0.000222518f, 0.000218462f, 0.000214443f, 0.000210464f, 0.000206522f, - 0.000202618f, 0.000198753f, 0.000194926f, 0.000191136f, 0.000187385f, - 0.000183671f, 0.000179996f, 0.000176358f, 0.000172758f, 0.000169195f, - 0.000165670f, 0.000162183f, 0.000158733f, 0.000155321f, 0.000151945f, - 0.000148607f, 0.000145307f, 0.000142043f, 0.000138816f, 0.000135626f, - 0.000132473f, 0.000129356f, 0.000126276f, 0.000123233f, 0.000120225f, - 0.000117255f, 0.000114320f, 0.000111421f, 0.000108558f, 0.000105731f, - 0.000102940f, 0.000100184f, 0.000097463f, 0.000094778f, 0.000092128f, - 0.000089513f, 0.000086933f, 0.000084388f, 0.000081878f, 0.000079402f, - 0.000076960f, 0.000074552f, 0.000072179f, 0.000069839f, 0.000067534f, - 0.000065261f, 0.000063023f, 0.000060817f, 0.000058645f, 0.000056506f, - 0.000054399f, 0.000052326f, 0.000050284f, 0.000048275f, 0.000046299f, - 0.000044354f, 0.000042441f, 0.000040560f, 0.000038710f, 0.000036891f, - 0.000035104f, 0.000033348f, 0.000031622f, 0.000029927f, 0.000028263f, - 0.000026628f, 0.000025024f, 0.000023450f, 0.000021905f, 0.000020390f, - 0.000018904f, 0.000017447f, 0.000016019f, 0.000014620f, 0.000013249f, - 0.000011907f, 0.000010593f, 0.000009307f, 0.000008048f, 0.000006818f, - 0.000005614f, 0.000004438f, 0.000003288f, 0.000002166f, 0.000001070f, - -0.000000000f + 1.000000000f, 0.000000000f,-0.000000000f, 0.000000000f,-0.000000000f, + 0.999993165f,-0.001679888f, 0.000529080f,-0.000151513f, 0.000027455f, + 0.999972661f,-0.003351212f, 0.001055794f,-0.000302183f, 0.000054683f, + 0.999938488f,-0.005013955f, 0.001580128f,-0.000452009f, 0.000081685f, + 0.999890647f,-0.006668099f, 0.002102071f,-0.000600987f, 0.000108459f, + 0.999829139f,-0.008313629f, 0.002621611f,-0.000749115f, 0.000135007f, + 0.999753966f,-0.009950528f, 0.003138734f,-0.000896389f, 0.000161328f, + 0.999665131f,-0.011578779f, 0.003653429f,-0.001042807f, 0.000187423f, + 0.999562634f,-0.013198368f, 0.004165684f,-0.001188367f, 0.000213291f, + 0.999446480f,-0.014809279f, 0.004675488f,-0.001333066f, 0.000238933f, + 0.999316672f,-0.016411497f, 0.005182828f,-0.001476901f, 0.000264348f, + 0.999173212f,-0.018005007f, 0.005687694f,-0.001619871f, 0.000289537f, + 0.999016105f,-0.019589795f, 0.006190074f,-0.001761971f, 0.000314501f, + 0.998845356f,-0.021165846f, 0.006689957f,-0.001903201f, 0.000339239f, + 0.998660968f,-0.022733147f, 0.007187332f,-0.002043558f, 0.000363751f, + 0.998462946f,-0.024291684f, 0.007682189f,-0.002183039f, 0.000388037f, + 0.998251297f,-0.025841443f, 0.008174516f,-0.002321643f, 0.000412099f, + 0.998026026f,-0.027382413f, 0.008664303f,-0.002459367f, 0.000435935f, + 0.997787138f,-0.028914579f, 0.009151540f,-0.002596209f, 0.000459547f, + 0.997534641f,-0.030437930f, 0.009636217f,-0.002732167f, 0.000482934f, + 0.997268542f,-0.031952453f, 0.010118324f,-0.002867240f, 0.000506097f, + 0.996988847f,-0.033458137f, 0.010597850f,-0.003001425f, 0.000529036f, + 0.996695563f,-0.034954970f, 0.011074786f,-0.003134721f, 0.000551752f, + 0.996388700f,-0.036442941f, 0.011549123f,-0.003267125f, 0.000574244f, + 0.996068266f,-0.037922039f, 0.012020851f,-0.003398637f, 0.000596512f, + 0.995734268f,-0.039392253f, 0.012489961f,-0.003529253f, 0.000618558f, + 0.995386717f,-0.040853574f, 0.012956443f,-0.003658973f, 0.000640382f, + 0.995025621f,-0.042305990f, 0.013420290f,-0.003787796f, 0.000661984f, + 0.994650990f,-0.043749493f, 0.013881491f,-0.003915718f, 0.000683363f, + 0.994262835f,-0.045184072f, 0.014340039f,-0.004042740f, 0.000704522f, + 0.993861166f,-0.046609719f, 0.014795924f,-0.004168860f, 0.000725459f, + 0.993445994f,-0.048026424f, 0.015249139f,-0.004294075f, 0.000746176f, + 0.993017331f,-0.049434180f, 0.015699676f,-0.004418386f, 0.000766672f, + 0.992575187f,-0.050832978f, 0.016147525f,-0.004541790f, 0.000786949f, + 0.992119574f,-0.052222809f, 0.016592680f,-0.004664287f, 0.000807006f, + 0.991650506f,-0.053603666f, 0.017035133f,-0.004785875f, 0.000826844f, + 0.991167995f,-0.054975543f, 0.017474875f,-0.004906553f, 0.000846464f, + 0.990672054f,-0.056338431f, 0.017911900f,-0.005026320f, 0.000865865f, + 0.990162696f,-0.057692323f, 0.018346201f,-0.005145175f, 0.000885049f, + 0.989639935f,-0.059037214f, 0.018777770f,-0.005263117f, 0.000904016f, + 0.989103786f,-0.060373097f, 0.019206599f,-0.005380146f, 0.000922766f, + 0.988554262f,-0.061699966f, 0.019632684f,-0.005496260f, 0.000941300f, + 0.987991380f,-0.063017815f, 0.020056015f,-0.005611458f, 0.000959619f, + 0.987415153f,-0.064326639f, 0.020476588f,-0.005725741f, 0.000977722f, + 0.986825598f,-0.065626433f, 0.020894396f,-0.005839106f, 0.000995611f, + 0.986222730f,-0.066917192f, 0.021309432f,-0.005951554f, 0.001013285f, + 0.985606567f,-0.068198912f, 0.021721690f,-0.006063084f, 0.001030746f, + 0.984977124f,-0.069471588f, 0.022131165f,-0.006173695f, 0.001047994f, + 0.984334418f,-0.070735217f, 0.022537850f,-0.006283387f, 0.001065030f, + 0.983678468f,-0.071989794f, 0.022941741f,-0.006392159f, 0.001081853f, + 0.983009290f,-0.073235317f, 0.023342830f,-0.006500011f, 0.001098466f, + 0.982326903f,-0.074471782f, 0.023741114f,-0.006606943f, 0.001114868f, + 0.981631326f,-0.075699186f, 0.024136587f,-0.006712954f, 0.001131059f, + 0.980922577f,-0.076917527f, 0.024529243f,-0.006818044f, 0.001147042f, + 0.980200675f,-0.078126804f, 0.024919078f,-0.006922213f, 0.001162815f, + 0.979465640f,-0.079327013f, 0.025306087f,-0.007025460f, 0.001178380f, + 0.978717491f,-0.080518153f, 0.025690266f,-0.007127786f, 0.001193738f, + 0.977956250f,-0.081700223f, 0.026071609f,-0.007229191f, 0.001208889f, + 0.977181936f,-0.082873221f, 0.026450113f,-0.007329674f, 0.001223833f, + 0.976394570f,-0.084037148f, 0.026825773f,-0.007429235f, 0.001238572f, + 0.975594175f,-0.085192002f, 0.027198586f,-0.007527875f, 0.001253106f, + 0.974780770f,-0.086337783f, 0.027568547f,-0.007625593f, 0.001267436f, + 0.973954379f,-0.087474491f, 0.027935652f,-0.007722391f, 0.001281562f, + 0.973115024f,-0.088602126f, 0.028299898f,-0.007818267f, 0.001295485f, + 0.972262727f,-0.089720690f, 0.028661282f,-0.007913223f, 0.001309207f, + 0.971397512f,-0.090830182f, 0.029019799f,-0.008007258f, 0.001322726f, + 0.970519401f,-0.091930604f, 0.029375448f,-0.008100373f, 0.001336045f, + 0.969628418f,-0.093021958f, 0.029728224f,-0.008192568f, 0.001349164f, + 0.968724588f,-0.094104245f, 0.030078125f,-0.008283845f, 0.001362084f, + 0.967807935f,-0.095177467f, 0.030425148f,-0.008374202f, 0.001374806f, + 0.966878483f,-0.096241627f, 0.030769290f,-0.008463642f, 0.001387329f, + 0.965936258f,-0.097296726f, 0.031110550f,-0.008552163f, 0.001399656f, + 0.964981285f,-0.098342768f, 0.031448923f,-0.008639768f, 0.001411786f, + 0.964013590f,-0.099379756f, 0.031784409f,-0.008726456f, 0.001423721f, + 0.963033199f,-0.100407693f, 0.032117005f,-0.008812229f, 0.001435461f, + 0.962040138f,-0.101426582f, 0.032446709f,-0.008897086f, 0.001447008f, + 0.961034434f,-0.102436428f, 0.032773519f,-0.008981030f, 0.001458361f, + 0.960016114f,-0.103437235f, 0.033097434f,-0.009064060f, 0.001469522f, + 0.958985206f,-0.104429007f, 0.033418451f,-0.009146178f, 0.001480492f, + 0.957941737f,-0.105411749f, 0.033736571f,-0.009227385f, 0.001491271f, + 0.956885736f,-0.106385466f, 0.034051790f,-0.009307680f, 0.001501860f, + 0.955817231f,-0.107350163f, 0.034364109f,-0.009387067f, 0.001512261f, + 0.954736250f,-0.108305845f, 0.034673526f,-0.009465545f, 0.001522473f, + 0.953642823f,-0.109252518f, 0.034980040f,-0.009543115f, 0.001532497f, + 0.952536979f,-0.110190189f, 0.035283651f,-0.009619779f, 0.001542336f, + 0.951418748f,-0.111118864f, 0.035584357f,-0.009695538f, 0.001551988f, + 0.950288160f,-0.112038548f, 0.035882158f,-0.009770393f, 0.001561456f, + 0.949145245f,-0.112949250f, 0.036177055f,-0.009844346f, 0.001570741f, + 0.947990034f,-0.113850976f, 0.036469046f,-0.009917397f, 0.001579842f, + 0.946822559f,-0.114743733f, 0.036758132f,-0.009989548f, 0.001588761f, + 0.945642850f,-0.115627529f, 0.037044312f,-0.010060800f, 0.001597498f, + 0.944450939f,-0.116502372f, 0.037327588f,-0.010131156f, 0.001606056f, + 0.943246858f,-0.117368270f, 0.037607958f,-0.010200615f, 0.001614434f, + 0.942030639f,-0.118225231f, 0.037885424f,-0.010269180f, 0.001622633f, + 0.940802316f,-0.119073264f, 0.038159985f,-0.010336852f, 0.001630655f, + 0.939561921f,-0.119912378f, 0.038431644f,-0.010403633f, 0.001638500f, + 0.938309487f,-0.120742582f, 0.038700400f,-0.010469524f, 0.001646169f, + 0.937045048f,-0.121563886f, 0.038966254f,-0.010534527f, 0.001653663f, + 0.935768638f,-0.122376299f, 0.039229208f,-0.010598644f, 0.001660984f, + 0.934480291f,-0.123179830f, 0.039489262f,-0.010661876f, 0.001668131f, + 0.933180042f,-0.123974491f, 0.039746418f,-0.010724225f, 0.001675106f, + 0.931867925f,-0.124760291f, 0.040000678f,-0.010785693f, 0.001681910f, + 0.930543975f,-0.125537242f, 0.040252042f,-0.010846282f, 0.001688544f, + 0.929208228f,-0.126305353f, 0.040500513f,-0.010905994f, 0.001695008f, + 0.927860720f,-0.127064637f, 0.040746092f,-0.010964829f, 0.001701305f, + 0.926501487f,-0.127815104f, 0.040988782f,-0.011022792f, 0.001707433f, + 0.925130565f,-0.128556767f, 0.041228583f,-0.011079882f, 0.001713396f, + 0.923747991f,-0.129289637f, 0.041465499f,-0.011136103f, 0.001719193f, + 0.922353802f,-0.130013726f, 0.041699532f,-0.011191456f, 0.001724825f, + 0.920948034f,-0.130729047f, 0.041930683f,-0.011245944f, 0.001730294f, + 0.919530726f,-0.131435613f, 0.042158956f,-0.011299568f, 0.001735601f, + 0.918101916f,-0.132133435f, 0.042384354f,-0.011352330f, 0.001740746f, + 0.916661641f,-0.132822528f, 0.042606878f,-0.011404234f, 0.001745730f, + 0.915209940f,-0.133502905f, 0.042826532f,-0.011455280f, 0.001750555f, + 0.913746852f,-0.134174578f, 0.043043318f,-0.011505472f, 0.001755221f, + 0.912272416f,-0.134837563f, 0.043257241f,-0.011554812f, 0.001759730f, + 0.910786671f,-0.135491873f, 0.043468303f,-0.011603301f, 0.001764082f, + 0.909289657f,-0.136137522f, 0.043676506f,-0.011650942f, 0.001768278f, + 0.907781413f,-0.136774525f, 0.043881856f,-0.011697738f, 0.001772320f, + 0.906261980f,-0.137402897f, 0.044084355f,-0.011743690f, 0.001776208f, + 0.904731398f,-0.138022653f, 0.044284007f,-0.011788802f, 0.001779944f, + 0.903189708f,-0.138633807f, 0.044480816f,-0.011833076f, 0.001783528f, + 0.901636952f,-0.139236376f, 0.044674785f,-0.011876514f, 0.001786962f, + 0.900073170f,-0.139830375f, 0.044865920f,-0.011919118f, 0.001790246f, + 0.898498403f,-0.140415819f, 0.045054222f,-0.011960892f, 0.001793381f, + 0.896912695f,-0.140992726f, 0.045239698f,-0.012001838f, 0.001796369f, + 0.895316086f,-0.141561111f, 0.045422352f,-0.012041958f, 0.001799211f, + 0.893708620f,-0.142120991f, 0.045602186f,-0.012081256f, 0.001801907f, + 0.892090339f,-0.142672383f, 0.045779208f,-0.012119733f, 0.001804459f, + 0.890461286f,-0.143215304f, 0.045953420f,-0.012157393f, 0.001806868f, + 0.888821505f,-0.143749771f, 0.046124828f,-0.012194238f, 0.001809134f, + 0.887171038f,-0.144275802f, 0.046293436f,-0.012230270f, 0.001811259f, + 0.885509930f,-0.144793414f, 0.046459250f,-0.012265494f, 0.001813243f, + 0.883838224f,-0.145302625f, 0.046622274f,-0.012299911f, 0.001815089f, + 0.882155965f,-0.145803453f, 0.046782515f,-0.012333524f, 0.001816796f, + 0.880463198f,-0.146295917f, 0.046939976f,-0.012366337f, 0.001818366f, + 0.878759967f,-0.146780035f, 0.047094664f,-0.012398351f, 0.001819800f, + 0.877046317f,-0.147255826f, 0.047246583f,-0.012429571f, 0.001821099f, + 0.875322295f,-0.147723309f, 0.047395741f,-0.012459998f, 0.001822264f, + 0.873587944f,-0.148182503f, 0.047542141f,-0.012489637f, 0.001823295f, + 0.871843312f,-0.148633428f, 0.047685790f,-0.012518489f, 0.001824196f, + 0.870088444f,-0.149076103f, 0.047826695f,-0.012546558f, 0.001824965f, + 0.868323386f,-0.149510548f, 0.047964860f,-0.012573847f, 0.001825604f, + 0.866548186f,-0.149936783f, 0.048100292f,-0.012600359f, 0.001826115f, + 0.864762890f,-0.150354828f, 0.048232997f,-0.012626097f, 0.001826498f, + 0.862967545f,-0.150764704f, 0.048362981f,-0.012651064f, 0.001826754f, + 0.861162199f,-0.151166432f, 0.048490252f,-0.012675264f, 0.001826885f, + 0.859346899f,-0.151560031f, 0.048614814f,-0.012698699f, 0.001826891f, + 0.857521693f,-0.151945524f, 0.048736676f,-0.012721373f, 0.001826774f, + 0.855686629f,-0.152322931f, 0.048855842f,-0.012743288f, 0.001826534f, + 0.853841755f,-0.152692274f, 0.048972321f,-0.012764449f, 0.001826173f, + 0.851987121f,-0.153053574f, 0.049086119f,-0.012784858f, 0.001825691f, + 0.850122774f,-0.153406854f, 0.049197244f,-0.012804518f, 0.001825091f, + 0.848248764f,-0.153752135f, 0.049305701f,-0.012823434f, 0.001824372f, + 0.846365140f,-0.154089440f, 0.049411498f,-0.012841607f, 0.001823536f, + 0.844471951f,-0.154418791f, 0.049514643f,-0.012859042f, 0.001822584f, + 0.842569248f,-0.154740210f, 0.049615142f,-0.012875742f, 0.001821517f, + 0.840657079f,-0.155053721f, 0.049713003f,-0.012891710f, 0.001820336f, + 0.838735496f,-0.155359346f, 0.049808234f,-0.012906950f, 0.001819042f, + 0.836804549f,-0.155657108f, 0.049900842f,-0.012921465f, 0.001817636f, + 0.834864288f,-0.155947032f, 0.049990834f,-0.012935259f, 0.001816120f, + 0.832914765f,-0.156229140f, 0.050078219f,-0.012948334f, 0.001814493f, + 0.830956029f,-0.156503456f, 0.050163005f,-0.012960695f, 0.001812758f, + 0.828988133f,-0.156770004f, 0.050245198f,-0.012972345f, 0.001810916f, + 0.827011128f,-0.157028808f, 0.050324808f,-0.012983287f, 0.001808967f, + 0.825025066f,-0.157279893f, 0.050401842f,-0.012993525f, 0.001806912f, + 0.823029998f,-0.157523282f, 0.050476308f,-0.013003063f, 0.001804753f, + 0.821025977f,-0.157759001f, 0.050548215f,-0.013011904f, 0.001802491f, + 0.819013055f,-0.157987074f, 0.050617571f,-0.013020051f, 0.001800126f, + 0.816991284f,-0.158207526f, 0.050684384f,-0.013027509f, 0.001797660f, + 0.814960718f,-0.158420382f, 0.050748664f,-0.013034280f, 0.001795094f, + 0.812921409f,-0.158625668f, 0.050810417f,-0.013040370f, 0.001792428f, + 0.810873410f,-0.158823410f, 0.050869654f,-0.013045780f, 0.001789664f, + 0.808816775f,-0.159013631f, 0.050926382f,-0.013050515f, 0.001786804f, + 0.806751557f,-0.159196360f, 0.050980610f,-0.013054579f, 0.001783847f, + 0.804677811f,-0.159371620f, 0.051032348f,-0.013057974f, 0.001780795f, + 0.802595589f,-0.159539440f, 0.051081605f,-0.013060706f, 0.001777649f, + 0.800504946f,-0.159699844f, 0.051128389f,-0.013062778f, 0.001774411f, + 0.798405936f,-0.159852860f, 0.051172709f,-0.013064192f, 0.001771080f, + 0.796298614f,-0.159998514f, 0.051214574f,-0.013064954f, 0.001767659f, + 0.794183034f,-0.160136832f, 0.051253995f,-0.013065067f, 0.001764147f, + 0.792059252f,-0.160267843f, 0.051290979f,-0.013064535f, 0.001760547f, + 0.789927322f,-0.160391572f, 0.051325537f,-0.013063361f, 0.001756860f, + 0.787787300f,-0.160508047f, 0.051357678f,-0.013061549f, 0.001753085f, + 0.785639241f,-0.160617296f, 0.051387412f,-0.013059104f, 0.001749225f, + 0.783483200f,-0.160719346f, 0.051414747f,-0.013056029f, 0.001745280f, + 0.781319234f,-0.160814225f, 0.051439694f,-0.013052327f, 0.001741252f, + 0.779147398f,-0.160901960f, 0.051462263f,-0.013048003f, 0.001737141f, + 0.776967749f,-0.160982580f, 0.051482462f,-0.013043061f, 0.001732948f, + 0.774780342f,-0.161056113f, 0.051500303f,-0.013037504f, 0.001728675f, + 0.772585234f,-0.161122587f, 0.051515795f,-0.013031336f, 0.001724323f, + 0.770382481f,-0.161182031f, 0.051528947f,-0.013024562f, 0.001719892f, + 0.768172142f,-0.161234473f, 0.051539771f,-0.013017185f, 0.001715383f, + 0.765954271f,-0.161279942f, 0.051548275f,-0.013009209f, 0.001710798f, + 0.763728927f,-0.161318466f, 0.051554471f,-0.013000638f, 0.001706137f, + 0.761496167f,-0.161350075f, 0.051558368f,-0.012991476f, 0.001701402f, + 0.759256048f,-0.161374798f, 0.051559977f,-0.012981727f, 0.001696593f, + 0.757008627f,-0.161392665f, 0.051559309f,-0.012971395f, 0.001691712f, + 0.754753963f,-0.161403704f, 0.051556372f,-0.012960484f, 0.001686760f, + 0.752492113f,-0.161407945f, 0.051551179f,-0.012948997f, 0.001681737f, + 0.750223135f,-0.161405418f, 0.051543739f,-0.012936940f, 0.001676644f, + 0.747947088f,-0.161396153f, 0.051534064f,-0.012924315f, 0.001671483f, + 0.745664029f,-0.161380179f, 0.051522163f,-0.012911128f, 0.001666254f, + 0.743374018f,-0.161357527f, 0.051508048f,-0.012897381f, 0.001660959f, + 0.741077112f,-0.161328227f, 0.051491729f,-0.012883079f, 0.001655598f, + 0.738773370f,-0.161292309f, 0.051473217f,-0.012868227f, 0.001650173f, + 0.736462852f,-0.161249804f, 0.051452522f,-0.012852827f, 0.001644684f, + 0.734145616f,-0.161200742f, 0.051429657f,-0.012836884f, 0.001639133f, + 0.731821721f,-0.161145154f, 0.051404631f,-0.012820403f, 0.001633520f, + 0.729491227f,-0.161083070f, 0.051377457f,-0.012803386f, 0.001627846f, + 0.727154193f,-0.161014523f, 0.051348144f,-0.012785839f, 0.001622112f, + 0.724810678f,-0.160939542f, 0.051316704f,-0.012767765f, 0.001616320f, + 0.722460743f,-0.160858158f, 0.051283148f,-0.012749169f, 0.001610470f, + 0.720104446f,-0.160770404f, 0.051247488f,-0.012730054f, 0.001604563f, + 0.717741848f,-0.160676310f, 0.051209734f,-0.012710425f, 0.001598600f, + 0.715373009f,-0.160575909f, 0.051169898f,-0.012690285f, 0.001592582f, + 0.712997988f,-0.160469230f, 0.051127991f,-0.012669640f, 0.001586510f, + 0.710616847f,-0.160356307f, 0.051084025f,-0.012648492f, 0.001580385f, + 0.708229645f,-0.160237171f, 0.051038012f,-0.012626846f, 0.001574207f, + 0.705836443f,-0.160111854f, 0.050989962f,-0.012604706f, 0.001567979f, + 0.703437301f,-0.159980389f, 0.050939887f,-0.012582077f, 0.001561700f, + 0.701032280f,-0.159842806f, 0.050887799f,-0.012558961f, 0.001555372f, + 0.698621441f,-0.159699138f, 0.050833709f,-0.012535365f, 0.001548995f, + 0.696204845f,-0.159549419f, 0.050777630f,-0.012511290f, 0.001542571f, + 0.693782552f,-0.159393679f, 0.050719572f,-0.012486743f, 0.001536101f, + 0.691354624f,-0.159231952f, 0.050659547f,-0.012461726f, 0.001529584f, + 0.688921121f,-0.159064270f, 0.050597568f,-0.012436245f, 0.001523023f, + 0.686482106f,-0.158890666f, 0.050533646f,-0.012410302f, 0.001516417f, + 0.684037639f,-0.158711173f, 0.050467793f,-0.012383903f, 0.001509769f, + 0.681587783f,-0.158525823f, 0.050400021f,-0.012357051f, 0.001503079f, + 0.679132597f,-0.158334650f, 0.050330342f,-0.012329751f, 0.001496347f, + 0.676672145f,-0.158137687f, 0.050258767f,-0.012302006f, 0.001489575f, + 0.674206487f,-0.157934966f, 0.050185310f,-0.012273821f, 0.001482764f, + 0.671735685f,-0.157726522f, 0.050109981f,-0.012245200f, 0.001475914f, + 0.669259802f,-0.157512387f, 0.050032793f,-0.012216147f, 0.001469026f, + 0.666778900f,-0.157292594f, 0.049953758f,-0.012186667f, 0.001462101f, + 0.664293039f,-0.157067178f, 0.049872888f,-0.012156762f, 0.001455141f, + 0.661802283f,-0.156836172f, 0.049790195f,-0.012126439f, 0.001448145f, + 0.659306693f,-0.156599609f, 0.049705692f,-0.012095699f, 0.001441115f, + 0.656806333f,-0.156357523f, 0.049619391f,-0.012064549f, 0.001434051f, + 0.654301263f,-0.156109948f, 0.049531303f,-0.012032992f, 0.001426955f, + 0.651791546f,-0.155856918f, 0.049441442f,-0.012001031f, 0.001419827f, + 0.649277246f,-0.155598467f, 0.049349819f,-0.011968672f, 0.001412668f, + 0.646758423f,-0.155334628f, 0.049256448f,-0.011935918f, 0.001405479f, + 0.644235142f,-0.155065436f, 0.049161340f,-0.011902774f, 0.001398261f, + 0.641707464f,-0.154790925f, 0.049064507f,-0.011869243f, 0.001391015f, + 0.639175452f,-0.154511129f, 0.048965963f,-0.011835330f, 0.001383741f, + 0.636639169f,-0.154226083f, 0.048865719f,-0.011801038f, 0.001376440f, + 0.634098677f,-0.153935820f, 0.048763788f,-0.011766373f, 0.001369113f, + 0.631554040f,-0.153640376f, 0.048660183f,-0.011731337f, 0.001361761f, + 0.629005320f,-0.153339784f, 0.048554915f,-0.011695936f, 0.001354384f, + 0.626452580f,-0.153034079f, 0.048447999f,-0.011660173f, 0.001346984f, + 0.623895883f,-0.152723296f, 0.048339445f,-0.011624053f, 0.001339561f, + 0.621335293f,-0.152407470f, 0.048229267f,-0.011587579f, 0.001332116f, + 0.618770871f,-0.152086634f, 0.048117478f,-0.011550756f, 0.001324650f, + 0.616202682f,-0.151760824f, 0.048004090f,-0.011513587f, 0.001317164f, + 0.613630788f,-0.151430075f, 0.047889115f,-0.011476077f, 0.001309657f, + 0.611055252f,-0.151094421f, 0.047772566f,-0.011438230f, 0.001302132f, + 0.608476138f,-0.150753897f, 0.047654456f,-0.011400050f, 0.001294589f, + 0.605893509f,-0.150408538f, 0.047534798f,-0.011361541f, 0.001287028f, + 0.603307427f,-0.150058380f, 0.047413604f,-0.011322706f, 0.001279451f, + 0.600717957f,-0.149703457f, 0.047290887f,-0.011283552f, 0.001271857f, + 0.598125161f,-0.149343804f, 0.047166660f,-0.011244080f, 0.001264249f, + 0.595529103f,-0.148979456f, 0.047040936f,-0.011204296f, 0.001256626f, + 0.592929846f,-0.148610450f, 0.046913726f,-0.011164202f, 0.001248990f, + 0.590327454f,-0.148236818f, 0.046785045f,-0.011123805f, 0.001241340f, + 0.587721989f,-0.147858598f, 0.046654904f,-0.011083106f, 0.001233679f, + 0.585113515f,-0.147475824f, 0.046523317f,-0.011042111f, 0.001226005f, + 0.582502096f,-0.147088532f, 0.046390297f,-0.011000824f, 0.001218321f, + 0.579887795f,-0.146696757f, 0.046255856f,-0.010959248f, 0.001210627f, + 0.577270674f,-0.146300533f, 0.046120007f,-0.010917387f, 0.001202924f, + 0.574650799f,-0.145899898f, 0.045982762f,-0.010875246f, 0.001195211f, + 0.572028231f,-0.145494885f, 0.045844136f,-0.010832828f, 0.001187491f, + 0.569403034f,-0.145085532f, 0.045704140f,-0.010790137f, 0.001179764f, + 0.566775272f,-0.144671872f, 0.045562787f,-0.010747178f, 0.001172030f, + 0.564145009f,-0.144253941f, 0.045420091f,-0.010703955f, 0.001164289f, + 0.561512306f,-0.143831776f, 0.045276064f,-0.010660470f, 0.001156544f, + 0.558877229f,-0.143405412f, 0.045130719f,-0.010616729f, 0.001148794f, + 0.556239839f,-0.142974883f, 0.044984069f,-0.010572735f, 0.001141040f, + 0.553600201f,-0.142540227f, 0.044836126f,-0.010528493f, 0.001133283f, + 0.550958378f,-0.142101478f, 0.044686904f,-0.010484005f, 0.001125523f, + 0.548314433f,-0.141658672f, 0.044536416f,-0.010439276f, 0.001117761f, + 0.545668429f,-0.141211844f, 0.044384675f,-0.010394310f, 0.001109997f, + 0.543020430f,-0.140761032f, 0.044231692f,-0.010349111f, 0.001102233f, + 0.540370499f,-0.140306269f, 0.044077482f,-0.010303683f, 0.001094469f, + 0.537718699f,-0.139847592f, 0.043922057f,-0.010258029f, 0.001086705f, + 0.535065094f,-0.139385036f, 0.043765430f,-0.010212153f, 0.001078942f, + 0.532409746f,-0.138918637f, 0.043607614f,-0.010166060f, 0.001071182f, + 0.529752718f,-0.138448432f, 0.043448622f,-0.010119753f, 0.001063423f, + 0.527094075f,-0.137974455f, 0.043288466f,-0.010073235f, 0.001055668f, + 0.524433878f,-0.137496742f, 0.043127159f,-0.010026512f, 0.001047916f, + 0.521772191f,-0.137015329f, 0.042964716f,-0.009979586f, 0.001040168f, + 0.519109077f,-0.136530252f, 0.042801147f,-0.009932461f, 0.001032425f, + 0.516444599f,-0.136041547f, 0.042636467f,-0.009885141f, 0.001024687f, + 0.513778819f,-0.135549248f, 0.042470687f,-0.009837631f, 0.001016955f, + 0.511111801f,-0.135053393f, 0.042303821f,-0.009789933f, 0.001009230f, + 0.508443608f,-0.134554017f, 0.042135882f,-0.009742051f, 0.001001511f, + 0.505774302f,-0.134051154f, 0.041966883f,-0.009693990f, 0.000993800f, + 0.503103946f,-0.133544842f, 0.041796836f,-0.009645752f, 0.000986098f, + 0.500432602f,-0.133035116f, 0.041625754f,-0.009597342f, 0.000978404f, + 0.497760334f,-0.132522012f, 0.041453650f,-0.009548764f, 0.000970719f, + 0.495087203f,-0.132005564f, 0.041280536f,-0.009500020f, 0.000963044f, + 0.492413273f,-0.131485810f, 0.041106427f,-0.009451116f, 0.000955380f, + 0.489738606f,-0.130962784f, 0.040931333f,-0.009402053f, 0.000947726f, + 0.487063264f,-0.130436523f, 0.040755269f,-0.009352837f, 0.000940083f, + 0.484387309f,-0.129907061f, 0.040578247f,-0.009303470f, 0.000932453f, + 0.481710804f,-0.129374435f, 0.040400279f,-0.009253957f, 0.000924835f, + 0.479033811f,-0.128838680f, 0.040221378f,-0.009204300f, 0.000917229f, + 0.476356392f,-0.128299831f, 0.040041558f,-0.009154505f, 0.000909637f, + 0.473678610f,-0.127757925f, 0.039860830f,-0.009104573f, 0.000902059f, + 0.471000525f,-0.127212997f, 0.039679208f,-0.009054509f, 0.000894496f, + 0.468322201f,-0.126665082f, 0.039496704f,-0.009004316f, 0.000886947f, + 0.465643698f,-0.126114216f, 0.039313331f,-0.008953998f, 0.000879414f, + 0.462965079f,-0.125560435f, 0.039129101f,-0.008903558f, 0.000871896f, + 0.460286405f,-0.125003773f, 0.038944027f,-0.008853000f, 0.000864395f, + 0.457607738f,-0.124444266f, 0.038758122f,-0.008802328f, 0.000856910f, + 0.454929140f,-0.123881950f, 0.038571399f,-0.008751544f, 0.000849443f, + 0.452250671f,-0.123316860f, 0.038383869f,-0.008700653f, 0.000841994f, + 0.449572393f,-0.122749031f, 0.038195545f,-0.008649657f, 0.000834562f, + 0.446894368f,-0.122178499f, 0.038006440f,-0.008598561f, 0.000827149f, + 0.444216656f,-0.121605299f, 0.037816567f,-0.008547367f, 0.000819755f, + 0.441539319f,-0.121029465f, 0.037625937f,-0.008496080f, 0.000812381f, + 0.438862417f,-0.120451035f, 0.037434564f,-0.008444702f, 0.000805026f, + 0.436186011f,-0.119870041f, 0.037242460f,-0.008393237f, 0.000797692f, + 0.433510163f,-0.119286520f, 0.037049637f,-0.008341688f, 0.000790378f, + 0.430834932f,-0.118700507f, 0.036856108f,-0.008290058f, 0.000783086f, + 0.428160380f,-0.118112036f, 0.036661885f,-0.008238352f, 0.000775815f, + 0.425486566f,-0.117521144f, 0.036466980f,-0.008186572f, 0.000768565f, + 0.422813551f,-0.116927863f, 0.036271406f,-0.008134722f, 0.000761339f, + 0.420141396f,-0.116332231f, 0.036075174f,-0.008082804f, 0.000754135f, + 0.417470160f,-0.115734281f, 0.035878299f,-0.008030823f, 0.000746954f, + 0.414799903f,-0.115134048f, 0.035680790f,-0.007978781f, 0.000739796f, + 0.412130686f,-0.114531567f, 0.035482662f,-0.007926681f, 0.000732662f, + 0.409462568f,-0.113926872f, 0.035283926f,-0.007874528f, 0.000725553f, + 0.406795609f,-0.113320000f, 0.035084593f,-0.007822324f, 0.000718468f, + 0.404129868f,-0.112710983f, 0.034884678f,-0.007770072f, 0.000711408f, + 0.401465405f,-0.112099857f, 0.034684190f,-0.007717775f, 0.000704373f, + 0.398802279f,-0.111486655f, 0.034483143f,-0.007665437f, 0.000697364f, + 0.396140548f,-0.110871414f, 0.034281549f,-0.007613061f, 0.000690381f, + 0.393480274f,-0.110254166f, 0.034079419f,-0.007560650f, 0.000683424f, + 0.390821513f,-0.109634947f, 0.033876767f,-0.007508207f, 0.000676494f, + 0.388164325f,-0.109013790f, 0.033673602f,-0.007455735f, 0.000669590f, + 0.385508769f,-0.108390730f, 0.033469939f,-0.007403238f, 0.000662714f, + 0.382854903f,-0.107765801f, 0.033265787f,-0.007350717f, 0.000655866f, + 0.380202785f,-0.107139037f, 0.033061161f,-0.007298177f, 0.000649046f, + 0.377552474f,-0.106510472f, 0.032856070f,-0.007245621f, 0.000642253f, + 0.374904028f,-0.105880140f, 0.032650528f,-0.007193050f, 0.000635490f, + 0.372257505f,-0.105248075f, 0.032444545f,-0.007140469f, 0.000628755f, + 0.369612962f,-0.104614312f, 0.032238134f,-0.007087881f, 0.000622049f, + 0.366970458f,-0.103978882f, 0.032031306f,-0.007035287f, 0.000615373f, + 0.364330050f,-0.103341821f, 0.031824073f,-0.006982692f, 0.000608726f, + 0.361691794f,-0.102703162f, 0.031616447f,-0.006930098f, 0.000602110f, + 0.359055750f,-0.102062939f, 0.031408439f,-0.006877508f, 0.000595523f, + 0.356421972f,-0.101421184f, 0.031200061f,-0.006824926f, 0.000588967f, + 0.353790520f,-0.100777932f, 0.030991325f,-0.006772353f, 0.000582442f, + 0.351161448f,-0.100133216f, 0.030782241f,-0.006719792f, 0.000575948f, + 0.348534815f,-0.099487069f, 0.030572821f,-0.006667247f, 0.000569485f, + 0.345910675f,-0.098839524f, 0.030363078f,-0.006614721f, 0.000563054f, + 0.343289087f,-0.098190615f, 0.030153021f,-0.006562215f, 0.000556654f, + 0.340670105f,-0.097540374f, 0.029942663f,-0.006509734f, 0.000550287f, + 0.338053786f,-0.096888834f, 0.029732015f,-0.006457279f, 0.000543951f, + 0.335440186f,-0.096236029f, 0.029521089f,-0.006404853f, 0.000537649f, + 0.332829360f,-0.095581991f, 0.029309894f,-0.006352460f, 0.000531378f, + 0.330221364f,-0.094926753f, 0.029098443f,-0.006300101f, 0.000525141f, + 0.327616253f,-0.094270347f, 0.028886747f,-0.006247780f, 0.000518937f, + 0.325014082f,-0.093612807f, 0.028674817f,-0.006195500f, 0.000512766f, + 0.322414906f,-0.092954163f, 0.028462663f,-0.006143262f, 0.000506629f, + 0.319818781f,-0.092294450f, 0.028250298f,-0.006091069f, 0.000500525f, + 0.317225760f,-0.091633699f, 0.028037732f,-0.006038925f, 0.000494456f, + 0.314635898f,-0.090971943f, 0.027824976f,-0.005986831f, 0.000488420f, + 0.312049250f,-0.090309213f, 0.027612040f,-0.005934790f, 0.000482419f, + 0.309465869f,-0.089645542f, 0.027398936f,-0.005882806f, 0.000476453f, + 0.306885810f,-0.088980961f, 0.027185675f,-0.005830879f, 0.000470521f, + 0.304309126f,-0.088315503f, 0.026972268f,-0.005779014f, 0.000464623f, + 0.301735870f,-0.087649199f, 0.026758724f,-0.005727211f, 0.000458761f, + 0.299166097f,-0.086982081f, 0.026545055f,-0.005675475f, 0.000452934f, + 0.296599859f,-0.086314180f, 0.026331272f,-0.005623807f, 0.000447143f, + 0.294037209f,-0.085645528f, 0.026117385f,-0.005572209f, 0.000441387f, + 0.291478200f,-0.084976157f, 0.025903405f,-0.005520685f, 0.000435666f, + 0.288922885f,-0.084306097f, 0.025689341f,-0.005469237f, 0.000429981f, + 0.286371316f,-0.083635380f, 0.025475206f,-0.005417866f, 0.000424332f, + 0.283823545f,-0.082964036f, 0.025261008f,-0.005366575f, 0.000418720f, + 0.281279624f,-0.082292098f, 0.025046759f,-0.005315368f, 0.000413143f, + 0.278739606f,-0.081619595f, 0.024832469f,-0.005264245f, 0.000407603f, + 0.276203542f,-0.080946559f, 0.024618148f,-0.005213209f, 0.000402099f, + 0.273671482f,-0.080273020f, 0.024403807f,-0.005162263f, 0.000396632f, + 0.271143479f,-0.079599009f, 0.024189455f,-0.005111409f, 0.000391201f, + 0.268619584f,-0.078924556f, 0.023975103f,-0.005060649f, 0.000385807f, + 0.266099847f,-0.078249692f, 0.023760760f,-0.005009985f, 0.000380450f, + 0.263584318f,-0.077574447f, 0.023546438f,-0.004959420f, 0.000375130f, + 0.261073049f,-0.076898851f, 0.023332145f,-0.004908956f, 0.000369847f, + 0.258566090f,-0.076222934f, 0.023117893f,-0.004858594f, 0.000364602f, + 0.256063490f,-0.075546727f, 0.022903690f,-0.004808337f, 0.000359393f, + 0.253565299f,-0.074870258f, 0.022689547f,-0.004758188f, 0.000354222f, + 0.251071568f,-0.074193558f, 0.022475473f,-0.004708148f, 0.000349088f, + 0.248582344f,-0.073516656f, 0.022261479f,-0.004658219f, 0.000343992f, + 0.246097678f,-0.072839582f, 0.022047574f,-0.004608404f, 0.000338934f, + 0.243617618f,-0.072162365f, 0.021833767f,-0.004558704f, 0.000333913f, + 0.241142213f,-0.071485035f, 0.021620069f,-0.004509122f, 0.000328930f, + 0.238671511f,-0.070807621f, 0.021406488f,-0.004459659f, 0.000323984f, + 0.236205561f,-0.070130151f, 0.021193035f,-0.004410318f, 0.000319077f, + 0.233744411f,-0.069452655f, 0.020979718f,-0.004361100f, 0.000314207f, + 0.231288109f,-0.068775161f, 0.020766548f,-0.004312008f, 0.000309375f, + 0.228836701f,-0.068097699f, 0.020553533f,-0.004263043f, 0.000304582f, + 0.226390237f,-0.067420297f, 0.020340683f,-0.004214207f, 0.000299826f, + 0.223948762f,-0.066742982f, 0.020128008f,-0.004165503f, 0.000295108f, + 0.221512323f,-0.066065785f, 0.019915515f,-0.004116932f, 0.000290429f, + 0.219080968f,-0.065388732f, 0.019703215f,-0.004068496f, 0.000285787f, + 0.216654743f,-0.064711852f, 0.019491117f,-0.004020196f, 0.000281184f, + 0.214233694f,-0.064035173f, 0.019279229f,-0.003972036f, 0.000276619f, + 0.211817866f,-0.063358724f, 0.019067561f,-0.003924015f, 0.000272092f, + 0.209407307f,-0.062682530f, 0.018856122f,-0.003876137f, 0.000267604f, + 0.207002061f,-0.062006621f, 0.018644920f,-0.003828403f, 0.000263153f, + 0.204602173f,-0.061331024f, 0.018433965f,-0.003780814f, 0.000258741f, + 0.202207689f,-0.060655766f, 0.018223264f,-0.003733373f, 0.000254367f, + 0.199818654f,-0.059980874f, 0.018012827f,-0.003686081f, 0.000250032f, + 0.197435111f,-0.059306375f, 0.017802663f,-0.003638940f, 0.000245734f, + 0.195057107f,-0.058632298f, 0.017592780f,-0.003591951f, 0.000241475f, + 0.192684683f,-0.057958667f, 0.017383186f,-0.003545116f, 0.000237254f, + 0.190317885f,-0.057285511f, 0.017173891f,-0.003498437f, 0.000233071f, + 0.187956756f,-0.056612855f, 0.016964902f,-0.003451915f, 0.000228927f, + 0.185601339f,-0.055940727f, 0.016756228f,-0.003405553f, 0.000224821f, + 0.183251678f,-0.055269152f, 0.016547878f,-0.003359350f, 0.000220752f, + 0.180907815f,-0.054598157f, 0.016339859f,-0.003313310f, 0.000216722f, + 0.178569792f,-0.053927768f, 0.016132180f,-0.003267434f, 0.000212731f, + 0.176237654f,-0.053258011f, 0.015924850f,-0.003221722f, 0.000208777f, + 0.173911440f,-0.052588912f, 0.015717875f,-0.003176177f, 0.000204861f, + 0.171591194f,-0.051920496f, 0.015511264f,-0.003130800f, 0.000200983f, + 0.169276957f,-0.051252790f, 0.015305026f,-0.003085592f, 0.000197143f, + 0.166968771f,-0.050585817f, 0.015099168f,-0.003040555f, 0.000193341f, + 0.164666676f,-0.049919605f, 0.014893698f,-0.002995691f, 0.000189577f, + 0.162370713f,-0.049254177f, 0.014688624f,-0.002951000f, 0.000185851f, + 0.160080923f,-0.048589560f, 0.014483954f,-0.002906485f, 0.000182163f, + 0.157797346f,-0.047925777f, 0.014279695f,-0.002862145f, 0.000178512f, + 0.155520023f,-0.047262854f, 0.014075855f,-0.002817984f, 0.000174899f, + 0.153248993f,-0.046600815f, 0.013872442f,-0.002774001f, 0.000171324f, + 0.150984296f,-0.045939685f, 0.013669462f,-0.002730199f, 0.000167786f, + 0.148725971f,-0.045279488f, 0.013466925f,-0.002686579f, 0.000164285f, + 0.146474057f,-0.044620249f, 0.013264837f,-0.002643141f, 0.000160822f, + 0.144228593f,-0.043961991f, 0.013063205f,-0.002599888f, 0.000157396f, + 0.141989618f,-0.043304738f, 0.012862038f,-0.002556820f, 0.000154008f, + 0.139757169f,-0.042648514f, 0.012661341f,-0.002513938f, 0.000150656f, + 0.137531285f,-0.041993344f, 0.012461123f,-0.002471244f, 0.000147342f, + 0.135312004f,-0.041339249f, 0.012261390f,-0.002428739f, 0.000144064f, + 0.133099362f,-0.040686254f, 0.012062150f,-0.002386425f, 0.000140824f, + 0.130893398f,-0.040034383f, 0.011863409f,-0.002344301f, 0.000137620f, + 0.128694148f,-0.039383657f, 0.011665175f,-0.002302370f, 0.000134453f, + 0.126501649f,-0.038734100f, 0.011467454f,-0.002260631f, 0.000131323f, + 0.124315937f,-0.038085735f, 0.011270254f,-0.002219088f, 0.000128229f, + 0.122137048f,-0.037438584f, 0.011073581f,-0.002177740f, 0.000125171f, + 0.119965018f,-0.036792671f, 0.010877441f,-0.002136588f, 0.000122150f, + 0.117799883f,-0.036148017f, 0.010681842f,-0.002095634f, 0.000119165f, + 0.115641679f,-0.035504644f, 0.010486791f,-0.002054878f, 0.000116216f, + 0.113490439f,-0.034862575f, 0.010292293f,-0.002014322f, 0.000113304f, + 0.111346199f,-0.034221832f, 0.010098355f,-0.001973967f, 0.000110427f, + 0.109208994f,-0.033582436f, 0.009904984f,-0.001933813f, 0.000107585f, + 0.107078857f,-0.032944409f, 0.009712186f,-0.001893861f, 0.000104780f, + 0.104955823f,-0.032307773f, 0.009519967f,-0.001854112f, 0.000102010f, + 0.102839925f,-0.031672548f, 0.009328334f,-0.001814568f, 0.000099275f, + 0.100731196f,-0.031038757f, 0.009137293f,-0.001775228f, 0.000096576f, + 0.098629671f,-0.030406420f, 0.008946849f,-0.001736095f, 0.000093911f, + 0.096535380f,-0.029775558f, 0.008757010f,-0.001697168f, 0.000091282f, + 0.094448358f,-0.029146192f, 0.008567781f,-0.001658448f, 0.000088688f, + 0.092368636f,-0.028518343f, 0.008379168f,-0.001619937f, 0.000086128f, + 0.090296247f,-0.027892030f, 0.008191177f,-0.001581635f, 0.000083603f, + 0.088231221f,-0.027267275f, 0.008003813f,-0.001543542f, 0.000081113f, + 0.086173591f,-0.026644097f, 0.007817084f,-0.001505661f, 0.000078656f, + 0.084123388f,-0.026022517f, 0.007630993f,-0.001467990f, 0.000076234f, + 0.082080642f,-0.025402554f, 0.007445548f,-0.001430532f, 0.000073846f, + 0.080045384f,-0.024784228f, 0.007260753f,-0.001393286f, 0.000071492f, + 0.078017645f,-0.024167559f, 0.007076615f,-0.001356254f, 0.000069172f, + 0.075997454f,-0.023552566f, 0.006893138f,-0.001319435f, 0.000066885f, + 0.073984841f,-0.022939269f, 0.006710328f,-0.001282832f, 0.000064632f, + 0.071979836f,-0.022327686f, 0.006528191f,-0.001246443f, 0.000062412f, + 0.069982467f,-0.021717836f, 0.006346731f,-0.001210271f, 0.000060225f, + 0.067992764f,-0.021109739f, 0.006165955f,-0.001174315f, 0.000058072f, + 0.066010755f,-0.020503413f, 0.005985866f,-0.001138576f, 0.000055951f, + 0.064036468f,-0.019898876f, 0.005806472f,-0.001103055f, 0.000053862f, + 0.062069932f,-0.019296147f, 0.005627775f,-0.001067751f, 0.000051807f, + 0.060111174f,-0.018695244f, 0.005449782f,-0.001032667f, 0.000049783f, + 0.058160222f,-0.018096185f, 0.005272497f,-0.000997801f, 0.000047792f, + 0.056217102f,-0.017498988f, 0.005095926f,-0.000963156f, 0.000045833f, + 0.054281842f,-0.016903671f, 0.004920073f,-0.000928730f, 0.000043905f, + 0.052354468f,-0.016310251f, 0.004744943f,-0.000894525f, 0.000042010f, + 0.050435006f,-0.015718746f, 0.004570540f,-0.000860541f, 0.000040146f, + 0.048523483f,-0.015129173f, 0.004396870f,-0.000826779f, 0.000038313f, + 0.046619924f,-0.014541549f, 0.004223937f,-0.000793238f, 0.000036511f, + 0.044724355f,-0.013955892f, 0.004051745f,-0.000759920f, 0.000034740f, + 0.042836800f,-0.013372217f, 0.003880299f,-0.000726824f, 0.000033000f, + 0.040957285f,-0.012790543f, 0.003709604f,-0.000693951f, 0.000031291f, + 0.039085833f,-0.012210884f, 0.003539663f,-0.000661301f, 0.000029612f, + 0.037222470f,-0.011633259f, 0.003370481f,-0.000628876f, 0.000027964f, + 0.035367219f,-0.011057682f, 0.003202062f,-0.000596674f, 0.000026345f, + 0.033520103f,-0.010484170f, 0.003034411f,-0.000564696f, 0.000024756f, + 0.031681147f,-0.009912739f, 0.002867532f,-0.000532943f, 0.000023197f, + 0.029850373f,-0.009343404f, 0.002701428f,-0.000501414f, 0.000021668f, + 0.028027804f,-0.008776182f, 0.002536104f,-0.000470111f, 0.000020168f, + 0.026213462f,-0.008211087f, 0.002371564f,-0.000439032f, 0.000018697f, + 0.024407370f,-0.007648135f, 0.002207811f,-0.000408180f, 0.000017255f, + 0.022609549f,-0.007087341f, 0.002044849f,-0.000377553f, 0.000015842f, + 0.020820021f,-0.006528720f, 0.001882682f,-0.000347151f, 0.000014457f, + 0.019038808f,-0.005972286f, 0.001721315f,-0.000316976f, 0.000013101f, + 0.017265930f,-0.005418056f, 0.001560750f,-0.000287027f, 0.000011773f, + 0.015501408f,-0.004866042f, 0.001400991f,-0.000257305f, 0.000010473f, + 0.013745262f,-0.004316260f, 0.001242041f,-0.000227808f, 0.000009201f, + 0.011997513f,-0.003768723f, 0.001083905f,-0.000198539f, 0.000007956f, + 0.010258180f,-0.003223445f, 0.000926585f,-0.000169495f, 0.000006739f, + 0.008527283f,-0.002680442f, 0.000770086f,-0.000140679f, 0.000005549f, + 0.006804842f,-0.002139725f, 0.000614409f,-0.000112090f, 0.000004386f, + 0.005090874f,-0.001601309f, 0.000459559f,-0.000083727f, 0.000003250f, + 0.003385399f,-0.001065208f, 0.000305539f,-0.000055591f, 0.000002140f, + 0.001688435f,-0.000531434f, 0.000152351f,-0.000027682f, 0.000001057f, }; -static const float ResamplerFilterDifference[RESAMPLER_FILTER_SIZE] = { - -0.000006318f, -0.000019312f, -0.000033081f, -0.000046730f, -0.000060380f, - -0.000074029f, -0.000087738f, -0.000101388f, -0.000115037f, -0.000128686f, - -0.000142336f, -0.000155985f, -0.000169694f, -0.000183165f, -0.000196993f, - -0.000210524f, -0.000224173f, -0.000237763f, -0.000251293f, -0.000265062f, - -0.000278592f, -0.000292122f, -0.000305772f, -0.000319421f, -0.000332832f, - -0.000346422f, -0.000359952f, -0.000373542f, -0.000387073f, -0.000400543f, - -0.000414014f, -0.000427663f, -0.000441074f, -0.000454485f, -0.000468016f, - -0.000481427f, -0.000494778f, -0.000508308f, -0.000521660f, -0.000535011f, - -0.000548482f, -0.000561833f, -0.000575066f, -0.000588536f, -0.000601768f, - -0.000615180f, -0.000628352f, -0.000641525f, -0.000655055f, -0.000668049f, - -0.000681281f, -0.000694513f, -0.000707746f, -0.000720799f, -0.000733972f, - -0.000747204f, -0.000760138f, -0.000773191f, -0.000786424f, -0.000799239f, - -0.000812411f, -0.000825346f, -0.000838339f, -0.000851214f, -0.000864267f, - -0.000877023f, -0.000890017f, -0.000902772f, -0.000915587f, -0.000928521f, - -0.000941217f, -0.000953972f, -0.000966668f, -0.000979364f, -0.000992119f, - -0.001004696f, -0.001017392f, -0.001029909f, -0.001042426f, -0.001055002f, - -0.001067519f, -0.001080096f, -0.001092434f, -0.001104832f, -0.001117289f, - -0.001129627f, -0.001141965f, -0.001154304f, -0.001166403f, -0.001178861f, - -0.001191020f, -0.001203120f, -0.001215160f, -0.001227558f, -0.001239419f, - -0.001251400f, -0.001263618f, -0.001275480f, -0.001287460f, -0.001299262f, - -0.001311302f, -0.001322985f, -0.001334965f, -0.001346648f, -0.001358271f, - -0.001370072f, -0.001381695f, -0.001393259f, -0.001404881f, -0.001416504f, - -0.001427948f, -0.001439393f, -0.001450837f, -0.001462340f, -0.001473546f, - -0.001484871f, -0.001496255f, -0.001507342f, -0.001518726f, -0.001529694f, - -0.001540840f, -0.001552045f, -0.001562893f, -0.001574039f, -0.001584947f, - -0.001595795f, -0.001606703f, -0.001617491f, -0.001628280f, -0.001639009f, - -0.001649678f, -0.001660407f, -0.001670957f, -0.001681507f, -0.001692057f, - -0.001702428f, -0.001713037f, -0.001723289f, -0.001733601f, -0.001743913f, - -0.001754165f, -0.001764417f, -0.001774490f, -0.001784563f, -0.001794696f, - -0.001804650f, -0.001814663f, -0.001824498f, -0.001834452f, -0.001844168f, - -0.001854002f, -0.001863778f, -0.001873314f, -0.001883030f, -0.001892567f, - -0.001902103f, -0.001911521f, -0.001920938f, -0.001930356f, -0.001939654f, - -0.001948893f, -0.001958191f, -0.001967311f, -0.001976430f, -0.001985550f, - -0.001994491f, -0.002003491f, -0.002012312f, -0.002021313f, -0.002030015f, - -0.002038717f, -0.002047479f, -0.002056181f, -0.002064645f, -0.002073288f, - -0.002081692f, -0.002090156f, -0.002098501f, -0.002106905f, -0.002115071f, - -0.002123356f, -0.002131462f, -0.002139568f, -0.002147555f, -0.002155662f, - -0.002163529f, -0.002171457f, -0.002179205f, -0.002187014f, -0.002194703f, - -0.002202272f, -0.002209961f, -0.002217531f, -0.002224922f, -0.002232373f, - -0.002239764f, -0.002247095f, -0.002254248f, -0.002261519f, -0.002268672f, - -0.002275705f, -0.002282679f, -0.002289712f, -0.002296627f, -0.002303362f, - -0.002310216f, -0.002316952f, -0.002323568f, -0.002330244f, -0.002336800f, - -0.002343237f, -0.002349675f, -0.002356052f, -0.002362311f, -0.002368629f, - -0.002374828f, -0.002380908f, -0.002386987f, -0.002392948f, -0.002398968f, - -0.002404809f, -0.002410650f, -0.002416372f, -0.002422094f, -0.002427757f, - -0.002433360f, -0.002438903f, -0.002444327f, -0.002449691f, -0.002455056f, - -0.002460301f, -0.002465487f, -0.002470732f, -0.002475739f, -0.002480865f, - -0.002485693f, -0.002490699f, -0.002495527f, -0.002500176f, -0.002505064f, - -0.002509654f, -0.002514243f, -0.002518773f, -0.002523303f, -0.002527595f, - -0.002531946f, -0.002536356f, -0.002540410f, -0.002544582f, -0.002548754f, - -0.002552748f, -0.002556741f, -0.002560616f, -0.002564371f, -0.002568305f, - -0.002571881f, -0.002575636f, -0.002579153f, -0.002582669f, -0.002586126f, - -0.002589583f, -0.002592862f, -0.002596140f, -0.002599359f, -0.002602518f, - -0.002605557f, -0.002608538f, -0.002611578f, -0.002614439f, -0.002617240f, - -0.002620041f, -0.002622724f, -0.002625346f, -0.002627909f, -0.002630413f, - -0.002632916f, -0.002635241f, -0.002637565f, -0.002639830f, -0.002642035f, - -0.002644122f, -0.002646267f, -0.002648175f, -0.002650142f, -0.002652109f, - -0.002653837f, -0.002655566f, -0.002657294f, -0.002658904f, -0.002660453f, - -0.002662003f, -0.002663374f, -0.002664745f, -0.002666116f, -0.002667189f, - -0.002668560f, -0.002669632f, -0.002670705f, -0.002671599f, -0.002672642f, - -0.002673417f, -0.002674311f, -0.002675056f, -0.002675682f, -0.002676308f, - -0.002676874f, -0.002677381f, -0.002677768f, -0.002678156f, -0.002678514f, - -0.002678722f, -0.002678931f, -0.002679020f, -0.002679110f, -0.002679020f, - -0.002679050f, -0.002678901f, -0.002678722f, -0.002678484f, -0.002678186f, - -0.002677828f, -0.002677292f, -0.002676904f, -0.002676338f, -0.002675682f, - -0.002675027f, -0.002674311f, -0.002673507f, -0.002672672f, -0.002671778f, - -0.002670765f, -0.002669752f, -0.002668649f, -0.002667487f, -0.002666295f, - -0.002665013f, -0.002663702f, -0.002662301f, -0.002660841f, -0.002659231f, - -0.002657771f, -0.002656132f, -0.002654433f, -0.002652735f, -0.002650917f, - -0.002649069f, -0.002647102f, -0.002645165f, -0.002643138f, -0.002641022f, - -0.002638906f, -0.002636671f, -0.002634317f, -0.002632141f, -0.002629727f, - -0.002627254f, -0.002624840f, -0.002622217f, -0.002619684f, -0.002616972f, - -0.002614290f, -0.002611518f, -0.002608687f, -0.002605796f, -0.002602875f, - -0.002599895f, -0.002596736f, -0.002593726f, -0.002590597f, -0.002587378f, - -0.002584100f, -0.002580822f, -0.002577394f, -0.002574027f, -0.002570510f, - -0.002566963f, -0.002563447f, -0.002559751f, -0.002556056f, -0.002552360f, - -0.002548456f, -0.002544701f, -0.002540767f, -0.002536893f, -0.002532810f, - -0.002528816f, -0.002524704f, -0.002520531f, -0.002516329f, -0.002512068f, - -0.002507776f, -0.002503395f, -0.002499044f, -0.002494544f, -0.002489939f, - -0.002485529f, -0.002480894f, -0.002476230f, -0.002471551f, -0.002466798f, - -0.002461985f, -0.002457172f, -0.002452269f, -0.002447322f, -0.002442330f, - -0.002437294f, -0.002432242f, -0.002427101f, -0.002421811f, -0.002416730f, - -0.002411440f, -0.002406135f, -0.002400786f, -0.002395362f, -0.002389938f, - -0.002384409f, -0.002378926f, -0.002373338f, -0.002367705f, -0.002362043f, - -0.002356306f, -0.002350554f, -0.002344713f, -0.002338931f, -0.002333075f, - -0.002327129f, -0.002321184f, -0.002315164f, -0.002309129f, -0.002303004f, - -0.002296925f, -0.002290726f, -0.002284512f, -0.002278253f, -0.002271980f, - -0.002265558f, -0.002259284f, -0.002252862f, -0.002246410f, -0.002239943f, - -0.002233401f, -0.002226844f, -0.002220228f, -0.002213612f, -0.002206936f, - -0.002200201f, -0.002193481f, -0.002186671f, -0.002179854f, -0.002172925f, - -0.002166122f, -0.002159171f, -0.002152205f, -0.002145231f, -0.002138190f, - -0.002131112f, -0.002124026f, -0.002116874f, -0.002109714f, -0.002102517f, - -0.002095275f, -0.002088003f, -0.002080709f, -0.002073303f, -0.002066024f, - -0.002058625f, -0.002051190f, -0.002043739f, -0.002036251f, -0.002028733f, - -0.002021186f, -0.002013609f, -0.002006002f, -0.001998372f, -0.001990698f, - -0.001983009f, -0.001975283f, -0.001967464f, -0.001959760f, -0.001951948f, - -0.001944117f, -0.001936268f, -0.001928370f, -0.001920462f, -0.001912523f, - -0.001904562f, -0.001896568f, -0.001888555f, -0.001880515f, -0.001872454f, - -0.001864363f, -0.001856182f, -0.001848117f, -0.001839954f, -0.001831776f, - -0.001823569f, -0.001815341f, -0.001807092f, -0.001798820f, -0.001790524f, - -0.001782209f, -0.001773877f, -0.001765518f, -0.001757140f, -0.001748742f, - -0.001740261f, -0.001731890f, -0.001723433f, -0.001714959f, -0.001706466f, - -0.001697953f, -0.001689423f, -0.001680876f, -0.001672311f, -0.001663728f, - -0.001655128f, -0.001646512f, -0.001637880f, -0.001629170f, -0.001620566f, - -0.001611889f, -0.001603196f, -0.001594484f, -0.001585761f, -0.001577025f, - -0.001568271f, -0.001559505f, -0.001550728f, -0.001541931f, -0.001533132f, - -0.001524314f, -0.001515483f, -0.001506586f, -0.001497786f, -0.001488928f, - -0.001480050f, -0.001471169f, -0.001462270f, -0.001453366f, -0.001444448f, - -0.001435522f, -0.001426589f, -0.001417641f, -0.001408692f, -0.001399733f, - -0.001390763f, -0.001381733f, -0.001372803f, -0.001363818f, -0.001354810f, - -0.001345813f, -0.001336802f, -0.001327783f, -0.001318768f, -0.001309730f, - -0.001300700f, -0.001291670f, -0.001282625f, -0.001273572f, -0.001264527f, - -0.001255415f, -0.001246415f, -0.001237355f, -0.001228295f, -0.001219213f, - -0.001210161f, -0.001201086f, -0.001192011f, -0.001182951f, -0.001173854f, - -0.001164794f, -0.001155719f, -0.001146644f, -0.001137555f, -0.001128443f, - -0.001119412f, -0.001110330f, -0.001101270f, -0.001092196f, -0.001083128f, - -0.001074038f, -0.001065001f, -0.001055919f, -0.001046866f, -0.001037806f, - -0.001028746f, -0.001019694f, -0.001010656f, -0.001001567f, -0.000992566f, - -0.000983544f, -0.000974506f, -0.000965491f, -0.000956468f, -0.000947453f, - -0.000938453f, -0.000929438f, -0.000920445f, -0.000911482f, -0.000902474f, - -0.000893541f, -0.000884496f, -0.000875585f, -0.000866644f, -0.000857703f, - -0.000848778f, -0.000839837f, -0.000830933f, -0.000822030f, -0.000813127f, - -0.000804260f, -0.000795372f, -0.000786506f, -0.000777654f, -0.000768811f, - -0.000759944f, -0.000751153f, -0.000742361f, -0.000733539f, -0.000724763f, - -0.000716001f, -0.000707224f, -0.000698492f, -0.000689745f, -0.000681043f, - -0.000672325f, -0.000663623f, -0.000654951f, -0.000646278f, -0.000637606f, - -0.000628993f, -0.000620380f, -0.000611767f, -0.000603199f, -0.000594586f, - -0.000586048f, -0.000577524f, -0.000568971f, -0.000560462f, -0.000551969f, - -0.000543505f, -0.000535041f, -0.000526607f, -0.000518143f, -0.000509769f, - -0.000501379f, -0.000493005f, -0.000484675f, -0.000476316f, -0.000468016f, - -0.000459731f, -0.000451431f, -0.000443190f, -0.000434965f, -0.000426725f, - -0.000418544f, -0.000410378f, -0.000402227f, -0.000394091f, -0.000385970f, - -0.000377893f, -0.000369802f, -0.000361770f, -0.000353739f, -0.000345752f, - -0.000337750f, -0.000329792f, -0.000321865f, -0.000313938f, -0.000306055f, - -0.000298187f, -0.000290349f, -0.000282511f, -0.000274733f, -0.000266939f, - -0.000259221f, -0.000251472f, -0.000243768f, -0.000236079f, -0.000228450f, - -0.000220820f, -0.000213221f, -0.000205651f, -0.000198096f, -0.000190571f, - -0.000183061f, -0.000175595f, -0.000168160f, -0.000160739f, -0.000153318f, - -0.000145987f, -0.000138611f, -0.000131324f, -0.000124037f, -0.000116780f, - -0.000109524f, -0.000102326f, -0.000095159f, -0.000088021f, -0.000080869f, - -0.000073805f, -0.000066742f, -0.000059679f, -0.000052691f, -0.000045687f, - -0.000038758f, -0.000031844f, -0.000024930f, -0.000018090f, -0.000011265f, - -0.000004441f, 0.000002325f, 0.000009075f, 0.000015780f, 0.000022471f, - 0.000029117f, 0.000035763f, 0.000042319f, 0.000048906f, 0.000055432f, - 0.000061929f, 0.000068396f, 0.000074849f, 0.000081256f, 0.000087619f, - 0.000093967f, 0.000100300f, 0.000106558f, 0.000112817f, 0.000119045f, - 0.000125214f, 0.000131369f, 0.000137508f, 0.000143588f, 0.000149652f, - 0.000155658f, 0.000161663f, 0.000167638f, 0.000173539f, 0.000179455f, - 0.000185296f, 0.000191167f, 0.000196919f, 0.000202700f, 0.000208423f, - 0.000214115f, 0.000219792f, 0.000225410f, 0.000231013f, 0.000236586f, - 0.000242084f, 0.000247598f, 0.000253052f, 0.000258476f, 0.000263870f, - 0.000269234f, 0.000274554f, 0.000279859f, 0.000285104f, 0.000290334f, - 0.000295490f, 0.000300661f, 0.000305787f, 0.000310853f, 0.000315934f, - 0.000320926f, 0.000325903f, 0.000330850f, 0.000335768f, 0.000340641f, - 0.000345469f, 0.000350282f, 0.000355050f, 0.000359803f, 0.000364468f, - 0.000369161f, 0.000373796f, 0.000378355f, 0.000382945f, 0.000387460f, - 0.000391930f, 0.000396401f, 0.000400826f, 0.000405192f, 0.000409558f, - 0.000413850f, 0.000418127f, 0.000422373f, 0.000426546f, 0.000430748f, - 0.000434861f, 0.000438988f, 0.000443041f, 0.000447050f, 0.000451058f, - 0.000455007f, 0.000458926f, 0.000462815f, 0.000466645f, 0.000470474f, - 0.000474244f, 0.000477999f, 0.000481665f, 0.000485376f, 0.000488982f, - 0.000492588f, 0.000496164f, 0.000499681f, 0.000503168f, 0.000506610f, - 0.000510037f, 0.000513420f, 0.000516787f, 0.000520080f, 0.000523359f, - 0.000526607f, 0.000529751f, 0.000532970f, 0.000536114f, 0.000539184f, - 0.000542283f, 0.000545278f, 0.000548288f, 0.000551224f, 0.000554159f, - 0.000557050f, 0.000559889f, 0.000562698f, 0.000565484f, 0.000568233f, - 0.000570931f, 0.000573598f, 0.000576250f, 0.000578851f, 0.000581399f, - 0.000583932f, 0.000586443f, 0.000588894f, 0.000591323f, 0.000593722f, - 0.000596076f, 0.000598386f, 0.000600673f, 0.000602946f, 0.000605099f, - 0.000607334f, 0.000609480f, 0.000611588f, 0.000613675f, 0.000615716f, - 0.000617720f, 0.000619695f, 0.000621639f, 0.000623547f, 0.000625424f, - 0.000627257f, 0.000629067f, 0.000630826f, 0.000632577f, 0.000634275f, - 0.000635937f, 0.000637576f, 0.000639170f, 0.000640750f, 0.000642270f, - 0.000643790f, 0.000645243f, 0.000646673f, 0.000648089f, 0.000649445f, - 0.000650786f, 0.000652038f, 0.000653364f, 0.000654578f, 0.000655793f, - 0.000656962f, 0.000658102f, 0.000659212f, 0.000660270f, 0.000661321f, - 0.000662334f, 0.000663288f, 0.000664257f, 0.000665158f, 0.000666037f, - 0.000666894f, 0.000667714f, 0.000668481f, 0.000669241f, 0.000669979f, - 0.000670649f, 0.000671312f, 0.000671953f, 0.000672549f, 0.000673100f, - 0.000673652f, 0.000674143f, 0.000674628f, 0.000675075f, 0.000675432f, - 0.000675865f, 0.000676215f, 0.000676543f, 0.000676841f, 0.000677086f, - 0.000677340f, 0.000677533f, 0.000677712f, 0.000677854f, 0.000677958f, - 0.000678062f, 0.000678115f, 0.000678137f, 0.000678144f, 0.000678107f, - 0.000678040f, 0.000677980f, 0.000677846f, 0.000677705f, 0.000677541f, - 0.000677332f, 0.000677109f, 0.000676855f, 0.000676572f, 0.000676263f, - 0.000675920f, 0.000675563f, 0.000675112f, 0.000674754f, 0.000674292f, - 0.000673834f, 0.000673324f, 0.000672799f, 0.000672262f, 0.000671666f, - 0.000671066f, 0.000670440f, 0.000669774f, 0.000669103f, 0.000668388f, - 0.000667654f, 0.000666898f, 0.000666108f, 0.000665307f, 0.000664469f, - 0.000663612f, 0.000662729f, 0.000661820f, 0.000660881f, 0.000659931f, - 0.000658948f, 0.000657942f, 0.000656914f, 0.000655863f, 0.000654746f, - 0.000653688f, 0.000652563f, 0.000651423f, 0.000650249f, 0.000649061f, - 0.000647850f, 0.000646606f, 0.000645354f, 0.000644065f, 0.000642769f, - 0.000641439f, 0.000640094f, 0.000638723f, 0.000637334f, 0.000635926f, - 0.000634488f, 0.000633033f, 0.000631562f, 0.000630064f, 0.000628548f, - 0.000627011f, 0.000625454f, 0.000623874f, 0.000622276f, 0.000620661f, - 0.000619022f, 0.000617364f, 0.000615645f, 0.000613993f, 0.000612279f, - 0.000610547f, 0.000608791f, 0.000607023f, 0.000605229f, 0.000603424f, - 0.000601593f, 0.000599753f, 0.000597889f, 0.000596011f, 0.000594109f, - 0.000592194f, 0.000590262f, 0.000588313f, 0.000586346f, 0.000584361f, - 0.000582362f, 0.000580344f, 0.000578311f, 0.000576260f, 0.000574195f, - 0.000572114f, 0.000570015f, 0.000567904f, 0.000565774f, 0.000563630f, - 0.000561428f, 0.000559297f, 0.000557108f, 0.000554904f, 0.000552687f, - 0.000550454f, 0.000548207f, 0.000545946f, 0.000543671f, 0.000541383f, - 0.000539081f, 0.000536765f, 0.000534437f, 0.000532095f, 0.000529740f, - 0.000527372f, 0.000524992f, 0.000522598f, 0.000520193f, 0.000517776f, - 0.000515345f, 0.000512904f, 0.000510451f, 0.000507986f, 0.000505510f, - 0.000503022f, 0.000500523f, 0.000497975f, 0.000495494f, 0.000492963f, - 0.000490420f, 0.000487870f, 0.000485308f, 0.000482734f, 0.000480153f, - 0.000477560f, 0.000474961f, 0.000472347f, 0.000469729f, 0.000467099f, - 0.000464460f, 0.000461814f, 0.000459160f, 0.000456492f, 0.000453821f, - 0.000451141f, 0.000448450f, 0.000445757f, 0.000443047f, 0.000440339f, - 0.000437619f, 0.000434890f, 0.000432156f, 0.000429420f, 0.000426665f, - 0.000423878f, 0.000421155f, 0.000418384f, 0.000415608f, 0.000412831f, - 0.000410046f, 0.000407251f, 0.000404453f, 0.000401655f, 0.000398841f, - 0.000396032f, 0.000393212f, 0.000390388f, 0.000387559f, 0.000384722f, - 0.000381887f, 0.000379046f, 0.000376200f, 0.000373350f, 0.000370493f, - 0.000367636f, 0.000364775f, 0.000361912f, 0.000359036f, 0.000356171f, - 0.000353293f, 0.000350416f, 0.000347532f, 0.000344623f, 0.000341764f, - 0.000338878f, 0.000335984f, 0.000333097f, 0.000330199f, 0.000327300f, - 0.000324406f, 0.000321500f, 0.000318602f, 0.000315700f, 0.000312794f, - 0.000309892f, 0.000306983f, 0.000304081f, 0.000301171f, 0.000298265f, - 0.000295352f, 0.000292446f, 0.000289541f, 0.000286628f, 0.000283718f, - 0.000280812f, 0.000277914f, 0.000274993f, 0.000272095f, 0.000269186f, - 0.000266258f, 0.000263374f, 0.000260476f, 0.000257570f, 0.000254679f, - 0.000251777f, 0.000248875f, 0.000245985f, 0.000243090f, 0.000240199f, - 0.000237312f, 0.000234425f, 0.000231542f, 0.000228655f, 0.000225779f, - 0.000222903f, 0.000220031f, 0.000217155f, 0.000214294f, 0.000211425f, - 0.000208564f, 0.000205707f, 0.000202853f, 0.000200007f, 0.000197157f, - 0.000194311f, 0.000191480f, 0.000188638f, 0.000185791f, 0.000182983f, - 0.000180162f, 0.000177346f, 0.000174530f, 0.000171725f, 0.000168923f, - 0.000166126f, 0.000163332f, 0.000160541f, 0.000157762f, 0.000154987f, - 0.000152212f, 0.000149447f, 0.000146687f, 0.000143930f, 0.000141181f, - 0.000138439f, 0.000135705f, 0.000132974f, 0.000130247f, 0.000127535f, - 0.000124816f, 0.000122119f, 0.000119418f, 0.000116725f, 0.000114042f, - 0.000111364f, 0.000108674f, 0.000106033f, 0.000103373f, 0.000100728f, - 0.000098083f, 0.000095449f, 0.000092819f, 0.000090208f, 0.000087593f, - 0.000084992f, 0.000082392f, 0.000079807f, 0.000077233f, 0.000074655f, - 0.000072096f, 0.000069536f, 0.000066992f, 0.000064459f, 0.000061929f, - 0.000059403f, 0.000056893f, 0.000054389f, 0.000051897f, 0.000049412f, - 0.000046935f, 0.000044469f, 0.000042010f, 0.000039551f, 0.000037123f, - 0.000034694f, 0.000032268f, 0.000029862f, 0.000027459f, 0.000025064f, - 0.000022691f, 0.000020307f, 0.000017952f, 0.000015602f, 0.000013247f, - 0.000010934f, 0.000008594f, 0.000006288f, 0.000003986f, 0.000001695f, - -0.000000589f, -0.000002854f, -0.000005119f, -0.000007369f, -0.000009615f, - -0.000011832f, -0.000014052f, -0.000016265f, -0.000018459f, -0.000020642f, - -0.000022821f, -0.000024986f, -0.000027131f, -0.000029277f, -0.000031408f, - -0.000033524f, -0.000035629f, -0.000037737f, -0.000039808f, -0.000041891f, - -0.000043951f, -0.000046007f, -0.000048041f, -0.000050064f, -0.000052094f, - -0.000054091f, -0.000056084f, -0.000058070f, -0.000060037f, -0.000061993f, - -0.000063941f, -0.000065874f, -0.000067800f, -0.000069708f, -0.000071604f, - -0.000073489f, -0.000075370f, -0.000077229f, -0.000079080f, -0.000080917f, - -0.000082746f, -0.000084557f, -0.000086352f, -0.000088152f, -0.000089929f, - -0.000091687f, -0.000093438f, -0.000095177f, -0.000096913f, -0.000098623f, - -0.000100322f, -0.000102021f, -0.000103690f, -0.000105359f, -0.000107009f, - -0.000108648f, -0.000110280f, -0.000111893f, -0.000113495f, -0.000115078f, - -0.000116657f, -0.000118230f, -0.000119772f, -0.000121318f, -0.000122838f, - -0.000124350f, -0.000125848f, -0.000127345f, -0.000128813f, -0.000130277f, - -0.000131730f, -0.000133157f, -0.000134595f, -0.000135988f, -0.000137400f, - -0.000138778f, -0.000140157f, -0.000141505f, -0.000142857f, -0.000144191f, - -0.000145517f, -0.000146817f, -0.000148114f, -0.000149388f, -0.000150666f, - -0.000151921f, -0.000153165f, -0.000154395f, -0.000155609f, -0.000156820f, - -0.000158004f, -0.000159189f, -0.000160355f, -0.000161506f, -0.000162646f, - -0.000163775f, -0.000164881f, -0.000165988f, -0.000167083f, -0.000168145f, - -0.000169218f, -0.000170264f, -0.000171307f, -0.000172324f, -0.000173341f, - -0.000174336f, -0.000175327f, -0.000176292f, -0.000177257f, -0.000178207f, - -0.000179138f, -0.000180058f, -0.000180975f, -0.000181865f, -0.000182752f, - -0.000183623f, -0.000184480f, -0.000185326f, -0.000186164f, -0.000186980f, - -0.000187792f, -0.000188582f, -0.000189368f, -0.000190143f, -0.000190891f, - -0.000191648f, -0.000192374f, -0.000193097f, -0.000193801f, -0.000194505f, - -0.000195183f, -0.000195853f, -0.000196513f, -0.000197157f, -0.000197794f, - -0.000198420f, -0.000199001f, -0.000199623f, -0.000200208f, -0.000200782f, - -0.000201344f, -0.000201892f, -0.000202429f, -0.000202954f, -0.000203468f, - -0.000203967f, -0.000204455f, -0.000204936f, -0.000205394f, -0.000205856f, - -0.000206295f, -0.000206724f, -0.000207141f, -0.000207547f, -0.000207946f, - -0.000208326f, -0.000208698f, -0.000209058f, -0.000209408f, -0.000209745f, - -0.000210065f, -0.000210386f, -0.000210684f, -0.000210978f, -0.000211256f, - -0.000211528f, -0.000211783f, -0.000212030f, -0.000212263f, -0.000212491f, - -0.000212703f, -0.000212908f, -0.000213092f, -0.000213277f, -0.000213446f, - -0.000213604f, -0.000213752f, -0.000213886f, -0.000214016f, -0.000214130f, - -0.000214234f, -0.000214327f, -0.000214415f, -0.000214487f, -0.000214545f, - -0.000214605f, -0.000214642f, -0.000214674f, -0.000214696f, -0.000214709f, - -0.000214707f, -0.000214674f, -0.000214679f, -0.000214649f, -0.000214612f, - -0.000214560f, -0.000214502f, -0.000214435f, -0.000214357f, -0.000214268f, - -0.000214169f, -0.000214059f, -0.000213947f, -0.000213819f, -0.000213677f, - -0.000213537f, -0.000213381f, -0.000213215f, -0.000213042f, -0.000212861f, - -0.000212666f, -0.000212466f, -0.000212256f, -0.000212036f, -0.000211809f, - -0.000211569f, -0.000211326f, -0.000211067f, -0.000210807f, -0.000210535f, - -0.000210257f, -0.000209961f, -0.000209667f, -0.000209359f, -0.000209045f, - -0.000208722f, -0.000208389f, -0.000208050f, -0.000207704f, -0.000207344f, - -0.000206983f, -0.000206610f, -0.000206229f, -0.000205842f, -0.000205445f, - -0.000205044f, -0.000204630f, -0.000204211f, -0.000203785f, -0.000203351f, - -0.000202909f, -0.000202459f, -0.000202003f, -0.000201537f, -0.000201069f, - -0.000200586f, -0.000200078f, -0.000199611f, -0.000199108f, -0.000198605f, - -0.000198090f, -0.000197569f, -0.000197043f, -0.000196506f, -0.000195967f, - -0.000195420f, -0.000194865f, -0.000194305f, -0.000193738f, -0.000193163f, - -0.000192585f, -0.000191999f, -0.000191407f, -0.000190807f, -0.000190204f, - -0.000189591f, -0.000188977f, -0.000188354f, -0.000187725f, -0.000187092f, - -0.000186451f, -0.000185805f, -0.000185154f, -0.000184498f, -0.000183835f, - -0.000183167f, -0.000182495f, -0.000181816f, -0.000181132f, -0.000180443f, - -0.000179750f, -0.000179050f, -0.000178346f, -0.000177636f, -0.000176923f, - -0.000176203f, -0.000175480f, -0.000174752f, -0.000174019f, -0.000173280f, - -0.000172539f, -0.000171792f, -0.000171040f, -0.000170285f, -0.000169525f, - -0.000168761f, -0.000167992f, -0.000167220f, -0.000166443f, -0.000165662f, - -0.000164878f, -0.000164090f, -0.000163272f, -0.000162501f, -0.000161701f, - -0.000160898f, -0.000160091f, -0.000159280f, -0.000158466f, -0.000157648f, - -0.000156827f, -0.000156003f, -0.000155175f, -0.000154344f, -0.000153510f, - -0.000152673f, -0.000151833f, -0.000150990f, -0.000150144f, -0.000149295f, - -0.000148444f, -0.000147589f, -0.000146732f, -0.000145872f, -0.000145010f, - -0.000144145f, -0.000143277f, -0.000142408f, -0.000141536f, -0.000140661f, - -0.000139785f, -0.000138906f, -0.000138025f, -0.000137142f, -0.000136257f, - -0.000135370f, -0.000134481f, -0.000133590f, -0.000132697f, -0.000131803f, - -0.000130907f, -0.000130009f, -0.000129110f, -0.000128209f, -0.000127306f, - -0.000126403f, -0.000125497f, -0.000124591f, -0.000123683f, -0.000122774f, - -0.000121864f, -0.000120952f, -0.000120040f, -0.000119127f, -0.000118212f, - -0.000117297f, -0.000116381f, -0.000115445f, -0.000114546f, -0.000113628f, - -0.000112709f, -0.000111789f, -0.000110869f, -0.000109948f, -0.000109027f, - -0.000108106f, -0.000107184f, -0.000106262f, -0.000105340f, -0.000104415f, - -0.000103494f, -0.000102570f, -0.000101647f, -0.000100723f, -0.000099801f, - -0.000098877f, -0.000097955f, -0.000097031f, -0.000096109f, -0.000095187f, - -0.000094265f, -0.000093343f, -0.000092422f, -0.000091502f, -0.000090580f, - -0.000089660f, -0.000088741f, -0.000087823f, -0.000086904f, -0.000085988f, - -0.000085071f, -0.000084155f, -0.000083240f, -0.000082325f, -0.000081412f, - -0.000080500f, -0.000079590f, -0.000078678f, -0.000077770f, -0.000076862f, - -0.000075955f, -0.000075049f, -0.000074145f, -0.000073241f, -0.000072340f, - -0.000071440f, -0.000070541f, -0.000069643f, -0.000068747f, -0.000067852f, - -0.000066960f, -0.000066068f, -0.000065166f, -0.000064289f, -0.000063405f, - -0.000062519f, -0.000061636f, -0.000060757f, -0.000059876f, -0.000058998f, - -0.000058125f, -0.000057249f, -0.000056379f, -0.000055509f, -0.000054643f, - -0.000053776f, -0.000052913f, -0.000052053f, -0.000051194f, -0.000050337f, - -0.000049485f, -0.000048630f, -0.000047781f, -0.000046935f, -0.000046090f, - -0.000045246f, -0.000044407f, -0.000043570f, -0.000042734f, -0.000041903f, - -0.000041073f, -0.000040242f, -0.000039421f, -0.000038599f, -0.000037779f, - -0.000036962f, -0.000036148f, -0.000035339f, -0.000034529f, -0.000033723f, - -0.000032922f, -0.000032122f, -0.000031324f, -0.000030532f, -0.000029741f, - -0.000028951f, -0.000028169f, -0.000027386f, -0.000026607f, -0.000025833f, - -0.000025058f, -0.000024288f, -0.000023524f, -0.000022760f, -0.000022001f, - -0.000021243f, -0.000020491f, -0.000019740f, -0.000018986f, -0.000018249f, - -0.000017510f, -0.000016774f, -0.000016038f, -0.000015311f, -0.000014583f, - -0.000013859f, -0.000013139f, -0.000012424f, -0.000011712f, -0.000011003f, - -0.000010296f, -0.000009594f, -0.000008895f, -0.000008201f, -0.000007508f, - -0.000006821f, -0.000006137f, -0.000005458f, -0.000004780f, -0.000004107f, - -0.000003438f, -0.000002770f, -0.000002110f, -0.000001453f, -0.000000797f, - -0.000000146f, 0.000000501f, 0.000001144f, 0.000001782f, 0.000002420f, - 0.000003050f, 0.000003677f, 0.000004301f, 0.000004923f, 0.000005536f, - 0.000006150f, 0.000006758f, 0.000007361f, 0.000007963f, 0.000008560f, - 0.000009151f, 0.000009741f, 0.000010326f, 0.000010905f, 0.000011482f, - 0.000012054f, 0.000012624f, 0.000013188f, 0.000013747f, 0.000014306f, - 0.000014856f, 0.000015406f, 0.000015951f, 0.000016493f, 0.000017026f, - 0.000017560f, 0.000018085f, 0.000018612f, 0.000019130f, 0.000019647f, - 0.000020158f, 0.000020666f, 0.000021168f, 0.000021671f, 0.000022165f, - 0.000022653f, 0.000023143f, 0.000023624f, 0.000024104f, 0.000024579f, - 0.000025048f, 0.000025516f, 0.000025976f, 0.000026437f, 0.000026888f, - 0.000027340f, 0.000027785f, 0.000028226f, 0.000028664f, 0.000029097f, - 0.000029526f, 0.000029950f, 0.000030371f, 0.000030789f, 0.000031200f, - 0.000031610f, 0.000032012f, 0.000032413f, 0.000032808f, 0.000033202f, - 0.000033589f, 0.000033971f, 0.000034352f, 0.000034725f, 0.000035098f, - 0.000035464f, 0.000035828f, 0.000036187f, 0.000036540f, 0.000036892f, - 0.000037238f, 0.000037580f, 0.000037918f, 0.000038252f, 0.000038582f, - 0.000038907f, 0.000039231f, 0.000039549f, 0.000039860f, 0.000040172f, - 0.000040476f, 0.000040780f, 0.000041076f, 0.000041369f, 0.000041659f, - 0.000041946f, 0.000042226f, 0.000042504f, 0.000042778f, 0.000043048f, - 0.000043313f, 0.000043575f, 0.000043831f, 0.000044088f, 0.000044335f, - 0.000044581f, 0.000044825f, 0.000045061f, 0.000045295f, 0.000045526f, - 0.000045752f, 0.000045975f, 0.000046193f, 0.000046407f, 0.000046619f, - 0.000046826f, 0.000047030f, 0.000047228f, 0.000047424f, 0.000047616f, - 0.000047805f, 0.000047988f, 0.000048169f, 0.000048346f, 0.000048519f, - 0.000048689f, 0.000048853f, 0.000049016f, 0.000049174f, 0.000049329f, - 0.000049480f, 0.000049627f, 0.000049771f, 0.000049912f, 0.000050047f, - 0.000050181f, 0.000050310f, 0.000050437f, 0.000050558f, 0.000050677f, - 0.000050792f, 0.000050904f, 0.000051012f, 0.000051118f, 0.000051214f, - 0.000051316f, 0.000051410f, 0.000051503f, 0.000051589f, 0.000051674f, - 0.000051755f, 0.000051832f, 0.000051906f, 0.000051977f, 0.000052044f, - 0.000052109f, 0.000052171f, 0.000052229f, 0.000052283f, 0.000052333f, - 0.000052382f, 0.000052427f, 0.000052468f, 0.000052508f, 0.000052543f, - 0.000052574f, 0.000052604f, 0.000052631f, 0.000052653f, 0.000052674f, - 0.000052691f, 0.000052704f, 0.000052716f, 0.000052724f, 0.000052729f, - 0.000052730f, 0.000052730f, 0.000052726f, 0.000052720f, 0.000052710f, - 0.000052697f, 0.000052682f, 0.000052664f, 0.000052643f, 0.000052620f, - 0.000052593f, 0.000052564f, 0.000052531f, 0.000052498f, 0.000052460f, - 0.000052420f, 0.000052377f, 0.000052333f, 0.000052284f, 0.000052234f, - 0.000052180f, 0.000052125f, 0.000052067f, 0.000052006f, 0.000051938f, - 0.000051877f, 0.000051809f, 0.000051738f, 0.000051666f, 0.000051590f, - 0.000051512f, 0.000051432f, 0.000051349f, 0.000051265f, 0.000051177f, - 0.000051088f, 0.000050996f, 0.000050901f, 0.000050805f, 0.000050707f, - 0.000050606f, 0.000050503f, 0.000050398f, 0.000050291f, 0.000050182f, - 0.000050070f, 0.000049957f, 0.000049842f, 0.000049724f, 0.000049604f, - 0.000049483f, 0.000049358f, 0.000049234f, 0.000049105f, 0.000048977f, - 0.000048845f, 0.000048712f, 0.000048577f, 0.000048439f, 0.000048302f, - 0.000048161f, 0.000048019f, 0.000047874f, 0.000047728f, 0.000047581f, - 0.000047431f, 0.000047280f, 0.000047128f, 0.000046974f, 0.000046818f, - 0.000046660f, 0.000046501f, 0.000046341f, 0.000046178f, 0.000046015f, - 0.000045849f, 0.000045683f, 0.000045514f, 0.000045344f, 0.000045168f, - 0.000045001f, 0.000044827f, 0.000044652f, 0.000044475f, 0.000044297f, - 0.000044117f, 0.000043937f, 0.000043755f, 0.000043572f, 0.000043387f, - 0.000043201f, 0.000043015f, 0.000042827f, 0.000042638f, 0.000042447f, - 0.000042256f, 0.000042063f, 0.000041869f, 0.000041675f, 0.000041479f, - 0.000041282f, 0.000041084f, 0.000040885f, 0.000040685f, 0.000040484f, - 0.000040283f, 0.000040080f, 0.000039876f, 0.000039672f, 0.000039466f, - 0.000039260f, 0.000039053f, 0.000038845f, 0.000038636f, 0.000038427f, - 0.000038217f, 0.000038006f, 0.000037794f, 0.000037581f, 0.000037368f, - 0.000037155f, 0.000036940f, 0.000036725f, 0.000036509f, 0.000036293f, - 0.000036076f, 0.000035858f, 0.000035640f, 0.000035422f, 0.000035202f, - 0.000034983f, 0.000034763f, 0.000034542f, 0.000034321f, 0.000034100f, - 0.000033873f, 0.000033655f, 0.000033432f, 0.000033209f, 0.000032986f, - 0.000032762f, 0.000032538f, 0.000032313f, 0.000032089f, 0.000031864f, - 0.000031638f, 0.000031413f, 0.000031187f, 0.000030961f, 0.000030735f, - 0.000030508f, 0.000030282f, 0.000030055f, 0.000029828f, 0.000029601f, - 0.000029374f, 0.000029147f, 0.000028920f, 0.000028692f, 0.000028465f, - 0.000028237f, 0.000028010f, 0.000027782f, 0.000027555f, 0.000027328f, - 0.000027100f, 0.000026873f, 0.000026645f, 0.000026418f, 0.000026191f, - 0.000025964f, 0.000025737f, 0.000025510f, 0.000025283f, 0.000025057f, - 0.000024830f, 0.000024604f, 0.000024378f, 0.000024152f, 0.000023927f, - 0.000023702f, 0.000023476f, 0.000023252f, 0.000023027f, 0.000022803f, - 0.000022579f, 0.000022355f, 0.000022132f, 0.000021908f, 0.000021686f, - 0.000021460f, 0.000021241f, 0.000021020f, 0.000020798f, 0.000020578f, - 0.000020357f, 0.000020137f, 0.000019917f, 0.000019698f, 0.000019480f, - 0.000019261f, 0.000019044f, 0.000018826f, 0.000018609f, 0.000018393f, - 0.000018177f, 0.000017962f, 0.000017748f, 0.000017533f, 0.000017320f, - 0.000017107f, 0.000016894f, 0.000016682f, 0.000016471f, 0.000016260f, - 0.000016050f, 0.000015841f, 0.000015632f, 0.000015424f, 0.000015216f, - 0.000015009f, 0.000014803f, 0.000014597f, 0.000014393f, 0.000014188f, - 0.000013985f, 0.000013782f, 0.000013580f, 0.000013379f, 0.000013178f, - 0.000012978f, 0.000012779f, 0.000012581f, 0.000012383f, 0.000012186f, - 0.000011990f, 0.000011795f, 0.000011600f, 0.000011407f, 0.000011214f, - 0.000011022f, 0.000010830f, 0.000010640f, 0.000010450f, 0.000010262f, - 0.000010074f, 0.000009884f, 0.000009700f, 0.000009515f, 0.000009331f, - 0.000009147f, 0.000008964f, 0.000008782f, 0.000008601f, 0.000008421f, - 0.000008242f, 0.000008064f, 0.000007887f, 0.000007710f, 0.000007535f, - 0.000007360f, 0.000007186f, 0.000007014f, 0.000006842f, 0.000006671f, - 0.000006501f, 0.000006332f, 0.000006164f, 0.000005997f, 0.000005831f, - 0.000005667f, 0.000005502f, 0.000005339f, 0.000005177f, 0.000005016f, - 0.000004855f, 0.000004696f, 0.000004538f, 0.000004381f, 0.000004225f, - 0.000004070f, 0.000003916f, 0.000003763f, 0.000003610f, 0.000003459f, - 0.000003309f, 0.000003160f, 0.000003012f, 0.000002865f, 0.000002719f, - 0.000002574f, 0.000002430f, 0.000002287f, 0.000002146f, 0.000002005f, - 0.000001865f, 0.000001726f, 0.000001589f, 0.000001452f, 0.000001316f, - 0.000001182f, 0.000001047f, 0.000000916f, 0.000000785f, 0.000000654f, - 0.000000525f, 0.000000397f, 0.000000270f, 0.000000143f, 0.000000019f, - -0.000000106f, -0.000000228f, -0.000000351f, -0.000000471f, -0.000000591f, - -0.000000710f, -0.000000827f, -0.000000944f, -0.000001059f, -0.000001174f, - -0.000001287f, -0.000001399f, -0.000001511f, -0.000001621f, -0.000001730f, - -0.000001838f, -0.000001945f, -0.000002051f, -0.000002156f, -0.000002260f, - -0.000002363f, -0.000002465f, -0.000002565f, -0.000002665f, -0.000002763f, - -0.000002861f, -0.000002958f, -0.000003053f, -0.000003147f, -0.000003241f, - -0.000003333f, -0.000003424f, -0.000003514f, -0.000003604f, -0.000003692f, - -0.000003779f, -0.000003865f, -0.000003950f, -0.000004034f, -0.000004117f, - -0.000004199f, -0.000004280f, -0.000004360f, -0.000004439f, -0.000004517f, - -0.000004594f, -0.000004670f, -0.000004744f, -0.000004819f, -0.000004891f, - -0.000004963f, -0.000005034f, -0.000005104f, -0.000005173f, -0.000005240f, - -0.000005307f, -0.000005373f, -0.000005438f, -0.000005502f, -0.000005565f, - -0.000005627f, -0.000005688f, -0.000005748f, -0.000005807f, -0.000005866f, - -0.000005922f, -0.000005979f, -0.000006034f, -0.000006089f, -0.000006142f, - -0.000006194f, -0.000006246f, -0.000006297f, -0.000006346f, -0.000006395f, - -0.000006443f, -0.000006490f, -0.000006536f, -0.000006581f, -0.000006625f, - -0.000006668f, -0.000006711f, -0.000006752f, -0.000006793f, -0.000006833f, - -0.000006872f, -0.000006909f, -0.000006947f, -0.000006983f, -0.000007018f, - -0.000007053f, -0.000007086f, -0.000007119f, -0.000007151f, -0.000007183f, - -0.000007213f, -0.000007242f, -0.000007271f, -0.000007299f, -0.000007326f, - -0.000007352f, -0.000007377f, -0.000007402f, -0.000007426f, -0.000007449f, - -0.000007471f, -0.000007493f, -0.000007513f, -0.000007533f, -0.000007552f, - -0.000007570f, -0.000007588f, -0.000007605f, -0.000007621f, -0.000007636f, - -0.000007651f, -0.000007665f, -0.000007678f, -0.000007690f, -0.000007702f, - -0.000007713f, -0.000007723f, -0.000007733f, -0.000007742f, -0.000007750f, - -0.000007757f, -0.000007764f, -0.000007770f, -0.000007775f, -0.000007780f, - -0.000007784f, -0.000007788f, -0.000007791f, -0.000007793f, -0.000007794f, - -0.000007795f, -0.000007795f, -0.000007795f, -0.000007794f, -0.000007792f, - -0.000007790f, -0.000007787f, -0.000007784f, -0.000007780f, -0.000007775f, - -0.000007770f, -0.000007764f, -0.000007758f, -0.000007751f, -0.000007743f, - -0.000007735f, -0.000007726f, -0.000007717f, -0.000007708f, -0.000007698f, - -0.000007687f, -0.000007675f, -0.000007663f, -0.000007651f, -0.000007638f, - -0.000007625f, -0.000007611f, -0.000007597f, -0.000007582f, -0.000007567f, - -0.000007551f, -0.000007535f, -0.000007518f, -0.000007501f, -0.000007483f, - -0.000007465f, -0.000007446f, -0.000007427f, -0.000007408f, -0.000007388f, - -0.000007368f, -0.000007347f, -0.000007326f, -0.000007305f, -0.000007283f, - -0.000007261f, -0.000007238f, -0.000007215f, -0.000007191f, -0.000007168f, - -0.000007143f, -0.000007119f, -0.000007094f, -0.000007068f, -0.000007043f, - -0.000007017f, -0.000006991f, -0.000006964f, -0.000006937f, -0.000006909f, - -0.000006882f, -0.000006854f, -0.000006826f, -0.000006797f, -0.000006768f, - -0.000006739f, -0.000006710f, -0.000006680f, -0.000006650f, -0.000006620f, - -0.000006589f, -0.000006558f, -0.000006527f, -0.000006496f, -0.000006464f, - -0.000006432f, -0.000006400f, -0.000006368f, -0.000006336f, -0.000006303f, - -0.000006270f, -0.000006237f, -0.000006203f, -0.000006170f, -0.000006136f, - -0.000006102f, -0.000006068f, -0.000006033f, -0.000005999f, -0.000005964f, - -0.000005929f, -0.000005894f, -0.000005859f, -0.000005823f, -0.000005788f, - -0.000005752f, -0.000005716f, -0.000005680f, -0.000005644f, -0.000005608f, - -0.000005571f, -0.000005535f, -0.000005498f, -0.000005461f, -0.000005424f, - -0.000005388f, -0.000005350f, -0.000005313f, -0.000005276f, -0.000005239f, - -0.000005201f, -0.000005164f, -0.000005126f, -0.000005088f, -0.000005051f, - -0.000005013f, -0.000004975f, -0.000004937f, -0.000004899f, -0.000004861f, - -0.000004823f, -0.000004784f, -0.000004746f, -0.000004708f, -0.000004670f, - -0.000004632f, -0.000004593f, -0.000004555f, -0.000004517f, -0.000004478f, - -0.000004440f, -0.000004401f, -0.000004363f, -0.000004324f, -0.000004286f, - -0.000004248f, -0.000004210f, -0.000004171f, -0.000004133f, -0.000004095f, - -0.000004056f, -0.000004018f, -0.000003980f, -0.000003942f, -0.000003904f, - -0.000003865f, -0.000003827f, -0.000003789f, -0.000003751f, -0.000003714f, - -0.000003676f, -0.000003638f, -0.000003600f, -0.000003562f, -0.000003525f, - -0.000003487f, -0.000003450f, -0.000003412f, -0.000003375f, -0.000003338f, - -0.000003301f, -0.000003264f, -0.000003227f, -0.000003190f, -0.000003153f, - -0.000003117f, -0.000003080f, -0.000003044f, -0.000003007f, -0.000002971f, - -0.000002935f, -0.000002899f, -0.000002863f, -0.000002827f, -0.000002791f, - -0.000002756f, -0.000002720f, -0.000002685f, -0.000002650f, -0.000002615f, - -0.000002580f, -0.000002545f, -0.000002511f, -0.000002476f, -0.000002442f, - -0.000002408f, -0.000002373f, -0.000002340f, -0.000002306f, -0.000002272f, - -0.000002239f, -0.000002205f, -0.000002172f, -0.000002139f, -0.000002106f, - -0.000002074f, -0.000002041f, -0.000002009f, -0.000001977f, -0.000001945f, - -0.000001913f, -0.000001881f, -0.000001850f, -0.000001818f, -0.000001787f, - -0.000001756f, -0.000001726f, -0.000001695f, -0.000001665f, -0.000001634f, - -0.000001604f, -0.000001574f, -0.000001545f, -0.000001515f, -0.000001486f, - -0.000001457f, -0.000001428f, -0.000001399f, -0.000001371f, -0.000001342f, - -0.000001314f, -0.000001286f, -0.000001258f, -0.000001231f, -0.000001203f, - -0.000001176f, -0.000001149f, -0.000001123f, -0.000001096f, -0.000001070f, - 0.000000000f -}; diff --git a/external/sdl/SDL/src/audio/SDL_audiocvt.c b/external/sdl/SDL/src/audio/SDL_audiocvt.c index d16c1c33..12735bc3 100644 --- a/external/sdl/SDL/src/audio/SDL_audiocvt.c +++ b/external/sdl/SDL/src/audio/SDL_audiocvt.c @@ -20,109 +20,14 @@ */ #include "SDL_internal.h" -// Functions for audio drivers to perform runtime conversion of audio format - #include "SDL_audio_c.h" -#include "../SDL_dataqueue.h" +#include "SDL_audioqueue.h" +#include "SDL_audioresample.h" - -/* SDL's resampler uses a "bandlimited interpolation" algorithm: - https://ccrma.stanford.edu/~jos/resample/ */ - -#include "SDL_audio_resampler_filter.h" - -static int GetResamplerPaddingFrames(const int iinrate, const int ioutrate) -{ - /* This function uses integer arithmetics to avoid precision loss caused - * by large floating point numbers. Sint32 is needed for the large number - * multiplication. The integers are assumed to be non-negative so that - * division rounds by truncation. */ - const Sint32 inrate = (Sint32) iinrate; - const Sint32 outrate = (Sint32) ioutrate; - SDL_assert(inrate >= 0); - SDL_assert(outrate >= 0); - if (inrate == outrate) { - return 0; - } else if (inrate > outrate) { - return (int) (((RESAMPLER_SAMPLES_PER_ZERO_CROSSING * inrate) + (outrate - 1)) / outrate); - } - return RESAMPLER_SAMPLES_PER_ZERO_CROSSING; -} - -static int GetHistoryBufferSampleFrames(const Sint32 required_resampler_frames) -{ - /* we want to keep enough input history to successfully resample data between arbitrary - frequencies without causing artifacts at the start of a chunk, but also to retain - history if the output frequency changes midstream. So we always demand at least 5000 - sample frames here--which is enough to cover a sudden resample from 192000Hz to 22050Hz - without problems--if the app is gradually changing the sample rate for a pitch effect - to any destination, this won't be a problem, and if they just need a dramatic - downsample that doesn't change, we give them the buffer they need and that won't be a - problem either. Upsamples don't need this at any frequency. The rest seems like an - acceptable loss. */ - return (int) SDL_max(required_resampler_frames, 5000); -} - -// lpadding and rpadding are expected to be buffers of (GetResamplePadding(inrate, outrate) * chans * sizeof (float)) bytes. -static void ResampleAudio(const int chans, const int inrate, const int outrate, - const float *lpadding, const float *rpadding, - const float *inbuf, const int inframes, - float *outbuf, const int outframes) -{ - /* This function uses integer arithmetics to avoid precision loss caused - * by large floating point numbers. For some operations, Sint32 or Sint64 - * are needed for the large number multiplications. The input integers are - * assumed to be non-negative so that division rounds by truncation and - * modulo is always non-negative. Note that the operator order is important - * for these integer divisions. */ - const int paddinglen = GetResamplerPaddingFrames(inrate, outrate); - float *dst = outbuf; - int i, j, chan; - - for (i = 0; i < outframes; i++) { - int srcindex = (int)((Sint64)i * inrate / outrate); - if (srcindex >= inframes) { // !!! FIXME: can we clamp this without an if statement on each iteration? - srcindex = inframes - 1; - } - - /* Calculating the following way avoids subtraction or modulo of large - * floats which have low result precision. - * interpolation1 - * = (i / outrate * inrate) - floor(i / outrate * inrate) - * = mod(i / outrate * inrate, 1) - * = mod(i * inrate, outrate) / outrate */ - const int srcfraction = ((Sint64)i) * inrate % outrate; - const float interpolation1 = ((float)srcfraction) / ((float)outrate); - const int filterindex1 = ((Sint32)srcfraction) * RESAMPLER_SAMPLES_PER_ZERO_CROSSING / outrate; - const float interpolation2 = 1.0f - interpolation1; - const int filterindex2 = ((Sint32)(outrate - srcfraction)) * RESAMPLER_SAMPLES_PER_ZERO_CROSSING / outrate; - - for (chan = 0; chan < chans; chan++) { - float outsample = 0.0f; - - // do this twice to calculate the sample, once for the "left wing" and then same for the right. - for (j = 0; (filterindex1 + (j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING)) < RESAMPLER_FILTER_SIZE; j++) { - const int filt_ind = filterindex1 + j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING; - const int srcframe = srcindex - j; - /* !!! FIXME: we can bubble this conditional out of here by doing a pre loop. */ - const float insample = (srcframe < 0) ? lpadding[((paddinglen + srcframe) * chans) + chan] : inbuf[(srcframe * chans) + chan]; - outsample += (float) (insample * (ResamplerFilter[filt_ind] + (interpolation1 * ResamplerFilterDifference[filt_ind]))); - } - - // Do the right wing! - for (j = 0; (filterindex2 + (j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING)) < RESAMPLER_FILTER_SIZE; j++) { - const int filt_ind = filterindex2 + j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING; - const int srcframe = srcindex + 1 + j; - // !!! FIXME: we can bubble this conditional out of here by doing a post loop. - const float insample = (srcframe >= inframes) ? rpadding[((srcframe - inframes) * chans) + chan] : inbuf[(srcframe * chans) + chan]; - outsample += (float) (insample * (ResamplerFilter[filt_ind] + (interpolation2 * ResamplerFilterDifference[filt_ind]))); - } - - *(dst++) = outsample; - } - } -} +#ifndef SDL_INT_MAX +#define SDL_INT_MAX ((int)(~0u>>1)) +#endif /* * CHANNEL LAYOUTS AS SDL EXPECTS THEM: @@ -247,7 +152,6 @@ static void AudioConvertByteswap(void *dst, const void *src, int num_samples, in CASESWAP(16); CASESWAP(32); - CASESWAP(64); #undef CASESWAP @@ -259,28 +163,24 @@ static void AudioConvertByteswap(void *dst, const void *src, int num_samples, in static void AudioConvertToFloat(float *dst, const void *src, int num_samples, SDL_AudioFormat src_fmt) { - SDL_assert( (SDL_AUDIO_BITSIZE(src_fmt) <= 8) || ((SDL_AUDIO_ISBIGENDIAN(src_fmt) == 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) ); // This only deals with native byte order. - - switch (src_fmt & ~SDL_AUDIO_MASK_ENDIAN) { + // Endian conversion is handled separately + switch (src_fmt & ~SDL_AUDIO_MASK_BIG_ENDIAN) { case SDL_AUDIO_S8: SDL_Convert_S8_to_F32(dst, (const Sint8 *) src, num_samples); break; case SDL_AUDIO_U8: SDL_Convert_U8_to_F32(dst, (const Uint8 *) src, num_samples); break; - case SDL_AUDIO_S16: SDL_Convert_S16_to_F32(dst, (const Sint16 *) src, num_samples); break; - case SDL_AUDIO_S32: SDL_Convert_S32_to_F32(dst, (const Sint32 *) src, num_samples); break; - case SDL_AUDIO_F32: if (dst != src) { SDL_memcpy(dst, src, num_samples * sizeof (float)); } break; // oh well, just pass it through. + case SDL_AUDIO_S16LE: SDL_Convert_S16_to_F32(dst, (const Sint16 *) src, num_samples); break; + case SDL_AUDIO_S32LE: SDL_Convert_S32_to_F32(dst, (const Sint32 *) src, num_samples); break; default: SDL_assert(!"Unexpected audio format!"); break; } } static void AudioConvertFromFloat(void *dst, const float *src, int num_samples, SDL_AudioFormat dst_fmt) { - SDL_assert( (SDL_AUDIO_BITSIZE(dst_fmt) <= 8) || ((SDL_AUDIO_ISBIGENDIAN(dst_fmt) == 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) ); // This only deals with native byte order. - - switch (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN) { + // Endian conversion is handled separately + switch (dst_fmt & ~SDL_AUDIO_MASK_BIG_ENDIAN) { case SDL_AUDIO_S8: SDL_Convert_F32_to_S8((Sint8 *) dst, src, num_samples); break; case SDL_AUDIO_U8: SDL_Convert_F32_to_U8((Uint8 *) dst, src, num_samples); break; - case SDL_AUDIO_S16: SDL_Convert_F32_to_S16((Sint16 *) dst, src, num_samples); break; - case SDL_AUDIO_S32: SDL_Convert_F32_to_S32((Sint32 *) dst, src, num_samples); break; - case SDL_AUDIO_F32: if (dst != src) { SDL_memcpy(dst, src, num_samples * sizeof (float)); } break; // oh well, just pass it through. + case SDL_AUDIO_S16LE: SDL_Convert_F32_to_S16((Sint16 *) dst, src, num_samples); break; + case SDL_AUDIO_S32LE: SDL_Convert_F32_to_S32((Sint32 *) dst, src, num_samples); break; default: SDL_assert(!"Unexpected audio format!"); break; } } @@ -290,12 +190,12 @@ static SDL_bool SDL_IsSupportedAudioFormat(const SDL_AudioFormat fmt) switch (fmt) { case SDL_AUDIO_U8: case SDL_AUDIO_S8: - case SDL_AUDIO_S16LSB: - case SDL_AUDIO_S16MSB: - case SDL_AUDIO_S32LSB: - case SDL_AUDIO_S32MSB: - case SDL_AUDIO_F32LSB: - case SDL_AUDIO_F32MSB: + case SDL_AUDIO_S16LE: + case SDL_AUDIO_S16BE: + case SDL_AUDIO_S32LE: + case SDL_AUDIO_S32BE: + case SDL_AUDIO_F32LE: + case SDL_AUDIO_F32BE: return SDL_TRUE; // supported. default: @@ -311,15 +211,15 @@ static SDL_bool SDL_IsSupportedChannelCount(const int channels) } -/* this does type and channel conversions _but not resampling_ (resampling - happens in SDL_AudioStream). This expects data to be aligned/padded for - SIMD access. This does not check parameter validity, - (beyond asserts), it expects you did that already! */ -/* all of this has to function as if src==dst (conversion in-place), but as a convenience - if you're just going to copy the final output elsewhere, you can specify a different - output pointer. */ -static void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_format, int src_channels, - void *dst, SDL_AudioFormat dst_format, int dst_channels) +// This does type and channel conversions _but not resampling_ (resampling happens in SDL_AudioStream). +// This does not check parameter validity, (beyond asserts), it expects you did that already! +// All of this has to function as if src==dst==scratch (conversion in-place), but as a convenience +// if you're just going to copy the final output elsewhere, you can specify a different output pointer. +// +// The scratch buffer must be able to store `num_frames * CalculateMaxSampleFrameSize(src_format, src_channels, dst_format, dst_channels)` bytes. +// If the scratch buffer is NULL, this restriction applies to the output buffer instead. +void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_format, int src_channels, + void *dst, SDL_AudioFormat dst_format, int dst_channels, void* scratch) { SDL_assert(src != NULL); SDL_assert(dst != NULL); @@ -336,8 +236,10 @@ static void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_fo SDL_Log("SDL_AUDIO_CONVERT: Convert format %04x->%04x, channels %u->%u", src_format, dst_format, src_channels, dst_channels); #endif - const int dst_bitsize = (int) SDL_AUDIO_BITSIZE(dst_format); const int src_bitsize = (int) SDL_AUDIO_BITSIZE(src_format); + const int dst_bitsize = (int) SDL_AUDIO_BITSIZE(dst_format); + + const int dst_sample_frame_size = (dst_bitsize / 8) * dst_channels; /* Type conversion goes like this now: - byteswap to CPU native format first if necessary. @@ -358,16 +260,16 @@ static void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_fo if (src_format == dst_format) { // nothing to do, we're already in the right format, just copy it over if necessary. if (src != dst) { - SDL_memcpy(dst, src, num_frames * src_channels * (dst_bitsize / 8)); + SDL_memcpy(dst, src, num_frames * dst_sample_frame_size); } return; } // just a byteswap needed? - if ((src_format & ~SDL_AUDIO_MASK_ENDIAN) == (dst_format & ~SDL_AUDIO_MASK_ENDIAN)) { + if ((src_format & ~SDL_AUDIO_MASK_BIG_ENDIAN) == (dst_format & ~SDL_AUDIO_MASK_BIG_ENDIAN)) { if (src_bitsize == 8) { if (src != dst) { - SDL_memcpy(dst, src, num_frames * src_channels * (dst_bitsize / 8)); + SDL_memcpy(dst, src, num_frames * dst_sample_frame_size); } return; // nothing to do, it's a 1-byte format. } @@ -376,21 +278,33 @@ static void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_fo } } + if (scratch == NULL) { + scratch = dst; + } + + const SDL_bool srcbyteswap = (SDL_AUDIO_ISBIGENDIAN(src_format) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN) && (src_bitsize > 8); + const SDL_bool srcconvert = !SDL_AUDIO_ISFLOAT(src_format); + const SDL_bool channelconvert = src_channels != dst_channels; + const SDL_bool dstconvert = !SDL_AUDIO_ISFLOAT(dst_format); + const SDL_bool dstbyteswap = (SDL_AUDIO_ISBIGENDIAN(dst_format) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN) && (dst_bitsize > 8); + // make sure we're in native byte order. - if ((SDL_AUDIO_ISBIGENDIAN(src_format) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN) && (src_bitsize > 8)) { - AudioConvertByteswap(dst, src, num_frames * src_channels, src_bitsize); - src = dst; // we've written to dst, future work will convert in-place. + if (srcbyteswap) { + // No point writing straight to dst. If we only need a byteswap, we wouldn't be bere. + AudioConvertByteswap(scratch, src, num_frames * src_channels, src_bitsize); + src = scratch; } // get us to float format. - if (!SDL_AUDIO_ISFLOAT(src_format)) { - AudioConvertToFloat((float *) dst, src, num_frames * src_channels, src_format); - src = dst; // we've written to dst, future work will convert in-place. + if (srcconvert) { + void* buf = (channelconvert || dstconvert || dstbyteswap) ? scratch : dst; + AudioConvertToFloat((float *) buf, src, num_frames * src_channels, src_format); + src = buf; } // Channel conversion - if (src_channels != dst_channels) { + if (channelconvert) { SDL_AudioChannelConverter channel_converter; SDL_AudioChannelConverter override = NULL; @@ -415,20 +329,22 @@ static void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_fo if (override) { channel_converter = override; } - channel_converter((float *) dst, (float *) src, num_frames); - src = dst; // we've written to dst, future work will convert in-place. + + void* buf = (dstconvert || dstbyteswap) ? scratch : dst; + channel_converter((float *) buf, (const float *) src, num_frames); + src = buf; } // Resampling is not done in here. SDL_AudioStream handles that. // Move to final data type. - if (!SDL_AUDIO_ISFLOAT(dst_format)) { - AudioConvertFromFloat(dst, (float *) src, num_frames * dst_channels, dst_format); - src = dst; // we've written to dst, future work will convert in-place. + if (dstconvert) { + AudioConvertFromFloat(dst, (const float *) src, num_frames * dst_channels, dst_format); + src = dst; } // make sure we're in final byte order. - if ((SDL_AUDIO_ISBIGENDIAN(dst_format) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN) && (dst_bitsize > 8)) { + if (dstbyteswap) { AudioConvertByteswap(dst, src, num_frames * dst_channels, dst_bitsize); src = dst; // we've written to dst, future work will convert in-place. } @@ -436,174 +352,60 @@ static void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_fo SDL_assert(src == dst); // if we got here, we _had_ to have done _something_. Otherwise, we should have memcpy'd! } -// figure out the largest thing we might need for ConvertAudio, which might grow data in-place. -static int CalculateMaxSampleFrameSize(SDL_AudioFormat src_format, int src_channels, SDL_AudioFormat dst_format, int dst_channels) +// Calculate the largest frame size needed to convert between the two formats. +static int CalculateMaxFrameSize(SDL_AudioFormat src_format, int src_channels, SDL_AudioFormat dst_format, int dst_channels) { - const int src_format_size = SDL_AUDIO_BITSIZE(src_format) / 8; - const int dst_format_size = SDL_AUDIO_BITSIZE(dst_format) / 8; + const int src_format_size = SDL_AUDIO_BYTESIZE(src_format); + const int dst_format_size = SDL_AUDIO_BYTESIZE(dst_format); const int max_app_format_size = SDL_max(src_format_size, dst_format_size); - const int max_format_size = SDL_max(max_app_format_size, sizeof (float)); // ConvertAudio converts to float internally. + const int max_format_size = SDL_max(max_app_format_size, sizeof (float)); // ConvertAudio and ResampleAudio use floats. const int max_channels = SDL_max(src_channels, dst_channels); return max_format_size * max_channels; } -// this assumes you're holding the stream's lock (or are still creating the stream). -static int SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_spec, const SDL_AudioSpec *dst_spec) +static Sint64 GetAudioStreamResampleRate(SDL_AudioStream* stream, int src_freq, Sint64 resample_offset) { - /* If increasing channels, do it after resampling, since we'd just - do more work to resample duplicate channels. If we're decreasing, do - it first so we resample the interpolated data instead of interpolating - the resampled data (!!! FIXME: decide if that works in practice, though!). - This is decided in pre_resample_channels. */ + src_freq = (int)((float)src_freq * stream->freq_ratio); - const SDL_AudioFormat src_format = src_spec->format; - const int src_channels = src_spec->channels; - const int src_rate = src_spec->freq; - const SDL_AudioFormat dst_format = dst_spec->format; - const int dst_channels = dst_spec->channels; - const int dst_rate = dst_spec->freq; - const int src_sample_frame_size = (SDL_AUDIO_BITSIZE(src_format) / 8) * src_channels; - const int dst_sample_frame_size = (SDL_AUDIO_BITSIZE(dst_format) / 8) * dst_channels; - const int max_sample_frame_size = CalculateMaxSampleFrameSize(src_format, src_channels, dst_format, dst_channels); - const int prev_history_buffer_frames = stream->history_buffer_frames; - const int pre_resample_channels = SDL_min(src_channels, dst_channels); - const int resampler_padding_frames = GetResamplerPaddingFrames(src_rate, dst_rate); - const size_t resampler_padding_allocation = ((size_t) resampler_padding_frames) * max_sample_frame_size; - const size_t future_buffer_allocation = resampler_padding_allocation; - const int history_buffer_frames = GetHistoryBufferSampleFrames(resampler_padding_frames); - const size_t history_buffer_allocation = history_buffer_frames * max_sample_frame_size; - Uint8 *history_buffer = stream->history_buffer; - Uint8 *future_buffer = stream->future_buffer; - float *padding; + Sint64 resample_rate = SDL_GetResampleRate(src_freq, stream->dst_spec.freq); - // do all the things that can fail upfront, so we can just return an error without changing the stream if anything goes wrong. - - // set up for (possibly new) conversions - - /* grow the padding buffers if necessary; these buffer sizes change if sample rate or source channel count is adjusted. - (we can replace these buffers in `stream` now even if we abandon this function when a later allocation fails, because - it's safe for these buffers to be overallocated and their contents don't matter.) */ - if (stream->resampler_padding_allocation < resampler_padding_allocation) { - // left_padding and right_padding are just scratch buffers, so we don't need to preserve existing contents. - padding = (float *) SDL_aligned_alloc(SDL_SIMDGetAlignment(), resampler_padding_allocation); - if (!padding) { - return SDL_OutOfMemory(); - } - SDL_aligned_free(stream->left_padding); - stream->left_padding = padding; - - padding = (float *) SDL_aligned_alloc(SDL_SIMDGetAlignment(), resampler_padding_allocation); - if (!padding) { - return SDL_OutOfMemory(); - } - SDL_aligned_free(stream->right_padding); - stream->right_padding = padding; - - stream->resampler_padding_allocation = resampler_padding_allocation; + // If src_freq == dst_freq, and we aren't between frames, don't resample + if ((resample_rate == 0x100000000) && (resample_offset == 0)) { + resample_rate = 0; } - // grow the history buffer if necessary; often times this won't be, as it already buffers more than immediately necessary in case of a dramatic downsample. + return resample_rate; +} + +static int UpdateAudioStreamInputSpec(SDL_AudioStream *stream, const SDL_AudioSpec *spec) +{ + if (AUDIO_SPECS_EQUAL(stream->input_spec, *spec)) { + return 0; + } + + const size_t history_buffer_allocation = SDL_GetResamplerHistoryFrames() * SDL_AUDIO_FRAMESIZE(*spec); + Uint8 *history_buffer = stream->history_buffer; + if (stream->history_buffer_allocation < history_buffer_allocation) { history_buffer = (Uint8 *) SDL_aligned_alloc(SDL_SIMDGetAlignment(), history_buffer_allocation); if (!history_buffer) { return SDL_OutOfMemory(); } - } - - // grow the future buffer if necessary; the buffer size changes if sample rate is adjusted. - if (stream->future_buffer_allocation < future_buffer_allocation) { - future_buffer = (Uint8 *) SDL_aligned_alloc(SDL_SIMDGetAlignment(), future_buffer_allocation); - if (!future_buffer) { - if (history_buffer != stream->history_buffer) { - SDL_aligned_free(history_buffer); - } - return SDL_OutOfMemory(); - } - } - - // okay, we've done all the things that can fail, now we can change stream state. - - // copy to new buffers and/or convert data; ConvertAudio will do a simple memcpy if format matches, and nothing at all if the buffer hasn't changed - if (stream->future_buffer) { - ConvertAudio(stream->future_buffer_filled_frames, stream->future_buffer, stream->src_spec.format, stream->src_spec.channels, future_buffer, src_format, src_channels); - } else if (future_buffer != NULL) { - SDL_memset(future_buffer, SDL_GetSilenceValueForFormat(src_format), future_buffer_allocation); - } - - if (stream->history_buffer) { - if (history_buffer_frames <= prev_history_buffer_frames) { - ConvertAudio(history_buffer_frames, stream->history_buffer, stream->src_spec.format, stream->src_spec.channels, history_buffer, src_format, src_channels); - } else { - ConvertAudio(prev_history_buffer_frames, stream->history_buffer, stream->src_spec.format, stream->src_spec.channels, history_buffer + ((history_buffer_frames - prev_history_buffer_frames) * src_sample_frame_size), src_format, src_channels); - SDL_memset(history_buffer, SDL_GetSilenceValueForFormat(src_format), (history_buffer_frames - prev_history_buffer_frames) * src_sample_frame_size); // silence oldest history samples. - } - } else if (history_buffer != NULL) { - SDL_memset(history_buffer, SDL_GetSilenceValueForFormat(src_format), history_buffer_allocation); - } - - if (future_buffer != stream->future_buffer) { - SDL_aligned_free(stream->future_buffer); - stream->future_buffer = future_buffer; - stream->future_buffer_allocation = future_buffer_allocation; - } - - if (history_buffer != stream->history_buffer) { SDL_aligned_free(stream->history_buffer); stream->history_buffer = history_buffer; stream->history_buffer_allocation = history_buffer_allocation; } - stream->resampler_padding_frames = resampler_padding_frames; - stream->history_buffer_frames = history_buffer_frames; - stream->max_sample_frame_size = max_sample_frame_size; - stream->src_sample_frame_size = src_sample_frame_size; - stream->dst_sample_frame_size = dst_sample_frame_size; - stream->pre_resample_channels = pre_resample_channels; - - if (src_spec != &stream->src_spec) { - SDL_memcpy(&stream->src_spec, src_spec, sizeof (SDL_AudioSpec)); - } - - if (dst_spec != &stream->dst_spec) { - SDL_memcpy(&stream->dst_spec, dst_spec, sizeof (SDL_AudioSpec)); - } + SDL_memset(history_buffer, SDL_GetSilenceValueForFormat(spec->format), history_buffer_allocation); + SDL_copyp(&stream->input_spec, spec); return 0; } SDL_AudioStream *SDL_CreateAudioStream(const SDL_AudioSpec *src_spec, const SDL_AudioSpec *dst_spec) { - // !!! FIXME: fail if audio isn't initialized - - if (!src_spec) { - SDL_InvalidParamError("src_spec"); - return NULL; - } else if (!dst_spec) { - SDL_InvalidParamError("dst_spec"); - return NULL; - } else if (!SDL_IsSupportedChannelCount(src_spec->channels)) { - SDL_InvalidParamError("src_spec->channels"); - return NULL; - } else if (!SDL_IsSupportedChannelCount(dst_spec->channels)) { - SDL_InvalidParamError("dst_spec->channels"); - return NULL; - } else if (src_spec->freq <= 0) { - SDL_InvalidParamError("src_spec->freq"); - return NULL; - } else if (dst_spec->freq <= 0) { - SDL_InvalidParamError("dst_spec->freq"); - return NULL; - } else if (src_spec->freq >= SDL_MAX_SINT32 / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) { - SDL_SetError("Source rate is too high"); - return NULL; - } else if (dst_spec->freq >= SDL_MAX_SINT32 / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) { - SDL_SetError("Destination rate is too high"); - return NULL; - } else if (!SDL_IsSupportedAudioFormat(src_spec->format)) { - SDL_InvalidParamError("src_spec->format"); - return NULL; - } else if (!SDL_IsSupportedAudioFormat(dst_spec->format)) { - SDL_InvalidParamError("dst_spec->format"); + if (!SDL_WasInit(SDL_INIT_AUDIO)) { + SDL_SetError("Audio subsystem is not initialized"); return NULL; } @@ -613,24 +415,24 @@ SDL_AudioStream *SDL_CreateAudioStream(const SDL_AudioSpec *src_spec, const SDL_ return NULL; } - const int packetlen = 4096; // !!! FIXME: good enough for now. - retval->queue = SDL_CreateDataQueue(packetlen, (size_t)packetlen * 2); - if (!retval->queue) { - SDL_DestroyAudioStream(retval); - return NULL; // SDL_CreateDataQueue should have called SDL_SetError. + retval->freq_ratio = 1.0f; + retval->queue = SDL_CreateAudioQueue(4096); + + if (retval->queue == NULL) { + SDL_free(retval); + return NULL; } - retval->lock = SDL_GetDataQueueMutex(retval->queue); - SDL_assert(retval->lock != NULL); + retval->lock = SDL_CreateMutex(); + if (retval->lock == NULL) { + SDL_free(retval->queue); + SDL_free(retval); + return NULL; + } - // Make sure we've chosen audio conversion functions (SIMD, scalar, etc.) - SDL_ChooseAudioConverters(); // !!! FIXME: let's do this during SDL_Init + OnAudioStreamCreated(retval); - retval->src_sample_frame_size = (SDL_AUDIO_BITSIZE(src_spec->format) / 8) * src_spec->channels; - retval->packetlen = packetlen; - SDL_memcpy(&retval->src_spec, src_spec, sizeof (SDL_AudioSpec)); - - if (SetAudioStreamFormat(retval, src_spec, dst_spec) == -1) { + if (SDL_SetAudioStreamFormat(retval, src_spec, dst_spec) == -1) { SDL_DestroyAudioStream(retval); return NULL; } @@ -638,7 +440,7 @@ SDL_AudioStream *SDL_CreateAudioStream(const SDL_AudioSpec *src_spec, const SDL_ return retval; } -int SDL_SetAudioStreamGetCallback(SDL_AudioStream *stream, SDL_AudioStreamRequestCallback callback, void *userdata) +int SDL_SetAudioStreamGetCallback(SDL_AudioStream *stream, SDL_AudioStreamCallback callback, void *userdata) { if (!stream) { return SDL_InvalidParamError("stream"); @@ -650,7 +452,7 @@ int SDL_SetAudioStreamGetCallback(SDL_AudioStream *stream, SDL_AudioStreamReques return 0; } -int SDL_SetAudioStreamPutCallback(SDL_AudioStream *stream, SDL_AudioStreamRequestCallback callback, void *userdata) +int SDL_SetAudioStreamPutCallback(SDL_AudioStream *stream, SDL_AudioStreamCallback callback, void *userdata) { if (!stream) { return SDL_InvalidParamError("stream"); @@ -677,14 +479,22 @@ int SDL_GetAudioStreamFormat(SDL_AudioStream *stream, SDL_AudioSpec *src_spec, S if (!stream) { return SDL_InvalidParamError("stream"); } + SDL_LockMutex(stream->lock); if (src_spec) { - SDL_memcpy(src_spec, &stream->src_spec, sizeof (SDL_AudioSpec)); + SDL_copyp(src_spec, &stream->src_spec); } if (dst_spec) { - SDL_memcpy(dst_spec, &stream->dst_spec, sizeof (SDL_AudioSpec)); + SDL_copyp(dst_spec, &stream->dst_spec); } SDL_UnlockMutex(stream->lock); + + if (src_spec && src_spec->format == 0) { + return SDL_SetError("Stream has no source format"); + } else if (dst_spec && dst_spec->format == 0) { + return SDL_SetError("Stream has no destination format"); + } + return 0; } @@ -694,6 +504,10 @@ int SDL_SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_s return SDL_InvalidParamError("stream"); } + // Picked mostly arbitrarily. + static const int min_freq = 4000; + static const int max_freq = 384000; + if (src_spec) { if (!SDL_IsSupportedAudioFormat(src_spec->format)) { return SDL_InvalidParamError("src_spec->format"); @@ -701,7 +515,9 @@ int SDL_SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_s return SDL_InvalidParamError("src_spec->channels"); } else if (src_spec->freq <= 0) { return SDL_InvalidParamError("src_spec->freq"); - } else if (src_spec->freq >= SDL_MAX_SINT32 / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) { + } else if (src_spec->freq < min_freq) { + return SDL_SetError("Source rate is too low"); + } else if (src_spec->freq > max_freq) { return SDL_SetError("Source rate is too high"); } } @@ -713,47 +529,151 @@ int SDL_SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_s return SDL_InvalidParamError("dst_spec->channels"); } else if (dst_spec->freq <= 0) { return SDL_InvalidParamError("dst_spec->freq"); - } else if (dst_spec->freq >= SDL_MAX_SINT32 / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) { + } else if (dst_spec->freq < min_freq) { + return SDL_SetError("Destination rate is too low"); + } else if (dst_spec->freq > max_freq) { return SDL_SetError("Destination rate is too high"); } } SDL_LockMutex(stream->lock); - const int retval = SetAudioStreamFormat(stream, src_spec ? src_spec : &stream->src_spec, dst_spec ? dst_spec : &stream->dst_spec); + + // quietly refuse to change the format of the end currently bound to a device. + if (stream->bound_device) { + if (stream->bound_device->physical_device->iscapture) { + dst_spec = NULL; + } else { + src_spec = NULL; + } + } + + if (src_spec) { + SDL_copyp(&stream->src_spec, src_spec); + } + + if (dst_spec) { + SDL_copyp(&stream->dst_spec, dst_spec); + } + SDL_UnlockMutex(stream->lock); - return retval; + return 0; +} + +float SDL_GetAudioStreamFrequencyRatio(SDL_AudioStream *stream) +{ + if (!stream) { + SDL_InvalidParamError("stream"); + return 0.0f; + } + + SDL_LockMutex(stream->lock); + float freq_ratio = stream->freq_ratio; + SDL_UnlockMutex(stream->lock); + + return freq_ratio; +} + +int SDL_SetAudioStreamFrequencyRatio(SDL_AudioStream *stream, float freq_ratio) +{ + if (!stream) { + return SDL_InvalidParamError("stream"); + } + + // Picked mostly arbitrarily. + static const float min_freq_ratio = 0.01f; + static const float max_freq_ratio = 100.0f; + + if (freq_ratio < min_freq_ratio) { + return SDL_SetError("Frequency ratio is too low"); + } else if (freq_ratio > max_freq_ratio) { + return SDL_SetError("Frequency ratio is too high"); + } + + SDL_LockMutex(stream->lock); + stream->freq_ratio = freq_ratio; + SDL_UnlockMutex(stream->lock); + + return 0; +} + +static int CheckAudioStreamIsFullySetup(SDL_AudioStream *stream) +{ + if (stream->src_spec.format == 0) { + return SDL_SetError("Stream has no source format"); + } else if (stream->dst_spec.format == 0) { + return SDL_SetError("Stream has no destination format"); + } + + return 0; } int SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len) { #if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: wants to put %d preconverted bytes", len); + SDL_Log("AUDIOSTREAM: wants to put %d bytes", len); #endif if (stream == NULL) { return SDL_InvalidParamError("stream"); } else if (buf == NULL) { return SDL_InvalidParamError("buf"); + } else if (len < 0) { + return SDL_InvalidParamError("len"); } else if (len == 0) { return 0; // nothing to do. } SDL_LockMutex(stream->lock); - const int prev_available = stream->put_callback ? SDL_GetAudioStreamAvailable(stream) : 0; + if (CheckAudioStreamIsFullySetup(stream) != 0) { + SDL_UnlockMutex(stream->lock); + return -1; + } - if ((len % stream->src_sample_frame_size) != 0) { + if ((len % SDL_AUDIO_FRAMESIZE(stream->src_spec)) != 0) { SDL_UnlockMutex(stream->lock); return SDL_SetError("Can't add partial sample frames"); } - // just queue the data, we convert/resample when dequeueing. - const int retval = SDL_WriteToDataQueue(stream->queue, buf, len); - stream->flushed = SDL_FALSE; + SDL_AudioTrack* track = NULL; - if (stream->put_callback) { - stream->put_callback(stream, SDL_GetAudioStreamAvailable(stream) - prev_available, stream->put_callback_userdata); + // When copying in large amounts of data, try and do as much work as possible + // outside of the stream lock, otherwise the output device is likely to be starved. + const int large_input_thresh = 1024 * 1024; + + if (len >= large_input_thresh) { + SDL_AudioSpec src_spec; + SDL_copyp(&src_spec, &stream->src_spec); + + SDL_UnlockMutex(stream->lock); + + size_t chunk_size = SDL_GetAudioQueueChunkSize(stream->queue); + track = SDL_CreateChunkedAudioTrack(&src_spec, buf, len, chunk_size); + + if (track == NULL) { + return -1; + } + + SDL_LockMutex(stream->lock); + } + + const int prev_available = stream->put_callback ? SDL_GetAudioStreamAvailable(stream) : 0; + + int retval = 0; + + if (track != NULL) { + SDL_AddTrackToAudioQueue(stream->queue, track); + } else { + retval = SDL_WriteToAudioQueue(stream->queue, &stream->src_spec, buf, len); + } + + if (retval == 0) { + stream->total_bytes_queued += len; + if (stream->put_callback) { + const int newavail = SDL_GetAudioStreamAvailable(stream) - prev_available; + stream->put_callback(stream->put_callback_userdata, stream, newavail, newavail); + } } SDL_UnlockMutex(stream->lock); @@ -761,7 +681,6 @@ int SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len) return retval; } - int SDL_FlushAudioStream(SDL_AudioStream *stream) { if (stream == NULL) { @@ -769,7 +688,7 @@ int SDL_FlushAudioStream(SDL_AudioStream *stream) } SDL_LockMutex(stream->lock); - stream->flushed = SDL_TRUE; + SDL_FlushAudioQueue(stream->queue); SDL_UnlockMutex(stream->lock); return 0; @@ -777,7 +696,7 @@ int SDL_FlushAudioStream(SDL_AudioStream *stream) /* this does not save the previous contents of stream->work_buffer. It's a work buffer!! The returned buffer is aligned/padded for use with SIMD instructions. */ -static Uint8 *EnsureStreamWorkBufferSize(SDL_AudioStream *stream, size_t newlen) +static Uint8 *EnsureAudioStreamWorkBufferSize(SDL_AudioStream *stream, size_t newlen) { if (stream->work_buffer_allocation >= newlen) { return stream->work_buffer; @@ -795,221 +714,288 @@ static Uint8 *EnsureStreamWorkBufferSize(SDL_AudioStream *stream, size_t newlen) return ptr; } -static int CalculateAudioStreamWorkBufSize(const SDL_AudioStream *stream, int len) +static void UpdateAudioStreamHistoryBuffer(SDL_AudioStream* stream, + Uint8* input_buffer, int input_bytes, Uint8* left_padding, int padding_bytes) { - int workbuflen = len; - int workbuf_frames = len / stream->dst_sample_frame_size; // start with requested sample frames - int inputlen = workbuf_frames * stream->max_sample_frame_size; + const int history_buffer_frames = SDL_GetResamplerHistoryFrames(); - if (inputlen > workbuflen) { - workbuflen = inputlen; + // Even if we aren't currently resampling, we always need to update the history buffer + Uint8 *history_buffer = stream->history_buffer; + int history_bytes = history_buffer_frames * SDL_AUDIO_FRAMESIZE(stream->input_spec); + + if (left_padding != NULL) { + // Fill in the left padding using the history buffer + SDL_assert(padding_bytes <= history_bytes); + SDL_memcpy(left_padding, history_buffer + history_bytes - padding_bytes, padding_bytes); } - if (stream->dst_spec.freq != stream->src_spec.freq) { - // calculate requested sample frames needed before resampling. Use a Uint64 so the multiplication doesn't overflow. - const int input_frames = ((int) ((((Uint64) workbuf_frames) * stream->src_spec.freq) / stream->dst_spec.freq)); - inputlen = input_frames * stream->max_sample_frame_size; - if (inputlen > workbuflen) { - workbuflen = inputlen; - } - // Calculate space needed to move to format/channels used for resampling stage. - inputlen = input_frames * stream->pre_resample_channels * sizeof (float); - if (inputlen > workbuflen) { - workbuflen = inputlen; - } - // Calculate space needed after resample (which lives in a second copy in the same buffer). - workbuflen += workbuf_frames * stream->pre_resample_channels * sizeof (float); + // Update the history buffer using the new input data + if (input_bytes >= history_bytes) { + SDL_memcpy(history_buffer, input_buffer + (input_bytes - history_bytes), history_bytes); + } else { + int preserve_bytes = history_bytes - input_bytes; + SDL_memmove(history_buffer, history_buffer + input_bytes, preserve_bytes); + SDL_memcpy(history_buffer + preserve_bytes, input_buffer, input_bytes); + } +} + +static Sint64 NextAudioStreamIter(SDL_AudioStream* stream, void** inout_iter, + Sint64* inout_resample_offset, SDL_AudioSpec* out_spec, SDL_bool* out_flushed) +{ + SDL_AudioSpec spec; + SDL_bool flushed; + size_t queued_bytes = SDL_NextAudioQueueIter(stream->queue, inout_iter, &spec, &flushed); + + if (out_spec) { + SDL_copyp(out_spec, &spec); } - return workbuflen; + // There is infinite audio available, whether or not we are resampling + if (queued_bytes == SDL_SIZE_MAX) { + *inout_resample_offset = 0; + + if (out_flushed) { + *out_flushed = SDL_FALSE; + } + + return SDL_MAX_SINT32; + } + + Sint64 resample_offset = *inout_resample_offset; + Sint64 resample_rate = GetAudioStreamResampleRate(stream, spec.freq, resample_offset); + Sint64 output_frames = (Sint64)(queued_bytes / SDL_AUDIO_FRAMESIZE(spec)); + + if (resample_rate) { + // Resampling requires padding frames to the left and right of the current position. + // Past the end of the track, the right padding is filled with silence. + // But we only want to do that if the track is actually finished (flushed). + if (!flushed) { + output_frames -= SDL_GetResamplerPaddingFrames(resample_rate); + } + + output_frames = SDL_GetResamplerOutputFrames(output_frames, resample_rate, &resample_offset); + } + + if (flushed) { + resample_offset = 0; + } + + *inout_resample_offset = resample_offset; + + if (out_flushed) { + *out_flushed = flushed; + } + + return output_frames; +} + +static Sint64 GetAudioStreamAvailableFrames(SDL_AudioStream* stream, Sint64* out_resample_offset) +{ + void* iter = SDL_BeginAudioQueueIter(stream->queue); + + Sint64 resample_offset = stream->resample_offset; + Sint64 output_frames = 0; + + while (iter) { + output_frames += NextAudioStreamIter(stream, &iter, &resample_offset, NULL, NULL); + + // Already got loads of frames. Just clamp it to something reasonable + if (output_frames >= SDL_MAX_SINT32) { + output_frames = SDL_MAX_SINT32; + break; + } + } + + if (out_resample_offset) { + *out_resample_offset = resample_offset; + } + + return output_frames; +} + +static Sint64 GetAudioStreamHead(SDL_AudioStream* stream, SDL_AudioSpec* out_spec, SDL_bool* out_flushed) +{ + void* iter = SDL_BeginAudioQueueIter(stream->queue); + + if (iter == NULL) { + SDL_zerop(out_spec); + *out_flushed = SDL_FALSE; + return 0; + } + + Sint64 resample_offset = stream->resample_offset; + return NextAudioStreamIter(stream, &iter, &resample_offset, out_spec, out_flushed); } // You must hold stream->lock and validate your parameters before calling this! -static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int len) +// Enough input data MUST be available! +static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int output_frames) { - const int max_available = SDL_GetAudioStreamAvailable(stream); - const SDL_AudioFormat src_format = stream->src_spec.format; - const int src_channels = stream->src_spec.channels; - const int src_rate = stream->src_spec.freq; - const int src_sample_frame_size = stream->src_sample_frame_size; - const SDL_AudioFormat dst_format = stream->dst_spec.format; - const int dst_channels = stream->dst_spec.channels; - const int dst_rate = stream->dst_spec.freq; - const int dst_sample_frame_size = stream->dst_sample_frame_size; - const int max_sample_frame_size = stream->max_sample_frame_size; - const int pre_resample_channels = stream->pre_resample_channels; - const int resampler_padding_frames = stream->resampler_padding_frames; - const int history_buffer_frames = stream->history_buffer_frames; - int future_buffer_filled_frames = stream->future_buffer_filled_frames; - Uint8 *future_buffer = stream->future_buffer; - Uint8 *history_buffer = stream->history_buffer; - float *resample_outbuf; - int input_frames; - int output_frames; - Uint8 *workbuf; - int workbuflen; - int workbuf_frames; - int br; + const SDL_AudioSpec* src_spec = &stream->input_spec; + const SDL_AudioSpec* dst_spec = &stream->dst_spec; + + const SDL_AudioFormat src_format = src_spec->format; + const int src_channels = src_spec->channels; + const int src_frame_size = SDL_AUDIO_FRAMESIZE(*src_spec); + + const SDL_AudioFormat dst_format = dst_spec->format; + const int dst_channels = dst_spec->channels; + + const int max_frame_size = CalculateMaxFrameSize(src_format, src_channels, dst_format, dst_channels); + const Sint64 resample_rate = GetAudioStreamResampleRate(stream, src_spec->freq, stream->resample_offset); #if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: asking for an output chunk of %d bytes.", len); + SDL_Log("AUDIOSTREAM: asking for %d frames.", output_frames); #endif - if (len > max_available) { - len = max_available; + SDL_assert(output_frames > 0); + + // Not resampling? It's an easy conversion (and maybe not even that!) + if (resample_rate == 0) { + Uint8* input_buffer = NULL; + + // If no conversion is happening, read straight into the output buffer. + // Note, this is just to avoid extra copies. + // Some other formats may fit directly into the output buffer, but i'd rather process data in a SIMD-aligned buffer. + if ((src_format == dst_format) && (src_channels == dst_channels)) { + input_buffer = buf; + } else { + input_buffer = EnsureAudioStreamWorkBufferSize(stream, output_frames * max_frame_size); + + if (!input_buffer) { + return -1; + } + } + + const int input_bytes = output_frames * src_frame_size; + if (SDL_ReadFromAudioQueue(stream->queue, input_buffer, input_bytes) != 0) { + SDL_assert(!"Not enough data in queue (read)"); + } + + stream->total_bytes_queued -= input_bytes; + + // Even if we aren't currently resampling, we always need to update the history buffer + UpdateAudioStreamHistoryBuffer(stream, input_buffer, input_bytes, NULL, 0); + + // Convert the data, if necessary + if (buf != input_buffer) { + ConvertAudio(output_frames, input_buffer, src_format, src_channels, buf, dst_format, dst_channels, input_buffer); + } + + return 0; } - output_frames = len / dst_sample_frame_size; + // Time to do some resampling! + // Calculate the number of input frames necessary for this request. + // Because resampling happens "between" frames, The same number of output_frames + // can require a different number of input_frames, depending on the resample_offset. + // Infact, input_frames can sometimes even be zero when upsampling. + const int input_frames = (int) SDL_GetResamplerInputFrames(output_frames, resample_rate, stream->resample_offset); + const int input_bytes = input_frames * src_frame_size; - if (output_frames == 0) { - return 0; // nothing to do. + const int resampler_padding_frames = SDL_GetResamplerPaddingFrames(resample_rate); + + // If increasing channels, do it after resampling, since we'd just + // do more work to resample duplicate channels. If we're decreasing, do + // it first so we resample the interpolated data instead of interpolating + // the resampled data. + const int resample_channels = SDL_min(src_channels, dst_channels); + + // The size of the frame used when resampling + const int resample_frame_size = resample_channels * sizeof(float); + + // The main portion of the work_buffer can be used to store 3 things: + // src_sample_frame_size * (left_padding+input_buffer+right_padding) + // resample_frame_size * (left_padding+input_buffer+right_padding) + // dst_sample_frame_size * output_frames + // + // ResampleAudio also requires an additional buffer if it can't write straight to the output: + // resample_frame_size * output_frames + // + // Note, ConvertAudio requires (num_frames * max_sample_frame_size) of scratch space + const int work_buffer_frames = input_frames + (resampler_padding_frames * 2); + int work_buffer_capacity = work_buffer_frames * max_frame_size; + int resample_buffer_offset = -1; + + // Check if we can resample directly into the output buffer. + // Note, this is just to avoid extra copies. + // Some other formats may fit directly into the output buffer, but i'd rather process data in a SIMD-aligned buffer. + if ((dst_format != SDL_AUDIO_F32) || (dst_channels != resample_channels)) { + // Allocate space for converting the resampled output to the destination format + int resample_convert_bytes = output_frames * max_frame_size; + work_buffer_capacity = SDL_max(work_buffer_capacity, resample_convert_bytes); + + // SIMD-align the buffer + int simd_alignment = (int) SDL_SIMDGetAlignment(); + work_buffer_capacity += simd_alignment - 1; + work_buffer_capacity -= work_buffer_capacity % simd_alignment; + + // Allocate space for the resampled output + int resample_bytes = output_frames * resample_frame_size; + resample_buffer_offset = work_buffer_capacity; + work_buffer_capacity += resample_bytes; } - // !!! FIXME: this could be less aggressive about allocation, if we decide the necessary size at each stage and select the maximum required. - workbuflen = CalculateAudioStreamWorkBufSize(stream, len); - workbuf = EnsureStreamWorkBufferSize(stream, workbuflen); - if (!workbuf) { + Uint8* work_buffer = EnsureAudioStreamWorkBufferSize(stream, work_buffer_capacity); + + if (!work_buffer) { return -1; } - // figure out how much data we need to fulfill the request. - input_frames = len / dst_sample_frame_size; // total sample frames caller wants - if (dst_rate != src_rate) { - // calculate requested sample frames needed before resampling. Use a Uint64 so the multiplication doesn't overflow. - const int resampled_input_frames = (int) ((((Uint64) input_frames) * src_rate) / dst_rate); - if (resampled_input_frames > 0) { - input_frames = resampled_input_frames; - } else { // uhoh, not enough input frames! - // if they are upsampling and we end up needing less than a frame of input, we reject it because it would cause artifacts on future reads to eat a full input frame. - // however, if the stream is flushed, we would just be padding any remaining input with silence anyhow, so use it up. - if (stream->flushed) { - SDL_assert(((size_t) ((input_frames * src_sample_frame_size) + future_buffer_filled_frames)) <= stream->future_buffer_allocation); - // leave input_frames alone; this will just shuffle what's available from the future buffer and pad with silence as appropriate, below. - } else { - return 0; - } - } + const int padding_bytes = resampler_padding_frames * src_frame_size; + + Uint8* work_buffer_tail = work_buffer; + + // Split the work_buffer into [left_padding][input_buffer][right_padding] + Uint8* left_padding = work_buffer_tail; + work_buffer_tail += padding_bytes; + + Uint8* input_buffer = work_buffer_tail; + work_buffer_tail += input_bytes; + + Uint8* right_padding = work_buffer_tail; + work_buffer_tail += padding_bytes; + + SDL_assert((work_buffer_tail - work_buffer) <= work_buffer_capacity); + + // Now read unconverted data from the queue into the work buffer to fulfill the request. + if (SDL_ReadFromAudioQueue(stream->queue, input_buffer, input_bytes) != 0) { + SDL_assert(!"Not enough data in queue (resample read)"); + } + stream->total_bytes_queued -= input_bytes; + + // Update the history buffer and fill in the left padding + UpdateAudioStreamHistoryBuffer(stream, input_buffer, input_bytes, left_padding, padding_bytes); + + // Fill in the right padding by peeking into the input queue (missing data is filled with silence) + if (SDL_PeekIntoAudioQueue(stream->queue, right_padding, padding_bytes) != 0) { + SDL_assert(!"Not enough data in queue (resample peek)"); } - workbuf_frames = 0; // no input has been moved to the workbuf yet. - - // move any previous right-padding to the start of the buffer to convert, as those would have been the next samples from the queue ("the future buffer"). - if (future_buffer_filled_frames) { - const int cpyframes = SDL_min(input_frames, future_buffer_filled_frames); - const int cpy = cpyframes * src_sample_frame_size; - SDL_memcpy(workbuf, future_buffer, cpy); - workbuf_frames = cpyframes; - if (future_buffer_filled_frames == cpyframes) { - stream->future_buffer_filled_frames = future_buffer_filled_frames = 0; - } else { // slide any remaining bytes to the start of the padding buffer, if this was a small request. - SDL_memmove(future_buffer, future_buffer + cpy, (future_buffer_filled_frames - cpyframes) * src_sample_frame_size); - future_buffer_filled_frames -= cpyframes; - stream->future_buffer_filled_frames = future_buffer_filled_frames; - } - } - - // we either consumed all the future buffer or we don't need to read more from the queue. If this assert fails, we will have data in the wrong order in the future buffer when we top it off. - SDL_assert((future_buffer_filled_frames == 0) || (workbuf_frames == input_frames)); - - // now read unconverted data from the queue into the work buffer to fulfill the request. - if (input_frames > workbuf_frames) { // need more data? - const int workbufpos = workbuf_frames * src_sample_frame_size; - const int request_bytes = (input_frames - workbuf_frames) * src_sample_frame_size; - int read_frames; - SDL_assert((workbufpos + request_bytes) <= workbuflen); - br = (int) SDL_ReadFromDataQueue(stream->queue, workbuf + workbufpos, request_bytes); - read_frames = br / src_sample_frame_size; - workbuf_frames += read_frames; - input_frames = workbuf_frames; // what we actually have to work with - } - - // for some resamples, we need to fill up the future buffer, too, to use as right padding. - if (future_buffer_filled_frames < resampler_padding_frames) { - const int cpyframes = resampler_padding_frames - future_buffer_filled_frames; - const int cpy = cpyframes * src_sample_frame_size; - int brframes; - br = (int) SDL_ReadFromDataQueue(stream->queue, future_buffer + (future_buffer_filled_frames * src_sample_frame_size), cpy); - brframes = br / src_sample_frame_size; - future_buffer_filled_frames += brframes; - stream->future_buffer_filled_frames = future_buffer_filled_frames; - if (br < cpy) { // we couldn't fill the future buffer with enough padding! - if (stream->flushed) { // that's okay, we're flushing, just silence the still-needed padding. - SDL_memset(future_buffer + (future_buffer_filled_frames * src_sample_frame_size), SDL_GetSilenceValueForFormat(src_format), cpy - br); - } else { // Drastic measures: steal from the work buffer! - const int stealcpyframes = SDL_min(workbuf_frames, cpyframes - brframes); - const int stealcpy = stealcpyframes * src_sample_frame_size; - SDL_memcpy(future_buffer + (future_buffer_filled_frames * src_sample_frame_size), workbuf + ((workbuf_frames - stealcpyframes) * src_sample_frame_size), stealcpy); - workbuf_frames -= stealcpyframes; - input_frames = workbuf_frames; // what we actually have to work with, now - future_buffer_filled_frames += stealcpyframes; - SDL_assert(future_buffer_filled_frames <= resampler_padding_frames); - } - } - } - - // Now, the work buffer has enough sample frames to fulfill the request (or all the frames available if not), and the future buffer is loaded if necessary. - - // If we have resampling padding buffers, convert the current history and future buffers to float32. - if (resampler_padding_frames > 0) { - const int history_buffer_bytes = history_buffer_frames * src_sample_frame_size; - const int resampler_padding_bytes = resampler_padding_frames * src_sample_frame_size; - SDL_assert(src_rate != dst_rate); - SDL_assert(history_buffer_bytes >= resampler_padding_bytes); - ConvertAudio(resampler_padding_frames, history_buffer + (history_buffer_bytes - resampler_padding_bytes), src_format, src_channels, stream->left_padding, SDL_AUDIO_F32, pre_resample_channels); - ConvertAudio(resampler_padding_frames, future_buffer, src_format, src_channels, stream->right_padding, SDL_AUDIO_F32, pre_resample_channels); - } - - // slide in new data to the history buffer, shuffling out the oldest, for the next run, since we've already updated left_padding with current data. - { - const int history_buffer_bytes = history_buffer_frames * src_sample_frame_size; - const int request_bytes = input_frames * src_sample_frame_size; - if (history_buffer_frames > input_frames) { - const int preserve_bytes = history_buffer_bytes - request_bytes; - SDL_memmove(history_buffer, history_buffer + request_bytes, preserve_bytes); - SDL_memcpy(history_buffer + preserve_bytes, workbuf, request_bytes); - } else { // are we just replacing the whole thing instead? - SDL_memcpy(history_buffer, (workbuf + request_bytes) - history_buffer_bytes, history_buffer_bytes); - } - } - - // Not resampling? It's an easy conversion (and maybe not even that!) - if (src_rate == dst_rate) { - SDL_assert(resampler_padding_frames == 0); - // see if we can do the conversion in-place (will fit in `buf` while in-progress), or if we need to do it in the workbuf and copy it over - if (max_sample_frame_size <= dst_sample_frame_size) { - ConvertAudio(input_frames, workbuf, src_format, src_channels, buf, dst_format, dst_channels); - } else { - ConvertAudio(input_frames, workbuf, src_format, src_channels, workbuf, dst_format, dst_channels); - SDL_memcpy(buf, workbuf, input_frames * dst_sample_frame_size); - } - return input_frames * dst_sample_frame_size; - } + SDL_assert(work_buffer_frames == input_frames + (resampler_padding_frames * 2)); // Resampling! get the work buffer to float32 format, etc, in-place. - ConvertAudio(input_frames, workbuf, src_format, src_channels, workbuf, SDL_AUDIO_F32, pre_resample_channels); + ConvertAudio(work_buffer_frames, work_buffer, src_format, src_channels, work_buffer, SDL_AUDIO_F32, resample_channels, NULL); - if ((dst_format == SDL_AUDIO_F32) && (dst_channels == pre_resample_channels)) { - resample_outbuf = (float *) buf; - } else { - const int output_bytes = output_frames * pre_resample_channels * sizeof (float); - resample_outbuf = (float *) ((workbuf + stream->work_buffer_allocation) - output_bytes); // do at the end of the buffer so we have room for final convert at front. + // Update the work_buffer pointers based on the new frame size + input_buffer = work_buffer + ((input_buffer - work_buffer) / src_frame_size * resample_frame_size); + work_buffer_tail = work_buffer + ((work_buffer_tail - work_buffer) / src_frame_size * resample_frame_size); + SDL_assert((work_buffer_tail - work_buffer) <= work_buffer_capacity); + + // Decide where the resampled output goes + void* resample_buffer = (resample_buffer_offset != -1) ? (work_buffer + resample_buffer_offset) : buf; + + SDL_ResampleAudio(resample_channels, + (const float *) input_buffer, input_frames, + (float*) resample_buffer, output_frames, + resample_rate, &stream->resample_offset); + + // Convert to the final format, if necessary + if (buf != resample_buffer) { + ConvertAudio(output_frames, resample_buffer, SDL_AUDIO_F32, resample_channels, buf, dst_format, dst_channels, work_buffer); } - ResampleAudio(pre_resample_channels, src_rate, dst_rate, - stream->left_padding, stream->right_padding, - (const float *) workbuf, input_frames, - resample_outbuf, output_frames); - - // Get us to the final format! - // see if we can do the conversion in-place (will fit in `buf` while in-progress), or if we need to do it in the workbuf and copy it over - if (max_sample_frame_size <= dst_sample_frame_size) { - ConvertAudio(output_frames, resample_outbuf, SDL_AUDIO_F32, pre_resample_channels, buf, dst_format, dst_channels); - } else { - ConvertAudio(output_frames, resample_outbuf, SDL_AUDIO_F32, pre_resample_channels, workbuf, dst_format, dst_channels); - SDL_memcpy(buf, workbuf, output_frames * dst_sample_frame_size); - } - - return (int) (output_frames * dst_sample_frame_size); + return 0; } // get converted/resampled data from the stream @@ -1033,62 +1019,88 @@ int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len) SDL_LockMutex(stream->lock); - len -= len % stream->dst_sample_frame_size; // chop off any fractional sample frame. + if (CheckAudioStreamIsFullySetup(stream) != 0) { + SDL_UnlockMutex(stream->lock); + return -1; + } + + const int dst_frame_size = SDL_AUDIO_FRAMESIZE(stream->dst_spec); + + len -= len % dst_frame_size; // chop off any fractional sample frame. // give the callback a chance to fill in more stream data if it wants. if (stream->get_callback) { - int approx_request = len / stream->dst_sample_frame_size; // start with sample frames desired - if (stream->src_spec.freq != stream->dst_spec.freq) { - // calculate difference in dataset size after resampling. Use a Uint64 so the multiplication doesn't overflow. - approx_request = (int) (size_t) ((((Uint64) approx_request) * stream->src_spec.freq) / stream->dst_spec.freq); - if (!stream->flushed) { // do we need to fill the future buffer to accomodate this, too? - approx_request += stream->resampler_padding_frames - stream->future_buffer_filled_frames; - } + Sint64 total_request = len / dst_frame_size; // start with sample frames desired + Sint64 additional_request = total_request; + + Sint64 resample_offset = 0; + Sint64 available_frames = GetAudioStreamAvailableFrames(stream, &resample_offset); + + additional_request -= SDL_min(additional_request, available_frames); + + Sint64 resample_rate = GetAudioStreamResampleRate(stream, stream->src_spec.freq, resample_offset); + + if (resample_rate) { + total_request = SDL_GetResamplerInputFrames(total_request, resample_rate, resample_offset); + additional_request = SDL_GetResamplerInputFrames(additional_request, resample_rate, resample_offset); } - approx_request *= stream->src_sample_frame_size; // convert sample frames to bytes. - const int already_have = SDL_GetAudioStreamAvailable(stream); - approx_request -= SDL_min(approx_request, already_have); // we definitely have this much output already packed in. - stream->get_callback(stream, approx_request, stream->get_callback_userdata); + total_request *= SDL_AUDIO_FRAMESIZE(stream->src_spec); // convert sample frames to bytes. + additional_request *= SDL_AUDIO_FRAMESIZE(stream->src_spec); // convert sample frames to bytes. + stream->get_callback(stream->get_callback_userdata, stream, (int) SDL_min(additional_request, SDL_INT_MAX), (int) SDL_min(total_request, SDL_INT_MAX)); } - // we convert in chunks, so we don't end up allocating a massive work buffer, etc. - int retval = 0; - while (len > 0) { // didn't ask for a whole sample frame, nothing to do - const int chunk_size = 1024 * 1024; // !!! FIXME: a megabyte might be overly-aggressive. - const int rc = GetAudioStreamDataInternal(stream, buf, SDL_min(len, chunk_size)); + // Process the data in chunks to avoid allocating too much memory (and potential integer overflows) + const int chunk_size = 4096; - if (rc == -1) { - #if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: output chunk ended up producing an error!"); - #endif - if (retval == 0) { - retval = -1; + int total = 0; + + while (total < len) { + // Audio is processed a track at a time. + SDL_AudioSpec input_spec; + SDL_bool flushed; + const Sint64 available_frames = GetAudioStreamHead(stream, &input_spec, &flushed); + + if (available_frames == 0) { + if (flushed) { + SDL_PopAudioQueueHead(stream->queue); + SDL_zero(stream->input_spec); + stream->resample_offset = 0; + continue; } + // There are no frames available, but the track hasn't been flushed, so more might be added later. break; - } else { - #if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: output chunk ended up being %d bytes.", rc); - #endif - buf += rc; - len -= rc; - retval += rc; - if (rc < chunk_size) { - break; - } } + + if (UpdateAudioStreamInputSpec(stream, &input_spec) != 0) { + total = total ? total : -1; + break; + } + + // Clamp the output length to the maximum currently available. + // GetAudioStreamDataInternal requires enough input data is available. + int output_frames = (len - total) / dst_frame_size; + output_frames = SDL_min(output_frames, chunk_size); + output_frames = (int) SDL_min(output_frames, available_frames); + + if (GetAudioStreamDataInternal(stream, &buf[total], output_frames) != 0) { + total = total ? total : -1; + break; + } + + total += output_frames * dst_frame_size; } SDL_UnlockMutex(stream->lock); #if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: Final result was %d", retval); + SDL_Log("AUDIOSTREAM: Final result was %d", total); #endif - return retval; + return total; } -// number of converted/resampled bytes available +// number of converted/resampled bytes available for output int SDL_GetAudioStreamAvailable(SDL_AudioStream *stream) { if (!stream) { @@ -1097,31 +1109,35 @@ int SDL_GetAudioStreamAvailable(SDL_AudioStream *stream) SDL_LockMutex(stream->lock); - // total bytes available in source format in data queue - size_t count = SDL_GetDataQueueSize(stream->queue); - - // total sample frames available in data queue - count /= stream->src_sample_frame_size; - count += stream->future_buffer_filled_frames; - - // sample frames after resampling - if (stream->src_spec.freq != stream->dst_spec.freq) { - if (!stream->flushed) { - // have to save some samples for padding. They aren't available until more data is added or the stream is flushed. - count = (count < ((size_t) stream->resampler_padding_frames)) ? 0 : (count - stream->resampler_padding_frames); - } - // calculate difference in dataset size after resampling. Use a Uint64 so the multiplication doesn't overflow. - count = (size_t) ((((Uint64) count) * stream->dst_spec.freq) / stream->src_spec.freq); + if (CheckAudioStreamIsFullySetup(stream) != 0) { + SDL_UnlockMutex(stream->lock); + return 0; } + Sint64 count = GetAudioStreamAvailableFrames(stream, NULL); + // convert from sample frames to bytes in destination format. - count *= stream->dst_sample_frame_size; + count *= SDL_AUDIO_FRAMESIZE(stream->dst_spec); SDL_UnlockMutex(stream->lock); // if this overflows an int, just clamp it to a maximum. - const int max_int = 0x7FFFFFFF; // !!! FIXME: This will blow up on weird processors. Is there an SDL_INT_MAX? - return (count >= ((size_t) max_int)) ? max_int : ((int) count); + return (int) SDL_min(count, SDL_INT_MAX); +} + +// number of sample frames that are currently queued as input. +int SDL_GetAudioStreamQueued(SDL_AudioStream *stream) +{ + if (!stream) { + return SDL_InvalidParamError("stream"); + } + + SDL_LockMutex(stream->lock); + const Uint64 total = stream->total_bytes_queued; + SDL_UnlockMutex(stream->lock); + + // if this overflows an int, just clamp it to a maximum. + return (int) SDL_min(total, SDL_INT_MAX); } int SDL_ClearAudioStream(SDL_AudioStream *stream) @@ -1131,29 +1147,38 @@ int SDL_ClearAudioStream(SDL_AudioStream *stream) } SDL_LockMutex(stream->lock); - SDL_ClearDataQueue(stream->queue, (size_t)stream->packetlen * 2); - if (stream->history_buffer != NULL) { - SDL_memset(stream->history_buffer, SDL_GetSilenceValueForFormat(stream->src_spec.format), stream->history_buffer_frames * stream->src_spec.channels * sizeof (float)); - } - stream->future_buffer_filled_frames = 0; - stream->flushed = SDL_FALSE; + + SDL_ClearAudioQueue(stream->queue); + SDL_zero(stream->input_spec); + stream->resample_offset = 0; + stream->total_bytes_queued = 0; + SDL_UnlockMutex(stream->lock); return 0; } void SDL_DestroyAudioStream(SDL_AudioStream *stream) { - if (stream) { - SDL_UnbindAudioStream(stream); - // do not destroy stream->lock! it's a copy of `stream->queue`'s mutex, so destroying the queue will handle it. - SDL_DestroyDataQueue(stream->queue); - SDL_aligned_free(stream->work_buffer); - SDL_aligned_free(stream->history_buffer); - SDL_aligned_free(stream->future_buffer); - SDL_aligned_free(stream->left_padding); - SDL_aligned_free(stream->right_padding); - SDL_free(stream); + if (stream == NULL) { + return; } + + OnAudioStreamDestroy(stream); + + const SDL_bool simplified = stream->simplified; + if (simplified) { + SDL_assert(stream->bound_device->simplified); + SDL_CloseAudioDevice(stream->bound_device->instance_id); // this will unbind the stream. + } else { + SDL_UnbindAudioStream(stream); + } + + SDL_aligned_free(stream->history_buffer); + SDL_aligned_free(stream->work_buffer); + SDL_DestroyAudioQueue(stream->queue); + SDL_DestroyMutex(stream->lock); + + SDL_free(stream); } int SDL_ConvertAudioSamples(const SDL_AudioSpec *src_spec, const Uint8 *src_data, int src_len, @@ -1206,4 +1231,3 @@ int SDL_ConvertAudioSamples(const SDL_AudioSpec *src_spec, const Uint8 *src_data SDL_DestroyAudioStream(stream); return retval; } - diff --git a/external/sdl/SDL/src/audio/SDL_audioqueue.c b/external/sdl/SDL/src/audio/SDL_audioqueue.c new file mode 100644 index 00000000..c27bb409 --- /dev/null +++ b/external/sdl/SDL/src/audio/SDL_audioqueue.c @@ -0,0 +1,516 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#include "SDL_audioqueue.h" + +#define AUDIO_SPECS_EQUAL(x, y) (((x).format == (y).format) && ((x).channels == (y).channels) && ((x).freq == (y).freq)) + +struct SDL_AudioTrack +{ + SDL_AudioSpec spec; + SDL_bool flushed; + SDL_AudioTrack *next; + + size_t (*avail)(void *ctx); + int (*write)(void *ctx, const Uint8 *buf, size_t len); + size_t (*read)(void *ctx, Uint8 *buf, size_t len, SDL_bool advance); + void (*destroy)(void *ctx); +}; + +struct SDL_AudioQueue +{ + SDL_AudioTrack *head; + SDL_AudioTrack *tail; + size_t chunk_size; +}; + +typedef struct SDL_AudioChunk SDL_AudioChunk; + +struct SDL_AudioChunk +{ + SDL_AudioChunk *next; + size_t head; + size_t tail; + Uint8 data[SDL_VARIABLE_LENGTH_ARRAY]; +}; + +typedef struct SDL_ChunkedAudioTrack +{ + SDL_AudioTrack track; + + size_t chunk_size; + + SDL_AudioChunk *head; + SDL_AudioChunk *tail; + size_t queued_bytes; + + SDL_AudioChunk *free_chunks; + size_t num_free_chunks; +} SDL_ChunkedAudioTrack; + +static void DestroyAudioChunk(SDL_AudioChunk *chunk) +{ + SDL_free(chunk); +} + +static void DestroyAudioChunks(SDL_AudioChunk *chunk) +{ + while (chunk) { + SDL_AudioChunk *next = chunk->next; + DestroyAudioChunk(chunk); + chunk = next; + } +} + +static void ResetAudioChunk(SDL_AudioChunk *chunk) +{ + chunk->next = NULL; + chunk->head = 0; + chunk->tail = 0; +} + +static SDL_AudioChunk *CreateAudioChunk(size_t chunk_size) +{ + SDL_AudioChunk *chunk = (SDL_AudioChunk *)SDL_malloc(sizeof(*chunk) + chunk_size); + + if (chunk == NULL) { + return NULL; + } + + ResetAudioChunk(chunk); + + return chunk; +} + +static void DestroyAudioTrackChunk(SDL_ChunkedAudioTrack *track, SDL_AudioChunk *chunk) +{ + // Keeping a list of free chunks reduces memory allocations, + // But also increases the amount of work to perform when freeing the track. + const size_t max_free_bytes = 64 * 1024; + + if (track->chunk_size * track->num_free_chunks < max_free_bytes) { + chunk->next = track->free_chunks; + track->free_chunks = chunk; + ++track->num_free_chunks; + } else { + DestroyAudioChunk(chunk); + } +} + +static SDL_AudioChunk *CreateAudioTrackChunk(SDL_ChunkedAudioTrack *track) +{ + if (track->num_free_chunks > 0) { + SDL_AudioChunk *chunk = track->free_chunks; + + track->free_chunks = chunk->next; + --track->num_free_chunks; + + ResetAudioChunk(chunk); + + return chunk; + } + + return CreateAudioChunk(track->chunk_size); +} + +static size_t AvailChunkedAudioTrack(void *ctx) +{ + SDL_ChunkedAudioTrack *track = ctx; + + return track->queued_bytes; +} + +static int WriteToChunkedAudioTrack(void *ctx, const Uint8 *data, size_t len) +{ + SDL_ChunkedAudioTrack *track = ctx; + + SDL_AudioChunk *chunk = track->tail; + + // Handle the first chunk + if (chunk == NULL) { + chunk = CreateAudioTrackChunk(track); + + if (chunk == NULL) { + return SDL_OutOfMemory(); + } + + SDL_assert((track->head == NULL) && (track->tail == NULL) && (track->queued_bytes == 0)); + track->head = chunk; + track->tail = chunk; + } + + size_t total = 0; + size_t old_tail = chunk->tail; + size_t chunk_size = track->chunk_size; + + while (chunk) { + size_t to_write = chunk_size - chunk->tail; + to_write = SDL_min(to_write, len - total); + SDL_memcpy(&chunk->data[chunk->tail], &data[total], to_write); + total += to_write; + + chunk->tail += to_write; + + if (total == len) { + break; + } + + SDL_AudioChunk *next = CreateAudioTrackChunk(track); + chunk->next = next; + chunk = next; + } + + // Roll back the changes if we couldn't write all the data + if (chunk == NULL) { + chunk = track->tail; + + SDL_AudioChunk *next = chunk->next; + chunk->next = NULL; + chunk->tail = old_tail; + + DestroyAudioChunks(next); + + return SDL_OutOfMemory(); + } + + track->tail = chunk; + track->queued_bytes += total; + + return 0; +} + +static size_t ReadFromChunkedAudioTrack(void *ctx, Uint8 *data, size_t len, SDL_bool advance) +{ + SDL_ChunkedAudioTrack *track = ctx; + SDL_AudioChunk *chunk = track->head; + + size_t total = 0; + size_t head = 0; + + while (chunk) { + head = chunk->head; + + size_t to_read = chunk->tail - head; + to_read = SDL_min(to_read, len - total); + SDL_memcpy(&data[total], &chunk->data[head], to_read); + total += to_read; + + SDL_AudioChunk *next = chunk->next; + + if (total == len) { + head += to_read; + break; + } + + if (advance) { + DestroyAudioTrackChunk(track, chunk); + } + + chunk = next; + } + + if (advance) { + if (chunk) { + chunk->head = head; + track->head = chunk; + } else { + track->head = NULL; + track->tail = NULL; + } + + track->queued_bytes -= total; + } + + return total; +} + +static void DestroyChunkedAudioTrack(void *ctx) +{ + SDL_ChunkedAudioTrack *track = ctx; + DestroyAudioChunks(track->head); + DestroyAudioChunks(track->free_chunks); + SDL_free(track); +} + +static SDL_AudioTrack *CreateChunkedAudioTrack(const SDL_AudioSpec *spec, size_t chunk_size) +{ + SDL_ChunkedAudioTrack *track = (SDL_ChunkedAudioTrack *)SDL_calloc(1, sizeof(*track)); + + if (track == NULL) { + SDL_OutOfMemory(); + return NULL; + } + + SDL_copyp(&track->track.spec, spec); + track->track.avail = AvailChunkedAudioTrack; + track->track.write = WriteToChunkedAudioTrack; + track->track.read = ReadFromChunkedAudioTrack; + track->track.destroy = DestroyChunkedAudioTrack; + + track->chunk_size = chunk_size; + + return &track->track; +} + +SDL_AudioQueue *SDL_CreateAudioQueue(size_t chunk_size) +{ + SDL_AudioQueue *queue = (SDL_AudioQueue *)SDL_calloc(1, sizeof(*queue)); + + if (queue == NULL) { + SDL_OutOfMemory(); + return NULL; + } + + queue->chunk_size = chunk_size; + + return queue; +} + +void SDL_DestroyAudioQueue(SDL_AudioQueue *queue) +{ + SDL_ClearAudioQueue(queue); + + SDL_free(queue); +} + +void SDL_ClearAudioQueue(SDL_AudioQueue *queue) +{ + SDL_AudioTrack *track = queue->head; + queue->head = NULL; + queue->tail = NULL; + + while (track) { + SDL_AudioTrack *next = track->next; + track->destroy(track); + track = next; + } +} + +static void SDL_FlushAudioTrack(SDL_AudioTrack *track) +{ + track->flushed = SDL_TRUE; + track->write = NULL; +} + +void SDL_FlushAudioQueue(SDL_AudioQueue *queue) +{ + SDL_AudioTrack *track = queue->tail; + + if (track) { + SDL_FlushAudioTrack(track); + } +} + +void SDL_PopAudioQueueHead(SDL_AudioQueue *queue) +{ + SDL_AudioTrack *track = queue->head; + + for (;;) { + SDL_bool flushed = track->flushed; + + SDL_AudioTrack *next = track->next; + track->destroy(track); + track = next; + + if (flushed) { + break; + } + } + + queue->head = track; + + if (track == NULL) { + queue->tail = NULL; + } +} + +size_t SDL_GetAudioQueueChunkSize(SDL_AudioQueue *queue) +{ + return queue->chunk_size; +} + +SDL_AudioTrack *SDL_CreateChunkedAudioTrack(const SDL_AudioSpec *spec, const Uint8 *data, size_t len, size_t chunk_size) +{ + SDL_AudioTrack *track = CreateChunkedAudioTrack(spec, chunk_size); + + if (track == NULL) { + return NULL; + } + + if (track->write(track, data, len) != 0) { + track->destroy(track); + return NULL; + } + + return track; +} + +void SDL_AddTrackToAudioQueue(SDL_AudioQueue *queue, SDL_AudioTrack *track) +{ + SDL_AudioTrack *tail = queue->tail; + + if (tail) { + // If the spec has changed, make sure to flush the previous track + if (!AUDIO_SPECS_EQUAL(tail->spec, track->spec)) { + SDL_FlushAudioTrack(tail); + } + + tail->next = track; + } else { + queue->head = track; + } + + queue->tail = track; +} + +int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const Uint8 *data, size_t len) +{ + if (len == 0) { + return 0; + } + + SDL_AudioTrack *track = queue->tail; + + if ((track != NULL) && !AUDIO_SPECS_EQUAL(track->spec, *spec)) { + SDL_FlushAudioTrack(track); + } + + if ((track == NULL) || (track->write == NULL)) { + SDL_AudioTrack *new_track = CreateChunkedAudioTrack(spec, queue->chunk_size); + + if (new_track == NULL) { + return SDL_OutOfMemory(); + } + + if (track) { + track->next = new_track; + } else { + queue->head = new_track; + } + + queue->tail = new_track; + + track = new_track; + } + + return track->write(track, data, len); +} + +void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue) +{ + return queue->head; +} + +size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, SDL_bool *out_flushed) +{ + SDL_AudioTrack *iter = *inout_iter; + SDL_assert(iter != NULL); + + SDL_copyp(out_spec, &iter->spec); + + SDL_bool flushed = SDL_FALSE; + size_t queued_bytes = 0; + + while (iter) { + SDL_AudioTrack *track = iter; + iter = iter->next; + + size_t avail = track->avail(track); + + if (avail >= SDL_SIZE_MAX - queued_bytes) { + queued_bytes = SDL_SIZE_MAX; + flushed = SDL_FALSE; + break; + } + + queued_bytes += avail; + flushed = track->flushed; + + if (flushed) { + break; + } + } + + *inout_iter = iter; + *out_flushed = flushed; + + return queued_bytes; +} + +int SDL_ReadFromAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len) +{ + size_t total = 0; + SDL_AudioTrack *track = queue->head; + + for (;;) { + if (track == NULL) { + return SDL_SetError("Reading past end of queue"); + } + + total += track->read(track, &data[total], len - total, SDL_TRUE); + + if (total == len) { + return 0; + } + + if (track->flushed) { + return SDL_SetError("Reading past end of flushed track"); + } + + SDL_AudioTrack *next = track->next; + + if (next == NULL) { + return SDL_SetError("Reading past end of incomplete track"); + } + + queue->head = next; + + track->destroy(track); + track = next; + } +} + +int SDL_PeekIntoAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len) +{ + size_t total = 0; + SDL_AudioTrack *track = queue->head; + + for (;;) { + if (track == NULL) { + return SDL_SetError("Peeking past end of queue"); + } + + total += track->read(track, &data[total], len - total, SDL_FALSE); + + if (total == len) { + return 0; + } + + if (track->flushed) { + // If we have run out of data, fill the rest with silence. + SDL_memset(&data[total], SDL_GetSilenceValueForFormat(track->spec.format), len - total); + return 0; + } + + track = track->next; + } +} diff --git a/external/sdl/SDL/src/audio/SDL_audioqueue.h b/external/sdl/SDL/src/audio/SDL_audioqueue.h new file mode 100644 index 00000000..76012e91 --- /dev/null +++ b/external/sdl/SDL/src/audio/SDL_audioqueue.h @@ -0,0 +1,77 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifndef SDL_audioqueue_h_ +#define SDL_audioqueue_h_ + +// Internal functions used by SDL_AudioStream for queueing audio. + +typedef struct SDL_AudioQueue SDL_AudioQueue; +typedef struct SDL_AudioTrack SDL_AudioTrack; + +// Create a new audio queue +SDL_AudioQueue *SDL_CreateAudioQueue(size_t chunk_size); + +// Destroy an audio queue +void SDL_DestroyAudioQueue(SDL_AudioQueue *queue); + +// Completely clear the queue +void SDL_ClearAudioQueue(SDL_AudioQueue *queue); + +// Mark the last track as flushed +void SDL_FlushAudioQueue(SDL_AudioQueue *queue); + +// Pop the current head track +// REQUIRES: The head track must exist, and must have been flushed +void SDL_PopAudioQueueHead(SDL_AudioQueue *queue); + +// Get the chunk size, mostly for use with SDL_CreateChunkedAudioTrack +// This can be called from any thread +size_t SDL_GetAudioQueueChunkSize(SDL_AudioQueue *queue); + +// Write data to the end of queue +// REQUIRES: If the spec has changed, the last track must have been flushed +int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const Uint8 *data, size_t len); + +// Create a track without needing to hold any locks +SDL_AudioTrack *SDL_CreateChunkedAudioTrack(const SDL_AudioSpec *spec, const Uint8 *data, size_t len, size_t chunk_size); + +// Add a track to the end of the queue +// REQUIRES: `track != NULL` +void SDL_AddTrackToAudioQueue(SDL_AudioQueue *queue, SDL_AudioTrack *track); + +// Iterate over the tracks in the queue +void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue); + +// Query and update the track iterator +// REQUIRES: `*inout_iter != NULL` (a valid iterator) +size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, SDL_bool *out_flushed); + +// Read data from the start of the queue +// REQUIRES: There must be enough data in the queue +int SDL_ReadFromAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len); + +// Peek into the start of the queue +// REQUIRES: There must be enough data in the queue, unless it has been flushed, in which case missing data is filled with silence. +int SDL_PeekIntoAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len); + +#endif // SDL_audioqueue_h_ diff --git a/external/sdl/SDL/src/audio/SDL_audioresample.c b/external/sdl/SDL/src/audio/SDL_audioresample.c new file mode 100644 index 00000000..6e3f4032 --- /dev/null +++ b/external/sdl/SDL/src/audio/SDL_audioresample.c @@ -0,0 +1,335 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#include "SDL_sysaudio.h" +#include "SDL_audioresample.h" + +/* SDL's resampler uses a "bandlimited interpolation" algorithm: + https://ccrma.stanford.edu/~jos/resample/ */ + +#include "SDL_audio_resampler_filter.h" + +/* For a given srcpos, `srcpos + frame` are sampled, where `-RESAMPLER_ZERO_CROSSINGS < frame <= RESAMPLER_ZERO_CROSSINGS`. + * Note, when upsampling, it is also possible to start sampling from `srcpos = -1`. */ +#define RESAMPLER_MAX_PADDING_FRAMES (RESAMPLER_ZERO_CROSSINGS + 1) + +#define RESAMPLER_FILTER_INTERP_BITS (32 - RESAMPLER_BITS_PER_ZERO_CROSSING) +#define RESAMPLER_FILTER_INTERP_RANGE (1 << RESAMPLER_FILTER_INTERP_BITS) + +#define RESAMPLER_SAMPLES_PER_FRAME (RESAMPLER_ZERO_CROSSINGS * 2) + +#define RESAMPLER_FULL_FILTER_SIZE (RESAMPLER_SAMPLES_PER_FRAME * (RESAMPLER_SAMPLES_PER_ZERO_CROSSING + 1)) + +static void ResampleFrame_Scalar(const float *src, float *dst, const float *raw_filter, float interp, int chans) +{ + int i, chan; + + float filter[RESAMPLER_SAMPLES_PER_FRAME]; + + // Interpolate between the nearest two filters + for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) { + filter[i] = (raw_filter[i] * (1.0f - interp)) + (raw_filter[i + RESAMPLER_SAMPLES_PER_FRAME] * interp); + } + + if (chans == 2) { + float out[2]; + out[0] = 0.0f; + out[1] = 0.0f; + + for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) { + const float scale = filter[i]; + out[0] += src[i * 2 + 0] * scale; + out[1] += src[i * 2 + 1] * scale; + } + + dst[0] = out[0]; + dst[1] = out[1]; + return; + } + + if (chans == 1) { + float out = 0.0f; + + for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) { + out += src[i] * filter[i]; + } + + dst[0] = out; + return; + } + + for (chan = 0; chan < chans; chan++) { + float f = 0.0f; + + for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) { + f += src[i * chans + chan] * filter[i]; + } + + dst[chan] = f; + } +} + +#ifdef SDL_SSE_INTRINSICS +static void SDL_TARGETING("sse") ResampleFrame_SSE(const float *src, float *dst, const float *raw_filter, float interp, int chans) +{ +#if RESAMPLER_SAMPLES_PER_FRAME != 10 +#error Invalid samples per frame +#endif + + // Load the filter + __m128 f0 = _mm_loadu_ps(raw_filter + 0); + __m128 f1 = _mm_loadu_ps(raw_filter + 4); + __m128 f2 = _mm_loadl_pi(_mm_setzero_ps(), (const __m64 *)(raw_filter + 8)); + + __m128 g0 = _mm_loadu_ps(raw_filter + 10); + __m128 g1 = _mm_loadu_ps(raw_filter + 14); + __m128 g2 = _mm_loadl_pi(_mm_setzero_ps(), (const __m64 *)(raw_filter + 18)); + + __m128 interp1 = _mm_set1_ps(interp); + __m128 interp2 = _mm_sub_ps(_mm_set1_ps(1.0f), _mm_set1_ps(interp)); + + // Linear interpolate the filter + f0 = _mm_add_ps(_mm_mul_ps(f0, interp2), _mm_mul_ps(g0, interp1)); + f1 = _mm_add_ps(_mm_mul_ps(f1, interp2), _mm_mul_ps(g1, interp1)); + f2 = _mm_add_ps(_mm_mul_ps(f2, interp2), _mm_mul_ps(g2, interp1)); + + if (chans == 2) { + // Duplicate each of the filter elements + g0 = _mm_unpackhi_ps(f0, f0); + f0 = _mm_unpacklo_ps(f0, f0); + g1 = _mm_unpackhi_ps(f1, f1); + f1 = _mm_unpacklo_ps(f1, f1); + f2 = _mm_unpacklo_ps(f2, f2); + + // Multiply the filter by the input + f0 = _mm_mul_ps(f0, _mm_loadu_ps(src + 0)); + g0 = _mm_mul_ps(g0, _mm_loadu_ps(src + 4)); + f1 = _mm_mul_ps(f1, _mm_loadu_ps(src + 8)); + g1 = _mm_mul_ps(g1, _mm_loadu_ps(src + 12)); + f2 = _mm_mul_ps(f2, _mm_loadu_ps(src + 16)); + + // Calculate the sum + f0 = _mm_add_ps(_mm_add_ps(_mm_add_ps(f0, g0), _mm_add_ps(f1, g1)), f2); + f0 = _mm_add_ps(f0, _mm_movehl_ps(f0, f0)); + + // Store the result + _mm_storel_pi((__m64 *)dst, f0); + return; + } + + if (chans == 1) { + // Multiply the filter by the input + f0 = _mm_mul_ps(f0, _mm_loadu_ps(src + 0)); + f1 = _mm_mul_ps(f1, _mm_loadu_ps(src + 4)); + f2 = _mm_mul_ps(f2, _mm_loadl_pi(_mm_setzero_ps(), (const __m64 *)(src + 8))); + + // Calculate the sum + f0 = _mm_add_ps(f0, f1); + f0 = _mm_add_ps(_mm_add_ps(f0, f2), _mm_movehl_ps(f0, f0)); + f0 = _mm_add_ss(f0, _mm_shuffle_ps(f0, f0, _MM_SHUFFLE(1, 1, 1, 1))); + + // Store the result + _mm_store_ss(dst, f0); + return; + } + + float filter[RESAMPLER_SAMPLES_PER_FRAME]; + _mm_storeu_ps(filter + 0, f0); + _mm_storeu_ps(filter + 4, f1); + _mm_storel_pi((__m64 *)(filter + 8), f2); + + int i, chan = 0; + + for (; chan + 4 <= chans; chan += 4) { + f0 = _mm_setzero_ps(); + + for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) { + f0 = _mm_add_ps(f0, _mm_mul_ps(_mm_loadu_ps(&src[i * chans + chan]), _mm_load1_ps(&filter[i]))); + } + + _mm_storeu_ps(&dst[chan], f0); + } + + for (; chan < chans; chan++) { + f0 = _mm_setzero_ps(); + + for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) { + f0 = _mm_add_ss(f0, _mm_mul_ss(_mm_load_ss(&src[i * chans + chan]), _mm_load_ss(&filter[i]))); + } + + _mm_store_ss(&dst[chan], f0); + } +} +#endif + +static void (*ResampleFrame)(const float *src, float *dst, const float *raw_filter, float interp, int chans); + +static float FullResamplerFilter[RESAMPLER_FULL_FILTER_SIZE]; + +void SDL_SetupAudioResampler(void) +{ + static SDL_bool setup = SDL_FALSE; + if (setup) { + return; + } + + // Build a table combining the left and right wings, for faster access + int i, j; + + for (i = 0; i < RESAMPLER_SAMPLES_PER_ZERO_CROSSING; ++i) { + for (j = 0; j < RESAMPLER_ZERO_CROSSINGS; j++) { + int lwing = (i * RESAMPLER_SAMPLES_PER_FRAME) + (RESAMPLER_ZERO_CROSSINGS - 1) - j; + int rwing = (RESAMPLER_FULL_FILTER_SIZE - 1) - lwing; + + float value = ResamplerFilter[(i * RESAMPLER_ZERO_CROSSINGS) + j]; + FullResamplerFilter[lwing] = value; + FullResamplerFilter[rwing] = value; + } + } + + for (i = 0; i < RESAMPLER_ZERO_CROSSINGS; ++i) { + int rwing = i + RESAMPLER_ZERO_CROSSINGS; + int lwing = (RESAMPLER_FULL_FILTER_SIZE - 1) - rwing; + + FullResamplerFilter[lwing] = 0.0f; + FullResamplerFilter[rwing] = 0.0f; + } + + ResampleFrame = ResampleFrame_Scalar; + +#ifdef SDL_SSE_INTRINSICS + if (SDL_HasSSE()) { + ResampleFrame = ResampleFrame_SSE; + } +#endif + + setup = SDL_TRUE; +} + +Sint64 SDL_GetResampleRate(int src_rate, int dst_rate) +{ + SDL_assert(src_rate > 0); + SDL_assert(dst_rate > 0); + + Sint64 sample_rate = ((Sint64)src_rate << 32) / (Sint64)dst_rate; + SDL_assert(sample_rate > 0); + + return sample_rate; +} + +int SDL_GetResamplerHistoryFrames(void) +{ + // Even if we aren't currently resampling, make sure to keep enough history in case we need to later. + + return RESAMPLER_MAX_PADDING_FRAMES; +} + +int SDL_GetResamplerPaddingFrames(Sint64 resample_rate) +{ + // This must always be <= SDL_GetResamplerHistoryFrames() + + return resample_rate ? RESAMPLER_MAX_PADDING_FRAMES : 0; +} + +// These are not general purpose. They do not check for all possible underflow/overflow +SDL_FORCE_INLINE Sint64 ResamplerAdd(Sint64 a, Sint64 b, Sint64 *ret) +{ + if ((b > 0) && (a > SDL_MAX_SINT64 - b)) { + return -1; + } + + *ret = a + b; + return 0; +} + +SDL_FORCE_INLINE Sint64 ResamplerMul(Sint64 a, Sint64 b, Sint64 *ret) +{ + if ((b > 0) && (a > SDL_MAX_SINT64 / b)) { + return -1; + } + + *ret = a * b; + return 0; +} + +Sint64 SDL_GetResamplerInputFrames(Sint64 output_frames, Sint64 resample_rate, Sint64 resample_offset) +{ + // Calculate the index of the last input frame, then add 1. + // ((((output_frames - 1) * resample_rate) + resample_offset) >> 32) + 1 + + Sint64 output_offset; + if (ResamplerMul(output_frames, resample_rate, &output_offset) || + ResamplerAdd(output_offset, -resample_rate + resample_offset + 0x100000000, &output_offset)) { + output_offset = SDL_MAX_SINT64; + } + + Sint64 input_frames = (Sint64)(Sint32)(output_offset >> 32); + input_frames = SDL_max(input_frames, 0); + + return input_frames; +} + +Sint64 SDL_GetResamplerOutputFrames(Sint64 input_frames, Sint64 resample_rate, Sint64 *inout_resample_offset) +{ + Sint64 resample_offset = *inout_resample_offset; + + // input_offset = (input_frames << 32) - resample_offset; + Sint64 input_offset; + if (ResamplerMul(input_frames, 0x100000000, &input_offset) || + ResamplerAdd(input_offset, -resample_offset, &input_offset)) { + input_offset = SDL_MAX_SINT64; + } + + // output_frames = div_ceil(input_offset, resample_rate) + Sint64 output_frames = (input_offset > 0) ? (((input_offset - 1) / resample_rate) + 1) : 0; + + *inout_resample_offset = (output_frames * resample_rate) - input_offset; + + return output_frames; +} + +void SDL_ResampleAudio(int chans, const float *src, int inframes, float *dst, int outframes, + Sint64 resample_rate, Sint64 *inout_resample_offset) +{ + int i; + Sint64 srcpos = *inout_resample_offset; + + SDL_assert(resample_rate > 0); + + for (i = 0; i < outframes; i++) { + int srcindex = (int)(Sint32)(srcpos >> 32); + Uint32 srcfraction = (Uint32)(srcpos & 0xFFFFFFFF); + srcpos += resample_rate; + + SDL_assert(srcindex >= -1 && srcindex < inframes); + + const float *filter = &FullResamplerFilter[(srcfraction >> RESAMPLER_FILTER_INTERP_BITS) * RESAMPLER_SAMPLES_PER_FRAME]; + const float interp = (float)(srcfraction & (RESAMPLER_FILTER_INTERP_RANGE - 1)) * (1.0f / RESAMPLER_FILTER_INTERP_RANGE); + + const float *frame = &src[(srcindex - (RESAMPLER_ZERO_CROSSINGS - 1)) * chans]; + ResampleFrame(frame, dst, filter, interp, chans); + + dst += chans; + } + + *inout_resample_offset = srcpos - ((Sint64)inframes << 32); +} diff --git a/external/sdl/SDL/src/audio/SDL_audioresample.h b/external/sdl/SDL/src/audio/SDL_audioresample.h new file mode 100644 index 00000000..84aaa2db --- /dev/null +++ b/external/sdl/SDL/src/audio/SDL_audioresample.h @@ -0,0 +1,43 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifndef SDL_audioresample_h_ +#define SDL_audioresample_h_ + +// Internal functions used by SDL_AudioStream for resampling audio. +// The resampler uses 32:32 fixed-point arithmetic to track its position. + +Sint64 SDL_GetResampleRate(const int src_rate, const int dst_rate); + +int SDL_GetResamplerHistoryFrames(void); +int SDL_GetResamplerPaddingFrames(Sint64 resample_rate); + +Sint64 SDL_GetResamplerInputFrames(Sint64 output_frames, Sint64 resample_rate, Sint64 resample_offset); +Sint64 SDL_GetResamplerOutputFrames(Sint64 input_frames, Sint64 resample_rate, Sint64 *inout_resample_offset); + +// Resample some audio. +// REQUIRES: `inframes >= SDL_GetResamplerInputFrames(outframes)` +// REQUIRES: At least `SDL_GetResamplerPaddingFrames(...)` extra frames to the left of src, and right of src+inframes +void SDL_ResampleAudio(int chans, const float *src, int inframes, float *dst, int outframes, + Sint64 resample_rate, Sint64 *inout_resample_offset); + +#endif // SDL_audioresample_h_ diff --git a/external/sdl/SDL/src/audio/SDL_audiotypecvt.c b/external/sdl/SDL/src/audio/SDL_audiotypecvt.c index aeb556cd..1b45a0d9 100644 --- a/external/sdl/SDL/src/audio/SDL_audiotypecvt.c +++ b/external/sdl/SDL/src/audio/SDL_audiotypecvt.c @@ -39,482 +39,510 @@ #define NEED_SCALAR_CONVERTER_FALLBACKS 1 #endif -#define DIVBY128 0.0078125f -#define DIVBY32768 0.000030517578125f -#define DIVBY8388607 0.00000011920930376163766f +#define DIVBY2147483648 0.0000000004656612873077392578125f /* 0x1p-31f */ #if NEED_SCALAR_CONVERTER_FALLBACKS -/* these all convert backwards because (currently) float32 is >= to the size of anything it converts to, so it lets us safely convert in-place. */ -#define AUDIOCVT_TOFLOAT_SCALAR(from, fromtype, equation) \ - static void SDL_Convert_##from##_to_F32_Scalar(float *dst, const fromtype *src, int num_samples) { \ - int i; \ - LOG_DEBUG_AUDIO_CONVERT(#from, "F32"); \ - for (i = num_samples - 1; i >= 0; --i) { \ - dst[i] = equation; \ - } \ +/* This code requires that floats are in the IEEE-754 binary32 format */ +SDL_COMPILE_TIME_ASSERT(float_bits, sizeof(float) == sizeof(Uint32)); + +union float_bits { + Uint32 u32; + float f32; +}; + +static void SDL_Convert_S8_to_F32_Scalar(float *dst, const Sint8 *src, int num_samples) +{ + int i; + + LOG_DEBUG_AUDIO_CONVERT("S8", "F32"); + + for (i = num_samples - 1; i >= 0; --i) { + /* 1) Construct a float in the range [65536.0, 65538.0) + * 2) Shift the float range to [-1.0, 1.0) */ + union float_bits x; + x.u32 = (Uint8)src[i] ^ 0x47800080u; + dst[i] = x.f32 - 65537.0f; } +} -AUDIOCVT_TOFLOAT_SCALAR(S8, Sint8, ((float)src[i]) * DIVBY128) -AUDIOCVT_TOFLOAT_SCALAR(U8, Uint8, (((float)src[i]) * DIVBY128) - 1.0f) -AUDIOCVT_TOFLOAT_SCALAR(S16, Sint16, ((float)src[i]) * DIVBY32768) -AUDIOCVT_TOFLOAT_SCALAR(S32, Sint32, ((float)(src[i] >> 8)) * DIVBY8388607) -#undef AUDIOCVT_FROMFLOAT_SCALAR +static void SDL_Convert_U8_to_F32_Scalar(float *dst, const Uint8 *src, int num_samples) +{ + int i; -/* these all convert forwards because (currently) float32 is >= to the size of anything it converts from, so it lets us safely convert in-place. */ -#define AUDIOCVT_FROMFLOAT_SCALAR(to, totype, clampmin, clampmax, equation) \ - static void SDL_Convert_F32_to_##to##_Scalar(totype *dst, const float *src, int num_samples) { \ - int i; \ - LOG_DEBUG_AUDIO_CONVERT("F32", #to); \ - for (i = 0; i < num_samples; i++) { \ - const float sample = src[i]; \ - if (sample >= 1.0f) { \ - dst[i] = (totype) (clampmax); \ - } else if (sample <= -1.0f) { \ - dst[i] = (totype) (clampmin); \ - } else { \ - dst[i] = (totype) (equation); \ - } \ - } \ + LOG_DEBUG_AUDIO_CONVERT("U8", "F32"); + + for (i = num_samples - 1; i >= 0; --i) { + /* 1) Construct a float in the range [65536.0, 65538.0) + * 2) Shift the float range to [-1.0, 1.0) */ + union float_bits x; + x.u32 = src[i] ^ 0x47800000u; + dst[i] = x.f32 - 65537.0f; } +} -AUDIOCVT_FROMFLOAT_SCALAR(S8, Sint8, -128, 127, sample * 127.0f); -AUDIOCVT_FROMFLOAT_SCALAR(U8, Uint8, 0, 255, (sample + 1.0f) * 127.0f); -AUDIOCVT_FROMFLOAT_SCALAR(S16, Sint16, -32768, 32767, sample * 32767.0f); -AUDIOCVT_FROMFLOAT_SCALAR(S32, Sint32, -2147483648LL, 2147483647, ((Sint32)(sample * 8388607.0f)) << 8); -#undef AUDIOCVT_FROMFLOAT_SCALAR +static void SDL_Convert_S16_to_F32_Scalar(float *dst, const Sint16 *src, int num_samples) +{ + int i; + + LOG_DEBUG_AUDIO_CONVERT("S16", "F32"); + + for (i = num_samples - 1; i >= 0; --i) { + /* 1) Construct a float in the range [256.0, 258.0) + * 2) Shift the float range to [-1.0, 1.0) */ + union float_bits x; + x.u32 = (Uint16)src[i] ^ 0x43808000u; + dst[i] = x.f32 - 257.0f; + } +} + +static void SDL_Convert_S32_to_F32_Scalar(float *dst, const Sint32 *src, int num_samples) +{ + int i; + + LOG_DEBUG_AUDIO_CONVERT("S32", "F32"); + + for (i = num_samples - 1; i >= 0; --i) { + dst[i] = (float)src[i] * DIVBY2147483648; + } +} + +/* Create a bit-mask based on the sign-bit. Should optimize to a single arithmetic-shift-right */ +#define SIGNMASK(x) (Uint32)(0u - ((Uint32)(x) >> 31)) + +static void SDL_Convert_F32_to_S8_Scalar(Sint8 *dst, const float *src, int num_samples) +{ + int i; + + LOG_DEBUG_AUDIO_CONVERT("F32", "S8"); + + for (i = 0; i < num_samples; ++i) { + /* 1) Shift the float range from [-1.0, 1.0] to [98303.0, 98305.0] + * 2) Shift the integer range from [0x47BFFF80, 0x47C00080] to [-128, 128] + * 3) Clamp the value to [-128, 127] */ + union float_bits x; + x.f32 = src[i] + 98304.0f; + + Uint32 y = x.u32 - 0x47C00000u; + Uint32 z = 0x7Fu - (y ^ SIGNMASK(y)); + y = y ^ (z & SIGNMASK(z)); + + dst[i] = (Sint8)(y & 0xFF); + } +} + +static void SDL_Convert_F32_to_U8_Scalar(Uint8 *dst, const float *src, int num_samples) +{ + int i; + + LOG_DEBUG_AUDIO_CONVERT("F32", "U8"); + + for (i = 0; i < num_samples; ++i) { + /* 1) Shift the float range from [-1.0, 1.0] to [98303.0, 98305.0] + * 2) Shift the integer range from [0x47BFFF80, 0x47C00080] to [-128, 128] + * 3) Clamp the value to [-128, 127] + * 4) Shift the integer range from [-128, 127] to [0, 255] */ + union float_bits x; + x.f32 = src[i] + 98304.0f; + + Uint32 y = x.u32 - 0x47C00000u; + Uint32 z = 0x7Fu - (y ^ SIGNMASK(y)); + y = (y ^ 0x80u) ^ (z & SIGNMASK(z)); + + dst[i] = (Uint8)(y & 0xFF); + } +} + +static void SDL_Convert_F32_to_S16_Scalar(Sint16 *dst, const float *src, int num_samples) +{ + int i; + + LOG_DEBUG_AUDIO_CONVERT("F32", "S16"); + + for (i = 0; i < num_samples; ++i) { + /* 1) Shift the float range from [-1.0, 1.0] to [383.0, 385.0] + * 2) Shift the integer range from [0x43BF8000, 0x43C08000] to [-32768, 32768] + * 3) Clamp values outside the [-32768, 32767] range */ + union float_bits x; + x.f32 = src[i] + 384.0f; + + Uint32 y = x.u32 - 0x43C00000u; + Uint32 z = 0x7FFFu - (y ^ SIGNMASK(y)); + y = y ^ (z & SIGNMASK(z)); + + dst[i] = (Sint16)(y & 0xFFFF); + } +} + +static void SDL_Convert_F32_to_S32_Scalar(Sint32 *dst, const float *src, int num_samples) +{ + int i; + + LOG_DEBUG_AUDIO_CONVERT("F32", "S32"); + + for (i = 0; i < num_samples; ++i) { + /* 1) Shift the float range from [-1.0, 1.0] to [-2147483648.0, 2147483648.0] + * 2) Set values outside the [-2147483648.0, 2147483647.0] range to -2147483648.0 + * 3) Convert the float to an integer, and fixup values outside the valid range */ + union float_bits x; + x.f32 = src[i]; + + Uint32 y = x.u32 + 0x0F800000u; + Uint32 z = y - 0xCF000000u; + z &= SIGNMASK(y ^ z); + x.u32 = y - z; + + dst[i] = (Sint32)x.f32 ^ (Sint32)SIGNMASK(z); + } +} + +#undef SIGNMASK #endif /* NEED_SCALAR_CONVERTER_FALLBACKS */ #ifdef SDL_SSE2_INTRINSICS static void SDL_TARGETING("sse2") SDL_Convert_S8_to_F32_SSE2(float *dst, const Sint8 *src, int num_samples) { - int i; + int i = num_samples; + + /* 1) Flip the sign bit to convert from S8 to U8 format + * 2) Construct a float in the range [65536.0, 65538.0) + * 3) Shift the float range to [-1.0, 1.0) + * dst[i] = i2f((src[i] ^ 0x80) | 0x47800000) - 65537.0 */ + const __m128i zero = _mm_setzero_si128(); + const __m128i flipper = _mm_set1_epi8(-0x80); + const __m128i caster = _mm_set1_epi16(0x4780 /* 0x47800000 = f2i(65536.0) */); + const __m128 offset = _mm_set1_ps(-65537.0); LOG_DEBUG_AUDIO_CONVERT("S8", "F32 (using SSE2)"); - src += num_samples - 1; - dst += num_samples - 1; + while (i >= 16) { + i -= 16; - /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */ - for (i = num_samples; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) { - *dst = ((float)*src) * DIVBY128; + const __m128i bytes = _mm_xor_si128(_mm_loadu_si128((const __m128i *)&src[i]), flipper); + + const __m128i shorts1 = _mm_unpacklo_epi8(bytes, zero); + const __m128i shorts2 = _mm_unpackhi_epi8(bytes, zero); + + const __m128 floats1 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts1, caster)), offset); + const __m128 floats2 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts1, caster)), offset); + const __m128 floats3 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts2, caster)), offset); + const __m128 floats4 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts2, caster)), offset); + + _mm_storeu_ps(&dst[i], floats1); + _mm_storeu_ps(&dst[i + 4], floats2); + _mm_storeu_ps(&dst[i + 8], floats3); + _mm_storeu_ps(&dst[i + 12], floats4); } - src -= 15; - dst -= 15; /* adjust to read SSE blocks from the start. */ - SDL_assert(!i || !(((size_t)dst) & 15)); - - /* Make sure src is aligned too. */ - if (!(((size_t)src) & 15)) { - /* Aligned! Do SSE blocks as long as we have 16 bytes available. */ - const __m128i *mmsrc = (const __m128i *)src; - const __m128i zero = _mm_setzero_si128(); - const __m128 divby128 = _mm_set1_ps(DIVBY128); - while (i >= 16) { /* 16 * 8-bit */ - const __m128i bytes = _mm_load_si128(mmsrc); /* get 16 sint8 into an XMM register. */ - /* treat as int16, shift left to clear every other sint16, then back right with sign-extend. Now sint16. */ - const __m128i shorts1 = _mm_srai_epi16(_mm_slli_epi16(bytes, 8), 8); - /* right-shift-sign-extend gets us sint16 with the other set of values. */ - const __m128i shorts2 = _mm_srai_epi16(bytes, 8); - /* unpack against zero to make these int32, shift to make them sign-extend, convert to float, multiply. Whew! */ - const __m128 floats1 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpacklo_epi16(shorts1, zero), 16), 16)), divby128); - const __m128 floats2 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpacklo_epi16(shorts2, zero), 16), 16)), divby128); - const __m128 floats3 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpackhi_epi16(shorts1, zero), 16), 16)), divby128); - const __m128 floats4 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpackhi_epi16(shorts2, zero), 16), 16)), divby128); - /* Interleave back into correct order, store. */ - _mm_store_ps(dst, _mm_unpacklo_ps(floats1, floats2)); - _mm_store_ps(dst + 4, _mm_unpackhi_ps(floats1, floats2)); - _mm_store_ps(dst + 8, _mm_unpacklo_ps(floats3, floats4)); - _mm_store_ps(dst + 12, _mm_unpackhi_ps(floats3, floats4)); - i -= 16; - mmsrc--; - dst -= 16; - } - - src = (const Sint8 *)mmsrc; - } - - src += 15; - dst += 15; /* adjust for any scalar finishing. */ - - /* Finish off any leftovers with scalar operations. */ while (i) { - *dst = ((float)*src) * DIVBY128; - i--; - src--; - dst--; + --i; + _mm_store_ss(&dst[i], _mm_add_ss(_mm_castsi128_ps(_mm_cvtsi32_si128((Uint8)src[i] ^ 0x47800080u)), offset)); } } static void SDL_TARGETING("sse2") SDL_Convert_U8_to_F32_SSE2(float *dst, const Uint8 *src, int num_samples) { - int i; + int i = num_samples; + + /* 1) Construct a float in the range [65536.0, 65538.0) + * 2) Shift the float range to [-1.0, 1.0) + * dst[i] = i2f(src[i] | 0x47800000) - 65537.0 */ + const __m128i zero = _mm_setzero_si128(); + const __m128i caster = _mm_set1_epi16(0x4780 /* 0x47800000 = f2i(65536.0) */); + const __m128 offset = _mm_set1_ps(-65537.0); LOG_DEBUG_AUDIO_CONVERT("U8", "F32 (using SSE2)"); - src += num_samples - 1; - dst += num_samples - 1; + while (i >= 16) { + i -= 16; - /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */ - for (i = num_samples; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) { - *dst = (((float)*src) * DIVBY128) - 1.0f; + const __m128i bytes = _mm_loadu_si128((const __m128i *)&src[i]); + + const __m128i shorts1 = _mm_unpacklo_epi8(bytes, zero); + const __m128i shorts2 = _mm_unpackhi_epi8(bytes, zero); + + const __m128 floats1 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts1, caster)), offset); + const __m128 floats2 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts1, caster)), offset); + const __m128 floats3 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts2, caster)), offset); + const __m128 floats4 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts2, caster)), offset); + + _mm_storeu_ps(&dst[i], floats1); + _mm_storeu_ps(&dst[i + 4], floats2); + _mm_storeu_ps(&dst[i + 8], floats3); + _mm_storeu_ps(&dst[i + 12], floats4); } - src -= 15; - dst -= 15; /* adjust to read SSE blocks from the start. */ - SDL_assert(!i || !(((size_t)dst) & 15)); - - /* Make sure src is aligned too. */ - if (!(((size_t)src) & 15)) { - /* Aligned! Do SSE blocks as long as we have 16 bytes available. */ - const __m128i *mmsrc = (const __m128i *)src; - const __m128i zero = _mm_setzero_si128(); - const __m128 divby128 = _mm_set1_ps(DIVBY128); - const __m128 minus1 = _mm_set1_ps(-1.0f); - while (i >= 16) { /* 16 * 8-bit */ - const __m128i bytes = _mm_load_si128(mmsrc); /* get 16 uint8 into an XMM register. */ - /* treat as int16, shift left to clear every other sint16, then back right with zero-extend. Now uint16. */ - const __m128i shorts1 = _mm_srli_epi16(_mm_slli_epi16(bytes, 8), 8); - /* right-shift-zero-extend gets us uint16 with the other set of values. */ - const __m128i shorts2 = _mm_srli_epi16(bytes, 8); - /* unpack against zero to make these int32, convert to float, multiply, add. Whew! */ - /* Note that AVX2 can do floating point multiply+add in one instruction, fwiw. SSE2 cannot. */ - const __m128 floats1 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(shorts1, zero)), divby128), minus1); - const __m128 floats2 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(shorts2, zero)), divby128), minus1); - const __m128 floats3 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(shorts1, zero)), divby128), minus1); - const __m128 floats4 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(shorts2, zero)), divby128), minus1); - /* Interleave back into correct order, store. */ - _mm_store_ps(dst, _mm_unpacklo_ps(floats1, floats2)); - _mm_store_ps(dst + 4, _mm_unpackhi_ps(floats1, floats2)); - _mm_store_ps(dst + 8, _mm_unpacklo_ps(floats3, floats4)); - _mm_store_ps(dst + 12, _mm_unpackhi_ps(floats3, floats4)); - i -= 16; - mmsrc--; - dst -= 16; - } - - src = (const Uint8 *)mmsrc; - } - - src += 15; - dst += 15; /* adjust for any scalar finishing. */ - - /* Finish off any leftovers with scalar operations. */ while (i) { - *dst = (((float)*src) * DIVBY128) - 1.0f; - i--; - src--; - dst--; + --i; + _mm_store_ss(&dst[i], _mm_add_ss(_mm_castsi128_ps(_mm_cvtsi32_si128((Uint8)src[i] ^ 0x47800000u)), offset)); } } static void SDL_TARGETING("sse2") SDL_Convert_S16_to_F32_SSE2(float *dst, const Sint16 *src, int num_samples) { - int i; + int i = num_samples; + + /* 1) Flip the sign bit to convert from S16 to U16 format + * 2) Construct a float in the range [256.0, 258.0) + * 3) Shift the float range to [-1.0, 1.0) + * dst[i] = i2f((src[i] ^ 0x8000) | 0x43800000) - 257.0 */ + const __m128i flipper = _mm_set1_epi16(-0x8000); + const __m128i caster = _mm_set1_epi16(0x4380 /* 0x43800000 = f2i(256.0) */); + const __m128 offset = _mm_set1_ps(-257.0f); LOG_DEBUG_AUDIO_CONVERT("S16", "F32 (using SSE2)"); - src += num_samples - 1; - dst += num_samples - 1; + while (i >= 16) { + i -= 16; - /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */ - for (i = num_samples; i && (((size_t)(dst - 7)) & 15); --i, --src, --dst) { - *dst = ((float)*src) * DIVBY32768; + const __m128i shorts1 = _mm_xor_si128(_mm_loadu_si128((const __m128i *)&src[i]), flipper); + const __m128i shorts2 = _mm_xor_si128(_mm_loadu_si128((const __m128i *)&src[i + 8]), flipper); + + const __m128 floats1 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts1, caster)), offset); + const __m128 floats2 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts1, caster)), offset); + const __m128 floats3 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts2, caster)), offset); + const __m128 floats4 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts2, caster)), offset); + + _mm_storeu_ps(&dst[i], floats1); + _mm_storeu_ps(&dst[i + 4], floats2); + _mm_storeu_ps(&dst[i + 8], floats3); + _mm_storeu_ps(&dst[i + 12], floats4); } - src -= 7; - dst -= 7; /* adjust to read SSE blocks from the start. */ - SDL_assert(!i || !(((size_t)dst) & 15)); - - /* Make sure src is aligned too. */ - if (!(((size_t)src) & 15)) { - /* Aligned! Do SSE blocks as long as we have 16 bytes available. */ - const __m128 divby32768 = _mm_set1_ps(DIVBY32768); - while (i >= 8) { /* 8 * 16-bit */ - const __m128i ints = _mm_load_si128((__m128i const *)src); /* get 8 sint16 into an XMM register. */ - /* treat as int32, shift left to clear every other sint16, then back right with sign-extend. Now sint32. */ - const __m128i a = _mm_srai_epi32(_mm_slli_epi32(ints, 16), 16); - /* right-shift-sign-extend gets us sint32 with the other set of values. */ - const __m128i b = _mm_srai_epi32(ints, 16); - /* Interleave these back into the right order, convert to float, multiply, store. */ - _mm_store_ps(dst, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi32(a, b)), divby32768)); - _mm_store_ps(dst + 4, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi32(a, b)), divby32768)); - i -= 8; - src -= 8; - dst -= 8; - } - } - - src += 7; - dst += 7; /* adjust for any scalar finishing. */ - - /* Finish off any leftovers with scalar operations. */ while (i) { - *dst = ((float)*src) * DIVBY32768; - i--; - src--; - dst--; + --i; + _mm_store_ss(&dst[i], _mm_add_ss(_mm_castsi128_ps(_mm_cvtsi32_si128((Uint16)src[i] ^ 0x43808000u)), offset)); } } static void SDL_TARGETING("sse2") SDL_Convert_S32_to_F32_SSE2(float *dst, const Sint32 *src, int num_samples) { - int i; + int i = num_samples; + + /* dst[i] = f32(src[i]) / f32(0x80000000) */ + const __m128 scaler = _mm_set1_ps(DIVBY2147483648); LOG_DEBUG_AUDIO_CONVERT("S32", "F32 (using SSE2)"); - /* Get dst aligned to 16 bytes */ - for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) { - *dst = ((float)(*src >> 8)) * DIVBY8388607; + while (i >= 16) { + i -= 16; + + const __m128i ints1 = _mm_loadu_si128((const __m128i *)&src[i]); + const __m128i ints2 = _mm_loadu_si128((const __m128i *)&src[i + 4]); + const __m128i ints3 = _mm_loadu_si128((const __m128i *)&src[i + 8]); + const __m128i ints4 = _mm_loadu_si128((const __m128i *)&src[i + 12]); + + const __m128 floats1 = _mm_mul_ps(_mm_cvtepi32_ps(ints1), scaler); + const __m128 floats2 = _mm_mul_ps(_mm_cvtepi32_ps(ints2), scaler); + const __m128 floats3 = _mm_mul_ps(_mm_cvtepi32_ps(ints3), scaler); + const __m128 floats4 = _mm_mul_ps(_mm_cvtepi32_ps(ints4), scaler); + + _mm_storeu_ps(&dst[i], floats1); + _mm_storeu_ps(&dst[i + 4], floats2); + _mm_storeu_ps(&dst[i + 8], floats3); + _mm_storeu_ps(&dst[i + 12], floats4); } - SDL_assert(!i || !(((size_t)dst) & 15)); - - /* Make sure src is aligned too. */ - if (!(((size_t)src) & 15)) { - /* Aligned! Do SSE blocks as long as we have 16 bytes available. */ - const __m128 divby8388607 = _mm_set1_ps(DIVBY8388607); - const __m128i *mmsrc = (const __m128i *)src; - while (i >= 4) { /* 4 * sint32 */ - /* shift out lowest bits so int fits in a float32. Small precision loss, but much faster. */ - _mm_store_ps(dst, _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_load_si128(mmsrc), 8)), divby8388607)); - i -= 4; - mmsrc++; - dst += 4; - } - src = (const Sint32 *)mmsrc; - } - - /* Finish off any leftovers with scalar operations. */ while (i) { - *dst = ((float)(*src >> 8)) * DIVBY8388607; - i--; - src++; - dst++; + --i; + _mm_store_ss(&dst[i], _mm_mul_ss(_mm_cvt_si2ss(_mm_setzero_ps(), src[i]), scaler)); } } static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S8_SSE2(Sint8 *dst, const float *src, int num_samples) { - int i; + int i = num_samples; + + /* 1) Shift the float range from [-1.0, 1.0] to [98303.0, 98305.0] + * 2) Extract the lowest 16 bits and clamp to [-128, 127] + * Overflow is correctly handled for inputs between roughly [-255.0, 255.0] + * dst[i] = clamp(i16(f2i(src[i] + 98304.0) & 0xFFFF), -128, 127) */ + const __m128 offset = _mm_set1_ps(98304.0f); + const __m128i mask = _mm_set1_epi16(0xFF); LOG_DEBUG_AUDIO_CONVERT("F32", "S8 (using SSE2)"); - /* Get dst aligned to 16 bytes */ - for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) { - const float sample = *src; - if (sample >= 1.0f) { - *dst = 127; - } else if (sample <= -1.0f) { - *dst = -128; - } else { - *dst = (Sint8)(sample * 127.0f); - } + while (i >= 16) { + const __m128 floats1 = _mm_loadu_ps(&src[0]); + const __m128 floats2 = _mm_loadu_ps(&src[4]); + const __m128 floats3 = _mm_loadu_ps(&src[8]); + const __m128 floats4 = _mm_loadu_ps(&src[12]); + + const __m128i ints1 = _mm_castps_si128(_mm_add_ps(floats1, offset)); + const __m128i ints2 = _mm_castps_si128(_mm_add_ps(floats2, offset)); + const __m128i ints3 = _mm_castps_si128(_mm_add_ps(floats3, offset)); + const __m128i ints4 = _mm_castps_si128(_mm_add_ps(floats4, offset)); + + const __m128i shorts1 = _mm_and_si128(_mm_packs_epi16(ints1, ints2), mask); + const __m128i shorts2 = _mm_and_si128(_mm_packs_epi16(ints3, ints4), mask); + + const __m128i bytes = _mm_packus_epi16(shorts1, shorts2); + + _mm_storeu_si128((__m128i*)dst, bytes); + + i -= 16; + src += 16; + dst += 16; } - SDL_assert(!i || !(((size_t)dst) & 15)); - - /* Make sure src is aligned too. */ - if (!(((size_t)src) & 15)) { - /* Aligned! Do SSE blocks as long as we have 16 bytes available. */ - const __m128 one = _mm_set1_ps(1.0f); - const __m128 negone = _mm_set1_ps(-1.0f); - const __m128 mulby127 = _mm_set1_ps(127.0f); - __m128i *mmdst = (__m128i *)dst; - while (i >= 16) { /* 16 * float32 */ - const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */ - const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 4)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */ - const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 8)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */ - const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 12)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */ - _mm_store_si128(mmdst, _mm_packs_epi16(_mm_packs_epi32(ints1, ints2), _mm_packs_epi32(ints3, ints4))); /* pack down, store out. */ - i -= 16; - src += 16; - mmdst++; - } - dst = (Sint8 *)mmdst; - } - - /* Finish off any leftovers with scalar operations. */ while (i) { - const float sample = *src; - if (sample >= 1.0f) { - *dst = 127; - } else if (sample <= -1.0f) { - *dst = -128; - } else { - *dst = (Sint8)(sample * 127.0f); - } - i--; - src++; - dst++; + const __m128i ints = _mm_castps_si128(_mm_add_ss(_mm_load_ss(src), offset)); + *dst = (Sint8)(_mm_cvtsi128_si32(_mm_packs_epi16(ints, ints)) & 0xFF); + + --i; + ++src; + ++dst; } } static void SDL_TARGETING("sse2") SDL_Convert_F32_to_U8_SSE2(Uint8 *dst, const float *src, int num_samples) { - int i; + int i = num_samples; + + /* 1) Shift the float range from [-1.0, 1.0] to [98304.0, 98306.0] + * 2) Extract the lowest 16 bits and clamp to [0, 255] + * Overflow is correctly handled for inputs between roughly [-254.0, 254.0] + * dst[i] = clamp(i16(f2i(src[i] + 98305.0) & 0xFFFF), 0, 255) */ + const __m128 offset = _mm_set1_ps(98305.0f); + const __m128i mask = _mm_set1_epi16(0xFF); LOG_DEBUG_AUDIO_CONVERT("F32", "U8 (using SSE2)"); - /* Get dst aligned to 16 bytes */ - for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) { - const float sample = *src; - if (sample >= 1.0f) { - *dst = 255; - } else if (sample <= -1.0f) { - *dst = 0; - } else { - *dst = (Uint8)((sample + 1.0f) * 127.0f); - } + while (i >= 16) { + const __m128 floats1 = _mm_loadu_ps(&src[0]); + const __m128 floats2 = _mm_loadu_ps(&src[4]); + const __m128 floats3 = _mm_loadu_ps(&src[8]); + const __m128 floats4 = _mm_loadu_ps(&src[12]); + + const __m128i ints1 = _mm_castps_si128(_mm_add_ps(floats1, offset)); + const __m128i ints2 = _mm_castps_si128(_mm_add_ps(floats2, offset)); + const __m128i ints3 = _mm_castps_si128(_mm_add_ps(floats3, offset)); + const __m128i ints4 = _mm_castps_si128(_mm_add_ps(floats4, offset)); + + const __m128i shorts1 = _mm_and_si128(_mm_packus_epi16(ints1, ints2), mask); + const __m128i shorts2 = _mm_and_si128(_mm_packus_epi16(ints3, ints4), mask); + + const __m128i bytes = _mm_packus_epi16(shorts1, shorts2); + + _mm_storeu_si128((__m128i*)dst, bytes); + + i -= 16; + src += 16; + dst += 16; } - SDL_assert(!i || !(((size_t)dst) & 15)); - - /* Make sure src is aligned too. */ - if (!(((size_t)src) & 15)) { - /* Aligned! Do SSE blocks as long as we have 16 bytes available. */ - const __m128 one = _mm_set1_ps(1.0f); - const __m128 negone = _mm_set1_ps(-1.0f); - const __m128 mulby127 = _mm_set1_ps(127.0f); - __m128i *mmdst = (__m128i *)dst; - while (i >= 16) { /* 16 * float32 */ - const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */ - const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 4)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */ - const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 8)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */ - const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 12)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */ - _mm_store_si128(mmdst, _mm_packus_epi16(_mm_packs_epi32(ints1, ints2), _mm_packs_epi32(ints3, ints4))); /* pack down, store out. */ - i -= 16; - src += 16; - mmdst++; - } - dst = (Uint8 *)mmdst; - } - - /* Finish off any leftovers with scalar operations. */ while (i) { - const float sample = *src; - if (sample >= 1.0f) { - *dst = 255; - } else if (sample <= -1.0f) { - *dst = 0; - } else { - *dst = (Uint8)((sample + 1.0f) * 127.0f); - } - i--; - src++; - dst++; + const __m128i ints = _mm_castps_si128(_mm_add_ss(_mm_load_ss(src), offset)); + *dst = (Uint8)(_mm_cvtsi128_si32(_mm_packus_epi16(ints, ints)) & 0xFF); + + --i; + ++src; + ++dst; } } static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S16_SSE2(Sint16 *dst, const float *src, int num_samples) { - int i; + int i = num_samples; + + /* 1) Shift the float range from [-1.0, 1.0] to [256.0, 258.0] + * 2) Shift the int range from [0x43800000, 0x43810000] to [-32768,32768] + * 3) Clamp to range [-32768,32767] + * Overflow is correctly handled for inputs between roughly [-257.0, +inf) + * dst[i] = clamp(f2i(src[i] + 257.0) - 0x43808000, -32768, 32767) */ + const __m128 offset = _mm_set1_ps(257.0f); LOG_DEBUG_AUDIO_CONVERT("F32", "S16 (using SSE2)"); - /* Get dst aligned to 16 bytes */ - for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) { - const float sample = *src; - if (sample >= 1.0f) { - *dst = 32767; - } else if (sample <= -1.0f) { - *dst = -32768; - } else { - *dst = (Sint16)(sample * 32767.0f); - } + while (i >= 16) { + const __m128 floats1 = _mm_loadu_ps(&src[0]); + const __m128 floats2 = _mm_loadu_ps(&src[4]); + const __m128 floats3 = _mm_loadu_ps(&src[8]); + const __m128 floats4 = _mm_loadu_ps(&src[12]); + + const __m128i ints1 = _mm_sub_epi32(_mm_castps_si128(_mm_add_ps(floats1, offset)), _mm_castps_si128(offset)); + const __m128i ints2 = _mm_sub_epi32(_mm_castps_si128(_mm_add_ps(floats2, offset)), _mm_castps_si128(offset)); + const __m128i ints3 = _mm_sub_epi32(_mm_castps_si128(_mm_add_ps(floats3, offset)), _mm_castps_si128(offset)); + const __m128i ints4 = _mm_sub_epi32(_mm_castps_si128(_mm_add_ps(floats4, offset)), _mm_castps_si128(offset)); + + const __m128i shorts1 = _mm_packs_epi32(ints1, ints2); + const __m128i shorts2 = _mm_packs_epi32(ints3, ints4); + + _mm_storeu_si128((__m128i*)&dst[0], shorts1); + _mm_storeu_si128((__m128i*)&dst[8], shorts2); + + i -= 16; + src += 16; + dst += 16; } - SDL_assert(!i || !(((size_t)dst) & 15)); - - /* Make sure src is aligned too. */ - if (!(((size_t)src) & 15)) { - /* Aligned! Do SSE blocks as long as we have 16 bytes available. */ - const __m128 one = _mm_set1_ps(1.0f); - const __m128 negone = _mm_set1_ps(-1.0f); - const __m128 mulby32767 = _mm_set1_ps(32767.0f); - __m128i *mmdst = (__m128i *)dst; - while (i >= 8) { /* 8 * float32 */ - const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby32767)); /* load 4 floats, clamp, convert to sint32 */ - const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 4)), one), mulby32767)); /* load 4 floats, clamp, convert to sint32 */ - _mm_store_si128(mmdst, _mm_packs_epi32(ints1, ints2)); /* pack to sint16, store out. */ - i -= 8; - src += 8; - mmdst++; - } - dst = (Sint16 *)mmdst; - } - - /* Finish off any leftovers with scalar operations. */ while (i) { - const float sample = *src; - if (sample >= 1.0f) { - *dst = 32767; - } else if (sample <= -1.0f) { - *dst = -32768; - } else { - *dst = (Sint16)(sample * 32767.0f); - } - i--; - src++; - dst++; + const __m128i ints = _mm_sub_epi32(_mm_castps_si128(_mm_add_ss(_mm_load_ss(src), offset)), _mm_castps_si128(offset)); + *dst = (Sint16)(_mm_cvtsi128_si32(_mm_packs_epi32(ints, ints)) & 0xFFFF); + + --i; + ++src; + ++dst; } } static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S32_SSE2(Sint32 *dst, const float *src, int num_samples) { - int i; + int i = num_samples; + + /* 1) Scale the float range from [-1.0, 1.0] to [-2147483648.0, 2147483648.0] + * 2) Convert to integer (values too small/large become 0x80000000 = -2147483648) + * 3) Fixup values which were too large (0x80000000 ^ 0xFFFFFFFF = 2147483647) + * dst[i] = i32(src[i] * 2147483648.0) ^ ((src[i] >= 2147483648.0) ? 0xFFFFFFFF : 0x00000000) */ + const __m128 limit = _mm_set1_ps(2147483648.0f); LOG_DEBUG_AUDIO_CONVERT("F32", "S32 (using SSE2)"); - /* Get dst aligned to 16 bytes */ - for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) { - const float sample = *src; - if (sample >= 1.0f) { - *dst = 2147483647; - } else if (sample <= -1.0f) { - *dst = (Sint32)-2147483648LL; - } else { - *dst = ((Sint32)(sample * 8388607.0f)) << 8; - } + while (i >= 16) { + const __m128 floats1 = _mm_loadu_ps(&src[0]); + const __m128 floats2 = _mm_loadu_ps(&src[4]); + const __m128 floats3 = _mm_loadu_ps(&src[8]); + const __m128 floats4 = _mm_loadu_ps(&src[12]); + + const __m128 values1 = _mm_mul_ps(floats1, limit); + const __m128 values2 = _mm_mul_ps(floats2, limit); + const __m128 values3 = _mm_mul_ps(floats3, limit); + const __m128 values4 = _mm_mul_ps(floats4, limit); + + const __m128i ints1 = _mm_xor_si128(_mm_cvttps_epi32(values1), _mm_castps_si128(_mm_cmpge_ps(values1, limit))); + const __m128i ints2 = _mm_xor_si128(_mm_cvttps_epi32(values2), _mm_castps_si128(_mm_cmpge_ps(values2, limit))); + const __m128i ints3 = _mm_xor_si128(_mm_cvttps_epi32(values3), _mm_castps_si128(_mm_cmpge_ps(values3, limit))); + const __m128i ints4 = _mm_xor_si128(_mm_cvttps_epi32(values4), _mm_castps_si128(_mm_cmpge_ps(values4, limit))); + + _mm_storeu_si128((__m128i*)&dst[0], ints1); + _mm_storeu_si128((__m128i*)&dst[4], ints2); + _mm_storeu_si128((__m128i*)&dst[8], ints3); + _mm_storeu_si128((__m128i*)&dst[12], ints4); + + i -= 16; + src += 16; + dst += 16; } - SDL_assert(!i || !(((size_t)dst) & 15)); - SDL_assert(!i || !(((size_t)src) & 15)); - - { - /* Aligned! Do SSE blocks as long as we have 16 bytes available. */ - const __m128 one = _mm_set1_ps(1.0f); - const __m128 negone = _mm_set1_ps(-1.0f); - const __m128 mulby8388607 = _mm_set1_ps(8388607.0f); - __m128i *mmdst = (__m128i *)dst; - while (i >= 4) { /* 4 * float32 */ - _mm_store_si128(mmdst, _mm_slli_epi32(_mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby8388607)), 8)); /* load 4 floats, clamp, convert to sint32 */ - i -= 4; - src += 4; - mmdst++; - } - dst = (Sint32 *)mmdst; - } - - /* Finish off any leftovers with scalar operations. */ while (i) { - const float sample = *src; - if (sample >= 1.0f) { - *dst = 2147483647; - } else if (sample <= -1.0f) { - *dst = (Sint32)-2147483648LL; - } else { - *dst = ((Sint32)(sample * 8388607.0f)) << 8; - } - i--; - src++; - dst++; + const __m128 floats = _mm_load_ss(src); + const __m128 values = _mm_mul_ss(floats, limit); + const __m128i ints = _mm_xor_si128(_mm_cvttps_epi32(values), _mm_castps_si128(_mm_cmpge_ss(values, limit))); + *dst = (Sint32)_mm_cvtsi128_si32(ints); + + --i; + ++src; + ++dst; } } #endif #ifdef SDL_NEON_INTRINSICS +#define DIVBY128 0.0078125f /* 0x1p-7f */ +#define DIVBY32768 0.000030517578125f /* 0x1p-15f */ +#define DIVBY8388607 0.00000011920930376163766f /* 0x1.000002p-23f */ + static void SDL_Convert_S8_to_F32_NEON(float *dst, const Sint8 *src, int num_samples) { int i; diff --git a/external/sdl/SDL/src/audio/SDL_mixer.c b/external/sdl/SDL/src/audio/SDL_mixer.c index c4630ef0..0a4992d8 100644 --- a/external/sdl/SDL/src/audio/SDL_mixer.c +++ b/external/sdl/SDL/src/audio/SDL_mixer.c @@ -131,7 +131,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format, } } break; - case SDL_AUDIO_S16LSB: + case SDL_AUDIO_S16LE: { Sint16 src1, src2; int dst_sample; @@ -155,7 +155,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format, } } break; - case SDL_AUDIO_S16MSB: + case SDL_AUDIO_S16BE: { Sint16 src1, src2; int dst_sample; @@ -179,7 +179,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format, } } break; - case SDL_AUDIO_S32LSB: + case SDL_AUDIO_S32LE: { const Uint32 *src32 = (Uint32 *)src; Uint32 *dst32 = (Uint32 *)dst; @@ -204,7 +204,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format, } } break; - case SDL_AUDIO_S32MSB: + case SDL_AUDIO_S32BE: { const Uint32 *src32 = (Uint32 *)src; Uint32 *dst32 = (Uint32 *)dst; @@ -229,7 +229,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format, } } break; - case SDL_AUDIO_F32LSB: + case SDL_AUDIO_F32LE: { const float fmaxvolume = 1.0f / ((float)SDL_MIX_MAXVOLUME); const float fvolume = (float)volume; @@ -257,7 +257,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format, } } break; - case SDL_AUDIO_F32MSB: + case SDL_AUDIO_F32BE: { const float fmaxvolume = 1.0f / ((float)SDL_MIX_MAXVOLUME); const float fvolume = (float)volume; diff --git a/external/sdl/SDL/src/audio/SDL_sysaudio.h b/external/sdl/SDL/src/audio/SDL_sysaudio.h index 7e170330..e0d93160 100644 --- a/external/sdl/SDL/src/audio/SDL_sysaudio.h +++ b/external/sdl/SDL/src/audio/SDL_sysaudio.h @@ -24,8 +24,6 @@ #ifndef SDL_sysaudio_h_ #define SDL_sysaudio_h_ -#include "../SDL_dataqueue.h" - #define DEBUG_AUDIOSTREAM 0 #define DEBUG_AUDIO_CONVERT 0 @@ -58,6 +56,8 @@ extern void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_sam #define DEFAULT_AUDIO_CAPTURE_CHANNELS 1 #define DEFAULT_AUDIO_CAPTURE_FREQUENCY 44100 +#define AUDIO_SPECS_EQUAL(x, y) (((x).format == (y).format) && ((x).channels == (y).channels) && ((x).freq == (y).freq)) + typedef struct SDL_AudioDevice SDL_AudioDevice; typedef struct SDL_LogicalAudioDevice SDL_LogicalAudioDevice; @@ -70,8 +70,9 @@ extern void SDL_QuitAudio(void); // Function to get a list of audio formats, ordered most similar to `format` to least, 0-terminated. Don't free results. const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format); -// Must be called at least once before using converters (SDL_CreateAudioStream will call it !!! FIXME but probably shouldn't). +// Must be called at least once before using converters. extern void SDL_ChooseAudioConverters(void); +extern void SDL_SetupAudioResampler(void); /* Backends should call this as devices are added to the system (such as a USB headset being plugged in), and should also be called for @@ -101,7 +102,7 @@ extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByCallback(SDL_bool (*callbac extern void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device); // Backends can call this to get a standardized name for a thread to power a specific audio device. -char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen); +extern char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen); // These functions are the heart of the audio threads. Backends can call them directly if they aren't using the SDL-provided thread. @@ -113,6 +114,14 @@ extern SDL_bool SDL_CaptureAudioThreadIterate(SDL_AudioDevice *device); extern void SDL_CaptureAudioThreadShutdown(SDL_AudioDevice *device); extern void SDL_AudioThreadFinalize(SDL_AudioDevice *device); +// this gets used from the audio device threads. It has rules, don't use this if you don't know how to use it! +extern void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_format, int src_channels, + void *dst, SDL_AudioFormat dst_format, int dst_channels, void* scratch); + +// Special case to let something in SDL_audiocvt.c access something in SDL_audio.c. Don't use this. +extern void OnAudioStreamCreated(SDL_AudioStream *stream); +extern void OnAudioStreamDestroy(SDL_AudioStream *stream); + typedef struct SDL_AudioDriverImpl { void (*DetectDevices)(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture); @@ -120,7 +129,7 @@ typedef struct SDL_AudioDriverImpl void (*ThreadInit)(SDL_AudioDevice *device); // Called by audio thread at start void (*ThreadDeinit)(SDL_AudioDevice *device); // Called by audio thread at end void (*WaitDevice)(SDL_AudioDevice *device); - void (*PlayDevice)(SDL_AudioDevice *device, const Uint8 *buffer, int buflen); // buffer and buflen are always from GetDeviceBuf, passed here for convenience. + int (*PlayDevice)(SDL_AudioDevice *device, const Uint8 *buffer, int buflen); // buffer and buflen are always from GetDeviceBuf, passed here for convenience. Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *device, int *buffer_size); void (*WaitCaptureDevice)(SDL_AudioDevice *device); int (*CaptureFromDevice)(SDL_AudioDevice *device, void *buffer, int buflen); @@ -145,6 +154,7 @@ typedef struct SDL_AudioDriver SDL_RWLock *device_list_lock; // A mutex for device detection SDL_AudioDevice *output_devices; // the list of currently-available audio output devices. SDL_AudioDevice *capture_devices; // the list of currently-available audio capture devices. + SDL_AudioStream *existing_streams; // a list of all existing SDL_AudioStreams. SDL_AudioDeviceID default_output_device_id; SDL_AudioDeviceID default_capture_device_id; SDL_AtomicInt output_device_count; @@ -153,46 +163,41 @@ typedef struct SDL_AudioDriver SDL_AtomicInt shutting_down; // non-zero during SDL_Quit, so we known not to accept any last-minute device hotplugs. } SDL_AudioDriver; +struct SDL_AudioQueue; // forward decl. + struct SDL_AudioStream { - SDL_DataQueue *queue; - SDL_Mutex *lock; // this is just a copy of `queue`'s mutex. We share a lock. + SDL_Mutex* lock; - SDL_AudioStreamRequestCallback get_callback; + SDL_AudioStreamCallback get_callback; void *get_callback_userdata; - SDL_AudioStreamRequestCallback put_callback; + SDL_AudioStreamCallback put_callback; void *put_callback_userdata; - Uint8 *work_buffer; // used for scratch space during data conversion/resampling. - Uint8 *history_buffer; // history for left padding and future sample rate changes. - Uint8 *future_buffer; // stuff that left the queue for the right padding and will be next read's data. - float *left_padding; // left padding for resampling. - float *right_padding; // right padding for resampling. - - SDL_bool flushed; - - size_t work_buffer_allocation; - size_t history_buffer_allocation; - size_t future_buffer_allocation; - size_t resampler_padding_allocation; - - int resampler_padding_frames; - int history_buffer_frames; - int future_buffer_filled_frames; - SDL_AudioSpec src_spec; SDL_AudioSpec dst_spec; + float freq_ratio; - int src_sample_frame_size; - int dst_sample_frame_size; - int max_sample_frame_size; + struct SDL_AudioQueue* queue; + Uint64 total_bytes_queued; - int pre_resample_channels; - int packetlen; + SDL_AudioSpec input_spec; // The spec of input data currently being processed + Sint64 resample_offset; + + Uint8 *work_buffer; // used for scratch space during data conversion/resampling. + size_t work_buffer_allocation; + + Uint8 *history_buffer; // history for left padding and future sample rate changes. + size_t history_buffer_allocation; + + SDL_bool simplified; // SDL_TRUE if created via SDL_OpenAudioDeviceStream SDL_LogicalAudioDevice *bound_device; SDL_AudioStream *next_binding; SDL_AudioStream *prev_binding; + + SDL_AudioStream *prev; // linked list of all existing streams (so we can free them on shutdown). + SDL_AudioStream *next; // linked list of all existing streams (so we can free them on shutdown). }; /* Logical devices are an abstraction in SDL3; you can open the same physical @@ -214,7 +219,16 @@ struct SDL_LogicalAudioDevice SDL_AudioStream *bound_streams; // SDL_TRUE if this was opened as a default device. - SDL_bool is_default; + SDL_bool opened_as_default; + + // SDL_TRUE if device was opened with SDL_OpenAudioDeviceStream (so it forbids binding changes, etc). + SDL_bool simplified; + + // If non-NULL, callback into the app that lets them access the final postmix buffer. + SDL_AudioPostmixCallback postmix; + + // App-supplied pointer for postmix callback. + void *postmix_userdata; // double-linked list of opened devices on the same physical device. SDL_LogicalAudioDevice *next; @@ -263,14 +277,22 @@ struct SDL_AudioDevice // SDL_TRUE if this is a capture device instead of an output device SDL_bool iscapture; - // Scratch buffer used for mixing. + // SDL_TRUE if audio thread can skip silence/mix/convert stages and just do a basic memcpy. + SDL_bool simple_copy; + + // Scratch buffers used for mixing. Uint8 *work_buffer; + Uint8 *mix_buffer; + float *postmix_buffer; + + // Size of work_buffer (and mix_buffer) in bytes. + int work_buffer_size; // A thread to feed the audio device SDL_Thread *thread; // SDL_TRUE if this physical device is currently opened by the backend. - SDL_bool is_opened; + SDL_bool currently_opened; // Data private to this driver struct SDL_PrivateAudioData *hidden; @@ -316,7 +338,4 @@ extern AudioBootStrap N3DSAUDIO_bootstrap; extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap; extern AudioBootStrap QSAAUDIO_bootstrap; -extern SDL_AudioDevice *get_audio_dev(SDL_AudioDeviceID id); -extern int get_max_num_audio_dev(void); - #endif // SDL_sysaudio_h_ diff --git a/external/sdl/SDL/src/audio/SDL_wave.c b/external/sdl/SDL/src/audio/SDL_wave.c index 27d540c4..2afb70bd 100644 --- a/external/sdl/SDL/src/audio/SDL_wave.c +++ b/external/sdl/SDL/src/audio/SDL_wave.c @@ -2039,10 +2039,10 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 * case ALAW_CODE: case MULAW_CODE: /* These can be easily stored in the byte order of the system. */ - spec->format = SDL_AUDIO_S16SYS; + spec->format = SDL_AUDIO_S16; break; case IEEE_FLOAT_CODE: - spec->format = SDL_AUDIO_F32LSB; + spec->format = SDL_AUDIO_F32LE; break; case PCM_CODE: switch (format->bitspersample) { @@ -2050,11 +2050,11 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 * spec->format = SDL_AUDIO_U8; break; case 16: - spec->format = SDL_AUDIO_S16LSB; + spec->format = SDL_AUDIO_S16LE; break; case 24: /* Has been shifted to 32 bits. */ case 32: - spec->format = SDL_AUDIO_S32LSB; + spec->format = SDL_AUDIO_S32LE; break; default: /* Just in case something unexpected happened in the checks. */ diff --git a/external/sdl/SDL/src/audio/aaudio/SDL_aaudio.c b/external/sdl/SDL/src/audio/aaudio/SDL_aaudio.c index b843ba6c..7692f4e2 100644 --- a/external/sdl/SDL/src/audio/aaudio/SDL_aaudio.c +++ b/external/sdl/SDL/src/audio/aaudio/SDL_aaudio.c @@ -111,13 +111,14 @@ static void AAUDIO_WaitDevice(SDL_AudioDevice *device) SDL_WaitSemaphore(device->hidden->semaphore); } -static void AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) +static int AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) { // AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here. if (SDL_AtomicGet(&device->hidden->error_callback_triggered)) { SDL_AtomicSet(&device->hidden->error_callback_triggered, 0); - SDL_AudioDeviceDisconnected(device); + return -1; } + return 0; } // no need for a FlushCapture implementation, just don't read mixbuf until the next iteration. @@ -197,9 +198,9 @@ static int AAUDIO_OpenDevice(SDL_AudioDevice *device) const aaudio_direction_t direction = (iscapture ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT); ctx.AAudioStreamBuilder_setDirection(builder, direction); aaudio_format_t format; - if ((device->spec.format == SDL_AUDIO_S32SYS) && (SDL_GetAndroidSDKVersion() >= 31)) { + if ((device->spec.format == SDL_AUDIO_S32) && (SDL_GetAndroidSDKVersion() >= 31)) { format = AAUDIO_FORMAT_PCM_I32; - } else if (device->spec.format == SDL_AUDIO_F32SYS) { + } else if (device->spec.format == SDL_AUDIO_F32) { format = AAUDIO_FORMAT_PCM_FLOAT; } else { format = AAUDIO_FORMAT_PCM_I16; // sint16 is a safe bet for everything else. @@ -244,11 +245,11 @@ static int AAUDIO_OpenDevice(SDL_AudioDevice *device) format = ctx.AAudioStream_getFormat(hidden->stream); if (format == AAUDIO_FORMAT_PCM_I16) { - device->spec.format = SDL_AUDIO_S16SYS; + device->spec.format = SDL_AUDIO_S16; } else if (format == AAUDIO_FORMAT_PCM_I32) { - device->spec.format = SDL_AUDIO_S32SYS; + device->spec.format = SDL_AUDIO_S32; } else if (format == AAUDIO_FORMAT_PCM_FLOAT) { - device->spec.format = SDL_AUDIO_F32SYS; + device->spec.format = SDL_AUDIO_F32; } else { return SDL_SetError("Got unexpected audio format %d from AAudioStream_getFormat", (int) format); } diff --git a/external/sdl/SDL/src/audio/alsa/SDL_alsa_audio.c b/external/sdl/SDL/src/audio/alsa/SDL_alsa_audio.c index 66a3b3b0..a8b9abfa 100644 --- a/external/sdl/SDL/src/audio/alsa/SDL_alsa_audio.c +++ b/external/sdl/SDL/src/audio/alsa/SDL_alsa_audio.c @@ -351,12 +351,11 @@ static void ALSA_WaitDevice(SDL_AudioDevice *device) } } -static void ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) +static int ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) { SDL_assert(buffer == device->hidden->mixbuf); Uint8 *sample_buf = device->hidden->mixbuf; - const int frame_size = ((SDL_AUDIO_BITSIZE(device->spec.format)) / 8) * - device->spec.channels; + const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec); snd_pcm_uframes_t frames_left = (snd_pcm_uframes_t) (buflen / frame_size); device->hidden->swizzle_func(device, sample_buf, frames_left); @@ -378,8 +377,7 @@ static void ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int bu SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA write failed (unrecoverable): %s", ALSA_snd_strerror(status)); - SDL_AudioDeviceDisconnected(device); - return; + return -1; } continue; } else if (status == 0) { @@ -391,6 +389,8 @@ static void ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int bu sample_buf += status * frame_size; frames_left -= status; } + + return 0; } static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) @@ -401,8 +401,7 @@ static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) static int ALSA_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen) { Uint8 *sample_buf = (Uint8 *)buffer; - const int frame_size = ((SDL_AUDIO_BITSIZE(device->spec.format)) / 8) * - device->spec.channels; + const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec); const int total_frames = buflen / frame_size; snd_pcm_uframes_t frames_left = total_frames; @@ -564,22 +563,22 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device) case SDL_AUDIO_S8: format = SND_PCM_FORMAT_S8; break; - case SDL_AUDIO_S16LSB: + case SDL_AUDIO_S16LE: format = SND_PCM_FORMAT_S16_LE; break; - case SDL_AUDIO_S16MSB: + case SDL_AUDIO_S16BE: format = SND_PCM_FORMAT_S16_BE; break; - case SDL_AUDIO_S32LSB: + case SDL_AUDIO_S32LE: format = SND_PCM_FORMAT_S32_LE; break; - case SDL_AUDIO_S32MSB: + case SDL_AUDIO_S32BE: format = SND_PCM_FORMAT_S32_BE; break; - case SDL_AUDIO_F32LSB: + case SDL_AUDIO_F32LE: format = SND_PCM_FORMAT_FLOAT_LE; break; - case SDL_AUDIO_F32MSB: + case SDL_AUDIO_F32BE: format = SND_PCM_FORMAT_FLOAT_BE; break; default: diff --git a/external/sdl/SDL/src/audio/android/SDL_androidaudio.c b/external/sdl/SDL/src/audio/android/SDL_androidaudio.c index 761ec5be..c0dade8a 100644 --- a/external/sdl/SDL/src/audio/android/SDL_androidaudio.c +++ b/external/sdl/SDL/src/audio/android/SDL_androidaudio.c @@ -87,9 +87,10 @@ static int ANDROIDAUDIO_OpenDevice(SDL_AudioDevice *device) // !!! FIXME: this needs a WaitDevice implementation. -static void ANDROIDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) +static int ANDROIDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) { Android_JNI_WriteAudioBuffer(); + return 0; } static Uint8 *ANDROIDAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) diff --git a/external/sdl/SDL/src/audio/coreaudio/SDL_coreaudio.m b/external/sdl/SDL/src/audio/coreaudio/SDL_coreaudio.m index b96db86b..bbe37161 100644 --- a/external/sdl/SDL/src/audio/coreaudio/SDL_coreaudio.m +++ b/external/sdl/SDL/src/audio/coreaudio/SDL_coreaudio.m @@ -27,7 +27,7 @@ #include "SDL_coreaudio.h" #include "../../thread/SDL_systhread.h" -#define DEBUG_COREAUDIO 1 +#define DEBUG_COREAUDIO 0 #if DEBUG_COREAUDIO #define CHECK_RESULT(msg) \ @@ -525,7 +525,7 @@ static SDL_bool UpdateAudioSession(SDL_AudioDevice *device, SDL_bool open, SDL_b #endif -static void COREAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) +static int COREAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { AudioQueueBufferRef current_buffer = device->hidden->current_buffer; SDL_assert(current_buffer != NULL); // should have been called from OutputBufferReadyCallback @@ -533,6 +533,7 @@ static void COREAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, i current_buffer->mAudioDataByteSize = current_buffer->mAudioDataBytesCapacity; device->hidden->current_buffer = NULL; AudioQueueEnqueueBuffer(device->hidden->audioQueue, current_buffer, 0, NULL); + return 0; } static Uint8 *COREAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) @@ -875,12 +876,12 @@ static int COREAUDIO_OpenDevice(SDL_AudioDevice *device) switch (test_format) { case SDL_AUDIO_U8: case SDL_AUDIO_S8: - case SDL_AUDIO_S16LSB: - case SDL_AUDIO_S16MSB: - case SDL_AUDIO_S32LSB: - case SDL_AUDIO_S32MSB: - case SDL_AUDIO_F32LSB: - case SDL_AUDIO_F32MSB: + case SDL_AUDIO_S16LE: + case SDL_AUDIO_S16BE: + case SDL_AUDIO_S32LE: + case SDL_AUDIO_S32BE: + case SDL_AUDIO_F32LE: + case SDL_AUDIO_F32BE: break; default: diff --git a/external/sdl/SDL/src/audio/directsound/SDL_directsound.c b/external/sdl/SDL/src/audio/directsound/SDL_directsound.c index f1ab2970..ab12c8cd 100644 --- a/external/sdl/SDL/src/audio/directsound/SDL_directsound.c +++ b/external/sdl/SDL/src/audio/directsound/SDL_directsound.c @@ -179,7 +179,7 @@ static BOOL CALLBACK FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVO if (str != NULL) { LPGUID cpyguid = (LPGUID)SDL_malloc(sizeof(GUID)); if (cpyguid) { - SDL_memcpy(cpyguid, guid, sizeof(GUID)); + SDL_copyp(cpyguid, guid); /* Note that spec is NULL, because we are required to connect to the * device before getting the channel mask and output format, making @@ -285,11 +285,14 @@ static void DSOUND_WaitDevice(SDL_AudioDevice *device) } } -static void DSOUND_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) +static int DSOUND_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) { // Unlock the buffer, allowing it to play SDL_assert(buflen == device->buffer_size); - IDirectSoundBuffer_Unlock(device->hidden->mixbuf, (LPVOID) buffer, buflen, NULL, 0); + if (IDirectSoundBuffer_Unlock(device->hidden->mixbuf, (LPVOID) buffer, buflen, NULL, 0) != DS_OK) { + return -1; + } + return 0; } static Uint8 *DSOUND_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) @@ -378,7 +381,7 @@ static int DSOUND_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int b return -1; } - SDL_assert(ptr1len == buflen); + SDL_assert(ptr1len == (DWORD)buflen); SDL_assert(ptr2 == NULL); SDL_assert(ptr2len == 0); @@ -614,7 +617,7 @@ static int DSOUND_OpenDevice(SDL_AudioDevice *device) } wfmt.Format.wBitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format); - wfmt.Format.nChannels = device->spec.channels; + wfmt.Format.nChannels = (WORD)device->spec.channels; wfmt.Format.nSamplesPerSec = device->spec.freq; wfmt.Format.nBlockAlign = wfmt.Format.nChannels * (wfmt.Format.wBitsPerSample / 8); wfmt.Format.nAvgBytesPerSec = wfmt.Format.nSamplesPerSec * wfmt.Format.nBlockAlign; diff --git a/external/sdl/SDL/src/audio/disk/SDL_diskaudio.c b/external/sdl/SDL/src/audio/disk/SDL_diskaudio.c index e7830ccc..b4cf2bee 100644 --- a/external/sdl/SDL/src/audio/disk/SDL_diskaudio.c +++ b/external/sdl/SDL/src/audio/disk/SDL_diskaudio.c @@ -40,15 +40,16 @@ static void DISKAUDIO_WaitDevice(SDL_AudioDevice *device) SDL_Delay(device->hidden->io_delay); } -static void DISKAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) +static int DISKAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { const int written = (int)SDL_RWwrite(device->hidden->io, buffer, (size_t)buffer_size); if (written != buffer_size) { // If we couldn't write, assume fatal error for now - SDL_AudioDeviceDisconnected(device); + return -1; } #ifdef DEBUG_AUDIO SDL_Log("DISKAUDIO: Wrote %d bytes of audio data", (int) written); #endif + return 0; } static Uint8 *DISKAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) diff --git a/external/sdl/SDL/src/audio/dsp/SDL_dspaudio.c b/external/sdl/SDL/src/audio/dsp/SDL_dspaudio.c index 004acef9..05572d22 100644 --- a/external/sdl/SDL/src/audio/dsp/SDL_dspaudio.c +++ b/external/sdl/SDL/src/audio/dsp/SDL_dspaudio.c @@ -111,12 +111,12 @@ static int DSP_OpenDevice(SDL_AudioDevice *device) format = AFMT_U8; } break; - case SDL_AUDIO_S16LSB: + case SDL_AUDIO_S16LE: if (value & AFMT_S16_LE) { format = AFMT_S16_LE; } break; - case SDL_AUDIO_S16MSB: + case SDL_AUDIO_S16BE: if (value & AFMT_S16_BE) { format = AFMT_S16_BE; } @@ -225,17 +225,17 @@ static void DSP_WaitDevice(SDL_AudioDevice *device) } } -static void DSP_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) +static int DSP_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) { struct SDL_PrivateAudioData *h = device->hidden; if (write(h->audio_fd, buffer, buflen) == -1) { perror("Audio write"); - SDL_AudioDeviceDisconnected(device); - return; + return -1; } #ifdef DEBUG_AUDIO fprintf(stderr, "Wrote %d bytes of audio data\n", h->mixlen); #endif + return 0; } static Uint8 *DSP_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) diff --git a/external/sdl/SDL/src/audio/emscripten/SDL_emscriptenaudio.c b/external/sdl/SDL/src/audio/emscripten/SDL_emscriptenaudio.c index 142d9e56..b8740a88 100644 --- a/external/sdl/SDL/src/audio/emscripten/SDL_emscriptenaudio.c +++ b/external/sdl/SDL/src/audio/emscripten/SDL_emscriptenaudio.c @@ -36,9 +36,9 @@ static Uint8 *EMSCRIPTENAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_ return device->hidden->mixbuf; } -static void EMSCRIPTENAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) +static int EMSCRIPTENAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { - const int framelen = (SDL_AUDIO_BITSIZE(device->spec.format) / 8) * device->spec.channels; + const int framelen = SDL_AUDIO_FRAMESIZE(device->spec); MAIN_THREAD_EM_ASM({ var SDL3 = Module['SDL3']; var numChannels = SDL3.audio.currentOutputBuffer['numberOfChannels']; @@ -53,11 +53,7 @@ static void EMSCRIPTENAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buf } } }, buffer, buffer_size / framelen); -} - -static void HandleAudioProcess(SDL_AudioDevice *device) // this fires when the main thread is idle. -{ - SDL_OutputAudioThreadIterate(device); + return 0; } @@ -92,11 +88,6 @@ static int EMSCRIPTENAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buff return buflen; } -static void HandleCaptureProcess(SDL_AudioDevice *device) // this fires when the main thread is idle. -{ - SDL_CaptureAudioThreadIterate(device); -} - static void EMSCRIPTENAUDIO_CloseDevice(SDL_AudioDevice *device) { if (!device->hidden) { @@ -107,32 +98,28 @@ static void EMSCRIPTENAUDIO_CloseDevice(SDL_AudioDevice *device) var SDL3 = Module['SDL3']; if ($0) { if (SDL3.capture.silenceTimer !== undefined) { - clearTimeout(SDL3.capture.silenceTimer); + clearInterval(SDL3.capture.silenceTimer); } if (SDL3.capture.stream !== undefined) { var tracks = SDL3.capture.stream.getAudioTracks(); for (var i = 0; i < tracks.length; i++) { SDL3.capture.stream.removeTrack(tracks[i]); } - SDL3.capture.stream = undefined; } if (SDL3.capture.scriptProcessorNode !== undefined) { SDL3.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) {}; SDL3.capture.scriptProcessorNode.disconnect(); - SDL3.capture.scriptProcessorNode = undefined; } if (SDL3.capture.mediaStreamNode !== undefined) { SDL3.capture.mediaStreamNode.disconnect(); - SDL3.capture.mediaStreamNode = undefined; - } - if (SDL3.capture.silenceBuffer !== undefined) { - SDL3.capture.silenceBuffer = undefined } SDL3.capture = undefined; } else { if (SDL3.audio.scriptProcessorNode != undefined) { SDL3.audio.scriptProcessorNode.disconnect(); - SDL3.audio.scriptProcessorNode = undefined; + } + if (SDL3.audio.silenceTimer !== undefined) { + clearInterval(SDL3.audio.silenceTimer); } SDL3.audio = undefined; } @@ -174,7 +161,9 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device) SDL3.audioContext = new webkitAudioContext(); } if (SDL3.audioContext) { - autoResumeAudioContext(SDL3.audioContext); + if ((typeof navigator.userActivation) === 'undefined') { // Firefox doesn't have this (as of August 2023), use autoResumeAudioContext instead. + autoResumeAudioContext(SDL3.audioContext); + } } } return SDL3.audioContext === undefined ? -1 : 0; @@ -227,8 +216,9 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device) var have_microphone = function(stream) { //console.log('SDL audio capture: we have a microphone! Replacing silence callback.'); if (SDL3.capture.silenceTimer !== undefined) { - clearTimeout(SDL3.capture.silenceTimer); + clearInterval(SDL3.capture.silenceTimer); SDL3.capture.silenceTimer = undefined; + SDL3.capture.silenceBuffer = undefined } SDL3.capture.mediaStreamNode = SDL3.audioContext.createMediaStreamSource(stream); SDL3.capture.scriptProcessorNode = SDL3.audioContext.createScriptProcessor($1, $0, 1); @@ -255,14 +245,14 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device) dynCall('vi', $2, [$3]); }; - SDL3.capture.silenceTimer = setTimeout(silence_callback, ($1 / SDL3.audioContext.sampleRate) * 1000); + SDL3.capture.silenceTimer = setInterval(silence_callback, ($1 / SDL3.audioContext.sampleRate) * 1000); if ((navigator.mediaDevices !== undefined) && (navigator.mediaDevices.getUserMedia !== undefined)) { navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(have_microphone).catch(no_microphone); } else if (navigator.webkitGetUserMedia !== undefined) { navigator.webkitGetUserMedia({ audio: true, video: false }, have_microphone, no_microphone); } - }, device->spec.channels, device->sample_frames, HandleCaptureProcess, device); + }, device->spec.channels, device->sample_frames, SDL_CaptureAudioThreadIterate, device); } else { // setup a ScriptProcessorNode MAIN_THREAD_EM_ASM({ @@ -270,11 +260,38 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device) SDL3.audio.scriptProcessorNode = SDL3.audioContext['createScriptProcessor']($1, 0, $0); SDL3.audio.scriptProcessorNode['onaudioprocess'] = function (e) { if ((SDL3 === undefined) || (SDL3.audio === undefined)) { return; } + // if we're actually running the node, we don't need the fake callback anymore, so kill it. + if (SDL3.audio.silenceTimer !== undefined) { + clearInterval(SDL3.audio.silenceTimer); + SDL3.audio.silenceTimer = undefined; + SDL3.audio.silenceBuffer = undefined; + } SDL3.audio.currentOutputBuffer = e['outputBuffer']; dynCall('vi', $2, [$3]); }; + SDL3.audio.scriptProcessorNode['connect'](SDL3.audioContext['destination']); - }, device->spec.channels, device->sample_frames, HandleAudioProcess, device); + + if (SDL3.audioContext.state === 'suspended') { // uhoh, autoplay is blocked. + SDL3.audio.silenceBuffer = SDL3.audioContext.createBuffer($0, $1, SDL3.audioContext.sampleRate); + SDL3.audio.silenceBuffer.getChannelData(0).fill(0.0); + var silence_callback = function() { + if ((typeof navigator.userActivation) !== 'undefined') { // Almost everything modern except Firefox (as of August 2023) + if (navigator.userActivation.hasBeenActive) { + SDL3.audioContext.resume(); + } + } + + // the buffer that gets filled here just gets ignored, so the app can make progress + // and/or avoid flooding audio queues until we can actually play audio. + SDL3.audio.currentOutputBuffer = SDL3.audio.silenceBuffer; + dynCall('vi', $2, [$3]); + SDL3.audio.currentOutputBuffer = undefined; + }; + + SDL3.audio.silenceTimer = setInterval(silence_callback, ($1 / SDL3.audioContext.sampleRate) * 1000); + } + }, device->spec.channels, device->sample_frames, SDL_OutputAudioThreadIterate, device); } return 0; diff --git a/external/sdl/SDL/src/audio/haiku/SDL_haikuaudio.cc b/external/sdl/SDL/src/audio/haiku/SDL_haikuaudio.cc index c34142f3..15204362 100644 --- a/external/sdl/SDL/src/audio/haiku/SDL_haikuaudio.cc +++ b/external/sdl/SDL/src/audio/haiku/SDL_haikuaudio.cc @@ -46,13 +46,14 @@ static Uint8 *HAIKUAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) return device->hidden->current_buffer; } -static void HAIKUAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) +static int HAIKUAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { // We already wrote our output right into the BSoundPlayer's callback's stream. Just clean up our stuff. SDL_assert(device->hidden->current_buffer != NULL); SDL_assert(device->hidden->current_buffer_len > 0); device->hidden->current_buffer = NULL; device->hidden->current_buffer_len = 0; + return 0; } // The Haiku callback for handling the audio buffer @@ -130,29 +131,29 @@ static int HAIKUAUDIO_OpenDevice(SDL_AudioDevice *device) format.format = media_raw_audio_format::B_AUDIO_UCHAR; break; - case SDL_AUDIO_S16LSB: + case SDL_AUDIO_S16LE: format.format = media_raw_audio_format::B_AUDIO_SHORT; break; - case SDL_AUDIO_S16MSB: + case SDL_AUDIO_S16BE: format.format = media_raw_audio_format::B_AUDIO_SHORT; format.byte_order = B_MEDIA_BIG_ENDIAN; break; - case SDL_AUDIO_S32LSB: + case SDL_AUDIO_S32LE: format.format = media_raw_audio_format::B_AUDIO_INT; break; - case SDL_AUDIO_S32MSB: + case SDL_AUDIO_S32BE: format.format = media_raw_audio_format::B_AUDIO_INT; format.byte_order = B_MEDIA_BIG_ENDIAN; break; - case SDL_AUDIO_F32LSB: + case SDL_AUDIO_F32LE: format.format = media_raw_audio_format::B_AUDIO_FLOAT; break; - case SDL_AUDIO_F32MSB: + case SDL_AUDIO_F32BE: format.format = media_raw_audio_format::B_AUDIO_FLOAT; format.byte_order = B_MEDIA_BIG_ENDIAN; break; diff --git a/external/sdl/SDL/src/audio/jack/SDL_jackaudio.c b/external/sdl/SDL/src/audio/jack/SDL_jackaudio.c index 3e3ed208..ce2fde14 100644 --- a/external/sdl/SDL/src/audio/jack/SDL_jackaudio.c +++ b/external/sdl/SDL/src/audio/jack/SDL_jackaudio.c @@ -149,7 +149,7 @@ static int jackProcessPlaybackCallback(jack_nframes_t nframes, void *arg) return 0; } -static void JACK_PlayDevice(SDL_AudioDevice *device, const Uint8 *ui8buffer, int buflen) +static int JACK_PlayDevice(SDL_AudioDevice *device, const Uint8 *ui8buffer, int buflen) { const float *buffer = (float *) ui8buffer; jack_port_t **ports = device->hidden->sdlports; @@ -167,6 +167,8 @@ static void JACK_PlayDevice(SDL_AudioDevice *device, const Uint8 *ui8buffer, int } } } + + return 0; } static Uint8 *JACK_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) @@ -307,7 +309,7 @@ static int JACK_OpenDevice(SDL_AudioDevice *device) /* !!! FIXME: docs say about buffer size: "This size may change, clients that depend on it must register a bufsize_callback so they will be notified if it does." */ /* Jack pretty much demands what it wants. */ - device->spec.format = SDL_AUDIO_F32SYS; + device->spec.format = SDL_AUDIO_F32; device->spec.freq = JACK_jack_get_sample_rate(client); device->spec.channels = channels; device->sample_frames = JACK_jack_get_buffer_size(client); diff --git a/external/sdl/SDL/src/audio/n3ds/SDL_n3dsaudio.c b/external/sdl/SDL/src/audio/n3ds/SDL_n3dsaudio.c index f17ac560..a7dc9fb0 100644 --- a/external/sdl/SDL/src/audio/n3ds/SDL_n3dsaudio.c +++ b/external/sdl/SDL/src/audio/n3ds/SDL_n3dsaudio.c @@ -161,7 +161,7 @@ static int N3DSAUDIO_OpenDevice(SDL_AudioDevice *device) SDL_memset(device->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS); - const int sample_frame_size = device->spec.channels * (SDL_AUDIO_BITSIZE(device->spec.format) / 8); + const int sample_frame_size = SDL_AUDIO_FRAMESIZE(device->spec); for (unsigned i = 0; i < NUM_BUFFERS; i++) { device->hidden->waveBuf[i].data_vaddr = data_vaddr; device->hidden->waveBuf[i].nsamples = device->buffer_size / sample_frame_size; @@ -176,7 +176,7 @@ static int N3DSAUDIO_OpenDevice(SDL_AudioDevice *device) return 0; } -static void N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) +static int N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) { contextLock(device); @@ -185,7 +185,7 @@ static void N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, i if (device->hidden->isCancelled || device->hidden->waveBuf[nextbuf].status != NDSP_WBUF_FREE) { contextUnlock(device); - return; + return 0; // !!! FIXME: is this a fatal error? If so, this should return -1. } device->hidden->nextbuf = (nextbuf + 1) % NUM_BUFFERS; @@ -196,6 +196,8 @@ static void N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, i DSP_FlushDataCache(device->hidden->waveBuf[nextbuf].data_vaddr, buflen); ndspChnWaveBufAdd(0, &device->hidden->waveBuf[nextbuf]); + + return 0; } static void N3DSAUDIO_WaitDevice(SDL_AudioDevice *device) diff --git a/external/sdl/SDL/src/audio/netbsd/SDL_netbsdaudio.c b/external/sdl/SDL/src/audio/netbsd/SDL_netbsdaudio.c index 4165b056..d8aafc08 100644 --- a/external/sdl/SDL/src/audio/netbsd/SDL_netbsdaudio.c +++ b/external/sdl/SDL/src/audio/netbsd/SDL_netbsdaudio.c @@ -130,7 +130,7 @@ static void NETBSDAUDIO_WaitDevice(SDL_AudioDevice *device) SDL_AudioDeviceDisconnected(device); return; } - const size_t remain = (size_t)((iscapture ? info.record.seek : info.play.seek) * (SDL_AUDIO_BITSIZE(device->spec.format) / 8)); + const size_t remain = (size_t)((iscapture ? info.record.seek : info.play.seek) * SDL_AUDIO_BYTESIZE(device->spec.format)); if (!iscapture && (remain >= device->buffer_size)) { SDL_Delay(10); } else if (iscapture && (remain < device->buffer_size)) { @@ -141,20 +141,18 @@ static void NETBSDAUDIO_WaitDevice(SDL_AudioDevice *device) } } -static void NETBSDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) +static int NETBSDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) { struct SDL_PrivateAudioData *h = device->hidden; const int written = write(h->audio_fd, buffer, buflen); - if (written == -1) { - // Non recoverable error has occurred. It should be reported!!! - SDL_AudioDeviceDisconnected(device); - perror("audio"); - return; + if (written != buflen) { // Treat even partial writes as fatal errors. + return -1; } #ifdef DEBUG_AUDIO fprintf(stderr, "Wrote %d bytes of audio data\n", written); #endif + return 0; } static Uint8 *NETBSDAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) @@ -183,7 +181,7 @@ static void NETBSDAUDIO_FlushCapture(SDL_AudioDevice *device) struct SDL_PrivateAudioData *h = device->hidden; audio_info_t info; if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) == 0) { - size_t remain = (size_t)(info.record.seek * (SDL_AUDIO_BITSIZE(device->spec.format) / 8)); + size_t remain = (size_t)(info.record.seek * SDL_AUDIO_BYTESIZE(device->spec.format)); while (remain > 0) { char buf[512]; const size_t len = SDL_min(sizeof(buf), remain); @@ -250,16 +248,16 @@ static int NETBSDAUDIO_OpenDevice(SDL_AudioDevice *device) case SDL_AUDIO_S8: encoding = AUDIO_ENCODING_SLINEAR; break; - case SDL_AUDIO_S16LSB: + case SDL_AUDIO_S16LE: encoding = AUDIO_ENCODING_SLINEAR_LE; break; - case SDL_AUDIO_S16MSB: + case SDL_AUDIO_S16BE: encoding = AUDIO_ENCODING_SLINEAR_BE; break; - case SDL_AUDIO_S32LSB: + case SDL_AUDIO_S32LE: encoding = AUDIO_ENCODING_SLINEAR_LE; break; - case SDL_AUDIO_S32MSB: + case SDL_AUDIO_S32BE: encoding = AUDIO_ENCODING_SLINEAR_BE; break; default: diff --git a/external/sdl/SDL/src/audio/openslES/SDL_openslES.c b/external/sdl/SDL/src/audio/openslES/SDL_openslES.c index 37c7dc8c..c4855eae 100644 --- a/external/sdl/SDL/src/audio/openslES/SDL_openslES.c +++ b/external/sdl/SDL/src/audio/openslES/SDL_openslES.c @@ -248,7 +248,7 @@ static int openslES_CreatePCMRecorder(SDL_AudioDevice *device) } // Just go with signed 16-bit audio as it's the most compatible - device->spec.format = SDL_AUDIO_S16SYS; + device->spec.format = SDL_AUDIO_S16; device->spec.channels = 1; //device->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/ @@ -424,12 +424,12 @@ static int openslES_CreatePCMPlayer(SDL_AudioDevice *device) if (!test_format) { // Didn't find a compatible format : LOGI("No compatible audio format, using signed 16-bit audio"); - test_format = SDL_AUDIO_S16SYS; + test_format = SDL_AUDIO_S16; } device->spec.format = test_format; } else { // Just go with signed 16-bit audio as it's the most compatible - device->spec.format = SDL_AUDIO_S16SYS; + device->spec.format = SDL_AUDIO_S16; } // Update the fragment size as size in bytes @@ -638,7 +638,7 @@ static void openslES_WaitDevice(SDL_AudioDevice *device) SDL_WaitSemaphore(audiodata->playsem); } -static void openslES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) +static int openslES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) { struct SDL_PrivateAudioData *audiodata = device->hidden; @@ -657,6 +657,8 @@ static void openslES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, in if (SL_RESULT_SUCCESS != result) { SDL_PostSemaphore(audiodata->playsem); } + + return 0; } /// n playn sem diff --git a/external/sdl/SDL/src/audio/pipewire/SDL_pipewire.c b/external/sdl/SDL/src/audio/pipewire/SDL_pipewire.c index 419fbfcb..0dda0d8c 100644 --- a/external/sdl/SDL/src/audio/pipewire/SDL_pipewire.c +++ b/external/sdl/SDL/src/audio/pipewire/SDL_pipewire.c @@ -898,22 +898,22 @@ static void initialize_spa_info(const SDL_AudioSpec *spec, struct spa_audio_info case SDL_AUDIO_S8: info->format = SPA_AUDIO_FORMAT_S8; break; - case SDL_AUDIO_S16LSB: + case SDL_AUDIO_S16LE: info->format = SPA_AUDIO_FORMAT_S16_LE; break; - case SDL_AUDIO_S16MSB: + case SDL_AUDIO_S16BE: info->format = SPA_AUDIO_FORMAT_S16_BE; break; - case SDL_AUDIO_S32LSB: + case SDL_AUDIO_S32LE: info->format = SPA_AUDIO_FORMAT_S32_LE; break; - case SDL_AUDIO_S32MSB: + case SDL_AUDIO_S32BE: info->format = SPA_AUDIO_FORMAT_S32_BE; break; - case SDL_AUDIO_F32LSB: + case SDL_AUDIO_F32LE: info->format = SPA_AUDIO_FORMAT_F32_LE; break; - case SDL_AUDIO_F32MSB: + case SDL_AUDIO_F32BE: info->format = SPA_AUDIO_FORMAT_F32_BE; break; } @@ -940,7 +940,7 @@ static Uint8 *PIPEWIRE_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) return (Uint8 *) spa_buf->datas[0].data; } -static void PIPEWIRE_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) +static int PIPEWIRE_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { struct pw_stream *stream = device->hidden->stream; struct pw_buffer *pw_buf = device->hidden->pw_buf; @@ -951,6 +951,8 @@ static void PIPEWIRE_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, in PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf); device->hidden->pw_buf = NULL; + + return 0; } static void output_callback(void *data) @@ -1106,7 +1108,7 @@ static int PIPEWIRE_OpenDevice(SDL_AudioDevice *device) } /* Size of a single audio frame in bytes */ - priv->stride = (SDL_AUDIO_BITSIZE(device->spec.format) / 8) * device->spec.channels; + priv->stride = SDL_AUDIO_FRAMESIZE(device->spec); if (device->sample_frames < min_period) { device->sample_frames = min_period; diff --git a/external/sdl/SDL/src/audio/ps2/SDL_ps2audio.c b/external/sdl/SDL/src/audio/ps2/SDL_ps2audio.c index e50a497d..2165194e 100644 --- a/external/sdl/SDL/src/audio/ps2/SDL_ps2audio.c +++ b/external/sdl/SDL/src/audio/ps2/SDL_ps2audio.c @@ -85,9 +85,10 @@ static int PS2AUDIO_OpenDevice(SDL_AudioDevice *device) return 0; } -static void PS2AUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) +static int PS2AUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) { - audsrv_play_audio((char *)buffer, buflen); + // this returns number of bytes accepted or a negative error. We assume anything other than buflen is a fatal error. + return (audsrv_play_audio((char *)buffer, buflen) != buflen) ? -1 : 0; } static void PS2AUDIO_WaitDevice(SDL_AudioDevice *device) diff --git a/external/sdl/SDL/src/audio/psp/SDL_pspaudio.c b/external/sdl/SDL/src/audio/psp/SDL_pspaudio.c index b5af4e88..d701b5f2 100644 --- a/external/sdl/SDL/src/audio/psp/SDL_pspaudio.c +++ b/external/sdl/SDL/src/audio/psp/SDL_pspaudio.c @@ -47,7 +47,7 @@ static int PSPAUDIO_OpenDevice(SDL_AudioDevice *device) } // device only natively supports S16LSB - device->spec.format = SDL_AUDIO_S16LSB; + device->spec.format = SDL_AUDIO_S16LE; /* PSP has some limitations with the Audio. It fully supports 44.1KHz (Mono & Stereo), however with frequencies different than 44.1KHz, it just supports Stereo, @@ -106,14 +106,16 @@ static int PSPAUDIO_OpenDevice(SDL_AudioDevice *device) return 0; } -static void PSPAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) +static int PSPAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) { + int rc; if (!isBasicAudioConfig(&device->spec)) { SDL_assert(device->spec.channels == 2); - sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, (void *) buffer); + rc = sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, (void *) buffer); } else { - sceAudioOutputPannedBlocking(device->hidden->channel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, (void *) buffer); + rc = sceAudioOutputPannedBlocking(device->hidden->channel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, (void *) buffer); } + return (rc == 0) ? 0 : -1; } static void PSPAUDIO_WaitDevice(SDL_AudioDevice *device) diff --git a/external/sdl/SDL/src/audio/pulseaudio/SDL_pulseaudio.c b/external/sdl/SDL/src/audio/pulseaudio/SDL_pulseaudio.c index fb6c3518..a0069cf2 100644 --- a/external/sdl/SDL/src/audio/pulseaudio/SDL_pulseaudio.c +++ b/external/sdl/SDL/src/audio/pulseaudio/SDL_pulseaudio.c @@ -388,7 +388,7 @@ static void PULSEAUDIO_WaitDevice(SDL_AudioDevice *device) PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); } -static void PULSEAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) +static int PULSEAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { struct SDL_PrivateAudioData *h = device->hidden; @@ -401,14 +401,14 @@ static void PULSEAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); if (rc < 0) { - SDL_AudioDeviceDisconnected(device); - return; + return -1; } /*printf("PULSEAUDIO FEED! nbytes=%d\n", buffer_size);*/ h->bytes_requested -= buffer_size; /*printf("PULSEAUDIO PLAYDEVICE END! written=%d\n", written);*/ + return 0; } static Uint8 *PULSEAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) @@ -606,22 +606,22 @@ static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *device) case SDL_AUDIO_U8: format = PA_SAMPLE_U8; break; - case SDL_AUDIO_S16LSB: + case SDL_AUDIO_S16LE: format = PA_SAMPLE_S16LE; break; - case SDL_AUDIO_S16MSB: + case SDL_AUDIO_S16BE: format = PA_SAMPLE_S16BE; break; - case SDL_AUDIO_S32LSB: + case SDL_AUDIO_S32LE: format = PA_SAMPLE_S32LE; break; - case SDL_AUDIO_S32MSB: + case SDL_AUDIO_S32BE: format = PA_SAMPLE_S32BE; break; - case SDL_AUDIO_F32LSB: + case SDL_AUDIO_F32LE: format = PA_SAMPLE_FLOAT32LE; break; - case SDL_AUDIO_F32MSB: + case SDL_AUDIO_F32BE: format = PA_SAMPLE_FLOAT32BE; break; default: @@ -723,17 +723,17 @@ static SDL_AudioFormat PulseFormatToSDLFormat(pa_sample_format_t format) case PA_SAMPLE_U8: return SDL_AUDIO_U8; case PA_SAMPLE_S16LE: - return SDL_AUDIO_S16LSB; + return SDL_AUDIO_S16LE; case PA_SAMPLE_S16BE: - return SDL_AUDIO_S16MSB; + return SDL_AUDIO_S16BE; case PA_SAMPLE_S32LE: - return SDL_AUDIO_S32LSB; + return SDL_AUDIO_S32LE; case PA_SAMPLE_S32BE: - return SDL_AUDIO_S32MSB; + return SDL_AUDIO_S32BE; case PA_SAMPLE_FLOAT32LE: - return SDL_AUDIO_F32LSB; + return SDL_AUDIO_F32LE; case PA_SAMPLE_FLOAT32BE: - return SDL_AUDIO_F32MSB; + return SDL_AUDIO_F32BE; default: return 0; } diff --git a/external/sdl/SDL/src/audio/qnx/SDL_qsa_audio.c b/external/sdl/SDL/src/audio/qnx/SDL_qsa_audio.c index 5c60e555..71037168 100644 --- a/external/sdl/SDL/src/audio/qnx/SDL_qsa_audio.c +++ b/external/sdl/SDL/src/audio/qnx/SDL_qsa_audio.c @@ -110,10 +110,10 @@ static void QSA_WaitDevice(SDL_AudioDevice *device) } } -static void QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) +static int QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) { if (SDL_AtomicGet(&device->shutdown) || !device->hidden) { - return; + return 0; } int towrite = buflen; @@ -125,7 +125,7 @@ static void QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buf // Check if samples playback got stuck somewhere in hardware or in the audio device driver if ((errno == EAGAIN) && (bw == 0)) { if (device->hidden->timeout_on_wait) { - return; // oh well, try again next time. !!! FIXME: Should we just disconnect the device in this case? + return 0; // oh well, try again next time. !!! FIXME: Should we just disconnect the device in this case? } } @@ -145,17 +145,17 @@ static void QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buf int status = snd_pcm_plugin_status(device->hidden->audio_handle, &cstatus); if (status < 0) { QSA_SetError("snd_pcm_plugin_status", status); - return; // !!! FIXME: disconnect the device? + return -1; } else if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY)) { status = snd_pcm_plugin_prepare(device->hidden->audio_handle, device->iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK); if (status < 0) { QSA_SetError("snd_pcm_plugin_prepare", status); - return; // !!! FIXME: disconnect the device? + return -1; } } continue; } else { - return; // !!! FIXME: disconnect the device? + return -1; } } else { // we wrote all remaining data @@ -165,9 +165,7 @@ static void QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buf } // If we couldn't write, assume fatal error for now - if (towrite != 0) { - SDL_AudioDeviceDisconnected(device); - } + return (towrite != 0) ? -1 : 0; } static Uint8 *QSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) @@ -311,7 +309,7 @@ static SDL_AudioFormat QnxFormatToSDLFormat(const int32_t qnxfmt) #undef CHECKFMT default: break; } - return SDL_AUDIO_S16SYS; // oh well. + return SDL_AUDIO_S16; // oh well. } static void QSA_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture) diff --git a/external/sdl/SDL/src/audio/sndio/SDL_sndioaudio.c b/external/sdl/SDL/src/audio/sndio/SDL_sndioaudio.c index a7ac91dd..0347c38e 100644 --- a/external/sdl/SDL/src/audio/sndio/SDL_sndioaudio.c +++ b/external/sdl/SDL/src/audio/sndio/SDL_sndioaudio.c @@ -175,16 +175,17 @@ static void SNDIO_WaitDevice(SDL_AudioDevice *device) } } -static void SNDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) +static int SNDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) { // !!! FIXME: this should be non-blocking so we can check device->shutdown. // this is set to blocking, because we _have_ to send the entire buffer down, but hopefully WaitDevice took most of the delay time. if (SNDIO_sio_write(device->hidden->dev, buffer, buflen) != buflen) { - SDL_AudioDeviceDisconnected(device); // If we couldn't write, assume fatal error for now + return -1; // If we couldn't write, assume fatal error for now } #ifdef DEBUG_AUDIO fprintf(stderr, "Wrote %d bytes of audio data\n", written); #endif + return 0; } static int SNDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen) @@ -283,13 +284,13 @@ static int SNDIO_OpenDevice(SDL_AudioDevice *device) } if ((par.bps == 4) && (par.sig) && (par.le)) { - device->spec.format = SDL_AUDIO_S32LSB; + device->spec.format = SDL_AUDIO_S32LE; } else if ((par.bps == 4) && (par.sig) && (!par.le)) { - device->spec.format = SDL_AUDIO_S32MSB; + device->spec.format = SDL_AUDIO_S32BE; } else if ((par.bps == 2) && (par.sig) && (par.le)) { - device->spec.format = SDL_AUDIO_S16LSB; + device->spec.format = SDL_AUDIO_S16LE; } else if ((par.bps == 2) && (par.sig) && (!par.le)) { - device->spec.format = SDL_AUDIO_S16MSB; + device->spec.format = SDL_AUDIO_S16BE; } else if ((par.bps == 1) && (par.sig)) { device->spec.format = SDL_AUDIO_S8; } else if ((par.bps == 1) && (!par.sig)) { diff --git a/external/sdl/SDL/src/audio/vita/SDL_vitaaudio.c b/external/sdl/SDL/src/audio/vita/SDL_vitaaudio.c index efe51955..53a54d10 100644 --- a/external/sdl/SDL/src/audio/vita/SDL_vitaaudio.c +++ b/external/sdl/SDL/src/audio/vita/SDL_vitaaudio.c @@ -71,7 +71,7 @@ static int VITAAUD_OpenDevice(SDL_AudioDevice *device) closefmts = SDL_ClosestAudioFormats(device->spec.format); while ((test_format = *(closefmts++)) != 0) { - if (test_format == SDL_AUDIO_S16LSB) { + if (test_format == SDL_AUDIO_S16LE) { device->spec.format = test_format; break; } @@ -130,9 +130,9 @@ static int VITAAUD_OpenDevice(SDL_AudioDevice *device) return 0; } -static void VITAAUD_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) +static int VITAAUD_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { - sceAudioOutOutput(device->hidden->port, buffer); + return (sceAudioOutOutput(device->hidden->port, buffer) == 0) ? 0 : -1; } // This function waits until it is possible to write a full sound buffer diff --git a/external/sdl/SDL/src/audio/wasapi/SDL_wasapi.c b/external/sdl/SDL/src/audio/wasapi/SDL_wasapi.c index 2041bc88..6e442c42 100644 --- a/external/sdl/SDL/src/audio/wasapi/SDL_wasapi.c +++ b/external/sdl/SDL/src/audio/wasapi/SDL_wasapi.c @@ -403,22 +403,22 @@ static Uint8 *WASAPI_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) // get an endpoint buffer from WASAPI. BYTE *buffer = NULL; - while (RecoverWasapiIfLost(device) && device->hidden->render) { - if (!WasapiFailed(device, IAudioRenderClient_GetBuffer(device->hidden->render, device->sample_frames, &buffer))) { - return (Uint8 *)buffer; + if (RecoverWasapiIfLost(device) && device->hidden->render) { + if (WasapiFailed(device, IAudioRenderClient_GetBuffer(device->hidden->render, device->sample_frames, &buffer))) { + SDL_assert(buffer == NULL); } - SDL_assert(buffer == NULL); } return (Uint8 *)buffer; } -static void WASAPI_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) +static int WASAPI_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) { if (device->hidden->render != NULL) { // definitely activated? // WasapiFailed() will mark the device for reacquisition or removal elsewhere. WasapiFailed(device, IAudioRenderClient_ReleaseBuffer(device->hidden->render, device->sample_frames, 0)); } + return 0; } static void WASAPI_WaitDevice(SDL_AudioDevice *device) @@ -620,7 +620,7 @@ static int mgmtthrtask_PrepDevice(void *userdata) return -1; } - device->hidden->framesize = (SDL_AUDIO_BITSIZE(device->spec.format) / 8) * device->spec.channels; + device->hidden->framesize = SDL_AUDIO_FRAMESIZE(device->spec); if (device->iscapture) { IAudioCaptureClient *capture = NULL; diff --git a/external/sdl/SDL/src/core/gdk/SDL_gdk.cpp b/external/sdl/SDL/src/core/gdk/SDL_gdk.cpp index d883a3c7..c2dd14e2 100644 --- a/external/sdl/SDL/src/core/gdk/SDL_gdk.cpp +++ b/external/sdl/SDL/src/core/gdk/SDL_gdk.cpp @@ -214,3 +214,23 @@ SDL_GDKSuspendComplete() SetEvent(plmSuspendComplete); } } + +extern "C" DECLSPEC int +SDL_GDKGetDefaultUser(XUserHandle *outUserHandle) +{ + XAsyncBlock block = { 0 }; + HRESULT result; + + if (FAILED(result = XUserAddAsync(XUserAddOptions::AddDefaultUserAllowingUI, &block))) { + return WIN_SetErrorFromHRESULT("XUserAddAsync", result); + } + + do { + result = XUserAddResult(&block, outUserHandle); + } while (result == E_PENDING); + if (FAILED(result)) { + return WIN_SetErrorFromHRESULT("XUserAddResult", result); + } + + return 0; +} diff --git a/external/sdl/SDL/src/core/linux/SDL_dbus.c b/external/sdl/SDL/src/core/linux/SDL_dbus.c index 11092c01..ba961be0 100644 --- a/external/sdl/SDL/src/core/linux/SDL_dbus.c +++ b/external/sdl/SDL/src/core/linux/SDL_dbus.c @@ -565,4 +565,70 @@ char *SDL_DBus_GetLocalMachineId(void) return NULL; } + +/* + * Convert file drops with mime type "application/vnd.portal.filetransfer" to file paths + * Result must be freed with dbus->free_string_array(). + * https://flatpak.github.io/xdg-desktop-portal/#gdbus-method-org-freedesktop-portal-FileTransfer.RetrieveFiles + */ +char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *path_count) +{ + DBusError err; + DBusMessageIter iter, iterDict; + char **paths = NULL; + DBusMessage *reply = NULL; + DBusMessage *msg = dbus.message_new_method_call("org.freedesktop.portal.Documents", /* Node */ + "/org/freedesktop/portal/documents", /* Path */ + "org.freedesktop.portal.FileTransfer", /* Interface */ + "RetrieveFiles"); /* Method */ + + /* Make sure we have a connection to the dbus session bus */ + if (!SDL_DBus_GetContext() || !dbus.session_conn) { + /* We either cannot connect to the session bus or were unable to + * load the D-Bus library at all. */ + return NULL; + } + + dbus.error_init(&err); + + /* First argument is a "application/vnd.portal.filetransfer" key from a DnD or clipboard event */ + if (!dbus.message_append_args(msg, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID)) { + SDL_OutOfMemory(); + dbus.message_unref(msg); + goto failed; + } + + /* Second argument is a variant dictionary for options. + * The spec doesn't define any entries yet so it's empty. */ + dbus.message_iter_init_append(msg, &iter); + if (!dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &iterDict) || + !dbus.message_iter_close_container(&iter, &iterDict)) { + SDL_OutOfMemory(); + dbus.message_unref(msg); + goto failed; + } + + reply = dbus.connection_send_with_reply_and_block(dbus.session_conn, msg, DBUS_TIMEOUT_USE_DEFAULT, &err); + dbus.message_unref(msg); + + if (reply) { + dbus.message_get_args(reply, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &paths, path_count, DBUS_TYPE_INVALID); + dbus.message_unref(reply); + } + + if (paths) { + return paths; + } + +failed: + if (dbus.error_is_set(&err)) { + SDL_SetError("%s: %s", err.name, err.message); + dbus.error_free(&err); + } else { + SDL_SetError("Error retrieving paths for documents portal \"%s\"", key); + } + + return NULL; +} + #endif diff --git a/external/sdl/SDL/src/core/linux/SDL_dbus.h b/external/sdl/SDL/src/core/linux/SDL_dbus.h index 9234c592..7a6feeb3 100644 --- a/external/sdl/SDL/src/core/linux/SDL_dbus.h +++ b/external/sdl/SDL/src/core/linux/SDL_dbus.h @@ -102,6 +102,8 @@ extern SDL_bool SDL_DBus_ScreensaverInhibit(SDL_bool inhibit); extern void SDL_DBus_PumpEvents(void); extern char *SDL_DBus_GetLocalMachineId(void); +extern char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *files_count); + #endif /* HAVE_DBUS_DBUS_H */ #endif /* SDL_dbus_h_ */ diff --git a/external/sdl/SDL/src/core/windows/SDL_immdevice.c b/external/sdl/SDL/src/core/windows/SDL_immdevice.c index 160297e3..780eff40 100644 --- a/external/sdl/SDL/src/core/windows/SDL_immdevice.c +++ b/external/sdl/SDL/src/core/windows/SDL_immdevice.c @@ -57,8 +57,14 @@ static const PROPERTYKEY SDL_PKEY_AudioEndpoint_GUID = { { 0x1da5d803, 0xd492, 0 static SDL_bool FindByDevIDCallback(SDL_AudioDevice *device, void *userdata) { - const SDL_IMMDevice_HandleData *handle = (const SDL_IMMDevice_HandleData *) device->handle; - return (SDL_wcscmp(handle->immdevice_id, (LPCWSTR) userdata) == 0) ? SDL_TRUE : SDL_FALSE; + LPCWSTR devid = (LPCWSTR)userdata; + if (devid && device && device->handle) { + const SDL_IMMDevice_HandleData *handle = (const SDL_IMMDevice_HandleData *)device->handle; + if (handle->immdevice_id && SDL_wcscmp(handle->immdevice_id, devid) == 0) { + return SDL_TRUE; + } + } + return SDL_FALSE; } static SDL_AudioDevice *SDL_IMMDevice_FindByDevID(LPCWSTR devid) diff --git a/external/sdl/SDL/src/core/windows/SDL_windows.c b/external/sdl/SDL/src/core/windows/SDL_windows.c index 467d4bb7..38986270 100644 --- a/external/sdl/SDL/src/core/windows/SDL_windows.c +++ b/external/sdl/SDL/src/core/windows/SDL_windows.c @@ -344,19 +344,19 @@ static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat) { if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) { - return SDL_AUDIO_F32SYS; + return SDL_AUDIO_F32; } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) { - return SDL_AUDIO_S16SYS; + return SDL_AUDIO_S16; } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) { - return SDL_AUDIO_S32SYS; + return SDL_AUDIO_S32; } else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { const WAVEFORMATEXTENSIBLE *ext = (const WAVEFORMATEXTENSIBLE *)waveformat; if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) { - return SDL_AUDIO_F32SYS; + return SDL_AUDIO_F32; } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) { - return SDL_AUDIO_S16SYS; + return SDL_AUDIO_S16; } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) { - return SDL_AUDIO_S32SYS; + return SDL_AUDIO_S32; } } return 0; diff --git a/external/sdl/SDL/src/dynapi/SDL_dynapi.sym b/external/sdl/SDL/src/dynapi/SDL_dynapi.sym index 800e2d15..54467c5f 100644 --- a/external/sdl/SDL/src/dynapi/SDL_dynapi.sym +++ b/external/sdl/SDL/src/dynapi/SDL_dynapi.sym @@ -172,6 +172,7 @@ SDL3_0.0.0 { SDL_GetGamepadAppleSFSymbolsNameForButton; SDL_GetGamepadAxis; SDL_GetGamepadAxisFromString; + SDL_GetGamepadBindings; SDL_GetGamepadButton; SDL_GetGamepadButtonFromString; SDL_GetGamepadFirmwareVersion; @@ -876,7 +877,7 @@ SDL3_0.0.0 { SDL_SetAudioStreamGetCallback; SDL_SetAudioStreamPutCallback; SDL_DestroyAudioStream; - SDL_CreateAndBindAudioStream; + SDL_OpenAudioDeviceStream; SDL_LoadWAV_RW; SDL_LoadWAV; SDL_MixAudioFormat; @@ -885,8 +886,8 @@ SDL3_0.0.0 { SDL_LoadWAV; SDL_PauseAudioDevice; SDL_ResumeAudioDevice; - SDL_IsAudioDevicePaused; - SDL_GetAudioStreamBinding; + SDL_AudioDevicePaused; + SDL_GetAudioStreamDevice; SDL_ShowWindowSystemMenu; SDL_ReadS16LE; SDL_ReadS16BE; @@ -899,6 +900,13 @@ SDL3_0.0.0 { SDL_WriteS32LE; SDL_WriteS32BE; SDL_WriteS64LE; + SDL_WriteS64BE; + SDL_GDKGetDefaultUser; + SDL_SetWindowFocusable; + SDL_GetAudioStreamFrequencyRatio; + SDL_SetAudioStreamFrequencyRatio; + SDL_SetAudioPostmixCallback; + SDL_GetAudioStreamQueued; # extra symbols go here (don't modify this line) local: *; }; diff --git a/external/sdl/SDL/src/dynapi/SDL_dynapi_overrides.h b/external/sdl/SDL/src/dynapi/SDL_dynapi_overrides.h index 4d3d2510..da300e29 100644 --- a/external/sdl/SDL/src/dynapi/SDL_dynapi_overrides.h +++ b/external/sdl/SDL/src/dynapi/SDL_dynapi_overrides.h @@ -196,6 +196,7 @@ #define SDL_GetGamepadAppleSFSymbolsNameForButton SDL_GetGamepadAppleSFSymbolsNameForButton_REAL #define SDL_GetGamepadAxis SDL_GetGamepadAxis_REAL #define SDL_GetGamepadAxisFromString SDL_GetGamepadAxisFromString_REAL +#define SDL_GetGamepadBindings SDL_GetGamepadBindings_REAL #define SDL_GetGamepadButton SDL_GetGamepadButton_REAL #define SDL_GetGamepadButtonFromString SDL_GetGamepadButtonFromString_REAL #define SDL_GetGamepadFirmwareVersion SDL_GetGamepadFirmwareVersion_REAL @@ -901,7 +902,7 @@ #define SDL_SetAudioStreamGetCallback SDL_SetAudioStreamGetCallback_REAL #define SDL_SetAudioStreamPutCallback SDL_SetAudioStreamPutCallback_REAL #define SDL_DestroyAudioStream SDL_DestroyAudioStream_REAL -#define SDL_CreateAndBindAudioStream SDL_CreateAndBindAudioStream_REAL +#define SDL_OpenAudioDeviceStream SDL_OpenAudioDeviceStream_REAL #define SDL_LoadWAV_RW SDL_LoadWAV_RW_REAL #define SDL_LoadWAV SDL_LoadWAV_REAL #define SDL_MixAudioFormat SDL_MixAudioFormat_REAL @@ -910,8 +911,8 @@ #define SDL_LoadWAV SDL_LoadWAV_REAL #define SDL_PauseAudioDevice SDL_PauseAudioDevice_REAL #define SDL_ResumeAudioDevice SDL_ResumeAudioDevice_REAL -#define SDL_IsAudioDevicePaused SDL_IsAudioDevicePaused_REAL -#define SDL_GetAudioStreamBinding SDL_GetAudioStreamBinding_REAL +#define SDL_AudioDevicePaused SDL_AudioDevicePaused_REAL +#define SDL_GetAudioStreamDevice SDL_GetAudioStreamDevice_REAL #define SDL_ShowWindowSystemMenu SDL_ShowWindowSystemMenu_REAL #define SDL_ReadS16LE SDL_ReadS16LE_REAL #define SDL_ReadS16BE SDL_ReadS16BE_REAL @@ -924,3 +925,10 @@ #define SDL_WriteS32LE SDL_WriteS32LE_REAL #define SDL_WriteS32BE SDL_WriteS32BE_REAL #define SDL_WriteS64LE SDL_WriteS64LE_REAL +#define SDL_WriteS64BE SDL_WriteS64BE_REAL +#define SDL_GDKGetDefaultUser SDL_GDKGetDefaultUser_REAL +#define SDL_SetWindowFocusable SDL_SetWindowFocusable_REAL +#define SDL_GetAudioStreamFrequencyRatio SDL_GetAudioStreamFrequencyRatio_REAL +#define SDL_SetAudioStreamFrequencyRatio SDL_SetAudioStreamFrequencyRatio_REAL +#define SDL_SetAudioPostmixCallback SDL_SetAudioPostmixCallback_REAL +#define SDL_GetAudioStreamQueued SDL_GetAudioStreamQueued_REAL diff --git a/external/sdl/SDL/src/dynapi/SDL_dynapi_procs.h b/external/sdl/SDL/src/dynapi/SDL_dynapi_procs.h index a8a4c1e4..a4ffe8d1 100644 --- a/external/sdl/SDL/src/dynapi/SDL_dynapi_procs.h +++ b/external/sdl/SDL/src/dynapi/SDL_dynapi_procs.h @@ -271,6 +271,7 @@ SDL_DYNAPI_PROC(const char*,SDL_GetGamepadAppleSFSymbolsNameForAxis,(SDL_Gamepad SDL_DYNAPI_PROC(const char*,SDL_GetGamepadAppleSFSymbolsNameForButton,(SDL_Gamepad *a, SDL_GamepadButton b),(a,b),return) SDL_DYNAPI_PROC(Sint16,SDL_GetGamepadAxis,(SDL_Gamepad *a, SDL_GamepadAxis b),(a,b),return) SDL_DYNAPI_PROC(SDL_GamepadAxis,SDL_GetGamepadAxisFromString,(const char *a),(a),return) +SDL_DYNAPI_PROC(SDL_GamepadBinding **,SDL_GetGamepadBindings,(SDL_Gamepad *a, int *b),(a,b),return) SDL_DYNAPI_PROC(Uint8,SDL_GetGamepadButton,(SDL_Gamepad *a, SDL_GamepadButton b),(a,b),return) SDL_DYNAPI_PROC(SDL_GamepadButton,SDL_GetGamepadButtonFromString,(const char *a),(a),return) SDL_DYNAPI_PROC(Uint16,SDL_GetGamepadFirmwareVersion,(SDL_Gamepad *a),(a),return) @@ -926,7 +927,7 @@ SDL_DYNAPI_PROC(const char*,SDL_GetCurrentAudioDriver,(void),(),return) SDL_DYNAPI_PROC(SDL_AudioDeviceID*,SDL_GetAudioOutputDevices,(int *a),(a),return) SDL_DYNAPI_PROC(SDL_AudioDeviceID*,SDL_GetAudioCaptureDevices,(int *a),(a),return) SDL_DYNAPI_PROC(char*,SDL_GetAudioDeviceName,(SDL_AudioDeviceID a),(a),return) -SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceFormat,(SDL_AudioDeviceID a, SDL_AudioSpec *b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceFormat,(SDL_AudioDeviceID a, SDL_AudioSpec *b, int *c),(a,b,c),return) SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_OpenAudioDevice,(SDL_AudioDeviceID a, const SDL_AudioSpec *b),(a,b),return) SDL_DYNAPI_PROC(void,SDL_CloseAudioDevice,(SDL_AudioDeviceID a),(a),) SDL_DYNAPI_PROC(int,SDL_BindAudioStreams,(SDL_AudioDeviceID a, SDL_AudioStream **b, int c),(a,b,c),return) @@ -943,10 +944,10 @@ SDL_DYNAPI_PROC(int,SDL_FlushAudioStream,(SDL_AudioStream *a),(a),return) SDL_DYNAPI_PROC(int,SDL_ClearAudioStream,(SDL_AudioStream *a),(a),return) SDL_DYNAPI_PROC(int,SDL_LockAudioStream,(SDL_AudioStream *a),(a),return) SDL_DYNAPI_PROC(int,SDL_UnlockAudioStream,(SDL_AudioStream *a),(a),return) -SDL_DYNAPI_PROC(int,SDL_SetAudioStreamGetCallback,(SDL_AudioStream *a, SDL_AudioStreamRequestCallback b, void *c),(a,b,c),return) -SDL_DYNAPI_PROC(int,SDL_SetAudioStreamPutCallback,(SDL_AudioStream *a, SDL_AudioStreamRequestCallback b, void *c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_SetAudioStreamGetCallback,(SDL_AudioStream *a, SDL_AudioStreamCallback b, void *c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_SetAudioStreamPutCallback,(SDL_AudioStream *a, SDL_AudioStreamCallback b, void *c),(a,b,c),return) SDL_DYNAPI_PROC(void,SDL_DestroyAudioStream,(SDL_AudioStream *a),(a),) -SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_CreateAndBindAudioStream,(SDL_AudioDeviceID a, const SDL_AudioSpec *b),(a,b),return) +SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_OpenAudioDeviceStream,(SDL_AudioDeviceID a, const SDL_AudioSpec *b, SDL_AudioStreamCallback c, void *d),(a,b,c,d),return) SDL_DYNAPI_PROC(int,SDL_LoadWAV_RW,(SDL_RWops *a, SDL_bool b, SDL_AudioSpec *c, Uint8 **d, Uint32 *e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(int,SDL_MixAudioFormat,(Uint8 *a, const Uint8 *b, SDL_AudioFormat c, Uint32 d, int e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(int,SDL_ConvertAudioSamples,(const SDL_AudioSpec *a, const Uint8 *b, int c, const SDL_AudioSpec *d, Uint8 **e, int *f),(a,b,c,d,e,f),return) @@ -954,8 +955,8 @@ SDL_DYNAPI_PROC(int,SDL_GetSilenceValueForFormat,(SDL_AudioFormat a),(a),return) SDL_DYNAPI_PROC(int,SDL_LoadWAV,(const char *a, SDL_AudioSpec *b, Uint8 **c, Uint32 *d),(a,b,c,d),return) SDL_DYNAPI_PROC(int,SDL_PauseAudioDevice,(SDL_AudioDeviceID a),(a),return) SDL_DYNAPI_PROC(int,SDL_ResumeAudioDevice,(SDL_AudioDeviceID a),(a),return) -SDL_DYNAPI_PROC(SDL_bool,SDL_IsAudioDevicePaused,(SDL_AudioDeviceID a),(a),return) -SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_GetAudioStreamBinding,(SDL_AudioStream *a),(a),return) +SDL_DYNAPI_PROC(SDL_bool,SDL_AudioDevicePaused,(SDL_AudioDeviceID a),(a),return) +SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_GetAudioStreamDevice,(SDL_AudioStream *a),(a),return) SDL_DYNAPI_PROC(int,SDL_ShowWindowSystemMenu,(SDL_Window *a, int b, int c),(a,b,c),return) SDL_DYNAPI_PROC(SDL_bool,SDL_ReadS16LE,(SDL_RWops *a, Sint16 *b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_ReadS16BE,(SDL_RWops *a, Sint16 *b),(a,b),return) @@ -968,3 +969,12 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS16BE,(SDL_RWops *a, Sint16 b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS32LE,(SDL_RWops *a, Sint32 b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS32BE,(SDL_RWops *a, Sint32 b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS64LE,(SDL_RWops *a, Sint64 b),(a,b),return) +SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS64BE,(SDL_RWops *a, Sint64 b),(a,b),return) +#ifdef __GDK__ +SDL_DYNAPI_PROC(int,SDL_GDKGetDefaultUser,(XUserHandle *a),(a),return) +#endif +SDL_DYNAPI_PROC(int,SDL_SetWindowFocusable,(SDL_Window *a, SDL_bool b),(a,b),return) +SDL_DYNAPI_PROC(float,SDL_GetAudioStreamFrequencyRatio,(SDL_AudioStream *a),(a),return) +SDL_DYNAPI_PROC(int,SDL_SetAudioStreamFrequencyRatio,(SDL_AudioStream *a, float b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_SetAudioPostmixCallback,(SDL_AudioDeviceID a, SDL_AudioPostmixCallback b, void *c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_GetAudioStreamQueued,(SDL_AudioStream *a),(a),return) diff --git a/external/sdl/SDL/src/events/SDL_events.c b/external/sdl/SDL/src/events/SDL_events.c index ac692faa..055009fb 100644 --- a/external/sdl/SDL/src/events/SDL_events.c +++ b/external/sdl/SDL/src/events/SDL_events.c @@ -418,7 +418,7 @@ static void SDL_LogEvent(const SDL_Event *event) break; #undef PRINT_FINGER_EVENT -#define PRINT_DROP_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (file='%s' timestamp=%u windowid=%u)", event->drop.file, (uint)event->drop.timestamp, (uint)event->drop.windowID) +#define PRINT_DROP_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (file='%s' timestamp=%u windowid=%u x=%f y=%f)", event->drop.file, (uint)event->drop.timestamp, (uint)event->drop.windowID, event->drop.x, event->drop.y) SDL_EVENT_CASE(SDL_EVENT_DROP_FILE) PRINT_DROP_EVENT(event); break; @@ -431,6 +431,9 @@ static void SDL_LogEvent(const SDL_Event *event) SDL_EVENT_CASE(SDL_EVENT_DROP_COMPLETE) PRINT_DROP_EVENT(event); break; + SDL_EVENT_CASE(SDL_EVENT_DROP_POSITION) + PRINT_DROP_EVENT(event); + break; #undef PRINT_DROP_EVENT #define PRINT_AUDIODEV_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%u iscapture=%s)", (uint)event->adevice.timestamp, (uint)event->adevice.which, event->adevice.iscapture ? "true" : "false") @@ -440,6 +443,9 @@ static void SDL_LogEvent(const SDL_Event *event) SDL_EVENT_CASE(SDL_EVENT_AUDIO_DEVICE_REMOVED) PRINT_AUDIODEV_EVENT(event); break; + SDL_EVENT_CASE(SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED) + PRINT_AUDIODEV_EVENT(event); + break; #undef PRINT_AUDIODEV_EVENT SDL_EVENT_CASE(SDL_EVENT_SENSOR_UPDATE) diff --git a/external/sdl/SDL/src/events/SDL_mouse.c b/external/sdl/SDL/src/events/SDL_mouse.c index fd8b62ce..ee9d4a25 100644 --- a/external/sdl/SDL/src/events/SDL_mouse.c +++ b/external/sdl/SDL/src/events/SDL_mouse.c @@ -233,13 +233,31 @@ void SDL_SetDefaultCursor(SDL_Cursor *cursor) if (mouse->def_cursor) { SDL_Cursor *default_cursor = mouse->def_cursor; + SDL_Cursor *prev, *curr; if (mouse->cur_cursor == mouse->def_cursor) { mouse->cur_cursor = NULL; } mouse->def_cursor = NULL; - SDL_DestroyCursor(default_cursor); + for (prev = NULL, curr = mouse->cursors; curr; + prev = curr, curr = curr->next) { + if (curr == default_cursor) { + if (prev) { + prev->next = curr->next; + } else { + mouse->cursors = curr->next; + } + + break; + } + } + + if (mouse->FreeCursor && default_cursor->driverdata) { + mouse->FreeCursor(default_cursor); + } else { + SDL_free(default_cursor); + } } mouse->def_cursor = cursor; @@ -591,6 +609,13 @@ static int SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_ } } + if (mouse->has_position && xrel == 0.0f && yrel == 0.0f) { /* Drop events that don't change state */ +#ifdef DEBUG_MOUSE + SDL_Log("Mouse event didn't change state - dropped!\n"); +#endif + return 0; + } + /* Ignore relative motion positioning the first touch */ if (mouseID == SDL_TOUCH_MOUSEID && !GetButtonState(mouse, SDL_TRUE)) { xrel = 0.0f; @@ -598,13 +623,6 @@ static int SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_ } if (mouse->has_position) { - if (xrel == 0.0f && yrel == 0.0f) { /* Drop events that don't change state */ -#ifdef DEBUG_MOUSE - SDL_Log("Mouse event didn't change state - dropped!\n"); -#endif - return 0; - } - /* Update internal mouse coordinates */ if (!mouse->relative_mode) { mouse->x = x; diff --git a/external/sdl/SDL/src/events/imKStoUCS.h b/external/sdl/SDL/src/events/imKStoUCS.h index 485ace59..4182c1b7 100644 --- a/external/sdl/SDL/src/events/imKStoUCS.h +++ b/external/sdl/SDL/src/events/imKStoUCS.h @@ -3,7 +3,7 @@ /* Copyright (C) 2003-2006,2008 Jamey Sharp, Josh Triplett -Copyright © 2009 Red Hat, Inc. +Copyright © 2009 Red Hat, Inc. Copyright 1990-1992,1999,2000,2004,2009,2010 Oracle and/or its affiliates. All rights reserved. diff --git a/external/sdl/SDL/src/filesystem/gdk/SDL_sysfilesystem.cpp b/external/sdl/SDL/src/filesystem/gdk/SDL_sysfilesystem.cpp new file mode 100644 index 00000000..517524bd --- /dev/null +++ b/external/sdl/SDL/src/filesystem/gdk/SDL_sysfilesystem.cpp @@ -0,0 +1,140 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifdef SDL_FILESYSTEM_XBOX + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* System dependent filesystem routines */ + +#include "../../core/windows/SDL_windows.h" +#include "SDL_hints.h" +#include "SDL_system.h" +#include "SDL_filesystem.h" +#include + +char * +SDL_GetBasePath(void) +{ + /* NOTE: This function is a UTF8 version of the Win32 SDL_GetBasePath()! + * The GDK actually _recommends_ the 'A' functions over the 'W' functions :o + */ + DWORD buflen = 128; + CHAR *path = NULL; + DWORD len = 0; + int i; + + while (SDL_TRUE) { + void *ptr = SDL_realloc(path, buflen * sizeof(CHAR)); + if (ptr == NULL) { + SDL_free(path); + SDL_OutOfMemory(); + return NULL; + } + + path = (CHAR *)ptr; + + len = GetModuleFileNameA(NULL, path, buflen); + /* if it truncated, then len >= buflen - 1 */ + /* if there was enough room (or failure), len < buflen - 1 */ + if (len < buflen - 1) { + break; + } + + /* buffer too small? Try again. */ + buflen *= 2; + } + + if (len == 0) { + SDL_free(path); + WIN_SetError("Couldn't locate our .exe"); + return NULL; + } + + for (i = len - 1; i > 0; i--) { + if (path[i] == '\\') { + break; + } + } + + SDL_assert(i > 0); /* Should have been an absolute path. */ + path[i + 1] = '\0'; /* chop off filename. */ + + return path; +} + +char * +SDL_GetPrefPath(const char *org, const char *app) +{ + XUserHandle user = NULL; + XAsyncBlock block = { 0 }; + char *folderPath; + HRESULT result; + const char *csid = SDL_GetHint("SDL_GDK_SERVICE_CONFIGURATION_ID"); + + if (app == NULL) { + SDL_InvalidParamError("app"); + return NULL; + } + + /* This should be set before calling SDL_GetPrefPath! */ + if (csid == NULL) { + SDL_LogWarn(SDL_LOG_CATEGORY_SYSTEM, "Set SDL_GDK_SERVICE_CONFIGURATION_ID before calling SDL_GetPrefPath!"); + return SDL_strdup("T:\\"); + } + + if (SDL_GDKGetDefaultUser(&user) < 0) { + /* Error already set, just return */ + return NULL; + } + + if (FAILED(result = XGameSaveFilesGetFolderWithUiAsync(user, csid, &block))) { + WIN_SetErrorFromHRESULT("XGameSaveFilesGetFolderWithUiAsync", result); + return NULL; + } + + folderPath = (char*) SDL_malloc(MAX_PATH); + do { + result = XGameSaveFilesGetFolderWithUiResult(&block, MAX_PATH, folderPath); + } while (result == E_PENDING); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT("XGameSaveFilesGetFolderWithUiResult", result); + SDL_free(folderPath); + return NULL; + } + + /* We aren't using 'app' here because the container rules are a lot more + * strict than the NTFS rules, so it will most likely be invalid :( + */ + SDL_strlcat(folderPath, "\\SDLPrefPath\\", MAX_PATH); + if (CreateDirectoryA(folderPath, NULL) == FALSE) { + if (GetLastError() != ERROR_ALREADY_EXISTS) { + WIN_SetError("CreateDirectoryA"); + SDL_free(folderPath); + return NULL; + } + } + return folderPath; +} + +#endif /* SDL_FILESYSTEM_XBOX */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/external/sdl/SDL/src/filesystem/windows/SDL_sysfilesystem.c b/external/sdl/SDL/src/filesystem/windows/SDL_sysfilesystem.c index 2dffe4c2..3027fb00 100644 --- a/external/sdl/SDL/src/filesystem/windows/SDL_sysfilesystem.c +++ b/external/sdl/SDL/src/filesystem/windows/SDL_sysfilesystem.c @@ -332,23 +332,3 @@ done: return retval; } #endif /* SDL_FILESYSTEM_WINDOWS */ - -#ifdef SDL_FILESYSTEM_XBOX -char *SDL_GetBasePath(void) -{ - SDL_Unsupported(); - return NULL; -} - -char *SDL_GetPrefPath(const char *org, const char *app) -{ - SDL_Unsupported(); - return NULL; -} - -char *SDL_GetUserFolder(SDL_Folder folder) -{ - SDL_Unsupported(); - return NULL; -} -#endif /* SDL_FILESYSTEM_XBOX */ diff --git a/external/sdl/SDL/src/hidapi/SDL_hidapi.c b/external/sdl/SDL/src/hidapi/SDL_hidapi.c index 5be9ff3a..b31683b3 100644 --- a/external/sdl/SDL/src/hidapi/SDL_hidapi.c +++ b/external/sdl/SDL/src/hidapi/SDL_hidapi.c @@ -873,10 +873,47 @@ typedef struct LIBUSB_hid_device_ LIBUSB_hid_device; #undef read_thread #undef return_data +/* If the platform has any backend other than libusb, try to avoid using + * libusb as the main backend for devices, since it detaches drivers and + * therefore makes devices inaccessible to the rest of the OS. + * + * We do this by whitelisting devices we know to be accessible _exclusively_ + * via libusb; these are typically devices that look like HIDs but have a + * quirk that requires direct access to the hardware. + */ +static const struct { + Uint16 vendor; + Uint16 product; +} SDL_libusb_whitelist[] = { + { 0x057e, 0x0337 } /* Nintendo WUP-028, Wii U/Switch GameCube Adapter */ +}; + +static SDL_bool IsInWhitelist(Uint16 vendor, Uint16 product) +{ + int i; + for (i = 0; i < SDL_arraysize(SDL_libusb_whitelist); i += 1) { + if (vendor == SDL_libusb_whitelist[i].vendor && + product == SDL_libusb_whitelist[i].product) { + return SDL_TRUE; + } + } + return SDL_FALSE; +} + #endif /* HAVE_LIBUSB */ #endif /* !SDL_HIDAPI_DISABLED */ +#if defined(HAVE_PLATFORM_BACKEND) || defined(HAVE_DRIVER_BACKEND) +/* We have another way to get HID devices, so use the whitelist to get devices where libusb is preferred */ +#define SDL_HIDAPI_LIBUSB_WHITELIST_DEFAULT SDL_TRUE +#else +/* libusb is the only way to get HID devices, so don't use the whitelist, get them all */ +#define SDL_HIDAPI_LIBUSB_WHITELIST_DEFAULT SDL_FALSE +#endif /* HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND */ + +static SDL_bool use_libusb_whitelist = SDL_HIDAPI_LIBUSB_WHITELIST_DEFAULT; + /* Shared HIDAPI Implementation */ struct hidapi_backend @@ -1117,6 +1154,8 @@ int SDL_hid_init(void) } #endif + use_libusb_whitelist = SDL_GetHintBoolean("SDL_HIDAPI_LIBUSB_WHITELIST", + SDL_HIDAPI_LIBUSB_WHITELIST_DEFAULT); #ifdef HAVE_LIBUSB if (SDL_getenv("SDL_HIDAPI_DISABLE_LIBUSB") != NULL) { SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, @@ -1280,144 +1319,170 @@ Uint32 SDL_hid_device_change_count(void) return counter; } +static void AddDeviceToEnumeration(const char *driver_name, struct hid_device_info *dev, struct SDL_hid_device_info **devs, struct SDL_hid_device_info **last) +{ + struct SDL_hid_device_info *new_dev; + +#ifdef DEBUG_HIDAPI + SDL_Log("Adding %s device to enumeration: %ls %ls 0x%.4hx/0x%.4hx/%d", + driver_name, dev->manufacturer_string, dev->product_string, dev->vendor_id, dev->product_id, dev->interface_number); +#else + (void)driver_name; +#endif + + new_dev = (struct SDL_hid_device_info *)SDL_malloc(sizeof(struct SDL_hid_device_info)); + if (new_dev == NULL) { + /* Don't bother returning an error, get as many devices as possible */ + return; + } + CopyHIDDeviceInfo(dev, new_dev); + + if ((*last) != NULL) { + (*last)->next = new_dev; + } else { + *devs = new_dev; + } + *last = new_dev; +} + +#if defined(HAVE_LIBUSB) || defined(HAVE_PLATFORM_BACKEND) +static void RemoveDeviceFromEnumeration(const char *driver_name, struct hid_device_info *dev, struct hid_device_info **devs, void (*free_device_info)(struct hid_device_info *)) +{ + struct hid_device_info *last = NULL, *curr, *next; + + for (curr = *devs; curr; curr = next) { + next = curr->next; + + if (dev->vendor_id == curr->vendor_id && + dev->product_id == curr->product_id && + (dev->interface_number < 0 || curr->interface_number < 0 || dev->interface_number == curr->interface_number)) { +#ifdef DEBUG_HIDAPI + SDL_Log("Skipping %s device: %ls %ls 0x%.4hx/0x%.4hx/%d", + driver_name, curr->manufacturer_string, curr->product_string, curr->vendor_id, curr->product_id, curr->interface_number); +#else + (void)driver_name; +#endif + if (last) { + last->next = next; + } else { + *devs = next; + } + + curr->next = NULL; + free_device_info(curr); + continue; + } + last = curr; + } +} +#endif /* HAVE_LIBUSB || HAVE_PLATFORM_BACKEND */ + +#ifdef HAVE_LIBUSB +static void RemoveNonWhitelistedDevicesFromEnumeration(struct hid_device_info **devs, void (*free_device_info)(struct hid_device_info *)) +{ + struct hid_device_info *last = NULL, *curr, *next; + + for (curr = *devs; curr; curr = next) { + next = curr->next; + + if (!IsInWhitelist(curr->vendor_id, curr->product_id)) { +#ifdef DEBUG_HIDAPI + SDL_Log("Device was not in libusb whitelist, skipping: %ls %ls 0x%.4hx/0x%.4hx/%d", + curr->manufacturer_string, curr->product_string, curr->vendor_id, curr->product_id, curr->interface_number); +#endif + if (last) { + last->next = next; + } else { + *devs = next; + } + + curr->next = NULL; + free_device_info(curr); + continue; + } + last = curr; + } +} +#endif /* HAVE_LIBUSB */ + struct SDL_hid_device_info *SDL_hid_enumerate(unsigned short vendor_id, unsigned short product_id) { -#if defined(HAVE_PLATFORM_BACKEND) || defined(HAVE_DRIVER_BACKEND) || defined(HAVE_LIBUSB) -#ifdef HAVE_LIBUSB - struct hid_device_info *usb_devs = NULL; - struct hid_device_info *usb_dev; -#endif -#ifdef HAVE_DRIVER_BACKEND struct hid_device_info *driver_devs = NULL; - struct hid_device_info *driver_dev; -#endif -#ifdef HAVE_PLATFORM_BACKEND + struct hid_device_info *usb_devs = NULL; struct hid_device_info *raw_devs = NULL; - struct hid_device_info *raw_dev; -#endif - struct SDL_hid_device_info *devs = NULL, *last = NULL, *new_dev; + struct hid_device_info *dev; + struct SDL_hid_device_info *devs = NULL, *last = NULL; if (SDL_hidapi_refcount == 0 && SDL_hid_init() != 0) { return NULL; } + /* Collect the available devices */ +#ifdef HAVE_DRIVER_BACKEND + driver_devs = DRIVER_hid_enumerate(vendor_id, product_id); +#endif + #ifdef HAVE_LIBUSB if (libusb_ctx.libhandle) { usb_devs = LIBUSB_hid_enumerate(vendor_id, product_id); -#ifdef DEBUG_HIDAPI - SDL_Log("libusb devices found:"); -#endif - for (usb_dev = usb_devs; usb_dev; usb_dev = usb_dev->next) { - new_dev = (struct SDL_hid_device_info *)SDL_malloc(sizeof(struct SDL_hid_device_info)); - if (new_dev == NULL) { - LIBUSB_hid_free_enumeration(usb_devs); - SDL_hid_free_enumeration(devs); - SDL_OutOfMemory(); - return NULL; - } - CopyHIDDeviceInfo(usb_dev, new_dev); -#ifdef DEBUG_HIDAPI - SDL_Log(" - %ls %ls 0x%.4hx 0x%.4hx", - usb_dev->manufacturer_string, usb_dev->product_string, - usb_dev->vendor_id, usb_dev->product_id); -#endif - if (last != NULL) { - last->next = new_dev; - } else { - devs = new_dev; - } - last = new_dev; + if (use_libusb_whitelist) { + RemoveNonWhitelistedDevicesFromEnumeration(&usb_devs, LIBUSB_hid_free_enumeration); } } #endif /* HAVE_LIBUSB */ -#ifdef HAVE_DRIVER_BACKEND - driver_devs = DRIVER_hid_enumerate(vendor_id, product_id); - for (driver_dev = driver_devs; driver_dev; driver_dev = driver_dev->next) { - new_dev = (struct SDL_hid_device_info *)SDL_malloc(sizeof(struct SDL_hid_device_info)); - CopyHIDDeviceInfo(driver_dev, new_dev); - - if (last != NULL) { - last->next = new_dev; - } else { - devs = new_dev; - } - last = new_dev; - } -#endif /* HAVE_DRIVER_BACKEND */ - #ifdef HAVE_PLATFORM_BACKEND if (udev_ctx) { raw_devs = PLATFORM_hid_enumerate(vendor_id, product_id); -#ifdef DEBUG_HIDAPI - SDL_Log("hidraw devices found:"); + } #endif - for (raw_dev = raw_devs; raw_dev; raw_dev = raw_dev->next) { - SDL_bool bFound = SDL_FALSE; -#ifdef DEBUG_HIDAPI - SDL_Log(" - %ls %ls 0x%.4hx 0x%.4hx", - raw_dev->manufacturer_string, raw_dev->product_string, - raw_dev->vendor_id, raw_dev->product_id); -#endif -#ifdef HAVE_LIBUSB - for (usb_dev = usb_devs; usb_dev; usb_dev = usb_dev->next) { - if (raw_dev->vendor_id == usb_dev->vendor_id && - raw_dev->product_id == usb_dev->product_id && - (raw_dev->interface_number < 0 || raw_dev->interface_number == usb_dev->interface_number)) { - bFound = SDL_TRUE; - break; - } - } -#endif -#ifdef HAVE_DRIVER_BACKEND - for (driver_dev = driver_devs; driver_dev; driver_dev = driver_dev->next) { - if (raw_dev->vendor_id == driver_dev->vendor_id && - raw_dev->product_id == driver_dev->product_id && - (raw_dev->interface_number < 0 || raw_dev->interface_number == driver_dev->interface_number)) { - bFound = SDL_TRUE; - break; - } - } -#endif - if (!bFound) { - new_dev = (struct SDL_hid_device_info *)SDL_malloc(sizeof(struct SDL_hid_device_info)); - if (new_dev == NULL) { -#ifdef HAVE_LIBUSB - if (libusb_ctx.libhandle) { - LIBUSB_hid_free_enumeration(usb_devs); - } -#endif - PLATFORM_hid_free_enumeration(raw_devs); - SDL_hid_free_enumeration(devs); - SDL_OutOfMemory(); - return NULL; - } - CopyHIDDeviceInfo(raw_dev, new_dev); - new_dev->next = NULL; - if (last != NULL) { - last->next = new_dev; - } else { - devs = new_dev; - } - last = new_dev; - } + /* Highest priority are custom driver devices */ + for (dev = driver_devs; dev; dev = dev->next) { + AddDeviceToEnumeration("driver", dev, &devs, &last); +#ifdef HAVE_LIBUSB + RemoveDeviceFromEnumeration("libusb", dev, &usb_devs, LIBUSB_hid_free_enumeration); +#endif +#ifdef HAVE_PLATFORM_BACKEND + RemoveDeviceFromEnumeration("raw", dev, &raw_devs, PLATFORM_hid_free_enumeration); +#endif + } + + /* If whitelist is in effect, libusb has priority, otherwise raw devices do */ + if (use_libusb_whitelist) { + for (dev = usb_devs; dev; dev = dev->next) { + AddDeviceToEnumeration("libusb", dev, &devs, &last); +#ifdef HAVE_PLATFORM_BACKEND + RemoveDeviceFromEnumeration("raw", dev, &raw_devs, PLATFORM_hid_free_enumeration); +#endif } - PLATFORM_hid_free_enumeration(raw_devs); - } -#endif /* HAVE_PLATFORM_BACKEND */ - + for (dev = raw_devs; dev; dev = dev->next) { + AddDeviceToEnumeration("platform", dev, &devs, &last); + } + } else { + for (dev = raw_devs; dev; dev = dev->next) { + AddDeviceToEnumeration("raw", dev, &devs, &last); #ifdef HAVE_LIBUSB - if (libusb_ctx.libhandle) { - LIBUSB_hid_free_enumeration(usb_devs); - } + RemoveDeviceFromEnumeration("libusb", dev, &usb_devs, LIBUSB_hid_free_enumeration); #endif - return devs; + } + for (dev = usb_devs; dev; dev = dev->next) { + AddDeviceToEnumeration("libusb", dev, &devs, &last); + } + } -#else - return NULL; -#endif /* HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND || HAVE_LIBUSB */ +#ifdef HAVE_DRIVER_BACKEND + DRIVER_hid_free_enumeration(driver_devs); +#endif +#ifdef HAVE_LIBUSB + LIBUSB_hid_free_enumeration(usb_devs); +#endif +#ifdef HAVE_PLATFORM_BACKEND + PLATFORM_hid_free_enumeration(raw_devs); +#endif + + return devs; } void SDL_hid_free_enumeration(struct SDL_hid_device_info *devs) diff --git a/external/sdl/SDL/src/hidapi/SDL_hidapi_libusb.h b/external/sdl/SDL/src/hidapi/SDL_hidapi_libusb.h index 80ea0aff..456a4bf1 100644 --- a/external/sdl/SDL/src/hidapi/SDL_hidapi_libusb.h +++ b/external/sdl/SDL/src/hidapi/SDL_hidapi_libusb.h @@ -23,6 +23,7 @@ /* #pragma push_macro/pop_macro works correctly only as of gcc >= 4.4.3 clang-3.0 _seems_ to be OK. */ +#pragma push_macro("calloc") #pragma push_macro("malloc") #pragma push_macro("realloc") #pragma push_macro("free") @@ -38,6 +39,7 @@ #pragma push_macro("tolower") #pragma push_macro("wcsdup") +#undef calloc #undef malloc #undef realloc #undef free @@ -53,6 +55,7 @@ #undef tolower #undef wcsdup +#define calloc SDL_calloc #define malloc SDL_malloc #define realloc SDL_realloc #define free SDL_free @@ -106,6 +109,7 @@ static int SDL_libusb_get_string_descriptor(libusb_device_handle *dev, #undef ICONV_CONST #undef UNDEF_ICONV_CONST #endif +#pragma pop_macro("calloc") #pragma pop_macro("malloc") #pragma pop_macro("realloc") #pragma pop_macro("free") diff --git a/external/sdl/SDL/src/hidapi/android/hid.cpp b/external/sdl/SDL/src/hidapi/android/hid.cpp index 1f4d8c6a..098feb85 100644 --- a/external/sdl/SDL/src/hidapi/android/hid.cpp +++ b/external/sdl/SDL/src/hidapi/android/hid.cpp @@ -1031,7 +1031,7 @@ extern "C" int hid_init(void) { - if ( !g_initialized ) + if ( !g_initialized && g_HIDDeviceManagerCallbackHandler ) { // HIDAPI doesn't work well with Android < 4.3 if (SDL_GetAndroidSDKVersion() >= 18) { @@ -1040,12 +1040,6 @@ int hid_init(void) g_JVM->AttachCurrentThread( &env, NULL ); pthread_setspecific( g_ThreadKey, (void*)env ); - if ( !g_HIDDeviceManagerCallbackHandler ) - { - LOGV( "hid_init() without callback handler" ); - return -1; - } - // Bluetooth is currently only used for Steam Controllers, so check that hint // before initializing Bluetooth, which will prompt the user for permission. bool init_usb = true; diff --git a/external/sdl/SDL/src/hidapi/configure.ac b/external/sdl/SDL/src/hidapi/configure.ac index cc7ceaca..1b205105 100644 --- a/external/sdl/SDL/src/hidapi/configure.ac +++ b/external/sdl/SDL/src/hidapi/configure.ac @@ -74,7 +74,7 @@ case $host in backend="mac" os="darwin" threads="pthreads" - LIBS="${LIBS} -framework IOKit -framework CoreFoundation -framework AppKit" + LIBS="${LIBS} -framework IOKit -framework CoreFoundation" ;; *-freebsd*) AC_MSG_RESULT([ (FreeBSD back-end)]) diff --git a/external/sdl/SDL/src/hidapi/dist/hidapi.podspec b/external/sdl/SDL/src/hidapi/dist/hidapi.podspec index 74642ef6..8588514a 100644 --- a/external/sdl/SDL/src/hidapi/dist/hidapi.podspec +++ b/external/sdl/SDL/src/hidapi/dist/hidapi.podspec @@ -26,6 +26,6 @@ Pod::Spec.new do |spec| spec.public_header_files = "hidapi/hidapi.h", "mac/hidapi_darwin.h" - spec.frameworks = "IOKit", "CoreFoundation", "AppKit" + spec.frameworks = "IOKit", "CoreFoundation" end diff --git a/external/sdl/SDL/src/hidapi/libusb/hid.c b/external/sdl/SDL/src/hidapi/libusb/hid.c index d6b73801..880f8d61 100644 --- a/external/sdl/SDL/src/hidapi/libusb/hid.c +++ b/external/sdl/SDL/src/hidapi/libusb/hid.c @@ -259,8 +259,16 @@ static int get_usage(uint8_t *report_descriptor, size_t size, //printf("Usage Page: %x\n", (uint32_t)*usage_page); } if (key_cmd == 0x8) { - *usage = get_bytes(report_descriptor, size, data_len, i); - usage_found = 1; + if (data_len == 4) { /* Usages 5.5 / Usage Page 6.2.2.7 */ + *usage_page = get_bytes(report_descriptor, size, 2, i + 2); + usage_page_found = 1; + *usage = get_bytes(report_descriptor, size, 2, i); + usage_found = 1; + } + else { + *usage = get_bytes(report_descriptor, size, data_len, i); + usage_found = 1; + } //printf("Usage: %x\n", (uint32_t)*usage); } @@ -1015,6 +1023,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, libusb_close(handle); handle = NULL; } + break; } } /* altsettings */ } /* interfaces */ diff --git a/external/sdl/SDL/src/hidapi/linux/hid.c b/external/sdl/SDL/src/hidapi/linux/hid.c index 50f85f3b..507a2b1f 100644 --- a/external/sdl/SDL/src/hidapi/linux/hid.c +++ b/external/sdl/SDL/src/hidapi/linux/hid.c @@ -206,7 +206,7 @@ static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) * Returns 1 if successful, 0 if an invalid key * Sets data_len and key_size when successful */ -static int get_hid_item_size(__u8 *report_descriptor, unsigned int pos, __u32 size, int *data_len, int *key_size) +static int get_hid_item_size(const __u8 *report_descriptor, __u32 size, unsigned int pos, int *data_len, int *key_size) { int key = report_descriptor[pos]; int size_code; @@ -262,7 +262,7 @@ static int get_hid_item_size(__u8 *report_descriptor, unsigned int pos, __u32 si * Get bytes from a HID Report Descriptor. * Only call with a num_bytes of 0, 1, 2, or 4. */ -static __u32 get_hid_report_bytes(__u8 *rpt, size_t len, size_t num_bytes, size_t cur) +static __u32 get_hid_report_bytes(const __u8 *rpt, size_t len, size_t num_bytes, size_t cur) { /* Return if there aren't enough bytes. */ if (cur + num_bytes >= len) @@ -285,6 +285,60 @@ static __u32 get_hid_report_bytes(__u8 *rpt, size_t len, size_t num_bytes, size_ return 0; } +/* + * Iterates until the end of a Collection. + * Assumes that *pos is exactly at the beginning of a Collection. + * Skips all nested Collection, i.e. iterates until the end of current level Collection. + * + * The return value is non-0 when an end of current Collection is found, + * 0 when error is occured (broken Descriptor, end of a Collection is found before its begin, + * or no Collection is found at all). + */ +static int hid_iterate_over_collection(const __u8 *report_descriptor, __u32 size, unsigned int *pos, int *data_len, int *key_size) +{ + int collection_level = 0; + + while (*pos < size) { + int key = report_descriptor[*pos]; + int key_cmd = key & 0xfc; + + /* Determine data_len and key_size */ + if (!get_hid_item_size(report_descriptor, size, *pos, data_len, key_size)) + return 0; /* malformed report */ + + switch (key_cmd) { + case 0xa0: /* Collection 6.2.2.4 (Main) */ + collection_level++; + break; + case 0xc0: /* End Collection 6.2.2.4 (Main) */ + collection_level--; + break; + } + + if (collection_level < 0) { + /* Broken descriptor or someone is using this function wrong, + * i.e. should be called exactly at the collection start */ + return 0; + } + + if (collection_level == 0) { + /* Found it! + * Also possible when called not at the collection start, but should not happen if used correctly */ + return 1; + } + + *pos += *data_len + *key_size; + } + + return 0; /* Did not find the end of a Collection */ +} + +struct hid_usage_iterator { + unsigned int pos; + int usage_page_found; + unsigned short usage_page; +}; + /* * Retrieves the device's Usage Page and Usage from the report descriptor. * The algorithm returns the current Usage Page/Usage pair whenever a new @@ -302,63 +356,64 @@ static __u32 get_hid_report_bytes(__u8 *rpt, size_t len, size_t num_bytes, size_ * 1 when finished processing descriptor. * -1 on a malformed report. */ -static int get_next_hid_usage(__u8 *report_descriptor, __u32 size, unsigned int *pos, unsigned short *usage_page, unsigned short *usage) +static int get_next_hid_usage(const __u8 *report_descriptor, __u32 size, struct hid_usage_iterator *ctx, unsigned short *usage_page, unsigned short *usage) { int data_len, key_size; - int initial = *pos == 0; /* Used to handle case where no top-level application collection is defined */ - int usage_pair_ready = 0; + int initial = ctx->pos == 0; /* Used to handle case where no top-level application collection is defined */ - /* Usage is a Local Item, it must be set before each Main Item (Collection) before a pair is returned */ int usage_found = 0; - while (*pos < size) { - int key = report_descriptor[*pos]; + while (ctx->pos < size) { + int key = report_descriptor[ctx->pos]; int key_cmd = key & 0xfc; /* Determine data_len and key_size */ - if (!get_hid_item_size(report_descriptor, *pos, size, &data_len, &key_size)) + if (!get_hid_item_size(report_descriptor, size, ctx->pos, &data_len, &key_size)) return -1; /* malformed report */ switch (key_cmd) { case 0x4: /* Usage Page 6.2.2.7 (Global) */ - *usage_page = get_hid_report_bytes(report_descriptor, size, data_len, *pos); + ctx->usage_page = get_hid_report_bytes(report_descriptor, size, data_len, ctx->pos); + ctx->usage_page_found = 1; break; case 0x8: /* Usage 6.2.2.8 (Local) */ - *usage = get_hid_report_bytes(report_descriptor, size, data_len, *pos); - usage_found = 1; + if (data_len == 4) { /* Usages 5.5 / Usage Page 6.2.2.7 */ + ctx->usage_page = get_hid_report_bytes(report_descriptor, size, 2, ctx->pos + 2); + ctx->usage_page_found = 1; + *usage = get_hid_report_bytes(report_descriptor, size, 2, ctx->pos); + usage_found = 1; + } + else { + *usage = get_hid_report_bytes(report_descriptor, size, data_len, ctx->pos); + usage_found = 1; + } break; case 0xa0: /* Collection 6.2.2.4 (Main) */ - /* A Usage Item (Local) must be found for the pair to be valid */ - if (usage_found) - usage_pair_ready = 1; + if (!hid_iterate_over_collection(report_descriptor, size, &ctx->pos, &data_len, &key_size)) { + return -1; + } - /* Usage is a Local Item, unset it */ - usage_found = 0; - break; + /* A pair is valid - to be reported when Collection is found */ + if (usage_found && ctx->usage_page_found) { + *usage_page = ctx->usage_page; + return 0; + } - case 0x80: /* Input 6.2.2.4 (Main) */ - case 0x90: /* Output 6.2.2.4 (Main) */ - case 0xb0: /* Feature 6.2.2.4 (Main) */ - case 0xc0: /* End Collection 6.2.2.4 (Main) */ - /* Usage is a Local Item, unset it */ - usage_found = 0; break; } /* Skip over this key and its associated data */ - *pos += data_len + key_size; - - /* Return usage pair */ - if (usage_pair_ready) - return 0; + ctx->pos += data_len + key_size; } /* If no top-level application collection is found and usage page/usage pair is found, pair is valid https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/top-level-collections */ - if (initial && usage_found) - return 0; /* success */ + if (initial && usage_found && ctx->usage_page_found) { + *usage_page = ctx->usage_page; + return 0; /* success */ + } return 1; /* finished processing */ } @@ -804,12 +859,14 @@ static struct hid_device_info * create_device_info_for_device(struct udev_device result = get_hid_report_descriptor_from_sysfs(sysfs_path, &report_desc); if (result >= 0) { unsigned short page = 0, usage = 0; - unsigned int pos = 0; + struct hid_usage_iterator usage_iterator; + memset(&usage_iterator, 0, sizeof(usage_iterator)); + /* * Parse the first usage and usage page * out of the report descriptor. */ - if (!get_next_hid_usage(report_desc.value, report_desc.size, &pos, &page, &usage)) { + if (!get_next_hid_usage(report_desc.value, report_desc.size, &usage_iterator, &page, &usage)) { cur_dev->usage_page = page; cur_dev->usage = usage; } @@ -818,7 +875,7 @@ static struct hid_device_info * create_device_info_for_device(struct udev_device * Parse any additional usage and usage pages * out of the report descriptor. */ - while (!get_next_hid_usage(report_desc.value, report_desc.size, &pos, &page, &usage)) { + while (!get_next_hid_usage(report_desc.value, report_desc.size, &usage_iterator, &page, &usage)) { /* Create new record for additional usage pairs */ struct hid_device_info *tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); struct hid_device_info *prev_dev = cur_dev; @@ -980,9 +1037,10 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, struct hidraw_report_descriptor report_desc; unsigned short page = 0, usage = 0; - unsigned int pos = 0; if (get_hid_report_descriptor_from_sysfs(sysfs_path, &report_desc) >= 0) { - get_next_hid_usage(report_desc.value, report_desc.size, &pos, &page, &usage); + struct hid_usage_iterator usage_iterator; + memset(&usage_iterator, 0, sizeof(usage_iterator)); + get_next_hid_usage(report_desc.value, report_desc.size, &usage_iterator, &page, &usage); } if (HIDAPI_IGNORE_DEVICE(bus_type, dev_vid, dev_pid, page, usage)) { continue; diff --git a/external/sdl/SDL/src/hidapi/mac/CMakeLists.txt b/external/sdl/SDL/src/hidapi/mac/CMakeLists.txt index a83664aa..ccb0b91d 100644 --- a/external/sdl/SDL/src/hidapi/mac/CMakeLists.txt +++ b/external/sdl/SDL/src/hidapi/mac/CMakeLists.txt @@ -12,7 +12,7 @@ find_package(Threads REQUIRED) target_link_libraries(hidapi_darwin PUBLIC hidapi_include PRIVATE Threads::Threads - PRIVATE "-framework IOKit" "-framework CoreFoundation" "-framework AppKit" + PRIVATE "-framework IOKit" "-framework CoreFoundation" ) set_target_properties(hidapi_darwin diff --git a/external/sdl/SDL/src/hidapi/mac/Makefile-manual b/external/sdl/SDL/src/hidapi/mac/Makefile-manual index 27084922..667c863c 100644 --- a/external/sdl/SDL/src/hidapi/mac/Makefile-manual +++ b/external/sdl/SDL/src/hidapi/mac/Makefile-manual @@ -12,7 +12,7 @@ CC=gcc COBJS=hid.o ../hidtest/test.o OBJS=$(COBJS) CFLAGS+=-I../hidapi -I. -Wall -g -c -LIBS=-framework IOKit -framework CoreFoundation -framework AppKit +LIBS=-framework IOKit -framework CoreFoundation hidtest: $(OBJS) diff --git a/external/sdl/SDL/src/hidapi/mac/hid.c b/external/sdl/SDL/src/hidapi/mac/hid.c index 20731186..20c66450 100644 --- a/external/sdl/SDL/src/hidapi/mac/hid.c +++ b/external/sdl/SDL/src/hidapi/mac/hid.c @@ -38,9 +38,6 @@ #include "hidapi_darwin.h" -/* As defined in AppKit.h, but we don't need the entire AppKit for a single constant. */ -extern const double NSAppKitVersionNumber; - /* Barrier implementation because Mac OSX doesn't have pthread_barrier. It also doesn't have clock_gettime(). So much for POSIX and SUSv2. This implementation came from Brent Priddy and was posted on @@ -477,7 +474,7 @@ int HID_API_EXPORT hid_init(void) register_global_error(NULL); if (!hid_mgr) { - is_macos_10_10_or_greater = (NSAppKitVersionNumber >= 1343); /* NSAppKitVersionNumber10_10 */ + is_macos_10_10_or_greater = (kCFCoreFoundationVersionNumber >= 1151.16); /* kCFCoreFoundationVersionNumber10_10 */ hid_darwin_set_open_exclusive(1); /* Backward compatibility */ return init_hid_manager(); } diff --git a/external/sdl/SDL/src/joystick/SDL_gamepad.c b/external/sdl/SDL/src/joystick/SDL_gamepad.c index 00b6e928..4cb4a148 100644 --- a/external/sdl/SDL/src/joystick/SDL_gamepad.c +++ b/external/sdl/SDL/src/joystick/SDL_gamepad.c @@ -57,52 +57,6 @@ static SDL_bool SDL_gamepads_initialized; static SDL_Gamepad *SDL_gamepads SDL_GUARDED_BY(SDL_joystick_lock) = NULL; -typedef enum -{ - SDL_GAMEPAD_BINDTYPE_NONE = 0, - SDL_GAMEPAD_BINDTYPE_BUTTON, - SDL_GAMEPAD_BINDTYPE_AXIS, - SDL_GAMEPAD_BINDTYPE_HAT -} SDL_GamepadBindingType; - -typedef struct -{ - SDL_GamepadBindingType inputType; - union - { - int button; - - struct - { - int axis; - int axis_min; - int axis_max; - } axis; - - struct - { - int hat; - int hat_mask; - } hat; - - } input; - - SDL_GamepadBindingType outputType; - union - { - SDL_GamepadButton button; - - struct - { - SDL_GamepadAxis axis; - int axis_min; - int axis_max; - } axis; - - } output; - -} SDL_GamepadBinding; - /* our hard coded list of mapping support */ typedef enum { @@ -3180,6 +3134,45 @@ SDL_Gamepad *SDL_GetGamepadFromPlayerIndex(int player_index) return retval; } +/* + * Get the SDL joystick layer bindings for this gamepad + */ +SDL_GamepadBinding **SDL_GetGamepadBindings(SDL_Gamepad *gamepad, int *count) +{ + SDL_GamepadBinding **bindings = NULL; + + if (count) { + *count = 0; + } + + SDL_LockJoysticks(); + { + CHECK_GAMEPAD_MAGIC(gamepad, NULL); + + size_t pointers_size = ((gamepad->num_bindings + 1) * sizeof(SDL_GamepadBinding *)); + size_t elements_size = (gamepad->num_bindings * sizeof(SDL_GamepadBinding)); + bindings = (SDL_GamepadBinding **)SDL_malloc(pointers_size + elements_size); + if (bindings) { + SDL_GamepadBinding *binding = (SDL_GamepadBinding *)((Uint8 *)bindings + pointers_size); + int i; + for (i = 0; i < gamepad->num_bindings; ++i, ++binding) { + bindings[i] = binding; + SDL_copyp(binding, &gamepad->bindings[i]); + } + bindings[i] = NULL; + + if (count) { + *count = gamepad->num_bindings; + } + } else { + SDL_OutOfMemory(); + } + } + SDL_UnlockJoysticks(); + + return bindings; +} + int SDL_RumbleGamepad(SDL_Gamepad *gamepad, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) { SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); diff --git a/external/sdl/SDL/src/joystick/SDL_joystick.c b/external/sdl/SDL/src/joystick/SDL_joystick.c index 4b6ff4cf..48385b4b 100644 --- a/external/sdl/SDL/src/joystick/SDL_joystick.c +++ b/external/sdl/SDL/src/joystick/SDL_joystick.c @@ -2640,8 +2640,11 @@ static SDL_bool SDL_IsJoystickProductWheel(Uint32 vidpid) MAKE_VIDPID(0x046d, 0xc262), /* Logitech G920 (active mode) */ MAKE_VIDPID(0x046d, 0xc268), /* Logitech PRO Racing Wheel (PC mode) */ MAKE_VIDPID(0x046d, 0xc269), /* Logitech PRO Racing Wheel (PS4/PS5 mode) */ + MAKE_VIDPID(0x046d, 0xc272), /* Logitech PRO Racing Wheel for Xbox (PC mode) */ MAKE_VIDPID(0x046d, 0xc26d), /* Logitech G923 (Xbox) */ MAKE_VIDPID(0x046d, 0xc26e), /* Logitech G923 */ + MAKE_VIDPID(0x046d, 0xc266), /* Logitech G923 for Playstation 4 and PC (PC mode) */ + MAKE_VIDPID(0x046d, 0xc267), /* Logitech G923 for Playstation 4 and PC (PS4 mode)*/ MAKE_VIDPID(0x046d, 0xca03), /* Logitech Momo Racing */ MAKE_VIDPID(0x044f, 0xb65d), /* Thrustmaster Wheel FFB */ MAKE_VIDPID(0x044f, 0xb66d), /* Thrustmaster Wheel FFB */ @@ -2653,6 +2656,17 @@ static SDL_bool SDL_IsJoystickProductWheel(Uint32 vidpid) MAKE_VIDPID(0x044f, 0xb65e), /* Thrustmaster T500RS */ MAKE_VIDPID(0x044f, 0xb664), /* Thrustmaster TX (initial mode) */ MAKE_VIDPID(0x044f, 0xb669), /* Thrustmaster TX (active mode) */ + MAKE_VIDPID(0x0483, 0x0522), /* Simagic Wheelbase (including M10, Alpha Mini, Alpha, Alpha U) */ + MAKE_VIDPID(0x0eb7, 0x0001), /* Fanatec ClubSport Wheel Base V2 */ + MAKE_VIDPID(0x0eb7, 0x0004), /* Fanatec ClubSport Wheel Base V2.5 */ + MAKE_VIDPID(0x0eb7, 0x0005), /* Fanatec CSL Elite Wheel Base+ (PS4) */ + MAKE_VIDPID(0x0eb7, 0x0006), /* Fanatec Podium Wheel Base DD1 */ + MAKE_VIDPID(0x0eb7, 0x0007), /* Fanatec Podium Wheel Base DD2 */ + MAKE_VIDPID(0x0eb7, 0x0011), /* Fanatec Forza Motorsport (CSR Wheel / CSR Elite Wheel) */ + MAKE_VIDPID(0x0eb7, 0x0020), /* Fanatec generic wheel / CSL DD / GT DD Pro */ + MAKE_VIDPID(0x0eb7, 0x0197), /* Fanatec Porsche Wheel (Turbo / GT3 RS / Turbo S / GT3 V2 / GT2) */ + MAKE_VIDPID(0x0eb7, 0x038e), /* Fanatec ClubSport Wheel Base V1 */ + MAKE_VIDPID(0x0eb7, 0x0e03), /* Fanatec CSL Elite Wheel Base */ MAKE_VIDPID(0x11ff, 0x0511), /* DragonRise Inc. Wired Wheel (initial mode) (also known as PXN V900 (PS3), Superdrive SV-750, or a Genesis Seaborg 400) */ }; int i; diff --git a/external/sdl/SDL/src/joystick/hidapi/SDL_hidapi_ps5.c b/external/sdl/SDL/src/joystick/hidapi/SDL_hidapi_ps5.c index 1f6df2a6..8446c553 100644 --- a/external/sdl/SDL/src/joystick/hidapi/SDL_hidapi_ps5.c +++ b/external/sdl/SDL/src/joystick/hidapi/SDL_hidapi_ps5.c @@ -102,7 +102,7 @@ typedef struct Uint8 rgucAccelX[2]; /* 21 */ Uint8 rgucAccelY[2]; /* 23 */ Uint8 rgucAccelZ[2]; /* 25 */ - Uint8 rgucSensorTimestamp[4]; /* 27 - 32 bit little endian */ + Uint8 rgucSensorTimestamp[4]; /* 27 - 16/32 bit little endian */ } PS5StatePacketCommon_t; @@ -154,7 +154,9 @@ typedef struct Uint8 rgucAccelX[2]; /* 21 */ Uint8 rgucAccelY[2]; /* 23 */ Uint8 rgucAccelZ[2]; /* 25 */ - Uint8 rgucSensorTimestamp[4]; /* 27 - 32 bit little endian */ + Uint8 rgucSensorTimestamp[2]; /* 27 - 16 bit little endian */ + Uint8 ucBatteryLevel; /* 29 */ + Uint8 ucUnknown; /* 30 */ Uint8 ucTouchpadCounter1; /* 31 - high bit clear + counter */ Uint8 rgucTouchpadData1[3]; /* 32 - X/Y, 12 bits per axis */ Uint8 ucTouchpadCounter2; /* 35 - high bit clear + counter */ @@ -494,6 +496,7 @@ static SDL_bool HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device) /* The Razer Wolverine V2 Pro doesn't respond to the detection protocol, but has a touchpad and sensors, but no vibration */ ctx->sensors_supported = SDL_TRUE; ctx->touchpad_supported = SDL_TRUE; + ctx->use_alternate_report = SDL_TRUE; } } ctx->effects_supported = (ctx->lightbar_supported || ctx->vibration_supported || ctx->playerled_supported); @@ -722,7 +725,7 @@ static void HIDAPI_DriverPS5_CheckPendingLEDReset(SDL_DriverPS5_Context *ctx) { SDL_bool led_reset_complete = SDL_FALSE; - if (ctx->enhanced_reports && ctx->sensors_supported) { + if (ctx->enhanced_reports && ctx->sensors_supported && !ctx->use_alternate_report) { const PS5StatePacketCommon_t *packet = &ctx->last_state.state; /* Check the timer to make sure the Bluetooth connection LED animation is complete */ @@ -1102,10 +1105,9 @@ static int HIDAPI_DriverPS5_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, return 0; } -static void HIDAPI_DriverPS5_HandleSimpleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5SimpleStatePacket_t *packet) +static void HIDAPI_DriverPS5_HandleSimpleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5SimpleStatePacket_t *packet, Uint64 timestamp) { Sint16 axis; - Uint64 timestamp = SDL_GetTicksNS(); if (ctx->last_state.simple.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) { { @@ -1204,10 +1206,9 @@ static void HIDAPI_DriverPS5_HandleSimpleStatePacket(SDL_Joystick *joystick, SDL SDL_memcpy(&ctx->last_state.simple, packet, sizeof(ctx->last_state.simple)); } -static void HIDAPI_DriverPS5_HandleStatePacketCommon(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketCommon_t *packet) +static void HIDAPI_DriverPS5_HandleStatePacketCommon(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketCommon_t *packet, Uint64 timestamp) { Sint16 axis; - Uint64 timestamp = SDL_GetTicksNS(); if (ctx->last_state.state.rgucButtonsAndHat[0] != packet->rgucButtonsAndHat[0]) { { @@ -1309,25 +1310,42 @@ static void HIDAPI_DriverPS5_HandleStatePacketCommon(SDL_Joystick *joystick, SDL SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis); if (ctx->report_sensors) { - Uint32 tick; - Uint32 delta; Uint64 sensor_timestamp; float data[3]; - tick = LOAD32(packet->rgucSensorTimestamp[0], - packet->rgucSensorTimestamp[1], - packet->rgucSensorTimestamp[2], - packet->rgucSensorTimestamp[3]); - if (ctx->last_tick < tick) { - delta = (tick - ctx->last_tick); - } else { - delta = (SDL_MAX_UINT32 - ctx->last_tick + tick + 1); - } - ctx->sensor_ticks += delta; - ctx->last_tick = tick; + if (ctx->use_alternate_report) { + /* 16-bit timestamp */ + Uint32 delta; + Uint16 tick = LOAD16(packet->rgucSensorTimestamp[0], + packet->rgucSensorTimestamp[1]); + if (ctx->last_tick < tick) { + delta = (tick - ctx->last_tick); + } else { + delta = (SDL_MAX_UINT16 - ctx->last_tick + tick + 1); + } + ctx->last_tick = tick; + ctx->sensor_ticks += delta; - /* Sensor timestamp is in 0.33us units */ - sensor_timestamp = (ctx->sensor_ticks * SDL_NS_PER_US) / 3; + /* Sensor timestamp is in 1us units */ + sensor_timestamp = SDL_US_TO_NS(ctx->sensor_ticks); + } else { + /* 32-bit timestamp */ + Uint32 delta; + Uint32 tick = LOAD32(packet->rgucSensorTimestamp[0], + packet->rgucSensorTimestamp[1], + packet->rgucSensorTimestamp[2], + packet->rgucSensorTimestamp[3]); + if (ctx->last_tick < tick) { + delta = (tick - ctx->last_tick); + } else { + delta = (SDL_MAX_UINT32 - ctx->last_tick + tick + 1); + } + ctx->last_tick = tick; + ctx->sensor_ticks += delta; + + /* Sensor timestamp is in 0.33us units */ + sensor_timestamp = (ctx->sensor_ticks * SDL_NS_PER_US) / 3; + } data[0] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 0, LOAD16(packet->rgucGyroX[0], packet->rgucGyroX[1])); data[1] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 1, LOAD16(packet->rgucGyroY[0], packet->rgucGyroY[1])); @@ -1341,13 +1359,12 @@ static void HIDAPI_DriverPS5_HandleStatePacketCommon(SDL_Joystick *joystick, SDL } } -static void HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacket_t *packet) +static void HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacket_t *packet, Uint64 timestamp) { static const float TOUCHPAD_SCALEX = 1.0f / 1920; static const float TOUCHPAD_SCALEY = 1.0f / 1070; Uint8 touchpad_state; int touchpad_x, touchpad_y; - Uint64 timestamp = SDL_GetTicksNS(); if (ctx->report_touchpad) { touchpad_state = !(packet->ucTouchpadCounter1 & 0x80) ? SDL_PRESSED : SDL_RELEASED; @@ -1378,13 +1395,12 @@ static void HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_d SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state)); } -static void HIDAPI_DriverPS5_HandleStatePacketAlt(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketAlt_t *packet) +static void HIDAPI_DriverPS5_HandleStatePacketAlt(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketAlt_t *packet, Uint64 timestamp) { static const float TOUCHPAD_SCALEX = 1.0f / 1920; static const float TOUCHPAD_SCALEY = 1.0f / 1070; Uint8 touchpad_state; int touchpad_x, touchpad_y; - Uint64 timestamp = SDL_GetTicksNS(); if (ctx->report_touchpad) { touchpad_state = !(packet->ucTouchpadCounter1 & 0x80) ? SDL_PRESSED : SDL_RELEASED; @@ -1447,6 +1463,8 @@ static SDL_bool HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device) } while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { + Uint64 timestamp = SDL_GetTicksNS(); + #ifdef DEBUG_PS5_PROTOCOL HIDAPI_DumpPacket("PS5 packet: size = %d", data, size); #endif @@ -1464,13 +1482,13 @@ static SDL_bool HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device) switch (data[0]) { case k_EPS5ReportIdState: if (size == 10 || size == 78) { - HIDAPI_DriverPS5_HandleSimpleStatePacket(joystick, device->dev, ctx, (PS5SimpleStatePacket_t *)&data[1]); + HIDAPI_DriverPS5_HandleSimpleStatePacket(joystick, device->dev, ctx, (PS5SimpleStatePacket_t *)&data[1], timestamp); } else { - HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, device->dev, ctx, (PS5StatePacketCommon_t *)&data[1]); + HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, device->dev, ctx, (PS5StatePacketCommon_t *)&data[1], timestamp); if (ctx->use_alternate_report) { - HIDAPI_DriverPS5_HandleStatePacketAlt(joystick, device->dev, ctx, (PS5StatePacketAlt_t *)&data[1]); + HIDAPI_DriverPS5_HandleStatePacketAlt(joystick, device->dev, ctx, (PS5StatePacketAlt_t *)&data[1], timestamp); } else { - HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[1]); + HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[1], timestamp); } } break; @@ -1478,11 +1496,11 @@ static SDL_bool HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device) /* This is the extended report, we can enable effects now in default mode */ HIDAPI_DriverPS5_UpdateEnhancedModeOnEnhancedReport(ctx); - HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, device->dev, ctx, (PS5StatePacketCommon_t *)&data[2]); + HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, device->dev, ctx, (PS5StatePacketCommon_t *)&data[2], timestamp); if (ctx->use_alternate_report) { - HIDAPI_DriverPS5_HandleStatePacketAlt(joystick, device->dev, ctx, (PS5StatePacketAlt_t *)&data[2]); + HIDAPI_DriverPS5_HandleStatePacketAlt(joystick, device->dev, ctx, (PS5StatePacketAlt_t *)&data[2], timestamp); } else { - HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[2]); + HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[2], timestamp); } if (ctx->led_reset_state == k_EDS5LEDResetStatePending) { HIDAPI_DriverPS5_CheckPendingLEDReset(ctx); diff --git a/external/sdl/SDL/src/joystick/hidapi/SDL_hidapijoystick.c b/external/sdl/SDL/src/joystick/hidapi/SDL_hidapijoystick.c index bf045ca5..06e6d260 100644 --- a/external/sdl/SDL/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/external/sdl/SDL/src/joystick/hidapi/SDL_hidapijoystick.c @@ -663,6 +663,8 @@ static void HIDAPI_UpdateJoystickSerial(SDL_HIDAPI_Device *device) { int i; + SDL_AssertJoysticksLocked(); + for (i = 0; i < device->num_joysticks; ++i) { SDL_Joystick *joystick = SDL_GetJoystickFromInstanceID(device->joysticks[i]); if (joystick && device->serial) { diff --git a/external/sdl/SDL/src/power/linux/SDL_syspower.c b/external/sdl/SDL/src/power/linux/SDL_syspower.c index c89c865a..53fb6f53 100644 --- a/external/sdl/SDL/src/power/linux/SDL_syspower.c +++ b/external/sdl/SDL/src/power/linux/SDL_syspower.c @@ -561,9 +561,17 @@ static void check_upower_device(DBusConnection *conn, const char *path, SDL_Powe st = SDL_POWERSTATE_UNKNOWN; /* uh oh */ } else if (ui32 == 1) { /* 1 == charging */ st = SDL_POWERSTATE_CHARGING; - } else if ((ui32 == 2) || (ui32 == 3)) { /* 2 == discharging, 3 == empty. */ + } else if ((ui32 == 2) || (ui32 == 3) || (ui32 == 6)) { + /* 2 == discharging; + * 3 == empty; + * 6 == "pending discharge" which GNOME interprets as equivalent + * to discharging */ st = SDL_POWERSTATE_ON_BATTERY; - } else if (ui32 == 4) { /* 4 == full */ + } else if ((ui32 == 4) || (ui32 == 5)) { + /* 4 == full; + * 5 == "pending charge" which GNOME shows as "Not charging", + * used when a battery is configured to stop charging at a + * lower than 100% threshold */ st = SDL_POWERSTATE_CHARGED; } else { st = SDL_POWERSTATE_UNKNOWN; /* uh oh */ diff --git a/external/sdl/SDL/src/render/SDL_render.c b/external/sdl/SDL/src/render/SDL_render.c index 82fe6509..1df03498 100644 --- a/external/sdl/SDL/src/render/SDL_render.c +++ b/external/sdl/SDL/src/render/SDL_render.c @@ -2534,7 +2534,7 @@ int SDL_SetRenderClipRect(SDL_Renderer *renderer, const SDL_Rect *rect) int retval; CHECK_RENDERER_MAGIC(renderer, -1) - if (rect && rect->w > 0 && rect->h > 0) { + if (rect && rect->w >= 0 && rect->h >= 0) { renderer->view->clipping_enabled = SDL_TRUE; renderer->view->clip_rect.x = rect->x; renderer->view->clip_rect.y = rect->y; diff --git a/external/sdl/SDL/src/render/metal/SDL_render_metal.m b/external/sdl/SDL/src/render/metal/SDL_render_metal.m index a83f9822..3f038f27 100644 --- a/external/sdl/SDL/src/render/metal/SDL_render_metal.m +++ b/external/sdl/SDL/src/render/metal/SDL_render_metal.m @@ -1754,8 +1754,22 @@ static SDL_Renderer *METAL_CreateRenderer(SDL_Window *window, Uint32 flags) return NULL; } - // !!! FIXME: MTLCopyAllDevices() can find other GPUs on macOS... - mtldevice = MTLCreateSystemDefaultDevice(); +#ifdef __MACOS__ + if (SDL_GetHintBoolean(SDL_HINT_RENDER_METAL_PREFER_LOW_POWER_DEVICE, SDL_TRUE)) { + NSArray> *devices = MTLCopyAllDevices(); + + for (id device in devices) { + if (device.isLowPower) { + mtldevice = device; + break; + } + } + } +#endif + + if (mtldevice == nil) { + mtldevice = MTLCreateSystemDefaultDevice(); + } if (mtldevice == nil) { SDL_free(renderer); diff --git a/external/sdl/SDL/src/test/SDL_test_common.c b/external/sdl/SDL/src/test/SDL_test_common.c index 4d09aabb..57b65ea6 100644 --- a/external/sdl/SDL/src/test/SDL_test_common.c +++ b/external/sdl/SDL/src/test/SDL_test_common.c @@ -25,6 +25,7 @@ static const char *common_usage[] = { "[-h | --help]", "[--trackmem]", + "[--randmem]", "[--log all|error|system|audio|video|render|input]", }; @@ -70,7 +71,7 @@ static const char *video_usage[] = { /* !!! FIXME: Float32? Sint32? */ static const char *audio_usage[] = { - "[--audio driver]", "[--rate N]", "[--format U8|S8|S16|S16LE|S16BE]", + "[--audio driver]", "[--rate N]", "[--format U8|S8|S16|S16LE|S16BE|S32|S32LE|S32BE|F32|F32LE|F32BE]", "[--channels N]" }; @@ -95,7 +96,8 @@ SDLTest_CommonState *SDLTest_CommonCreateState(char **argv, Uint32 flags) for (i = 1; argv[i]; ++i) { if (SDL_strcasecmp(argv[i], "--trackmem") == 0) { SDLTest_TrackAllocations(); - break; + } else if (SDL_strcasecmp(argv[i], "--randmem") == 0) { + SDLTest_RandFillAllocations(); } } @@ -145,8 +147,8 @@ SDLTest_CommonState *SDLTest_CommonCreateState(char **argv, Uint32 flags) } void SDLTest_CommonDestroyState(SDLTest_CommonState *state) { - SDLTest_LogAllocations(); SDL_free(state); + SDLTest_LogAllocations(); } #define SEARCHARG(dim) \ @@ -170,6 +172,10 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index) /* Already handled in SDLTest_CommonCreateState() */ return 1; } + if (SDL_strcasecmp(argv[index], "--randmem") == 0) { + /* Already handled in SDLTest_CommonCreateState() */ + return 1; + } if (SDL_strcasecmp(argv[index], "--log") == 0) { ++index; if (!argv[index]) { @@ -624,16 +630,37 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index) return 2; } if (SDL_strcasecmp(argv[index], "S16LE") == 0) { - state->audio_format = SDL_AUDIO_S16LSB; + state->audio_format = SDL_AUDIO_S16LE; return 2; } if (SDL_strcasecmp(argv[index], "S16BE") == 0) { - state->audio_format = SDL_AUDIO_S16MSB; + state->audio_format = SDL_AUDIO_S16BE; + return 2; + } + if (SDL_strcasecmp(argv[index], "S32") == 0) { + state->audio_format = SDL_AUDIO_S32; + return 2; + } + if (SDL_strcasecmp(argv[index], "S32LE") == 0) { + state->audio_format = SDL_AUDIO_S32LE; + return 2; + } + if (SDL_strcasecmp(argv[index], "S32BE") == 0) { + state->audio_format = SDL_AUDIO_S32BE; + return 2; + } + if (SDL_strcasecmp(argv[index], "F32") == 0) { + state->audio_format = SDL_AUDIO_F32; + return 2; + } + if (SDL_strcasecmp(argv[index], "F32LE") == 0) { + state->audio_format = SDL_AUDIO_F32LE; + return 2; + } + if (SDL_strcasecmp(argv[index], "F32BE") == 0) { + state->audio_format = SDL_AUDIO_F32BE; return 2; } - - /* !!! FIXME: Float32? Sint32? */ - return -1; } if (SDL_strcasecmp(argv[index], "--channels") == 0) { @@ -2019,6 +2046,8 @@ void SDLTest_CommonEvent(SDLTest_CommonState *state, SDL_Event *event, int *done { SDL_Window *window = SDL_GetWindowFromID(event->window.windowID); if (window) { + /* Clear cache to avoid stale textures */ + SDLTest_CleanupTextDrawing(); for (i = 0; i < state->num_windows; ++i) { if (window == state->windows[i]) { if (state->targets[i]) { @@ -2408,7 +2437,6 @@ void SDLTest_CommonQuit(SDLTest_CommonState *state) common_usage_audio = NULL; common_usage_videoaudio = NULL; - SDL_free(state->windows); if (state->targets) { for (i = 0; i < state->num_windows; ++i) { if (state->targets[i]) { @@ -2425,6 +2453,12 @@ void SDLTest_CommonQuit(SDLTest_CommonState *state) } SDL_free(state->renderers); } + if (state->windows) { + for (i = 0; i < state->num_windows; i++) { + SDL_DestroyWindow(state->windows[i]); + } + SDL_free(state->windows); + } if (state->flags & SDL_INIT_VIDEO) { SDL_QuitSubSystem(SDL_INIT_VIDEO); } diff --git a/external/sdl/SDL/src/test/SDL_test_font.c b/external/sdl/SDL/src/test/SDL_test_font.c index 6d36a4b4..59205580 100644 --- a/external/sdl/SDL/src/test/SDL_test_font.c +++ b/external/sdl/SDL/src/test/SDL_test_font.c @@ -3126,11 +3126,12 @@ struct SDLTest_CharTextureCache */ static struct SDLTest_CharTextureCache *SDLTest_CharTextureCacheList; +int FONT_CHARACTER_SIZE = 8; + int SDLTest_DrawCharacter(SDL_Renderer *renderer, float x, float y, Uint32 c) { const Uint32 charWidth = FONT_CHARACTER_SIZE; const Uint32 charHeight = FONT_CHARACTER_SIZE; - const Uint32 charSize = FONT_CHARACTER_SIZE; SDL_FRect srect; SDL_FRect drect; int result; @@ -3149,8 +3150,8 @@ int SDLTest_DrawCharacter(SDL_Renderer *renderer, float x, float y, Uint32 c) */ srect.x = 0.0f; srect.y = 0.0f; - srect.w = (float)charWidth; - srect.h = (float)charHeight; + srect.w = 8.0f; + srect.h = 8.0f; /* * Setup destination rectangle @@ -3190,7 +3191,7 @@ int SDLTest_DrawCharacter(SDL_Renderer *renderer, float x, float y, Uint32 c) return -1; } - charpos = SDLTest_FontData + ci * charSize; + charpos = SDLTest_FontData + ci * 8; linepos = (Uint8 *)character->pixels; pitch = character->pitch; @@ -3221,6 +3222,8 @@ int SDLTest_DrawCharacter(SDL_Renderer *renderer, float x, float y, Uint32 c) if (cache->charTextureCache[ci] == NULL) { return -1; } + + SDL_SetTextureScaleMode(cache->charTextureCache[ci], SDL_SCALEMODE_NEAREST); } /* diff --git a/external/sdl/SDL/src/test/SDL_test_memory.c b/external/sdl/SDL/src/test/SDL_test_memory.c index 65c3c2a6..526686ad 100644 --- a/external/sdl/SDL/src/test/SDL_test_memory.c +++ b/external/sdl/SDL/src/test/SDL_test_memory.c @@ -25,6 +25,26 @@ #include #endif +#ifdef __WINDOWS__ +#include +#include + +static void *s_dbghelp; + +typedef BOOL (__stdcall *dbghelp_SymInitialize_fn)(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess); + +typedef BOOL (__stdcall *dbghelp_SymFromAddr_fn)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol); +static dbghelp_SymFromAddr_fn dbghelp_SymFromAddr; + +#ifdef _WIN64 +typedef BOOL (__stdcall *dbghelp_SymGetLineFromAddr_fn)(HANDLE hProcess, DWORD64 qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line); +#else +typedef BOOL (__stdcall *dbghelp_SymGetLineFromAddr_fn)(HANDLE hProcess, DWORD qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE Line); +#endif +static dbghelp_SymGetLineFromAddr_fn dbghelp_SymGetLineFromAddr; + +#endif + /* This is a simple tracking allocator to demonstrate the use of SDL's memory allocation replacement functionality. @@ -32,12 +52,14 @@ for production code. */ +#define MAXIMUM_TRACKED_STACK_DEPTH 32 + typedef struct SDL_tracked_allocation { void *mem; size_t size; - Uint64 stack[10]; - char stack_names[10][256]; + Uint64 stack[MAXIMUM_TRACKED_STACK_DEPTH]; + char stack_names[MAXIMUM_TRACKED_STACK_DEPTH][256]; struct SDL_tracked_allocation *next; } SDL_tracked_allocation; @@ -48,6 +70,7 @@ static SDL_realloc_func SDL_realloc_orig = NULL; static SDL_free_func SDL_free_orig = NULL; static int s_previous_allocations = 0; static SDL_tracked_allocation *s_tracked_allocations[256]; +static SDL_bool s_randfill_allocations = SDL_FALSE; static unsigned int get_allocation_bucket(void *mem) { @@ -58,16 +81,28 @@ static unsigned int get_allocation_bucket(void *mem) return index; } -static SDL_bool SDL_IsAllocationTracked(void *mem) +static SDL_tracked_allocation* SDL_GetTrackedAllocation(void *mem) { SDL_tracked_allocation *entry; int index = get_allocation_bucket(mem); for (entry = s_tracked_allocations[index]; entry; entry = entry->next) { if (mem == entry->mem) { - return SDL_TRUE; + return entry; } } - return SDL_FALSE; + return NULL; +} + +static size_t SDL_GetTrackedAllocationSize(void *mem) +{ + SDL_tracked_allocation *entry = SDL_GetTrackedAllocation(mem); + + return entry ? entry->size : SIZE_MAX; +} + +static SDL_bool SDL_IsAllocationTracked(void *mem) +{ + return SDL_GetTrackedAllocation(mem) != NULL; } static void SDL_TrackAllocation(void *mem, size_t size) @@ -114,6 +149,40 @@ static void SDL_TrackAllocation(void *mem, size_t size) } } } +#elif defined(__WINDOWS__) + { + Uint32 count; + PVOID frames[63]; + Uint32 i; + + count = CaptureStackBackTrace(1, SDL_arraysize(frames), frames, NULL); + + entry->size = SDL_min(count, MAXIMUM_TRACKED_STACK_DEPTH); + for (i = 0; i < entry->size; i++) { + char symbol_buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbol_buffer; + DWORD64 dwDisplacement = 0; + DWORD lineColumn = 0; + pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); + pSymbol->MaxNameLen = MAX_SYM_NAME; + IMAGEHLP_LINE line; + line.SizeOfStruct = sizeof(line); + + entry->stack[i] = (Uint64)(uintptr_t)frames[i]; + if (s_dbghelp) { + if (!dbghelp_SymFromAddr(GetCurrentProcess(), (DWORD64)(uintptr_t)frames[i], &dwDisplacement, pSymbol)) { + SDL_strlcpy(pSymbol->Name, "???", MAX_SYM_NAME); + dwDisplacement = 0; + } + if (!dbghelp_SymGetLineFromAddr(GetCurrentProcess(), (DWORD64)(uintptr_t)frames[i], &lineColumn, &line)) { + line.FileName = ""; + line.LineNumber = 0; + } + + SDL_snprintf(entry->stack_names[i], sizeof(entry->stack_names[i]), "%s+0x%llx %s:%u", pSymbol->Name, (unsigned long long)dwDisplacement, line.FileName, (Uint32)line.LineNumber); + } + } + } #endif /* HAVE_LIBUNWIND_H */ entry->next = s_tracked_allocations[index]; @@ -140,6 +209,19 @@ static void SDL_UntrackAllocation(void *mem) } } +static void rand_fill_memory(void* ptr, size_t start, size_t end) +{ + Uint8* mem = (Uint8*) ptr; + size_t i; + + if (!s_randfill_allocations) + return; + + for (i = start; i < end; ++i) { + mem[i] = SDLTest_RandomUint8(); + } +} + static void *SDLCALL SDLTest_TrackedMalloc(size_t size) { void *mem; @@ -147,6 +229,7 @@ static void *SDLCALL SDLTest_TrackedMalloc(size_t size) mem = SDL_malloc_orig(size); if (mem) { SDL_TrackAllocation(mem, size); + rand_fill_memory(mem, 0, size); } return mem; } @@ -165,14 +248,20 @@ static void *SDLCALL SDLTest_TrackedCalloc(size_t nmemb, size_t size) static void *SDLCALL SDLTest_TrackedRealloc(void *ptr, size_t size) { void *mem; - - SDL_assert(ptr == NULL || SDL_IsAllocationTracked(ptr)); + size_t old_size = 0; + if (ptr) { + old_size = SDL_GetTrackedAllocationSize(ptr); + SDL_assert(old_size != SIZE_MAX); + } mem = SDL_realloc_orig(ptr, size); - if (mem && mem != ptr) { - if (ptr) { - SDL_UntrackAllocation(ptr); - } + if (ptr) { + SDL_UntrackAllocation(ptr); + } + if (mem) { SDL_TrackAllocation(mem, size); + if (size > old_size) { + rand_fill_memory(mem, old_size, size); + } } return mem; } @@ -190,10 +279,10 @@ static void SDLCALL SDLTest_TrackedFree(void *ptr) SDL_free_orig(ptr); } -int SDLTest_TrackAllocations(void) +void SDLTest_TrackAllocations(void) { if (SDL_malloc_orig) { - return 0; + return; } SDLTest_Crc32Init(&s_crc32_context); @@ -202,6 +291,30 @@ int SDLTest_TrackAllocations(void) if (s_previous_allocations != 0) { SDL_Log("SDLTest_TrackAllocations(): There are %d previous allocations, disabling free() validation", s_previous_allocations); } +#ifdef __WINDOWS__ + { + s_dbghelp = SDL_LoadObject("dbghelp.dll"); + if (s_dbghelp) { + dbghelp_SymInitialize_fn dbghelp_SymInitialize; + dbghelp_SymInitialize = (dbghelp_SymInitialize_fn)SDL_LoadFunction(s_dbghelp, "SymInitialize"); + dbghelp_SymFromAddr = (dbghelp_SymFromAddr_fn)SDL_LoadFunction(s_dbghelp, "SymFromAddr"); +#ifdef _WIN64 + dbghelp_SymGetLineFromAddr = (dbghelp_SymGetLineFromAddr_fn)SDL_LoadFunction(s_dbghelp, "SymGetLineFromAddr64"); +#else + dbghelp_SymGetLineFromAddr = (dbghelp_SymGetLineFromAddr_fn)SDL_LoadFunction(s_dbghelp, "SymGetLineFromAddr"); +#endif + if (!dbghelp_SymFromAddr || !dbghelp_SymFromAddr || !dbghelp_SymGetLineFromAddr) { + SDL_UnloadObject(s_dbghelp); + s_dbghelp = NULL; + } else { + if (!dbghelp_SymInitialize(GetCurrentProcess(), NULL, TRUE)) { + SDL_UnloadObject(s_dbghelp); + s_dbghelp = NULL; + } + } + } + } +#endif SDL_GetMemoryFunctions(&SDL_malloc_orig, &SDL_calloc_orig, @@ -212,7 +325,13 @@ int SDLTest_TrackAllocations(void) SDLTest_TrackedCalloc, SDLTest_TrackedRealloc, SDLTest_TrackedFree); - return 0; +} + +void SDLTest_RandFillAllocations() +{ + SDLTest_TrackAllocations(); + + s_randfill_allocations = SDL_TRUE; } void SDLTest_LogAllocations(void) @@ -245,8 +364,6 @@ void SDLTest_LogAllocations(void) SDL_strlcpy(line, "Memory allocations:\n", sizeof(line)); ADD_LINE(); - SDL_strlcpy(line, "Expect 2 allocations from within SDL_GetErrBuf()\n", sizeof(line)); - ADD_LINE(); count = 0; total_allocated = 0; diff --git a/external/sdl/SDL/src/video/SDL_bmp.c b/external/sdl/SDL/src/video/SDL_bmp.c index 68e634fd..998bee1f 100644 --- a/external/sdl/SDL/src/video/SDL_bmp.c +++ b/external/sdl/SDL/src/video/SDL_bmp.c @@ -560,7 +560,7 @@ SDL_Surface *SDL_LoadBMP_RW(SDL_RWops *src, SDL_bool freesrc) } break; default: - if (SDL_RWread(src, bits, surface->pitch) != surface->pitch) { + if (SDL_RWread(src, bits, surface->pitch) != (size_t)surface->pitch) { goto done; } if (biBitCount == 8 && palette && biClrUsed < (1u << biBitCount)) { @@ -743,7 +743,7 @@ int SDL_SaveBMP_RW(SDL_Surface *surface, SDL_RWops *dst, SDL_bool freedst) } if (SDL_LockSurface(intermediate_surface) == 0) { - const int bw = intermediate_surface->w * intermediate_surface->format->BytesPerPixel; + const size_t bw = intermediate_surface->w * intermediate_surface->format->BytesPerPixel; /* Set the BMP file header values */ bfSize = 0; /* We'll write this when we're done */ diff --git a/external/sdl/SDL/src/video/SDL_sysvideo.h b/external/sdl/SDL/src/video/SDL_sysvideo.h index 26d39120..0d55708a 100644 --- a/external/sdl/SDL/src/video/SDL_sysvideo.h +++ b/external/sdl/SDL/src/video/SDL_sysvideo.h @@ -23,7 +23,7 @@ #ifndef SDL_sysvideo_h_ #define SDL_sysvideo_h_ -#include "SDL_vulkan_internal.h" +#include /* The SDL video driver */ @@ -264,6 +264,7 @@ struct SDL_VideoDevice void (*DestroyWindowFramebuffer)(SDL_VideoDevice *_this, SDL_Window *window); void (*OnWindowEnter)(SDL_VideoDevice *_this, SDL_Window *window); int (*FlashWindow)(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation); + int (*SetWindowFocusable)(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable); /* * * */ /* @@ -442,8 +443,8 @@ struct SDL_VideoDevice /* Data used by the Vulkan drivers */ struct { - PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; - PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; + SDL_FunctionPointer vkGetInstanceProcAddr; + SDL_FunctionPointer vkEnumerateInstanceExtensionProperties; int loader_loaded; char loader_path[256]; void *loader_handle; @@ -543,7 +544,7 @@ extern void SDL_OnWindowFocusGained(SDL_Window *window); extern void SDL_OnWindowFocusLost(SDL_Window *window); extern void SDL_OnWindowDisplayChanged(SDL_Window *window); extern void SDL_UpdateWindowGrab(SDL_Window *window); -extern SDL_Window *SDL_GetFocusWindow(void); +extern SDL_Window *SDL_GetToplevelForKeyboardFocus(void); extern SDL_bool SDL_ShouldAllowTopmost(void); diff --git a/external/sdl/SDL/src/video/SDL_video.c b/external/sdl/SDL/src/video/SDL_video.c index 708cbc37..fbd63a8c 100644 --- a/external/sdl/SDL/src/video/SDL_video.c +++ b/external/sdl/SDL/src/video/SDL_video.c @@ -1740,7 +1740,7 @@ Uint32 SDL_GetWindowPixelFormat(SDL_Window *window) } #define CREATE_FLAGS \ - (SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED | SDL_WINDOW_METAL | SDL_WINDOW_TRANSPARENT) + (SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED | SDL_WINDOW_METAL | SDL_WINDOW_TRANSPARENT | SDL_WINDOW_NOT_FOCUSABLE) static SDL_INLINE SDL_bool IsAcceptingDragAndDrop(void) { @@ -3205,6 +3205,24 @@ int SDL_SetWindowInputFocus(SDL_Window *window) return _this->SetWindowInputFocus(_this, window); } +int SDL_SetWindowFocusable(SDL_Window *window, SDL_bool focusable) +{ + CHECK_WINDOW_MAGIC(window, -1); + + const int want = (focusable != SDL_FALSE); /* normalize the flag. */ + const int have = !(window->flags & SDL_WINDOW_NOT_FOCUSABLE); + if ((want != have) && (_this->SetWindowFocusable)) { + if (want) { + window->flags &= ~SDL_WINDOW_NOT_FOCUSABLE; + } else { + window->flags |= SDL_WINDOW_NOT_FOCUSABLE; + } + _this->SetWindowFocusable(_this, window, (SDL_bool)want); + } + + return 0; +} + void SDL_UpdateWindowGrab(SDL_Window *window) { SDL_bool keyboard_grabbed, mouse_grabbed; @@ -3543,21 +3561,18 @@ void SDL_OnWindowFocusLost(SDL_Window *window) } } -/* !!! FIXME: is this different than SDL_GetKeyboardFocus()? - !!! FIXME: Also, SDL_GetKeyboardFocus() is O(1), this isn't. */ -SDL_Window *SDL_GetFocusWindow(void) +SDL_Window *SDL_GetToplevelForKeyboardFocus(void) { - SDL_Window *window; + SDL_Window *focus = SDL_GetKeyboardFocus(); - if (_this == NULL) { - return NULL; - } - for (window = _this->windows; window; window = window->next) { - if (window->flags & SDL_WINDOW_INPUT_FOCUS) { - return window; + if (focus) { + /* Get the toplevel parent window. */ + while (focus->parent) { + focus = focus->parent; } } - return NULL; + + return focus; } void SDL_DestroyWindow(SDL_Window *window) @@ -4741,7 +4756,7 @@ void SDL_StartTextInput(void) /* Then show the on-screen keyboard, if any */ if (SDL_GetHintBoolean(SDL_HINT_ENABLE_SCREEN_KEYBOARD, SDL_TRUE)) { - window = SDL_GetFocusWindow(); + window = SDL_GetKeyboardFocus(); if (window && _this && _this->ShowScreenKeyboard) { _this->ShowScreenKeyboard(_this, window); } @@ -4785,7 +4800,7 @@ void SDL_StopTextInput(void) /* Hide the on-screen keyboard, if any */ if (SDL_GetHintBoolean(SDL_HINT_ENABLE_SCREEN_KEYBOARD, SDL_TRUE)) { - window = SDL_GetFocusWindow(); + window = SDL_GetKeyboardFocus(); if (window && _this && _this->HideScreenKeyboard) { _this->HideScreenKeyboard(_this, window); } @@ -5101,9 +5116,9 @@ void SDL_OnApplicationWillResignActive(void) if (_this) { SDL_Window *window; for (window = _this->windows; window != NULL; window = window->next) { - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_FOCUS_LOST, 0, 0); SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0); } + SDL_SetKeyboardFocus(NULL); } SDL_SendAppEvent(SDL_EVENT_WILL_ENTER_BACKGROUND); } @@ -5125,7 +5140,7 @@ void SDL_OnApplicationDidBecomeActive(void) if (_this) { SDL_Window *window; for (window = _this->windows; window != NULL; window = window->next) { - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_FOCUS_GAINED, 0, 0); + SDL_SetKeyboardFocus(window); SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0); } } diff --git a/external/sdl/SDL/src/video/android/SDL_androidvulkan.c b/external/sdl/SDL/src/video/android/SDL_androidvulkan.c index 685b7c4e..8a0af196 100644 --- a/external/sdl/SDL/src/video/android/SDL_androidvulkan.c +++ b/external/sdl/SDL/src/video/android/SDL_androidvulkan.c @@ -28,6 +28,8 @@ #if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_ANDROID) +#include "../SDL_vulkan_internal.h" + #include "SDL_androidvideo.h" #include "SDL_androidwindow.h" diff --git a/external/sdl/SDL/src/video/cocoa/SDL_cocoavideo.m b/external/sdl/SDL/src/video/cocoa/SDL_cocoavideo.m index d59590ae..3b6092b0 100644 --- a/external/sdl/SDL/src/video/cocoa/SDL_cocoavideo.m +++ b/external/sdl/SDL/src/video/cocoa/SDL_cocoavideo.m @@ -119,6 +119,7 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void) device->SetWindowHitTest = Cocoa_SetWindowHitTest; device->AcceptDragAndDrop = Cocoa_AcceptDragAndDrop; device->FlashWindow = Cocoa_FlashWindow; + device->SetWindowFocusable = Cocoa_SetWindowFocusable; device->shape_driver.CreateShaper = Cocoa_CreateShaper; device->shape_driver.SetWindowShape = Cocoa_SetWindowShape; diff --git a/external/sdl/SDL/src/video/cocoa/SDL_cocoawindow.h b/external/sdl/SDL/src/video/cocoa/SDL_cocoawindow.h index ceb27db9..1408784b 100644 --- a/external/sdl/SDL/src/video/cocoa/SDL_cocoawindow.h +++ b/external/sdl/SDL/src/video/cocoa/SDL_cocoawindow.h @@ -169,5 +169,6 @@ extern int Cocoa_GetWindowWMInfo(SDL_VideoDevice *_this, SDL_Window *window, str extern int Cocoa_SetWindowHitTest(SDL_Window *window, SDL_bool enabled); extern void Cocoa_AcceptDragAndDrop(SDL_Window *window, SDL_bool accept); extern int Cocoa_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation); +extern int Cocoa_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable); #endif /* SDL_cocoawindow_h_ */ diff --git a/external/sdl/SDL/src/video/cocoa/SDL_cocoawindow.m b/external/sdl/SDL/src/video/cocoa/SDL_cocoawindow.m index 10071cdf..ce110528 100644 --- a/external/sdl/SDL/src/video/cocoa/SDL_cocoawindow.m +++ b/external/sdl/SDL/src/video/cocoa/SDL_cocoawindow.m @@ -116,7 +116,7 @@ - (BOOL)canBecomeKeyWindow { SDL_Window *window = [self findSDLWindow]; - if (window && !(window->flags & SDL_WINDOW_TOOLTIP)) { + if (window && !(window->flags & (SDL_WINDOW_TOOLTIP | SDL_WINDOW_NOT_FOCUSABLE))) { return YES; } else { return NO; @@ -643,7 +643,7 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window) int newVisibility = [[change objectForKey:@"new"] intValue]; if (newVisibility) { SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_SHOWN, 0, 0); - } else { + } else if (![_data.nswindow isMiniaturized]) { SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_HIDDEN, 0, 0); } } @@ -662,7 +662,7 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window) if (wasVisible != isVisible) { if (isVisible) { SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_SHOWN, 0, 0); - } else { + } else if (![_data.nswindow isMiniaturized]) { SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_HIDDEN, 0, 0); } @@ -955,7 +955,12 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window) - (void)windowDidDeminiaturize:(NSNotification *)aNotification { - SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_RESTORED, 0, 0); + /* isZoomed always returns true if the window is not resizable */ + if ((_data.window->flags & SDL_WINDOW_RESIZABLE) && [_data.nswindow isZoomed]) { + SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0); + } else { + SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_RESTORED, 0, 0); + } } - (void)windowDidBecomeKey:(NSNotification *)aNotification @@ -2165,16 +2170,17 @@ void Cocoa_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) if (SDL_WINDOW_IS_POPUP(window)) { NSWindow *nsparent = ((__bridge SDL_CocoaWindowData *)window->parent->driverdata).nswindow; [nsparent addChildWindow:nswindow ordered:NSWindowAbove]; - } - if (bActivate) { - [nswindow makeKeyAndOrderFront:nil]; } else { - /* Order this window below the key window if we're not activating it */ - if ([NSApp keyWindow]) { - [nswindow orderWindow:NSWindowBelow relativeTo:[[NSApp keyWindow] windowNumber]]; + if (bActivate) { + [nswindow makeKeyAndOrderFront:nil]; + } else { + /* Order this window below the key window if we're not activating it */ + if ([NSApp keyWindow]) { + [nswindow orderWindow:NSWindowBelow relativeTo:[[NSApp keyWindow] windowNumber]]; + } } - [nswindow setIsVisible:YES]; } + [nswindow setIsVisible:YES]; [windowData.listener resumeVisibleObservation]; } } @@ -2185,7 +2191,18 @@ void Cocoa_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) @autoreleasepool { NSWindow *nswindow = ((__bridge SDL_CocoaWindowData *)window->driverdata).nswindow; - [nswindow orderOut:nil]; + /* orderOut has no effect on miniaturized windows, so close must be used to remove + * the window from the desktop and window list in this case. + * + * SDL holds a strong reference to the window (oneShot/releasedWhenClosed are 'NO'), + * and calling 'close' doesn't send a 'windowShouldClose' message, so it's safe to + * use for this purpose as nothing is implicitly released. + */ + if (![nswindow isMiniaturized]) { + [nswindow orderOut:nil]; + } else { + [nswindow close]; + } /* Transfer keyboard focus back to the parent */ if (window->flags & SDL_WINDOW_POPUP_MENU) { @@ -2218,13 +2235,13 @@ void Cocoa_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window) if (SDL_WINDOW_IS_POPUP(window)) { NSWindow *nsparent = ((__bridge SDL_CocoaWindowData *)window->parent->driverdata).nswindow; [nsparent addChildWindow:nswindow ordered:NSWindowAbove]; - } - - if (bActivate) { - [NSApp activateIgnoringOtherApps:YES]; - [nswindow makeKeyAndOrderFront:nil]; } else { - [nswindow orderFront:nil]; + if (bActivate) { + [NSApp activateIgnoringOtherApps:YES]; + [nswindow makeKeyAndOrderFront:nil]; + } else { + [nswindow orderFront:nil]; + } } } [windowData.listener resumeVisibleObservation]; @@ -2662,6 +2679,11 @@ int Cocoa_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOpera } } +int Cocoa_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable) +{ + return 0; /* just succeed, the real work is done elsewhere. */ +} + int Cocoa_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity) { @autoreleasepool { diff --git a/external/sdl/SDL/src/video/emscripten/SDL_emscriptenevents.c b/external/sdl/SDL/src/video/emscripten/SDL_emscriptenevents.c index 69a7b980..e862bbc8 100644 --- a/external/sdl/SDL/src/video/emscripten/SDL_emscriptenevents.c +++ b/external/sdl/SDL/src/video/emscripten/SDL_emscriptenevents.c @@ -732,7 +732,7 @@ static EM_BOOL Emscripten_HandleFocus(int eventType, const EmscriptenFocusEvent } sdl_event_type = (eventType == EMSCRIPTEN_EVENT_FOCUS) ? SDL_EVENT_WINDOW_FOCUS_GAINED : SDL_EVENT_WINDOW_FOCUS_LOST; - SDL_SendWindowEvent(window_data->window, sdl_event_type, 0, 0); + SDL_SetKeyboardFocus(sdl_event_type == SDL_EVENT_WINDOW_FOCUS_GAINED ? window_data->window : NULL); return SDL_EventEnabled(sdl_event_type); } diff --git a/external/sdl/SDL/src/video/emscripten/SDL_emscriptenmouse.c b/external/sdl/SDL/src/video/emscripten/SDL_emscriptenmouse.c index 0cc3ec41..9aa79467 100644 --- a/external/sdl/SDL/src/video/emscripten/SDL_emscriptenmouse.c +++ b/external/sdl/SDL/src/video/emscripten/SDL_emscriptenmouse.c @@ -31,6 +31,15 @@ #include "../../events/SDL_mouse_c.h" +/* older Emscriptens don't have this, but we need to for wasm64 compatibility. */ +#ifndef MAIN_THREAD_EM_ASM_PTR + #ifdef __wasm64__ + #error You need to upgrade your Emscripten compiler to support wasm64 + #else + #define MAIN_THREAD_EM_ASM_PTR MAIN_THREAD_EM_ASM_INT + #endif +#endif + static SDL_Cursor *Emscripten_CreateCursorFromString(const char *cursor_str, SDL_bool is_custom) { SDL_Cursor *cursor; @@ -74,7 +83,7 @@ static SDL_Cursor *Emscripten_CreateCursor(SDL_Surface *surface, int hot_x, int } /* *INDENT-OFF* */ /* clang-format off */ - cursor_url = (const char *)MAIN_THREAD_EM_ASM_INT({ + cursor_url = (const char *)MAIN_THREAD_EM_ASM_PTR({ var w = $0; var h = $1; var hot_x = $2; diff --git a/external/sdl/SDL/src/video/kmsdrm/SDL_kmsdrmvulkan.c b/external/sdl/SDL/src/video/kmsdrm/SDL_kmsdrmvulkan.c index dac3ab32..619053c9 100644 --- a/external/sdl/SDL/src/video/kmsdrm/SDL_kmsdrmvulkan.c +++ b/external/sdl/SDL/src/video/kmsdrm/SDL_kmsdrmvulkan.c @@ -28,6 +28,8 @@ #if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_KMSDRM) +#include "../SDL_vulkan_internal.h" + #include "SDL_kmsdrmvideo.h" #include "SDL_kmsdrmdyn.h" #include "SDL_kmsdrmvulkan.h" diff --git a/external/sdl/SDL/src/video/uikit/SDL_uikitvideo.m b/external/sdl/SDL/src/video/uikit/SDL_uikitvideo.m index 0b07afb0..0e1919bd 100644 --- a/external/sdl/SDL/src/video/uikit/SDL_uikitvideo.m +++ b/external/sdl/SDL/src/video/uikit/SDL_uikitvideo.m @@ -249,7 +249,7 @@ void UIKit_ForceUpdateHomeIndicator(void) { #if !TARGET_OS_TV /* Force the main SDL window to re-evaluate home indicator state */ - SDL_Window *focus = SDL_GetFocusWindow(); + SDL_Window *focus = SDL_GetKeyboardFocus(); if (focus) { SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)focus->driverdata; if (data != nil) { diff --git a/external/sdl/SDL/src/video/uikit/SDL_uikitviewcontroller.m b/external/sdl/SDL/src/video/uikit/SDL_uikitviewcontroller.m index dfebb086..7dc1abee 100644 --- a/external/sdl/SDL/src/video/uikit/SDL_uikitviewcontroller.m +++ b/external/sdl/SDL/src/video/uikit/SDL_uikitviewcontroller.m @@ -624,7 +624,7 @@ SDL_bool UIKit_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window) int UIKit_SetTextInputRect(SDL_VideoDevice *_this, const SDL_Rect *rect) { @autoreleasepool { - SDL_uikitviewcontroller *vc = GetWindowViewController(SDL_GetFocusWindow()); + SDL_uikitviewcontroller *vc = GetWindowViewController(SDL_GetKeyboardFocus()); if (vc != nil) { vc.textInputRect = *rect; diff --git a/external/sdl/SDL/src/video/vivante/SDL_vivantevulkan.c b/external/sdl/SDL/src/video/vivante/SDL_vivantevulkan.c index a84d893d..24d91070 100644 --- a/external/sdl/SDL/src/video/vivante/SDL_vivantevulkan.c +++ b/external/sdl/SDL/src/video/vivante/SDL_vivantevulkan.c @@ -28,6 +28,8 @@ #if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_VIVANTE) +#include "../SDL_vulkan_internal.h" + #include "SDL_vivantevideo.h" #include "SDL_vivantevulkan.h" diff --git a/external/sdl/SDL/src/video/wayland/SDL_waylanddatamanager.h b/external/sdl/SDL/src/video/wayland/SDL_waylanddatamanager.h index 6660b18b..1ffaf730 100644 --- a/external/sdl/SDL/src/video/wayland/SDL_waylanddatamanager.h +++ b/external/sdl/SDL/src/video/wayland/SDL_waylanddatamanager.h @@ -29,6 +29,7 @@ #define TEXT_MIME "text/plain;charset=utf-8" #define FILE_MIME "text/uri-list" +#define FILE_PORTAL_MIME "application/vnd.portal.filetransfer" typedef struct { diff --git a/external/sdl/SDL/src/video/wayland/SDL_waylanddyn.c b/external/sdl/SDL/src/video/wayland/SDL_waylanddyn.c index 572813e9..dde99bf4 100644 --- a/external/sdl/SDL/src/video/wayland/SDL_waylanddyn.c +++ b/external/sdl/SDL/src/video/wayland/SDL_waylanddyn.c @@ -34,34 +34,30 @@ typedef struct const char *libname; } waylanddynlib; -#ifndef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL -#define SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL NULL -#endif -#ifndef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR -#define SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR NULL -#endif -#ifndef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON -#define SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON NULL -#endif -#ifndef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR -#define SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR NULL -#endif - static waylanddynlib waylandlibs[] = { { NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC }, +#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL { NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL }, +#endif +#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR { NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR }, +#endif +#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON { NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON }, - { NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR } +#endif +#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR + { NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR }, +#endif + { NULL, NULL } }; static void *WAYLAND_GetSym(const char *fnname, int *pHasModule, SDL_bool required) { - int i; void *fn = NULL; - for (i = 0; i < SDL_TABLESIZE(waylandlibs); i++) { - if (waylandlibs[i].lib != NULL) { - fn = SDL_LoadFunction(waylandlibs[i].lib, fnname); + waylanddynlib *dynlib; + for (dynlib = waylandlibs; dynlib->libname; dynlib++) { + if (dynlib->lib != NULL) { + fn = SDL_LoadFunction(dynlib->lib, fnname); if (fn != NULL) { break; } @@ -69,10 +65,11 @@ static void *WAYLAND_GetSym(const char *fnname, int *pHasModule, SDL_bool requir } #if DEBUG_DYNAMIC_WAYLAND - if (fn != NULL) - SDL_Log("WAYLAND: Found '%s' in %s (%p)\n", fnname, waylandlibs[i].libname, fn); - else + if (fn != NULL) { + SDL_Log("WAYLAND: Found '%s' in %s (%p)\n", fnname, dynlib->libname, fn); + } else { SDL_Log("WAYLAND: Symbol '%s' NOT FOUND!\n", fnname); + } #endif if (fn == NULL && required) { diff --git a/external/sdl/SDL/src/video/wayland/SDL_waylandevents.c b/external/sdl/SDL/src/video/wayland/SDL_waylandevents.c index 5a14655e..498f69c5 100644 --- a/external/sdl/SDL/src/video/wayland/SDL_waylandevents.c +++ b/external/sdl/SDL/src/video/wayland/SDL_waylandevents.c @@ -823,7 +823,7 @@ static void pointer_handle_axis(void *data, struct wl_pointer *pointer, { struct SDL_WaylandInput *input = data; - if (wl_seat_get_version(input->seat) >= 5) { + if (wl_seat_get_version(input->seat) >= WL_POINTER_FRAME_SINCE_VERSION) { input->pointer_curr_axis_info.timestamp_ns = Wayland_GetPointerTimestamp(input, time); pointer_handle_axis_common(input, AXIS_EVENT_CONTINUOUS, axis, value); } else { @@ -1848,28 +1848,40 @@ static void data_device_handle_enter(void *data, struct wl_data_device *wl_data_ data_device->drag_offer = wl_data_offer_get_user_data(id); /* TODO: SDL Support more mime types */ - has_mime = Wayland_data_offer_has_mime( - data_device->drag_offer, FILE_MIME); - - /* If drag_mime is NULL this will decline the offer */ - wl_data_offer_accept(id, serial, - (has_mime == SDL_TRUE) ? FILE_MIME : NULL); +#ifdef SDL_USE_LIBDBUS + if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_PORTAL_MIME)) { + has_mime = SDL_TRUE; + wl_data_offer_accept(id, serial, FILE_PORTAL_MIME); + } +#endif + if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_MIME)) { + has_mime = SDL_TRUE; + wl_data_offer_accept(id, serial, FILE_MIME); + } /* SDL only supports "copy" style drag and drop */ - if (has_mime == SDL_TRUE) { + if (has_mime) { dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + } else { + /* drag_mime is NULL this will decline the offer */ + wl_data_offer_accept(id, serial, NULL); } - if (wl_data_offer_get_version(data_device->drag_offer->offer) >= 3) { + if (wl_data_offer_get_version(data_device->drag_offer->offer) >= + WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) { wl_data_offer_set_actions(data_device->drag_offer->offer, dnd_action, dnd_action); } /* find the current window */ - if (surface && SDL_WAYLAND_own_surface(surface)) { - SDL_WindowData *window = (SDL_WindowData *)wl_surface_get_user_data(surface); - if (window) { - data_device->dnd_window = window->sdlwindow; - } + if (surface) { + if (SDL_WAYLAND_own_surface(surface)) { + SDL_WindowData *window = (SDL_WindowData *)wl_surface_get_user_data(surface); + if (window) { + data_device->dnd_window = window->sdlwindow; + } + } else { + data_device->dnd_window = NULL; + } } } } @@ -1877,11 +1889,10 @@ static void data_device_handle_enter(void *data, struct wl_data_device *wl_data_ static void data_device_handle_leave(void *data, struct wl_data_device *wl_data_device) { SDL_WaylandDataDevice *data_device = data; - SDL_WaylandDataOffer *offer = NULL; - if (data_device->selection_offer != NULL) { - data_device->selection_offer = NULL; - Wayland_data_offer_destroy(offer); + if (data_device->drag_offer != NULL) { + Wayland_data_offer_destroy(data_device->drag_offer); + data_device->drag_offer = NULL; } } @@ -1890,27 +1901,15 @@ static void data_device_handle_motion(void *data, struct wl_data_device *wl_data { SDL_WaylandDataDevice *data_device = data; - if (data_device->drag_offer != NULL) { - /* TODO: SDL Support more mime types */ - size_t length; - void *buffer = Wayland_data_offer_receive(data_device->drag_offer, - FILE_MIME, &length); - if (buffer) { - char *saveptr = NULL; - char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr); - while (token != NULL) { - char *fn = Wayland_URIToLocal(token); - if (fn) { - double dx; - double dy; - dx = wl_fixed_to_double(x); - dy = wl_fixed_to_double(y); - SDL_SendDropPosition(data_device->dnd_window, fn, (float)dx, (float)dy); - } - token = SDL_strtok_r(NULL, "\r\n", &saveptr); - } - SDL_free(buffer); - } + if (data_device->drag_offer != NULL && data_device->dnd_window) { + const float dx = (float)wl_fixed_to_double(x); + const float dy = (float)wl_fixed_to_double(y); + + /* XXX: Send the filename here if the event system ever starts passing it though. + * Any future implementation should cache the filenames, as otherwise this could + * hammer the DBus interface hundreds or even thousands of times per second. + */ + SDL_SendDropPosition(data_device->dnd_window, NULL, dx, dy); } } @@ -2039,25 +2038,66 @@ static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_d { SDL_WaylandDataDevice *data_device = data; - if (data_device->drag_offer != NULL) { + if (data_device->drag_offer != NULL && data_device->dnd_window) { /* TODO: SDL Support more mime types */ size_t length; - void *buffer = Wayland_data_offer_receive(data_device->drag_offer, - FILE_MIME, &length); - if (buffer) { - char *saveptr = NULL; - char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr); - while (token != NULL) { - char *fn = Wayland_URIToLocal(token); - if (fn) { - SDL_SendDropFile(data_device->dnd_window, fn); + SDL_bool drop_handled = SDL_FALSE; +#ifdef SDL_USE_LIBDBUS + if (Wayland_data_offer_has_mime( + data_device->drag_offer, FILE_PORTAL_MIME)) { + void *buffer = Wayland_data_offer_receive(data_device->drag_offer, + FILE_PORTAL_MIME, &length); + if (buffer) { + SDL_DBusContext *dbus = SDL_DBus_GetContext(); + if (dbus) { + int path_count = 0; + char **paths = SDL_DBus_DocumentsPortalRetrieveFiles(buffer, &path_count); + /* If dropped files contain a directory the list is empty */ + if (paths && path_count > 0) { + for (int i = 0; i < path_count; i++) { + SDL_SendDropFile(data_device->dnd_window, paths[i]); + } + dbus->free_string_array(paths); + SDL_SendDropComplete(data_device->dnd_window); + drop_handled = SDL_TRUE; + } } - token = SDL_strtok_r(NULL, "\r\n", &saveptr); + SDL_free(buffer); } - SDL_SendDropComplete(data_device->dnd_window); - SDL_free(buffer); + } +#endif + /* If XDG document portal fails fallback. + * When running a flatpak sandbox this will most likely be a list of + * non paths that are not visible to the application + */ + if (!drop_handled && Wayland_data_offer_has_mime( + data_device->drag_offer, FILE_MIME)) { + void *buffer = Wayland_data_offer_receive(data_device->drag_offer, + FILE_MIME, &length); + if (buffer) { + char *saveptr = NULL; + char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr); + while (token != NULL) { + char *fn = Wayland_URIToLocal(token); + if (fn) { + SDL_SendDropFile(data_device->dnd_window, fn); + } + token = SDL_strtok_r(NULL, "\r\n", &saveptr); + } + SDL_SendDropComplete(data_device->dnd_window); + SDL_free(buffer); + drop_handled = SDL_TRUE; + } + } + + if (drop_handled && wl_data_offer_get_version(data_device->drag_offer->offer) >= + WL_DATA_OFFER_FINISH_SINCE_VERSION) { + wl_data_offer_finish(data_device->drag_offer->offer); } } + + Wayland_data_offer_destroy(data_device->drag_offer); + data_device->drag_offer = NULL; } static void data_device_handle_selection(void *data, struct wl_data_device *wl_data_device, diff --git a/external/sdl/SDL/src/video/wayland/SDL_waylandvideo.c b/external/sdl/SDL/src/video/wayland/SDL_waylandvideo.c index a891b907..4d1b3d10 100644 --- a/external/sdl/SDL/src/video/wayland/SDL_waylandvideo.c +++ b/external/sdl/SDL/src/video/wayland/SDL_waylandvideo.c @@ -916,7 +916,7 @@ static int Wayland_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *di /* When an emulated, exclusive fullscreen window has focus, treat the mode dimensions as the display bounds. */ if (display->fullscreen_window && display->fullscreen_window->fullscreen_exclusive && - display->fullscreen_window == SDL_GetFocusWindow() && + display->fullscreen_window->driverdata->active && display->fullscreen_window->current_fullscreen_mode.w != 0 && display->fullscreen_window->current_fullscreen_mode.h != 0) { rect->w = display->fullscreen_window->current_fullscreen_mode.w; diff --git a/external/sdl/SDL/src/video/wayland/SDL_waylandvulkan.c b/external/sdl/SDL/src/video/wayland/SDL_waylandvulkan.c index c886695c..1f667512 100644 --- a/external/sdl/SDL/src/video/wayland/SDL_waylandvulkan.c +++ b/external/sdl/SDL/src/video/wayland/SDL_waylandvulkan.c @@ -28,9 +28,12 @@ #if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_WAYLAND) +#include "../SDL_vulkan_internal.h" + #include "SDL_waylandvideo.h" #include "SDL_waylandwindow.h" +#include "../SDL_vulkan_internal.h" #include "SDL_waylandvulkan.h" #include diff --git a/external/sdl/SDL/src/video/wayland/SDL_waylandvulkan.h b/external/sdl/SDL/src/video/wayland/SDL_waylandvulkan.h index 618f936c..18aa3ed5 100644 --- a/external/sdl/SDL/src/video/wayland/SDL_waylandvulkan.h +++ b/external/sdl/SDL/src/video/wayland/SDL_waylandvulkan.h @@ -29,7 +29,7 @@ #ifndef SDL_waylandvulkan_h_ #define SDL_waylandvulkan_h_ -#include "../SDL_vulkan_internal.h" +#include #include "../SDL_sysvideo.h" #if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_WAYLAND) diff --git a/external/sdl/SDL/src/video/wayland/SDL_waylandwindow.c b/external/sdl/SDL/src/video/wayland/SDL_waylandwindow.c index 4568ecb4..88e0682c 100644 --- a/external/sdl/SDL/src/video/wayland/SDL_waylandwindow.c +++ b/external/sdl/SDL/src/video/wayland/SDL_waylandwindow.c @@ -639,7 +639,7 @@ static void handle_configure_xdg_toplevel(void *data, SDL_bool fullscreen = SDL_FALSE; SDL_bool maximized = SDL_FALSE; SDL_bool floating = SDL_TRUE; - SDL_bool focused = SDL_FALSE; + SDL_bool active = SDL_FALSE; SDL_bool suspended = SDL_FALSE; wl_array_for_each (state, states) { switch (*state) { @@ -652,7 +652,7 @@ static void handle_configure_xdg_toplevel(void *data, floating = SDL_FALSE; break; case XDG_TOPLEVEL_STATE_ACTIVATED: - focused = SDL_TRUE; + active = SDL_TRUE; break; case XDG_TOPLEVEL_STATE_TILED_LEFT: case XDG_TOPLEVEL_STATE_TILED_RIGHT: @@ -715,7 +715,7 @@ static void handle_configure_xdg_toplevel(void *data, * dependent, but in general, we can assume that the flag should remain set until * the next focused configure event occurs. */ - if (focused || !(window->flags & SDL_WINDOW_MINIMIZED)) { + if (active || !(window->flags & SDL_WINDOW_MINIMIZED)) { SDL_SendWindowEvent(window, maximized ? SDL_EVENT_WINDOW_MAXIMIZED : SDL_EVENT_WINDOW_RESTORED, 0, 0); @@ -745,15 +745,11 @@ static void handle_configure_xdg_toplevel(void *data, } } - /* Similar to maximized/restore events above, send focus events too! */ - SDL_SendWindowEvent(window, - focused ? SDL_EVENT_WINDOW_FOCUS_GAINED : SDL_EVENT_WINDOW_FOCUS_LOST, - 0, 0); - wind->requested_window_width = width; wind->requested_window_height = height; wind->floating = floating; wind->suspended = suspended; + wind->active = active; if (wind->surface_status == WAYLAND_SURFACE_STATUS_WAITING_FOR_CONFIGURE) { wind->surface_status = WAYLAND_SURFACE_STATUS_WAITING_FOR_FRAME; } @@ -855,11 +851,10 @@ static void handle_configure_zxdg_decoration(void *data, WAYLAND_wl_display_roundtrip(driverdata->waylandData->display); Wayland_HideWindow(device, window); + SDL_zero(driverdata->shell_surface); driverdata->shell_surface_type = WAYLAND_SURFACE_LIBDECOR; - if (!window->is_hiding && !(window->flags & SDL_WINDOW_HIDDEN)) { - Wayland_ShowWindow(device, window); - } + Wayland_ShowWindow(device, window); } } @@ -921,7 +916,7 @@ static void decoration_frame_configure(struct libdecor_frame *frame, int width, height; SDL_bool prev_fullscreen = wind->is_fullscreen; - SDL_bool focused = SDL_FALSE; + SDL_bool active = SDL_FALSE; SDL_bool fullscreen = SDL_FALSE; SDL_bool maximized = SDL_FALSE; SDL_bool tiled = SDL_FALSE; @@ -935,7 +930,7 @@ static void decoration_frame_configure(struct libdecor_frame *frame, if (libdecor_configuration_get_window_state(configuration, &window_state)) { fullscreen = (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) != 0; maximized = (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) != 0; - focused = (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) != 0; + active = (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) != 0; tiled = (window_state & tiled_states) != 0; #ifdef SDL_HAVE_LIBDECOR_VER_0_1_2 suspended = (window_state & LIBDECOR_WINDOW_STATE_SUSPENDED) != 0; @@ -954,18 +949,13 @@ static void decoration_frame_configure(struct libdecor_frame *frame, * dependent, but in general, we can assume that the flag should remain set until * the next focused configure event occurs. */ - if (focused || !(window->flags & SDL_WINDOW_MINIMIZED)) { + if (active || !(window->flags & SDL_WINDOW_MINIMIZED)) { SDL_SendWindowEvent(window, maximized ? SDL_EVENT_WINDOW_MAXIMIZED : SDL_EVENT_WINDOW_RESTORED, 0, 0); } } - /* Similar to maximized/restore events above, send focus events too! */ - SDL_SendWindowEvent(window, - focused ? SDL_EVENT_WINDOW_FOCUS_GAINED : SDL_EVENT_WINDOW_FOCUS_LOST, - 0, 0); - /* For fullscreen or fixed-size windows we know our size. * Always assume the configure is wrong. */ @@ -1055,6 +1045,7 @@ static void decoration_frame_configure(struct libdecor_frame *frame, /* Store the new state. */ wind->floating = floating; wind->suspended = suspended; + wind->active = active; /* Calculate the new window geometry */ wind->requested_window_width = width; @@ -1529,12 +1520,6 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) /* Restore state that was set prior to this call */ Wayland_SetWindowTitle(_this, window); - if (window->flags & SDL_WINDOW_MAXIMIZED) { - Wayland_MaximizeWindow(_this, window); - } - if (window->flags & SDL_WINDOW_MINIMIZED) { - Wayland_MinimizeWindow(_this, window); - } /* We have to wait until the surface gets a "configure" event, or use of * this surface will fail. This is a new rule for xdg_shell. @@ -1940,11 +1925,6 @@ void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) return; } - /* Set this flag now even if we never actually maximized, eventually - * ShowWindow will take care of it along with the other window state. - */ - window->flags &= ~SDL_WINDOW_MAXIMIZED; - #ifdef HAVE_LIBDECOR_H if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) { if (wind->shell_surface.libdecor.frame == NULL) { @@ -2025,11 +2005,6 @@ void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) return; } - /* Set this flag now even if we don't actually maximize yet, eventually - * ShowWindow will take care of it along with the other window state. - */ - window->flags |= SDL_WINDOW_MAXIMIZED; - #ifdef HAVE_LIBDECOR_H if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) { if (wind->shell_surface.libdecor.frame == NULL) { @@ -2057,6 +2032,8 @@ void Wayland_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window) SDL_VideoData *viddata = _this->driverdata; SDL_WindowData *wind = window->driverdata; + /* Maximized and minimized flags are mutually exclusive */ + window->flags &= ~SDL_WINDOW_MAXIMIZED; window->flags |= SDL_WINDOW_MINIMIZED; #ifdef HAVE_LIBDECOR_H diff --git a/external/sdl/SDL/src/video/wayland/SDL_waylandwindow.h b/external/sdl/SDL/src/video/wayland/SDL_waylandwindow.h index 98dc8b75..dc13561a 100644 --- a/external/sdl/SDL/src/video/wayland/SDL_waylandwindow.h +++ b/external/sdl/SDL/src/video/wayland/SDL_waylandwindow.h @@ -123,6 +123,7 @@ struct SDL_WindowData SDL_DisplayID last_displayID; SDL_bool floating; SDL_bool suspended; + SDL_bool active; SDL_bool is_fullscreen; SDL_bool in_fullscreen_transition; SDL_bool fullscreen_was_positioned; diff --git a/external/sdl/SDL/src/video/windows/SDL_windowsvideo.c b/external/sdl/SDL/src/video/windows/SDL_windowsvideo.c index 52bbb8e6..66ad8e9b 100644 --- a/external/sdl/SDL/src/video/windows/SDL_windowsvideo.c +++ b/external/sdl/SDL/src/video/windows/SDL_windowsvideo.c @@ -22,6 +22,9 @@ #ifdef SDL_VIDEO_DRIVER_WINDOWS +#ifdef SDL_VIDEO_VULKAN +#include "../SDL_vulkan_internal.h" +#endif #include "../SDL_sysvideo.h" #include "../SDL_pixels_c.h" #include "../../SDL_hints_c.h" @@ -208,6 +211,7 @@ static SDL_VideoDevice *WIN_CreateDevice(void) device->AcceptDragAndDrop = WIN_AcceptDragAndDrop; device->FlashWindow = WIN_FlashWindow; device->ShowWindowSystemMenu = WIN_ShowWindowSystemMenu; + device->SetWindowFocusable = WIN_SetWindowFocusable; device->shape_driver.CreateShaper = Win32_CreateShaper; device->shape_driver.SetWindowShape = Win32_SetWindowShape; diff --git a/external/sdl/SDL/src/video/windows/SDL_windowsvulkan.c b/external/sdl/SDL/src/video/windows/SDL_windowsvulkan.c index 060e2ac0..f0b288b4 100644 --- a/external/sdl/SDL/src/video/windows/SDL_windowsvulkan.c +++ b/external/sdl/SDL/src/video/windows/SDL_windowsvulkan.c @@ -28,6 +28,8 @@ #if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_WINDOWS) +#include "../SDL_vulkan_internal.h" + #include "SDL_windowsvideo.h" #include "SDL_windowswindow.h" diff --git a/external/sdl/SDL/src/video/windows/SDL_windowsvulkan.h b/external/sdl/SDL/src/video/windows/SDL_windowsvulkan.h index 707e9d48..17b69687 100644 --- a/external/sdl/SDL/src/video/windows/SDL_windowsvulkan.h +++ b/external/sdl/SDL/src/video/windows/SDL_windowsvulkan.h @@ -29,11 +29,11 @@ #ifndef SDL_windowsvulkan_h_ #define SDL_windowsvulkan_h_ +#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_WINDOWS) + #include "../SDL_vulkan_internal.h" #include "../SDL_sysvideo.h" -#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_WINDOWS) - int WIN_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path); void WIN_Vulkan_UnloadLibrary(SDL_VideoDevice *_this); SDL_bool WIN_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, diff --git a/external/sdl/SDL/src/video/windows/SDL_windowswindow.c b/external/sdl/SDL/src/video/windows/SDL_windowswindow.c index 6218078e..159a7abd 100644 --- a/external/sdl/SDL/src/video/windows/SDL_windowswindow.c +++ b/external/sdl/SDL/src/video/windows/SDL_windowswindow.c @@ -46,6 +46,22 @@ #endif typedef HRESULT (WINAPI *DwmSetWindowAttribute_t)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute); +/* Transparent window support */ +#ifndef DWM_BB_ENABLE +#define DWM_BB_ENABLE 0x00000001 +#endif +#ifndef DWM_BB_BLURREGION +#define DWM_BB_BLURREGION 0x00000002 +#endif +typedef struct +{ + DWORD flags; + BOOL enable; + HRGN blur_region; + BOOL transition_on_maxed; +} DWM_BLURBEHIND; +typedef HRESULT(WINAPI *DwmEnableBlurBehindWindow_t)(HWND hwnd, const DWM_BLURBEHIND *pBlurBehind); + /* Windows CE compatibility */ #ifndef SWP_NOCOPYBITS #define SWP_NOCOPYBITS 0 @@ -128,10 +144,11 @@ static DWORD GetWindowStyleEx(SDL_Window *window) { DWORD style = 0; - if (SDL_WINDOW_IS_POPUP(window)) { - style = WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE; - } else if (window->flags & SDL_WINDOW_UTILITY) { - style = WS_EX_TOOLWINDOW; + if (SDL_WINDOW_IS_POPUP(window) || (window->flags & SDL_WINDOW_UTILITY)) { + style |= WS_EX_TOOLWINDOW; + } + if (SDL_WINDOW_IS_POPUP(window) || (window->flags & SDL_WINDOW_NOT_FOCUSABLE)) { + style |= WS_EX_NOACTIVATE; } return style; } @@ -173,23 +190,9 @@ static int WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL m if (WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice())) { /* With per-monitor v2, the window border/titlebar size depend on the DPI, so we need to call AdjustWindowRectExForDpi instead of AdjustWindowRectEx. */ - UINT unused; - RECT screen_rect; - HMONITOR mon; - - screen_rect.left = *x; - screen_rect.top = *y; - screen_rect.right = (LONG)*x + *width; - screen_rect.bottom = (LONG)*y + *height; - - mon = MonitorFromRect(&screen_rect, MONITOR_DEFAULTTONEAREST); - if (videodata != NULL) { - /* GetDpiForMonitor docs promise to return the same hdpi / vdpi */ - if (videodata->GetDpiForMonitor(mon, MDT_EFFECTIVE_DPI, &frame_dpi, &unused) != S_OK) { - frame_dpi = 96; - } - + SDL_WindowData *data = window->driverdata; + frame_dpi = (data && videodata->GetDpiForWindow) ? videodata->GetDpiForWindow(data->hwnd) : 96; if (videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, frame_dpi) == 0) { return WIN_SetError("AdjustWindowRectExForDpi()"); } @@ -583,6 +586,25 @@ int WIN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window) ShowWindow(hwnd, SW_SHOWMINNOACTIVE); } + /* FIXME: does not work on all hardware configurations with different renders (i.e. hybrid GPUs) */ + if (window->flags & SDL_WINDOW_TRANSPARENT) { + void *handle = SDL_LoadObject("dwmapi.dll"); + if (handle) { + DwmEnableBlurBehindWindow_t DwmEnableBlurBehindWindowFunc = (DwmEnableBlurBehindWindow_t)SDL_LoadFunction(handle, "DwmEnableBlurBehindWindow"); + if (DwmEnableBlurBehindWindowFunc) { + /* The region indicates which part of the window will be blurred and rest will be transparent. This + is because the alpha value of the window will be used for non-blurred areas + We can use (-1, -1, 0, 0) boundary to make sure no pixels are being blurred + */ + HRGN rgn = CreateRectRgn(-1, -1, 0, 0); + DWM_BLURBEHIND bb = {DWM_BB_ENABLE | DWM_BB_BLURREGION, TRUE, rgn, FALSE}; + DwmEnableBlurBehindWindowFunc(hwnd, &bb); + DeleteObject(rgn); + } + SDL_UnloadObject(handle); + } + } + if (!(window->flags & SDL_WINDOW_OPENGL)) { return 0; } @@ -1504,6 +1526,31 @@ void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y) ClientToScreen(data->hwnd, &pt); SendMessage(data->hwnd, WM_POPUPSYSTEMMENU, 0, MAKELPARAM(pt.x, pt.y)); } + +int WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable) +{ + SDL_WindowData *data = window->driverdata; + HWND hwnd = data->hwnd; + const LONG style = GetWindowLong(hwnd, GWL_EXSTYLE); + + SDL_assert(style != 0); + + if (focusable) { + if (style & WS_EX_NOACTIVATE) { + if (SetWindowLong(hwnd, GWL_EXSTYLE, style & ~WS_EX_NOACTIVATE) == 0) { + return WIN_SetError("SetWindowLong()"); + } + } + } else { + if (!(style & WS_EX_NOACTIVATE)) { + if (SetWindowLong(hwnd, GWL_EXSTYLE, style | WS_EX_NOACTIVATE) == 0) { + return WIN_SetError("SetWindowLong()"); + } + } + } + + return 0; +} #endif /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/ void WIN_UpdateDarkModeForHWND(HWND hwnd) diff --git a/external/sdl/SDL/src/video/windows/SDL_windowswindow.h b/external/sdl/SDL/src/video/windows/SDL_windowswindow.h index f6f0331a..527fee78 100644 --- a/external/sdl/SDL/src/video/windows/SDL_windowswindow.h +++ b/external/sdl/SDL/src/video/windows/SDL_windowswindow.h @@ -109,6 +109,7 @@ extern int WIN_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Flash extern void WIN_UpdateDarkModeForHWND(HWND hwnd); extern int WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags); extern void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y); +extern int WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable); /* Ends C function definitions when using C++ */ #ifdef __cplusplus diff --git a/external/sdl/SDL/src/video/x11/SDL_x11clipboard.c b/external/sdl/SDL/src/video/x11/SDL_x11clipboard.c index 30b48e8e..3bec6709 100644 --- a/external/sdl/SDL/src/video/x11/SDL_x11clipboard.c +++ b/external/sdl/SDL/src/video/x11/SDL_x11clipboard.c @@ -92,9 +92,7 @@ static int SetSelectionData(SDL_VideoDevice *_this, Atom selection, SDL_Clipboar clipboard->mime_count = mime_count; clipboard->sequence = sequence; - if (!clipboard_owner) { - X11_XSetSelectionOwner(display, selection, window, CurrentTime); - } + X11_XSetSelectionOwner(display, selection, window, CurrentTime); return 0; } diff --git a/external/sdl/SDL/src/video/x11/SDL_x11events.c b/external/sdl/SDL/src/video/x11/SDL_x11events.c index d70dcda0..d414ee66 100644 --- a/external/sdl/SDL/src/video/x11/SDL_x11events.c +++ b/external/sdl/SDL/src/video/x11/SDL_x11events.c @@ -1579,12 +1579,19 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) } } - if (changed & SDL_WINDOW_MAXIMIZED) { - if (flags & SDL_WINDOW_MAXIMIZED) { - SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0); - } else { - SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0); - } + if ((changed & SDL_WINDOW_MAXIMIZED) && ((flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) { + SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0); + } + if ((changed & SDL_WINDOW_MINIMIZED) && (flags & SDL_WINDOW_MINIMIZED)) { + SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0); + } + if (((changed & SDL_WINDOW_MAXIMIZED) || (changed & SDL_WINDOW_MINIMIZED)) && + (!(flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) { + SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0); + } + + if (changed & SDL_WINDOW_OCCLUDED) { + SDL_SendWindowEvent(data->window, (flags & SDL_WINDOW_OCCLUDED) ? SDL_EVENT_WINDOW_OCCLUDED : SDL_EVENT_WINDOW_EXPOSED, 0, 0); } } else if (xevent->xproperty.atom == videodata->XKLAVIER_STATE) { /* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify diff --git a/external/sdl/SDL/src/video/x11/SDL_x11video.c b/external/sdl/SDL/src/video/x11/SDL_x11video.c index 70cc85a3..4192bf33 100644 --- a/external/sdl/SDL/src/video/x11/SDL_x11video.c +++ b/external/sdl/SDL/src/video/x11/SDL_x11video.c @@ -39,8 +39,6 @@ #include "SDL_x11opengles.h" #endif -#include "SDL_x11vulkan.h" - /* Initialization/Query functions */ static int X11_VideoInit(SDL_VideoDevice *_this); static void X11_VideoQuit(SDL_VideoDevice *_this); @@ -214,6 +212,7 @@ static SDL_VideoDevice *X11_CreateDevice(void) device->AcceptDragAndDrop = X11_AcceptDragAndDrop; device->FlashWindow = X11_FlashWindow; device->ShowWindowSystemMenu = X11_ShowWindowSystemMenu; + device->SetWindowFocusable = X11_SetWindowFocusable; #ifdef SDL_VIDEO_DRIVER_X11_XFIXES device->SetWindowMouseRect = X11_SetWindowMouseRect; diff --git a/external/sdl/SDL/src/video/x11/SDL_x11video.h b/external/sdl/SDL/src/video/x11/SDL_x11video.h index 2f6590da..09efd645 100644 --- a/external/sdl/SDL/src/video/x11/SDL_x11video.h +++ b/external/sdl/SDL/src/video/x11/SDL_x11video.h @@ -25,30 +25,6 @@ #include "../SDL_sysvideo.h" -#include -#include -#include -#include - -#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR -#include -#endif -#ifdef SDL_VIDEO_DRIVER_X11_XDBE -#include -#endif -#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 -#include -#endif -#ifdef SDL_VIDEO_DRIVER_X11_XRANDR -#include -#endif -#ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER -#include -#endif -#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE -#include -#endif - #include "../../core/linux/SDL_dbus.h" #include "../../core/linux/SDL_ime.h" diff --git a/external/sdl/SDL/src/video/x11/SDL_x11vulkan.c b/external/sdl/SDL/src/video/x11/SDL_x11vulkan.c index e04c0072..b81b9804 100644 --- a/external/sdl/SDL/src/video/x11/SDL_x11vulkan.c +++ b/external/sdl/SDL/src/video/x11/SDL_x11vulkan.c @@ -22,6 +22,8 @@ #if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_X11) +#include "../SDL_vulkan_internal.h" + #include "SDL_x11video.h" #include "SDL_x11vulkan.h" diff --git a/external/sdl/SDL/src/video/x11/SDL_x11vulkan.h b/external/sdl/SDL/src/video/x11/SDL_x11vulkan.h index f9b99101..30b047e0 100644 --- a/external/sdl/SDL/src/video/x11/SDL_x11vulkan.h +++ b/external/sdl/SDL/src/video/x11/SDL_x11vulkan.h @@ -23,11 +23,11 @@ #ifndef SDL_x11vulkan_h_ #define SDL_x11vulkan_h_ -#include "../SDL_vulkan_internal.h" +#include #if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_X11) -/*typedef struct xcb_connection_t xcb_connection_t;*/ +typedef struct xcb_connection_t xcb_connection_t; typedef xcb_connection_t *(*PFN_XGetXCBConnection)(Display *dpy); int X11_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path); diff --git a/external/sdl/SDL/src/video/x11/SDL_x11window.c b/external/sdl/SDL/src/video/x11/SDL_x11window.c index 2eb231bc..a1c5bb82 100644 --- a/external/sdl/SDL/src/video/x11/SDL_x11window.c +++ b/external/sdl/SDL/src/video/x11/SDL_x11window.c @@ -238,7 +238,7 @@ Uint32 X11_GetNetWMState(SDL_VideoDevice *_this, SDL_Window *window, Window xwin for (i = 0; i < numItems; ++i) { if (atoms[i] == _NET_WM_STATE_HIDDEN) { - flags |= SDL_WINDOW_HIDDEN; + flags |= SDL_WINDOW_MINIMIZED | SDL_WINDOW_OCCLUDED; } else if (atoms[i] == _NET_WM_STATE_FOCUSED) { flags |= SDL_WINDOW_INPUT_FOCUS; } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_VERT) { @@ -604,8 +604,14 @@ int X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window) return SDL_SetError("Couldn't create window"); } - SetWindowBordered(display, screen, w, - !(window->flags & SDL_WINDOW_BORDERLESS)); + /* Do not set borderless window if in desktop fullscreen, this causes + flickering in multi-monitor setups */ + if (!((window->pending_flags & SDL_WINDOW_FULLSCREEN) && + (window->flags & SDL_WINDOW_BORDERLESS) && + !window->fullscreen_exclusive)) { + SetWindowBordered(display, screen, w, + !(window->flags & SDL_WINDOW_BORDERLESS)); + } sizehints = X11_XAllocSizeHints(); /* Setup the normal size hints */ @@ -623,7 +629,7 @@ int X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window) /* Setup the input hints so we get keyboard input */ wmhints = X11_XAllocWMHints(); - wmhints->input = True; + wmhints->input = !(window->flags & SDL_WINDOW_NOT_FOCUSABLE) ? True : False; wmhints->window_group = data->window_group; wmhints->flags = InputHint | WindowGroupHint; @@ -1982,4 +1988,24 @@ void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y) X11_XFlush(display); } +int X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable) +{ + SDL_WindowData *data = window->driverdata; + Display *display = data->videodata->display; + XWMHints *wmhints; + + wmhints = X11_XGetWMHints(display, data->xwindow); + if (wmhints == NULL) { + return SDL_SetError("Couldn't get WM hints"); + } + + wmhints->input = focusable ? True : False; + wmhints->flags |= InputHint; + + X11_XSetWMHints(display, data->xwindow, wmhints); + X11_XFree(wmhints); + + return 0; +} + #endif /* SDL_VIDEO_DRIVER_X11 */ diff --git a/external/sdl/SDL/src/video/x11/SDL_x11window.h b/external/sdl/SDL/src/video/x11/SDL_x11window.h index ff6b98ba..b9d6ab0d 100644 --- a/external/sdl/SDL/src/video/x11/SDL_x11window.h +++ b/external/sdl/SDL/src/video/x11/SDL_x11window.h @@ -116,6 +116,7 @@ extern int X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled); extern void X11_AcceptDragAndDrop(SDL_Window *window, SDL_bool accept); extern int X11_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation); extern void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y); +extern int X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable); int SDL_X11_SetWindowTitle(Display *display, Window xwindow, char *title); void X11_UpdateWindowPosition(SDL_Window *window); diff --git a/external/sdl/SDL/src/video/yuv2rgb/yuv_rgb.c b/external/sdl/SDL/src/video/yuv2rgb/yuv_rgb.c index 30c4c0c4..bf400201 100644 --- a/external/sdl/SDL/src/video/yuv2rgb/yuv_rgb.c +++ b/external/sdl/SDL/src/video/yuv2rgb/yuv_rgb.c @@ -241,6 +241,7 @@ void rgb24_yuv420_std( #ifdef SDL_SSE2_INTRINSICS +/* SDL doesn't use these atm and compiling them adds seconds onto the build. --ryan. #define SSE_FUNCTION_NAME yuv420_rgb565_sse #define STD_FUNCTION_NAME yuv420_rgb565_std #define YUV_FORMAT YUV_FORMAT_420 @@ -248,12 +249,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuv420_rgb565_sseu -#define STD_FUNCTION_NAME yuv420_rgb565_std -#define YUV_FORMAT YUV_FORMAT_420 -#define RGB_FORMAT RGB_FORMAT_RGB565 -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuv420_rgb24_sse #define STD_FUNCTION_NAME yuv420_rgb24_std #define YUV_FORMAT YUV_FORMAT_420 @@ -261,12 +256,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuv420_rgb24_sseu -#define STD_FUNCTION_NAME yuv420_rgb24_std -#define YUV_FORMAT YUV_FORMAT_420 -#define RGB_FORMAT RGB_FORMAT_RGB24 -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuv420_rgba_sse #define STD_FUNCTION_NAME yuv420_rgba_std #define YUV_FORMAT YUV_FORMAT_420 @@ -274,12 +263,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuv420_rgba_sseu -#define STD_FUNCTION_NAME yuv420_rgba_std -#define YUV_FORMAT YUV_FORMAT_420 -#define RGB_FORMAT RGB_FORMAT_RGBA -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuv420_bgra_sse #define STD_FUNCTION_NAME yuv420_bgra_std #define YUV_FORMAT YUV_FORMAT_420 @@ -287,12 +270,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuv420_bgra_sseu -#define STD_FUNCTION_NAME yuv420_bgra_std -#define YUV_FORMAT YUV_FORMAT_420 -#define RGB_FORMAT RGB_FORMAT_BGRA -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuv420_argb_sse #define STD_FUNCTION_NAME yuv420_argb_std #define YUV_FORMAT YUV_FORMAT_420 @@ -300,12 +277,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuv420_argb_sseu -#define STD_FUNCTION_NAME yuv420_argb_std -#define YUV_FORMAT YUV_FORMAT_420 -#define RGB_FORMAT RGB_FORMAT_ARGB -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuv420_abgr_sse #define STD_FUNCTION_NAME yuv420_abgr_std #define YUV_FORMAT YUV_FORMAT_420 @@ -313,12 +284,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuv420_abgr_sseu -#define STD_FUNCTION_NAME yuv420_abgr_std -#define YUV_FORMAT YUV_FORMAT_420 -#define RGB_FORMAT RGB_FORMAT_ABGR -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuv422_rgb565_sse #define STD_FUNCTION_NAME yuv422_rgb565_std #define YUV_FORMAT YUV_FORMAT_422 @@ -326,12 +291,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuv422_rgb565_sseu -#define STD_FUNCTION_NAME yuv422_rgb565_std -#define YUV_FORMAT YUV_FORMAT_422 -#define RGB_FORMAT RGB_FORMAT_RGB565 -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuv422_rgb24_sse #define STD_FUNCTION_NAME yuv422_rgb24_std #define YUV_FORMAT YUV_FORMAT_422 @@ -339,12 +298,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuv422_rgb24_sseu -#define STD_FUNCTION_NAME yuv422_rgb24_std -#define YUV_FORMAT YUV_FORMAT_422 -#define RGB_FORMAT RGB_FORMAT_RGB24 -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuv422_rgba_sse #define STD_FUNCTION_NAME yuv422_rgba_std #define YUV_FORMAT YUV_FORMAT_422 @@ -352,12 +305,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuv422_rgba_sseu -#define STD_FUNCTION_NAME yuv422_rgba_std -#define YUV_FORMAT YUV_FORMAT_422 -#define RGB_FORMAT RGB_FORMAT_RGBA -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuv422_bgra_sse #define STD_FUNCTION_NAME yuv422_bgra_std #define YUV_FORMAT YUV_FORMAT_422 @@ -365,12 +312,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuv422_bgra_sseu -#define STD_FUNCTION_NAME yuv422_bgra_std -#define YUV_FORMAT YUV_FORMAT_422 -#define RGB_FORMAT RGB_FORMAT_BGRA -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuv422_argb_sse #define STD_FUNCTION_NAME yuv422_argb_std #define YUV_FORMAT YUV_FORMAT_422 @@ -378,12 +319,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuv422_argb_sseu -#define STD_FUNCTION_NAME yuv422_argb_std -#define YUV_FORMAT YUV_FORMAT_422 -#define RGB_FORMAT RGB_FORMAT_ARGB -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuv422_abgr_sse #define STD_FUNCTION_NAME yuv422_abgr_std #define YUV_FORMAT YUV_FORMAT_422 @@ -391,12 +326,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuv422_abgr_sseu -#define STD_FUNCTION_NAME yuv422_abgr_std -#define YUV_FORMAT YUV_FORMAT_422 -#define RGB_FORMAT RGB_FORMAT_ABGR -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuvnv12_rgb565_sse #define STD_FUNCTION_NAME yuvnv12_rgb565_std #define YUV_FORMAT YUV_FORMAT_NV12 @@ -404,12 +333,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuvnv12_rgb565_sseu -#define STD_FUNCTION_NAME yuvnv12_rgb565_std -#define YUV_FORMAT YUV_FORMAT_NV12 -#define RGB_FORMAT RGB_FORMAT_RGB565 -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuvnv12_rgb24_sse #define STD_FUNCTION_NAME yuvnv12_rgb24_std #define YUV_FORMAT YUV_FORMAT_NV12 @@ -417,12 +340,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuvnv12_rgb24_sseu -#define STD_FUNCTION_NAME yuvnv12_rgb24_std -#define YUV_FORMAT YUV_FORMAT_NV12 -#define RGB_FORMAT RGB_FORMAT_RGB24 -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuvnv12_rgba_sse #define STD_FUNCTION_NAME yuvnv12_rgba_std #define YUV_FORMAT YUV_FORMAT_NV12 @@ -430,12 +347,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuvnv12_rgba_sseu -#define STD_FUNCTION_NAME yuvnv12_rgba_std -#define YUV_FORMAT YUV_FORMAT_NV12 -#define RGB_FORMAT RGB_FORMAT_RGBA -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuvnv12_bgra_sse #define STD_FUNCTION_NAME yuvnv12_bgra_std #define YUV_FORMAT YUV_FORMAT_NV12 @@ -443,12 +354,6 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuvnv12_bgra_sseu -#define STD_FUNCTION_NAME yuvnv12_bgra_std -#define YUV_FORMAT YUV_FORMAT_NV12 -#define RGB_FORMAT RGB_FORMAT_BGRA -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuvnv12_argb_sse #define STD_FUNCTION_NAME yuvnv12_argb_std #define YUV_FORMAT YUV_FORMAT_NV12 @@ -456,18 +361,115 @@ void rgb24_yuv420_std( #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" -#define SSE_FUNCTION_NAME yuvnv12_argb_sseu -#define STD_FUNCTION_NAME yuvnv12_argb_std -#define YUV_FORMAT YUV_FORMAT_NV12 -#define RGB_FORMAT RGB_FORMAT_ARGB -#include "yuv_rgb_sse_func.h" - #define SSE_FUNCTION_NAME yuvnv12_abgr_sse #define STD_FUNCTION_NAME yuvnv12_abgr_std #define YUV_FORMAT YUV_FORMAT_NV12 #define RGB_FORMAT RGB_FORMAT_ABGR #define SSE_ALIGNED #include "yuv_rgb_sse_func.h" +*/ + +#define SSE_FUNCTION_NAME yuv420_rgb565_sseu +#define STD_FUNCTION_NAME yuv420_rgb565_std +#define YUV_FORMAT YUV_FORMAT_420 +#define RGB_FORMAT RGB_FORMAT_RGB565 +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuv420_rgb24_sseu +#define STD_FUNCTION_NAME yuv420_rgb24_std +#define YUV_FORMAT YUV_FORMAT_420 +#define RGB_FORMAT RGB_FORMAT_RGB24 +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuv420_rgba_sseu +#define STD_FUNCTION_NAME yuv420_rgba_std +#define YUV_FORMAT YUV_FORMAT_420 +#define RGB_FORMAT RGB_FORMAT_RGBA +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuv420_bgra_sseu +#define STD_FUNCTION_NAME yuv420_bgra_std +#define YUV_FORMAT YUV_FORMAT_420 +#define RGB_FORMAT RGB_FORMAT_BGRA +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuv420_argb_sseu +#define STD_FUNCTION_NAME yuv420_argb_std +#define YUV_FORMAT YUV_FORMAT_420 +#define RGB_FORMAT RGB_FORMAT_ARGB +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuv420_abgr_sseu +#define STD_FUNCTION_NAME yuv420_abgr_std +#define YUV_FORMAT YUV_FORMAT_420 +#define RGB_FORMAT RGB_FORMAT_ABGR +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuv422_rgb565_sseu +#define STD_FUNCTION_NAME yuv422_rgb565_std +#define YUV_FORMAT YUV_FORMAT_422 +#define RGB_FORMAT RGB_FORMAT_RGB565 +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuv422_rgb24_sseu +#define STD_FUNCTION_NAME yuv422_rgb24_std +#define YUV_FORMAT YUV_FORMAT_422 +#define RGB_FORMAT RGB_FORMAT_RGB24 +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuv422_rgba_sseu +#define STD_FUNCTION_NAME yuv422_rgba_std +#define YUV_FORMAT YUV_FORMAT_422 +#define RGB_FORMAT RGB_FORMAT_RGBA +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuv422_bgra_sseu +#define STD_FUNCTION_NAME yuv422_bgra_std +#define YUV_FORMAT YUV_FORMAT_422 +#define RGB_FORMAT RGB_FORMAT_BGRA +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuv422_argb_sseu +#define STD_FUNCTION_NAME yuv422_argb_std +#define YUV_FORMAT YUV_FORMAT_422 +#define RGB_FORMAT RGB_FORMAT_ARGB +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuv422_abgr_sseu +#define STD_FUNCTION_NAME yuv422_abgr_std +#define YUV_FORMAT YUV_FORMAT_422 +#define RGB_FORMAT RGB_FORMAT_ABGR +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuvnv12_rgb565_sseu +#define STD_FUNCTION_NAME yuvnv12_rgb565_std +#define YUV_FORMAT YUV_FORMAT_NV12 +#define RGB_FORMAT RGB_FORMAT_RGB565 +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuvnv12_rgb24_sseu +#define STD_FUNCTION_NAME yuvnv12_rgb24_std +#define YUV_FORMAT YUV_FORMAT_NV12 +#define RGB_FORMAT RGB_FORMAT_RGB24 +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuvnv12_rgba_sseu +#define STD_FUNCTION_NAME yuvnv12_rgba_std +#define YUV_FORMAT YUV_FORMAT_NV12 +#define RGB_FORMAT RGB_FORMAT_RGBA +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuvnv12_bgra_sseu +#define STD_FUNCTION_NAME yuvnv12_bgra_std +#define YUV_FORMAT YUV_FORMAT_NV12 +#define RGB_FORMAT RGB_FORMAT_BGRA +#include "yuv_rgb_sse_func.h" + +#define SSE_FUNCTION_NAME yuvnv12_argb_sseu +#define STD_FUNCTION_NAME yuvnv12_argb_std +#define YUV_FORMAT YUV_FORMAT_NV12 +#define RGB_FORMAT RGB_FORMAT_ARGB +#include "yuv_rgb_sse_func.h" #define SSE_FUNCTION_NAME yuvnv12_abgr_sseu #define STD_FUNCTION_NAME yuvnv12_abgr_std @@ -476,6 +478,7 @@ void rgb24_yuv420_std( #include "yuv_rgb_sse_func.h" +/* SDL doesn't use these atm and compiling them adds seconds onto the build. --ryan. #define UNPACK_RGB24_32_STEP1(RGB1, RGB2, RGB3, RGB4, RGB5, RGB6, R1, R2, G1, G2, B1, B2) \ R1 = _mm_unpacklo_epi8(RGB1, RGB4); \ R2 = _mm_unpackhi_epi8(RGB1, RGB4); \ @@ -515,7 +518,9 @@ V = _mm_add_epi16(_mm_mullo_epi16(R, _mm_set1_epi16(param->matrix[2][0])), \ V = _mm_add_epi16(V, _mm_mullo_epi16(B, _mm_set1_epi16(param->matrix[2][2]))); \ V = _mm_add_epi16(V, _mm_set1_epi16(128< - #ifdef __EMSCRIPTEN__ #include #endif @@ -25,22 +23,10 @@ #include #include -static SDL_Window *window; -static SDL_Renderer *renderer; +static SDLTest_CommonState *state; static SDLTest_TextWindow *textwin; static int done; -/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ -static void -quit(int rc) -{ - SDL_Quit(); - /* Let 'main()' return normally */ - if (rc != 0) { - exit(rc); - } -} - static void print_string(char **text, size_t *maxlen, const char *fmt, ...) { @@ -171,6 +157,7 @@ PrintText(const char *eventtype, const char *text) static void loop(void) { SDL_Event event; + int i; /* Check for events */ /*SDL_WaitEvent(&event); emscripten does not like waiting*/ @@ -234,11 +221,13 @@ static void loop(void) } } - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); - SDL_RenderClear(renderer); - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDLTest_TextWindowDisplay(textwin, renderer); - SDL_RenderPresent(renderer); + for (i = 0; i < state->num_windows; i++) { + SDL_SetRenderDrawColor(state->renderers[i], 0, 0, 0, 255); + SDL_RenderClear(state->renderers[i]); + SDL_SetRenderDrawColor(state->renderers[i], 255, 255, 255, 255); + SDLTest_TextWindowDisplay(textwin, state->renderers[i]); + SDL_RenderPresent(state->renderers[i]); + } /* Slow down framerate */ SDL_Delay(100); @@ -252,13 +241,14 @@ static void loop(void) int main(int argc, char *argv[]) { - SDLTest_CommonState *state; + int w, h; /* Initialize test framework */ - state = SDLTest_CommonCreateState(argv, 0); + state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO); if (state == NULL) { return 1; } + state->window_title = "CheckKeys Test"; /* Enable standard application logging */ SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); @@ -275,31 +265,22 @@ int main(int argc, char *argv[]) SDL_SetHint(SDL_HINT_IME_SUPPORT_EXTENDED_TEXT, "1"); /* Initialize SDL */ - if (SDL_Init(SDL_INIT_VIDEO) < 0) { + if (!SDLTest_CommonInit(state)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); return 1; } - /* Set 640x480 video mode */ - window = SDL_CreateWindow("CheckKeys Test", 640, 480, 0); - if (window == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create 640x480 window: %s\n", - SDL_GetError()); - quit(2); - } - - renderer = SDL_CreateRenderer(window, NULL, 0); - if (renderer == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", - SDL_GetError()); - quit(2); - } - - textwin = SDLTest_TextWindowCreate(0, 0, 640, 480); + SDL_GetWindowSize(state->windows[0], &w, &h); + textwin = SDLTest_TextWindowCreate(0.f, 0.f, (float)w, (float)h); #ifdef __IOS__ - /* Creating the context creates the view, which we need to show keyboard */ - SDL_GL_CreateContext(window); + { + int i; + /* Creating the context creates the view, which we need to show keyboard */ + for (i = 0; i < state->num_windows; i++) { + SDL_GL_CreateContext(state->windows[i]); + } + } #endif SDL_StartTextInput(); @@ -319,7 +300,8 @@ int main(int argc, char *argv[]) } #endif - SDL_Quit(); - SDLTest_CommonDestroyState(state); + SDLTest_TextWindowDestroy(textwin); + SDLTest_CleanupTextDrawing(); + SDLTest_CommonQuit(state); return 0; } diff --git a/external/sdl/SDL/test/checkkeysthreads.c b/external/sdl/SDL/test/checkkeysthreads.c index 89d5188d..811584c0 100644 --- a/external/sdl/SDL/test/checkkeysthreads.c +++ b/external/sdl/SDL/test/checkkeysthreads.c @@ -303,6 +303,9 @@ int main(int argc, char *argv[]) } #endif + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_WaitThread(thread, NULL); SDL_Quit(); SDLTest_CommonDestroyState(state); diff --git a/external/sdl/SDL/test/emscripten/server.py b/external/sdl/SDL/test/emscripten/server.py index be751725..7787e3b9 100755 --- a/external/sdl/SDL/test/emscripten/server.py +++ b/external/sdl/SDL/test/emscripten/server.py @@ -3,13 +3,15 @@ # Based on http/server.py from Python from argparse import ArgumentParser +import contextlib from http.server import SimpleHTTPRequestHandler +from http.server import ThreadingHTTPServer +import socket from socketserver import TCPServer -def serve_forever(port: int, ServerClass): - handler = SimpleHTTPRequestHandler - handler.extensions_map = { +class MyHTTPRequestHandler(SimpleHTTPRequestHandler): + extensions_map = { ".manifest": "text/cache-manifest", ".html": "text/html", ".png": "image/png", @@ -21,14 +23,25 @@ def serve_forever(port: int, ServerClass): "": "application/octet-stream", } + def end_headers(self): + self.send_my_headers() + SimpleHTTPRequestHandler.end_headers(self) + + def send_my_headers(self): + self.send_header("Cache-Control", "no-cache, no-store, must-revalidate") + self.send_header("Pragma", "no-cache") + self.send_header("Expires", "0") + + +def serve_forever(port: int, ServerClass): + handler = MyHTTPRequestHandler + addr = ("0.0.0.0", port) HandlerClass = SimpleHTTPRequestHandler with ServerClass(addr, handler) as httpd: host, port = httpd.socket.getsockname()[:2] url_host = f"[{host}]" if ":" in host else host - print( - f"Serving HTTP on {host} port {port} (http://{url_host}:{port}/) ..." - ) + print(f"Serving HTTP on {host} port {port} (http://{url_host}:{port}/) ...") try: httpd.serve_forever() except KeyboardInterrupt: @@ -42,16 +55,11 @@ def main(): parser.add_argument("-d", dest="directory", type=str, default=None) args = parser.parse_args() - import contextlib - import http.server - import socket - - class DualStackServer(http.server.ThreadingHTTPServer): + class DualStackServer(ThreadingHTTPServer): def server_bind(self): # suppress exception when protocol is IPv4 with contextlib.suppress(Exception): - self.socket.setsockopt( - socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) return super().server_bind() def finish_request(self, request, client_address): diff --git a/external/sdl/SDL/test/icon.h b/external/sdl/SDL/test/icon.h new file mode 100644 index 00000000..fe5ada5e --- /dev/null +++ b/external/sdl/SDL/test/icon.h @@ -0,0 +1,52 @@ +unsigned char icon_bmp[] = { + 0x42, 0x4d, 0x42, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x6d, 0x0b, 0x00, 0x00, 0x6d, 0x0b, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x22, 0x22, 0x22, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x22, + 0x21, 0x11, 0x11, 0x12, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x12, 0x21, 0x11, 0x11, 0x11, 0x11, 0x12, 0x21, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x22, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x21, 0x11, + 0x22, 0x22, 0x22, 0x22, 0x11, 0x12, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x12, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x22, 0x22, 0x22, + 0x22, 0x21, 0x12, 0x22, 0x22, 0x22, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x22, 0x22, 0x22, 0x22, 0x21, 0x12, 0x22, 0x22, 0x22, 0x22, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x22, 0x22, 0x11, 0x11, 0x22, 0x22, 0x11, 0x11, 0x22, 0x22, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x22, 0x22, 0x11, 0x01, 0x22, 0x22, 0x11, + 0x01, 0x22, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x22, 0x11, + 0x11, 0x22, 0x22, 0x11, 0x11, 0x22, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x12, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x22, 0x22, 0x22, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 +}; +unsigned int icon_bmp_len = 578; diff --git a/external/sdl/SDL/test/loopwave.c b/external/sdl/SDL/test/loopwave.c index e6cc4df1..1fa1b91e 100644 --- a/external/sdl/SDL/test/loopwave.c +++ b/external/sdl/SDL/test/loopwave.c @@ -31,15 +31,14 @@ static struct SDL_AudioSpec spec; Uint8 *sound; /* Pointer to wave data */ Uint32 soundlen; /* Length of wave data */ - Uint32 soundpos; } wave; -static SDL_AudioDeviceID device; static SDL_AudioStream *stream; static void fillerup(void) { - if (SDL_GetAudioStreamAvailable(stream) < (int) ((wave.soundlen / 2))) { + const int minimum = (wave.soundlen / SDL_AUDIO_FRAMESIZE(wave.spec)) / 2; + if (SDL_GetAudioStreamQueued(stream) < minimum) { SDL_PutAudioStreamData(stream, wave.sound, wave.soundlen); } } @@ -58,30 +57,22 @@ quit(int rc) static void close_audio(void) { - if (device != 0) { + if (stream) { SDL_DestroyAudioStream(stream); stream = NULL; - SDL_CloseAudioDevice(device); - device = 0; } } static void open_audio(void) { - device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &wave.spec); - if (!device) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", SDL_GetError()); - SDL_free(wave.sound); - quit(2); - } - stream = SDL_CreateAndBindAudioStream(device, &wave.spec); + stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &wave.spec, NULL, NULL); if (!stream) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create audio stream: %s\n", SDL_GetError()); - SDL_CloseAudioDevice(device); SDL_free(wave.sound); quit(2); } + SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(stream)); } diff --git a/external/sdl/SDL/test/testaudio.c b/external/sdl/SDL/test/testaudio.c index f7401486..94de7847 100644 --- a/external/sdl/SDL/test/testaudio.c +++ b/external/sdl/SDL/test/testaudio.c @@ -10,6 +10,8 @@ #include "testutils.h" #define POOF_LIFETIME 250 +#define VISUALIZER_WIDTH 100 +#define VISUALIZER_HEIGHT 50 typedef struct Texture { @@ -50,6 +52,15 @@ struct Thing SDL_bool iscapture; SDL_AudioSpec spec; Thing *physdev; + SDL_bool visualizer_enabled; + SDL_bool visualizer_updated; + SDL_Texture *visualizer; + SDL_Mutex *postmix_lock; + float *postmix_buffer; + int postmix_buflen; + int postmix_allocated; + SDL_AudioSpec postmix_spec; + SDL_AtomicInt postmix_updated; } logdev; struct { SDL_AudioSpec spec; @@ -98,6 +109,7 @@ static SDLTest_CommonState *state = NULL; static Thing *things = NULL; static char *current_titlebar = NULL; +static Thing *mouseover_thing = NULL; static Thing *droppable_highlighted_thing = NULL; static Thing *dragging_thing = NULL; static int dragging_button = -1; @@ -110,10 +122,16 @@ static Texture *soundboard_texture = NULL; static Texture *soundboard_levels_texture = NULL; static void DestroyTexture(Texture *tex); +static void DestroyThing(Thing *thing); + /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ static void Quit(int rc) { + while (things != NULL) { + DestroyThing(things); /* make sure all the audio devices are closed, etc. */ + } + DestroyTexture(physdev_texture); DestroyTexture(logdev_texture); DestroyTexture(audio_texture); @@ -121,6 +139,7 @@ static void Quit(int rc) DestroyTexture(soundboard_texture); DestroyTexture(soundboard_levels_texture); SDLTest_CommonQuit(state); + /* Let 'main()' return normally */ if (rc != 0) { exit(rc); @@ -193,6 +212,8 @@ static Thing *UpdateMouseOver(const float x, const float y) thing = FindThingAtPoint(x, y); } + mouseover_thing = thing; + if (!thing) { SetDefaultTitleBar(); } else if (thing->titlebar) { @@ -202,7 +223,7 @@ static Thing *UpdateMouseOver(const float x, const float y) return thing; } -static Thing *CreateThing(ThingType what, float x, float y, float z, float w, float h, Texture *texture, char *titlebar) +static Thing *CreateThing(ThingType what, float x, float y, float z, float w, float h, Texture *texture, const char *titlebar) { Thing *last = NULL; Thing *i; @@ -232,7 +253,7 @@ static Thing *CreateThing(ThingType what, float x, float y, float z, float w, fl thing->scale = 1.0f; thing->createticks = SDL_GetTicks(); thing->texture = texture; - thing->titlebar = titlebar; + thing->titlebar = titlebar ? xstrdup(titlebar) : NULL; /* insert in list by Z order (furthest from the "camera" first, so they get drawn over; negative Z is not drawn at all). */ if (things == NULL) { @@ -272,21 +293,40 @@ static void DestroyThing(Thing *thing) return; } + if (mouseover_thing == thing) { + mouseover_thing = NULL; + } + + if (droppable_highlighted_thing == thing) { + droppable_highlighted_thing = NULL; + } + + if (dragging_thing == thing) { + dragging_thing = NULL; + } + switch (thing->what) { case THING_POOF: break; case THING_NULL: break; case THING_TRASHCAN: break; + case THING_LOGDEV: case THING_LOGDEV_CAPTURE: SDL_CloseAudioDevice(thing->data.logdev.devid); + SDL_DestroyTexture(thing->data.logdev.visualizer); + SDL_DestroyMutex(thing->data.logdev.postmix_lock); + SDL_free(thing->data.logdev.postmix_buffer); break; + case THING_PHYSDEV: case THING_PHYSDEV_CAPTURE: SDL_free(thing->data.physdev.name); break; + case THING_WAV: SDL_free(thing->data.wav.buf); break; + case THING_STREAM: SDL_DestroyAudioStream(thing->data.stream.stream); break; @@ -416,12 +456,12 @@ static const char *AudioFmtToString(const SDL_AudioFormat fmt) #define FMTCASE(x) case SDL_AUDIO_##x: return #x FMTCASE(U8); FMTCASE(S8); - FMTCASE(S16LSB); - FMTCASE(S16MSB); - FMTCASE(S32LSB); - FMTCASE(S32MSB); - FMTCASE(F32LSB); - FMTCASE(F32MSB); + FMTCASE(S16LE); + FMTCASE(S16BE); + FMTCASE(S32LE); + FMTCASE(S32BE); + FMTCASE(F32LE); + FMTCASE(F32BE); #undef FMTCASE } return "?"; @@ -513,7 +553,7 @@ static void StreamThing_ontick(Thing *thing, Uint64 now) if (!available || (SDL_GetAudioStreamFormat(thing->data.stream.stream, NULL, &spec) < 0)) { DestroyThingInPoof(thing); } else { - const int ticksleft = (int) ((((Uint64) ((available / (SDL_AUDIO_BITSIZE(spec.format) / 8)) / spec.channels)) * 1000) / spec.freq); + const int ticksleft = (int) ((((Uint64) (available / SDL_AUDIO_FRAMESIZE(spec))) * 1000) / spec.freq); const float pct = thing->data.stream.total_ticks ? (((float) (ticksleft)) / ((float) thing->data.stream.total_ticks)) : 0.0f; thing->progress = 1.0f - pct; } @@ -553,7 +593,7 @@ static void StreamThing_ondrop(Thing *thing, int button, float x, float y) SDL_UnbindAudioStream(thing->data.stream.stream); /* unbind from current device */ if (thing->line_connected_to->what == THING_LOGDEV_CAPTURE) { SDL_FlushAudioStream(thing->data.stream.stream); - thing->data.stream.total_ticks = (int) (((((Uint64) (SDL_GetAudioStreamAvailable(thing->data.stream.stream) / (SDL_AUDIO_BITSIZE(spec->format) / 8))) / spec->channels) * 1000) / spec->freq); + thing->data.stream.total_ticks = (int) ((((Uint64) (SDL_GetAudioStreamAvailable(thing->data.stream.stream) / SDL_AUDIO_FRAMESIZE(*spec))) * 1000) / spec->freq); } } @@ -590,13 +630,13 @@ static void StreamThing_ondraw(Thing *thing, SDL_Renderer *renderer) static Thing *CreateStreamThing(const SDL_AudioSpec *spec, const Uint8 *buf, const Uint32 buflen, const char *fname, const float x, const float y) { static const ThingType can_be_dropped_onto[] = { THING_TRASHCAN, THING_LOGDEV, THING_LOGDEV_CAPTURE, THING_NULL }; - Thing *thing = CreateThing(THING_STREAM, x, y, 0, -1, -1, soundboard_texture, fname ? xstrdup(fname) : NULL); + Thing *thing = CreateThing(THING_STREAM, x, y, 0, -1, -1, soundboard_texture, fname); SDL_Log("Adding audio stream for %s", fname ? fname : "(null)"); thing->data.stream.stream = SDL_CreateAudioStream(spec, spec); if (buf && buflen) { SDL_PutAudioStreamData(thing->data.stream.stream, buf, (int) buflen); SDL_FlushAudioStream(thing->data.stream.stream); - thing->data.stream.total_ticks = (int) (((((Uint64) (SDL_GetAudioStreamAvailable(thing->data.stream.stream) / (SDL_AUDIO_BITSIZE(spec->format) / 8))) / spec->channels) * 1000) / spec->freq); + thing->data.stream.total_ticks = (int) ((((Uint64) (SDL_GetAudioStreamAvailable(thing->data.stream.stream) / SDL_AUDIO_FRAMESIZE(*spec))) * 1000) / spec->freq); } thing->ontick = StreamThing_ontick; thing->ondrag = StreamThing_ondrag; @@ -656,6 +696,7 @@ static Thing *LoadWavThing(const char *fname, float x, float y) SDL_asprintf(&titlebar, "WAV file (\"%s\", %s, %s, %uHz)", nodirs, AudioFmtToString(spec.format), AudioChansToStr(spec.channels), (unsigned int) spec.freq); thing = CreateThing(THING_WAV, x - (audio_texture->w / 2), y - (audio_texture->h / 2), 5, -1, -1, audio_texture, titlebar); + SDL_free(titlebar); SDL_memcpy(&thing->data.wav.spec, &spec, sizeof (SDL_AudioSpec)); thing->data.wav.buf = buf; thing->data.wav.buflen = buflen; @@ -726,8 +767,10 @@ static void DeviceThing_ondrag(Thing *thing, int button, float x, float y) static void SetLogicalDeviceTitlebar(Thing *thing) { SDL_AudioSpec *spec = &thing->data.logdev.spec; - SDL_GetAudioDeviceFormat(thing->data.logdev.devid, spec); - SDL_asprintf(&thing->titlebar, "Logical device #%u (%s, %s, %s, %uHz)", (unsigned int) thing->data.logdev.devid, thing->data.logdev.iscapture ? "CAPTURE" : "OUTPUT", AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq); + int frames = 0; + SDL_GetAudioDeviceFormat(thing->data.logdev.devid, spec, &frames); + SDL_free(thing->titlebar); + SDL_asprintf(&thing->titlebar, "Logical device #%u (%s, %s, %s, %uHz, %d frames)", (unsigned int) thing->data.logdev.devid, thing->data.logdev.iscapture ? "CAPTURE" : "OUTPUT", AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames); } static void LogicalDeviceThing_ondrop(Thing *thing, int button, float x, float y) @@ -739,6 +782,135 @@ static void LogicalDeviceThing_ondrop(Thing *thing, int button, float x, float y } } +static void SDLCALL PostmixCallback(void *userdata, const SDL_AudioSpec *spec, float *buffer, int buflen) +{ + Thing *thing = (Thing *) userdata; + + SDL_LockMutex(thing->data.logdev.postmix_lock); + + if (thing->data.logdev.postmix_allocated < buflen) { + void *ptr = SDL_realloc(thing->data.logdev.postmix_buffer, buflen); + if (!ptr) { + SDL_UnlockMutex(thing->data.logdev.postmix_lock); + return; /* oh well. */ + } + thing->data.logdev.postmix_buffer = (float *) ptr; + thing->data.logdev.postmix_allocated = buflen; + } + + SDL_copyp(&thing->data.logdev.postmix_spec, spec); + SDL_memcpy(thing->data.logdev.postmix_buffer, buffer, buflen); + thing->data.logdev.postmix_buflen = buflen; + SDL_AtomicSet(&thing->data.logdev.postmix_updated, 1); + + SDL_UnlockMutex(thing->data.logdev.postmix_lock); +} + +static void UpdateVisualizer(SDL_Renderer *renderer, SDL_Texture *visualizer, const int channels, const float *buffer, const int buflen) +{ + static const SDL_Color channel_colors[8] = { + { 255, 255, 255, 255 }, + { 255, 0, 0, 255 }, + { 0, 255, 0, 255 }, + { 0, 0, 255, 255 }, + { 255, 255, 0, 255 }, + { 0, 255, 255, 255 }, + { 255, 0, 255, 255 }, + { 127, 127, 127, 255 } + }; + + SDL_SetRenderTarget(renderer, visualizer); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + + if (buffer && buflen) { + const int frames = (buflen / sizeof (float)) / channels; + const int skip = frames / (VISUALIZER_WIDTH * 2); + int i, j; + + for (i = channels - 1; i >= 0; i--) { + const SDL_Color *color = &channel_colors[i % SDL_arraysize(channel_colors)]; + SDL_FPoint points[VISUALIZER_WIDTH + 2]; + float prevx = 0.0f; + int pointidx = 1; + + points[0].x = 0.0f; + points[0].y = VISUALIZER_HEIGHT * 0.5f; + + for (j = 0; j < (SDL_arraysize(points)-1); j++) { + const float val = buffer[((j * skip) * channels) + i]; + const float x = prevx + 2; + const float y = (VISUALIZER_HEIGHT * 0.5f) - (VISUALIZER_HEIGHT * (val * 0.5f)); + SDL_assert(pointidx < SDL_arraysize(points)); + points[pointidx].x = x; + points[pointidx].y = y; + pointidx++; + prevx = x; + } + + SDL_SetRenderDrawColor(renderer, color->r, color->g, color->b, 255); + SDL_RenderLines(renderer, points, pointidx); + } + } + + SDL_SetRenderTarget(renderer, NULL); +} + +static void LogicalDeviceThing_ontick(Thing *thing, Uint64 now) +{ + const SDL_bool ismousedover = (thing == mouseover_thing) ? SDL_TRUE : SDL_FALSE; + + if (!thing->data.logdev.visualizer || !thing->data.logdev.postmix_lock) { /* need these to work, skip if they failed. */ + return; + } + + if (thing->data.logdev.visualizer_enabled != ismousedover) { + thing->data.logdev.visualizer_enabled = ismousedover; + if (!ismousedover) { + SDL_SetAudioPostmixCallback(thing->data.logdev.devid, NULL, NULL); + } else { + if (thing->data.logdev.postmix_buffer) { + SDL_memset(thing->data.logdev.postmix_buffer, '\0', thing->data.logdev.postmix_buflen); + } + SDL_AtomicSet(&thing->data.logdev.postmix_updated, 1); /* so this will at least clear the texture later. */ + SDL_SetAudioPostmixCallback(thing->data.logdev.devid, PostmixCallback, thing); + } + } +} + +static void LogicalDeviceThing_ondraw(Thing *thing, SDL_Renderer *renderer) +{ + if (thing->data.logdev.visualizer_enabled) { + SDL_FRect dst; + dst.w = thing->rect.w; + dst.h = thing->rect.h; + dst.x = thing->rect.x + ((thing->rect.w - dst.w) / 2); + dst.y = thing->rect.y + ((thing->rect.h - dst.h) / 2); + + if (SDL_AtomicGet(&thing->data.logdev.postmix_updated)) { + float *buffer; + int channels; + int buflen; + + SDL_LockMutex(thing->data.logdev.postmix_lock); + channels = thing->data.logdev.postmix_spec.channels; + buflen = thing->data.logdev.postmix_buflen; + buffer = (float *) SDL_malloc(thing->data.logdev.postmix_buflen); + if (buffer) { + SDL_memcpy(buffer, thing->data.logdev.postmix_buffer, thing->data.logdev.postmix_buflen); + SDL_AtomicSet(&thing->data.logdev.postmix_updated, 0); + } + SDL_UnlockMutex(thing->data.logdev.postmix_lock); + + UpdateVisualizer(renderer, thing->data.logdev.visualizer, channels, buffer, buflen); + SDL_free(buffer); + } + + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 30); + SDL_RenderTexture(renderer, thing->data.logdev.visualizer, NULL, &dst); + } +} + static Thing *CreateLogicalDeviceThing(Thing *parent, const SDL_AudioDeviceID which, const float x, const float y) { static const ThingType can_be_dropped_onto[] = { THING_TRASHCAN, THING_NULL }; @@ -751,9 +923,16 @@ static Thing *CreateLogicalDeviceThing(Thing *parent, const SDL_AudioDeviceID wh thing->data.logdev.devid = which; thing->data.logdev.iscapture = iscapture; thing->data.logdev.physdev = physthing; + thing->data.logdev.visualizer = SDL_CreateTexture(state->renderers[0], SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, VISUALIZER_WIDTH, VISUALIZER_HEIGHT); + thing->data.logdev.postmix_lock = SDL_CreateMutex(); + if (thing->data.logdev.visualizer) { + SDL_SetTextureBlendMode(thing->data.logdev.visualizer, SDL_BLENDMODE_BLEND); + } thing->line_connected_to = physthing; + thing->ontick = LogicalDeviceThing_ontick; thing->ondrag = DeviceThing_ondrag; thing->ondrop = LogicalDeviceThing_ondrop; + thing->ondraw = LogicalDeviceThing_ondraw; thing->can_be_dropped_onto = can_be_dropped_onto; SetLogicalDeviceTitlebar(thing); @@ -762,14 +941,16 @@ static Thing *CreateLogicalDeviceThing(Thing *parent, const SDL_AudioDeviceID wh static void SetPhysicalDeviceTitlebar(Thing *thing) { + int frames = 0; SDL_AudioSpec *spec = &thing->data.physdev.spec; - SDL_GetAudioDeviceFormat(thing->data.physdev.devid, spec); + SDL_GetAudioDeviceFormat(thing->data.physdev.devid, spec, &frames); + SDL_free(thing->titlebar); if (thing->data.physdev.devid == SDL_AUDIO_DEVICE_DEFAULT_CAPTURE) { - SDL_asprintf(&thing->titlebar, "Default system device (CAPTURE, %s, %s, %uHz)", AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq); + SDL_asprintf(&thing->titlebar, "Default system device (CAPTURE, %s, %s, %uHz, %d frames)", AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames); } else if (thing->data.physdev.devid == SDL_AUDIO_DEVICE_DEFAULT_OUTPUT) { - SDL_asprintf(&thing->titlebar, "Default system device (OUTPUT, %s, %s, %uHz)", AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq); + SDL_asprintf(&thing->titlebar, "Default system device (OUTPUT, %s, %s, %uHz, %d frames)", AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames); } else { - SDL_asprintf(&thing->titlebar, "Physical device #%u (%s, \"%s\", %s, %s, %uHz)", (unsigned int) thing->data.physdev.devid, thing->data.physdev.iscapture ? "CAPTURE" : "OUTPUT", thing->data.physdev.name, AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq); + SDL_asprintf(&thing->titlebar, "Physical device #%u (%s, \"%s\", %s, %s, %uHz, %d frames)", (unsigned int) thing->data.physdev.devid, thing->data.physdev.iscapture ? "CAPTURE" : "OUTPUT", thing->data.physdev.name, AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames); } } @@ -965,7 +1146,7 @@ static void Loop(void) case SDL_EVENT_AUDIO_DEVICE_ADDED: CreatePhysicalDeviceThing(event.adevice.which, event.adevice.iscapture); break; - + case SDL_EVENT_AUDIO_DEVICE_REMOVED: { const SDL_AudioDeviceID which = event.adevice.which; Thing *i, *next; diff --git a/external/sdl/SDL/test/testaudiocapture.c b/external/sdl/SDL/test/testaudiocapture.c index fd83d29f..efc4be10 100644 --- a/external/sdl/SDL/test/testaudiocapture.c +++ b/external/sdl/SDL/test/testaudiocapture.c @@ -28,8 +28,8 @@ static int done = 0; static void loop(void) { - const SDL_AudioDeviceID devid_in = SDL_GetAudioStreamBinding(stream_in); - const SDL_AudioDeviceID devid_out = SDL_GetAudioStreamBinding(stream_out); + const SDL_AudioDeviceID devid_in = SDL_GetAudioStreamDevice(stream_in); + const SDL_AudioDeviceID devid_out = SDL_GetAudioStreamDevice(stream_out); SDL_bool please_quit = SDL_FALSE; SDL_Event e; @@ -54,7 +54,7 @@ static void loop(void) } } - if (!SDL_IsAudioDevicePaused(devid_in)) { + if (!SDL_AudioDevicePaused(devid_in)) { SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); } else { SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); @@ -179,12 +179,16 @@ int main(int argc, char **argv) exit(1); } SDL_PauseAudioDevice(device); - SDL_GetAudioDeviceFormat(device, &outspec); - stream_out = SDL_CreateAndBindAudioStream(device, &outspec); + SDL_GetAudioDeviceFormat(device, &outspec, NULL); + stream_out = SDL_CreateAudioStream(&outspec, &outspec); if (!stream_out) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create an audio stream for playback: %s!\n", SDL_GetError()); SDL_Quit(); exit(1); + } else if (SDL_BindAudioStream(device, stream_out) == -1) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't bind an audio stream for playback: %s!\n", SDL_GetError()); + SDL_Quit(); + exit(1); } SDL_Log("Opening capture device %s%s%s...\n", @@ -199,12 +203,16 @@ int main(int argc, char **argv) exit(1); } SDL_PauseAudioDevice(device); - SDL_GetAudioDeviceFormat(device, &inspec); - stream_in = SDL_CreateAndBindAudioStream(device, &inspec); + SDL_GetAudioDeviceFormat(device, &inspec, NULL); + stream_in = SDL_CreateAudioStream(&inspec, &inspec); if (!stream_in) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create an audio stream for capture: %s!\n", SDL_GetError()); SDL_Quit(); exit(1); + } else if (SDL_BindAudioStream(device, stream_in) == -1) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't bind an audio stream for capture: %s!\n", SDL_GetError()); + SDL_Quit(); + exit(1); } SDL_SetAudioStreamFormat(stream_in, NULL, &outspec); /* make sure we output at the playback format. */ diff --git a/external/sdl/SDL/test/testaudiohotplug.c b/external/sdl/SDL/test/testaudiohotplug.c index b919aa37..6f319848 100644 --- a/external/sdl/SDL/test/testaudiohotplug.c +++ b/external/sdl/SDL/test/testaudiohotplug.c @@ -69,7 +69,7 @@ static void iteration(void) done = 1; } } else if (e.type == SDL_EVENT_AUDIO_DEVICE_ADDED) { - const SDL_AudioDeviceID which = (SDL_AudioDeviceID ) e.adevice.which; + const SDL_AudioDeviceID which = (SDL_AudioDeviceID) e.adevice.which; const SDL_bool iscapture = e.adevice.iscapture ? SDL_TRUE : SDL_FALSE; char *name = SDL_GetAudioDeviceName(which); if (name != NULL) { @@ -80,20 +80,15 @@ static void iteration(void) continue; } if (!iscapture) { - dev = SDL_OpenAudioDevice(which, &spec); - if (!dev) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open '%s': %s\n", name, SDL_GetError()); + SDL_AudioStream *stream = SDL_OpenAudioDeviceStream(which, &spec, NULL, NULL); + if (!stream) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create/bind an audio stream to %u ('%s'): %s", (unsigned int) which, name, SDL_GetError()); } else { - SDL_AudioStream *stream; - SDL_Log("Opened '%s' as %u\n", name, (unsigned int)dev); - stream = SDL_CreateAndBindAudioStream(dev, &spec); - if (!stream) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create/bind an audio stream to %u ('%s'): %s", (unsigned int) dev, name, SDL_GetError()); - SDL_CloseAudioDevice(dev); - } + SDL_Log("Opened '%s' as %u\n", name, (unsigned int) which); /* !!! FIXME: laziness, this used to loop the audio, but we'll just play it once for now on each connect. */ SDL_PutAudioStreamData(stream, sound, soundlen); SDL_FlushAudioStream(stream); + SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(stream)); /* !!! FIXME: this is leaking the stream for now. We'll wire it up to a dictionary or whatever later. */ } } @@ -101,7 +96,7 @@ static void iteration(void) } else if (e.type == SDL_EVENT_AUDIO_DEVICE_REMOVED) { dev = (SDL_AudioDeviceID)e.adevice.which; SDL_Log("%s device %u removed.\n", devtypestr(e.adevice.iscapture), (unsigned int)dev); - SDL_CloseAudioDevice(dev); + /* !!! FIXME: we need to keep track of our streams and destroy them here. */ } } } diff --git a/external/sdl/SDL/test/testaudioinfo.c b/external/sdl/SDL/test/testaudioinfo.c index 636d4aaf..8034f562 100644 --- a/external/sdl/SDL/test/testaudioinfo.c +++ b/external/sdl/SDL/test/testaudioinfo.c @@ -19,6 +19,7 @@ print_devices(SDL_bool iscapture) SDL_AudioSpec spec; const char *typestr = ((iscapture) ? "capture" : "output"); int n = 0; + int frames; SDL_AudioDeviceID *devices = iscapture ? SDL_GetAudioCaptureDevices(&n) : SDL_GetAudioOutputDevices(&n); if (devices == NULL) { @@ -37,10 +38,11 @@ print_devices(SDL_bool iscapture) SDL_Log(" %d Error: %s\n", i, SDL_GetError()); } - if (SDL_GetAudioDeviceFormat(devices[i], &spec) == 0) { + if (SDL_GetAudioDeviceFormat(devices[i], &spec, &frames) == 0) { SDL_Log(" Sample Rate: %d\n", spec.freq); SDL_Log(" Channels: %d\n", spec.channels); SDL_Log(" SDL_AudioFormat: %X\n", spec.format); + SDL_Log(" Buffer Size: %d frames\n", frames); } } SDL_Log("\n"); @@ -53,6 +55,7 @@ int main(int argc, char **argv) SDL_AudioSpec spec; int i; int n; + int frames; SDLTest_CommonState *state; /* Initialize test framework */ @@ -92,22 +95,24 @@ int main(int argc, char **argv) print_devices(SDL_FALSE); print_devices(SDL_TRUE); - if (SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec) < 0) { + if (SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec, &frames) < 0) { SDL_Log("Error when calling SDL_GetAudioDeviceFormat(default output): %s\n", SDL_GetError()); } else { SDL_Log("Default Output Device:\n"); SDL_Log("Sample Rate: %d\n", spec.freq); SDL_Log("Channels: %d\n", spec.channels); SDL_Log("SDL_AudioFormat: %X\n", spec.format); + SDL_Log("Buffer Size: %d frames\n", frames); } - if (SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_CAPTURE, &spec) < 0) { + if (SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_CAPTURE, &spec, &frames) < 0) { SDL_Log("Error when calling SDL_GetAudioDeviceFormat(default capture): %s\n", SDL_GetError()); } else { SDL_Log("Default Capture Device:\n"); SDL_Log("Sample Rate: %d\n", spec.freq); SDL_Log("Channels: %d\n", spec.channels); SDL_Log("SDL_AudioFormat: %X\n", spec.format); + SDL_Log("Buffer Size: %d frames\n", frames); } SDL_Quit(); diff --git a/external/sdl/SDL/test/testaudiostreamdynamicresample.c b/external/sdl/SDL/test/testaudiostreamdynamicresample.c index 96c4caff..9dacc87d 100644 --- a/external/sdl/SDL/test/testaudiostreamdynamicresample.c +++ b/external/sdl/SDL/test/testaudiostreamdynamicresample.c @@ -24,24 +24,198 @@ #include #define SLIDER_WIDTH_PERC 500.f / 600.f -#define SLIDER_HEIGHT_PERC 100.f / 480.f +#define SLIDER_HEIGHT_PERC 70.f / 480.f static int done; static SDLTest_CommonState *state; -static int multiplier = 100; -static SDL_FRect slider_area; -static SDL_FRect slider_fill_area; static SDL_AudioSpec spec; static SDL_AudioStream *stream; static Uint8 *audio_buf = NULL; static Uint32 audio_len = 0; +static SDL_bool auto_loop = SDL_TRUE; +static SDL_bool auto_flush = SDL_FALSE; + +static Uint64 last_get_callback = 0; +static int last_get_amount_additional = 0; +static int last_get_amount_total = 0; + +typedef struct Slider +{ + SDL_FRect area; + SDL_bool changed; + char fmtlabel[64]; + float pos; + int flags; + float min; + float mid; + float max; + float value; +} Slider; + +#define NUM_SLIDERS 3 +Slider sliders[NUM_SLIDERS]; +static int active_slider = -1; + +static void init_slider(int index, const char* fmtlabel, int flags, float value, float min, float max) +{ + Slider* slider = &sliders[index]; + + slider->area.x = state->window_w * (1.f - SLIDER_WIDTH_PERC) / 2; + slider->area.y = state->window_h * (0.2f + (index * SLIDER_HEIGHT_PERC * 1.4f)); + slider->area.w = SLIDER_WIDTH_PERC * state->window_w; + slider->area.h = SLIDER_HEIGHT_PERC * state->window_h; + slider->changed = SDL_TRUE; + SDL_strlcpy(slider->fmtlabel, fmtlabel, SDL_arraysize(slider->fmtlabel)); + slider->flags = flags; + slider->min = min; + slider->max = max; + slider->value = value; + + if (slider->flags & 1) { + slider->pos = (value - slider->min + 0.5f) / (slider->max - slider->min + 1.0f); + } else { + slider->pos = 0.5f; + slider->mid = value; + } +} + +static float lerp(float v0, float v1, float t) +{ + return (1 - t) * v0 + t * v1; +} + +static void draw_text(SDL_Renderer* renderer, int x, int y, const char* text) +{ + SDL_SetRenderDrawColor(renderer, 0xFD, 0xF6, 0xE3, 0xFF); + SDLTest_DrawString(renderer, (float) x, (float) y, text); +} + +static void draw_textf(SDL_Renderer* renderer, int x, int y, const char* fmt, ...) +{ + char text[256]; + va_list ap; + + va_start(ap, fmt); + SDL_vsnprintf(text, SDL_arraysize(text), fmt, ap); + va_end(ap); + + draw_text(renderer, x, y, text); +} + +static void queue_audio() +{ + Uint8* new_data = NULL; + int new_len = 0; + int retval = 0; + SDL_AudioSpec new_spec; + + new_spec.format = spec.format; + new_spec.channels = (int) sliders[2].value; + new_spec.freq = (int) sliders[1].value; + + SDL_Log("Converting audio from %i to %i", spec.freq, new_spec.freq); + + retval = retval ? retval : SDL_ConvertAudioSamples(&spec, audio_buf, audio_len, &new_spec, &new_data, &new_len); + retval = retval ? retval : SDL_SetAudioStreamFormat(stream, &new_spec, NULL); + retval = retval ? retval : SDL_PutAudioStreamData(stream, new_data, new_len); + + if (auto_flush) { + retval = retval ? retval : SDL_FlushAudioStream(stream); + } + + SDL_free(new_data); + + if (retval) { + SDL_Log("Failed to queue audio: %s", SDL_GetError()); + } else { + SDL_Log("Queued audio"); + } +} + +static void skip_audio(float amount) +{ + float speed; + SDL_AudioSpec dst_spec, new_spec; + int num_frames; + int retval = 0; + void* buf = NULL; + + SDL_LockAudioStream(stream); + + speed = SDL_GetAudioStreamFrequencyRatio(stream); + SDL_GetAudioStreamFormat(stream, NULL, &dst_spec); + + /* Gimme that crunchy audio */ + new_spec.format = SDL_AUDIO_S8; + new_spec.channels = 1; + new_spec.freq = 4000; + + SDL_SetAudioStreamFrequencyRatio(stream, 100.0f); + SDL_SetAudioStreamFormat(stream, NULL, &new_spec); + + num_frames = (int)(new_spec.freq * ((speed * amount) / 100.0f)); + buf = SDL_malloc(num_frames); + + if (buf != NULL) { + retval = SDL_GetAudioStreamData(stream, buf, num_frames); + SDL_free(buf); + } + + SDL_SetAudioStreamFormat(stream, NULL, &dst_spec); + SDL_SetAudioStreamFrequencyRatio(stream, speed); + + SDL_UnlockAudioStream(stream); + + if (retval >= 0) { + SDL_Log("Skipped %.2f seconds", amount); + } else { + SDL_Log("Failed to skip: %s", SDL_GetError()); + } +} + +static const char *AudioFmtToString(const SDL_AudioFormat fmt) +{ + switch (fmt) { + #define FMTCASE(x) case SDL_AUDIO_##x: return #x + FMTCASE(U8); + FMTCASE(S8); + FMTCASE(S16LE); + FMTCASE(S16BE); + FMTCASE(S32LE); + FMTCASE(S32BE); + FMTCASE(F32LE); + FMTCASE(F32BE); + #undef FMTCASE + } + return "?"; +} + +static const char *AudioChansToStr(const int channels) +{ + switch (channels) { + case 1: return "Mono"; + case 2: return "Stereo"; + case 3: return "2.1"; + case 4: return "Quad"; + case 5: return "4.1"; + case 6: return "5.1"; + case 7: return "6.1"; + case 8: return "7.1"; + default: break; + } + return "?"; +} + static void loop(void) { - int i; + int i, j; SDL_Event e; - int newmultiplier = multiplier; + SDL_FPoint p; + SDL_AudioSpec src_spec, dst_spec; + int available_bytes = 0; + float available_seconds = 0; while (SDL_PollEvent(&e)) { SDLTest_CommonEvent(state, &e, &done); @@ -50,63 +224,148 @@ static void loop(void) emscripten_cancel_main_loop(); } #endif - if (e.type == SDL_EVENT_MOUSE_MOTION) { - if (e.motion.state & SDL_BUTTON_LMASK) { - const SDL_FPoint p = { e.motion.x, e.motion.y }; - if (SDL_PointInRectFloat(&p, &slider_area)) { - const float w = SDL_roundf(p.x - slider_area.x); - slider_fill_area.w = w; - newmultiplier = ((int) ((w / slider_area.w) * 800.0f)) - 400; + if (e.type == SDL_EVENT_KEY_DOWN) { + SDL_Keycode sym = e.key.keysym.sym; + if (sym == SDLK_q) { + if (SDL_AudioDevicePaused(state->audio_id)) { + SDL_ResumeAudioDevice(state->audio_id); + } else { + SDL_PauseAudioDevice(state->audio_id); } + } else if (sym == SDLK_w) { + auto_loop = auto_loop ? SDL_FALSE : SDL_TRUE; + } else if (sym == SDLK_e) { + auto_flush = auto_flush ? SDL_FALSE : SDL_TRUE; + } else if (sym == SDLK_a) { + SDL_ClearAudioStream(stream); + SDL_Log("Cleared audio stream"); + } else if (sym == SDLK_s) { + queue_audio(); + } else if (sym == SDLK_d) { + float amount = 1.0f; + amount *= (e.key.keysym.mod & SDL_KMOD_CTRL) ? 10.0f : 1.0f; + amount *= (e.key.keysym.mod & SDL_KMOD_SHIFT) ? 10.0f : 1.0f; + skip_audio(amount); } } } - if (multiplier != newmultiplier) { - SDL_AudioSpec newspec; - char title[64]; - int newfreq = spec.freq; - - multiplier = newmultiplier; - if (multiplier == 0) { - SDL_snprintf(title, sizeof (title), "Drag the slider: Normal speed"); - } else if (multiplier < 0) { - SDL_snprintf(title, sizeof (title), "Drag the slider: %.2fx slow", (-multiplier / 100.0f) + 1.0f); - } else { - SDL_snprintf(title, sizeof (title), "Drag the slider: %.2fx fast", (multiplier / 100.0f) + 1.0f); + if (SDL_GetMouseState(&p.x, &p.y) & SDL_BUTTON_LMASK) { + if (active_slider == -1) { + for (i = 0; i < NUM_SLIDERS; ++i) { + if (SDL_PointInRectFloat(&p, &sliders[i].area)) { + active_slider = i; + break; + } + } } - for (i = 0; i < state->num_windows; i++) { - SDL_SetWindowTitle(state->windows[i], title); - } - - /* this math sucks, but whatever. */ - if (multiplier < 0) { - newfreq = spec.freq + (int) ((spec.freq * (multiplier / 400.0f)) * 0.75f); - } else if (multiplier > 0) { - newfreq = spec.freq + (int) (spec.freq * (multiplier / 100.0f)); - } - /* SDL_Log("newfreq=%d multiplier=%d\n", newfreq, multiplier); */ - SDL_memcpy(&newspec, &spec, sizeof (spec)); - newspec.freq = newfreq; - SDL_SetAudioStreamFormat(stream, &newspec, NULL); + } else { + active_slider = -1; } - /* keep it looping. */ - if (SDL_GetAudioStreamAvailable(stream) < ((int) (audio_len / 2))) { - SDL_PutAudioStreamData(stream, audio_buf, audio_len); + if (active_slider != -1) { + Slider* slider = &sliders[active_slider]; + + float value = (p.x - slider->area.x) / slider->area.w; + value = SDL_clamp(value, 0.0f, 1.0f); + slider->pos = value; + + if (slider->flags & 1) { + value = slider->min + (value * (slider->max - slider->min + 1.0f)); + value = SDL_clamp(value, slider->min, slider->max); + } else { + value = (value * 2.0f) - 1.0f; + value = (value >= 0) + ? lerp(slider->mid, slider->max, value) + : lerp(slider->mid, slider->min, -value); + } + + if (value != slider->value) { + slider->value = value; + slider->changed = SDL_TRUE; + } + } + + if (sliders[0].changed) { + sliders[0].changed = SDL_FALSE; + SDL_SetAudioStreamFrequencyRatio(stream, sliders[0].value); + } + + if (SDL_GetAudioStreamFormat(stream, &src_spec, &dst_spec) == 0) { + available_bytes = SDL_GetAudioStreamAvailable(stream); + available_seconds = (float)available_bytes / (float)(SDL_AUDIO_FRAMESIZE(dst_spec) * dst_spec.freq); + + /* keep it looping. */ + if (auto_loop && (available_seconds < 10.0f)) { + queue_audio(); + } } for (i = 0; i < state->num_windows; i++) { - SDL_SetRenderDrawColor(state->renderers[i], 0, 0, 255, 255); - SDL_RenderClear(state->renderers[i]); - SDL_SetRenderDrawColor(state->renderers[i], 0, 0, 0, 255); - SDL_RenderFillRect(state->renderers[i], &slider_area); - SDL_SetRenderDrawColor(state->renderers[i], 255, 0, 0, 255); - SDL_RenderFillRect(state->renderers[i], &slider_fill_area); - SDL_RenderPresent(state->renderers[i]); + int draw_y = 0; + SDL_Renderer* rend = state->renderers[i]; + + SDL_SetRenderDrawColor(rend, 0x00, 0x2B, 0x36, 0xFF); + SDL_RenderClear(rend); + + for (j = 0; j < NUM_SLIDERS; ++j) { + Slider* slider = &sliders[j]; + SDL_FRect area; + + SDL_copyp(&area, &slider->area); + + SDL_SetRenderDrawColor(rend, 0x07, 0x36, 0x42, 0xFF); + SDL_RenderFillRect(rend, &area); + + area.w *= slider->pos; + + SDL_SetRenderDrawColor(rend, 0x58, 0x6E, 0x75, 0xFF); + SDL_RenderFillRect(rend, &area); + + draw_textf(rend, (int)slider->area.x, (int)slider->area.y, slider->fmtlabel, + (slider->flags & 2) ? ((float)(int)slider->value) : slider->value); + } + + draw_textf(rend, 0, draw_y, "%7s, Loop: %3s, Flush: %3s", + SDL_AudioDevicePaused(state->audio_id) ? "Paused" : "Playing", auto_loop ? "On" : "Off", auto_flush ? "On" : "Off"); + draw_y += FONT_LINE_HEIGHT; + + draw_textf(rend, 0, draw_y, "Available: %4.2f (%i bytes)", available_seconds, available_bytes); + draw_y += FONT_LINE_HEIGHT; + + SDL_LockAudioStream(stream); + + draw_textf(rend, 0, draw_y, "Get Callback: %i/%i bytes, %2i ms ago", + last_get_amount_additional, last_get_amount_total, (int)(SDL_GetTicks() - last_get_callback)); + draw_y += FONT_LINE_HEIGHT; + + SDL_UnlockAudioStream(stream); + + draw_y = state->window_h - FONT_LINE_HEIGHT * 3; + + draw_textf(rend, 0, draw_y, "Wav: %6s/%6s/%i", + AudioFmtToString(spec.format), AudioChansToStr(spec.channels), spec.freq); + draw_y += FONT_LINE_HEIGHT; + + draw_textf(rend, 0, draw_y, "Src: %6s/%6s/%i", + AudioFmtToString(src_spec.format), AudioChansToStr(src_spec.channels), src_spec.freq); + draw_y += FONT_LINE_HEIGHT; + + draw_textf(rend, 0, draw_y, "Dst: %6s/%6s/%i", + AudioFmtToString(dst_spec.format), AudioChansToStr(dst_spec.channels), dst_spec.freq); + draw_y += FONT_LINE_HEIGHT; + + SDL_RenderPresent(rend); } } +static void SDLCALL our_get_callback(void *userdata, SDL_AudioStream *strm, int additional_amount, int total_amount) +{ + last_get_callback = SDL_GetTicks(); + last_get_amount_additional = additional_amount; + last_get_amount_total = total_amount; +} + int main(int argc, char *argv[]) { char *filename = NULL; @@ -142,20 +401,14 @@ int main(int argc, char *argv[]) i += consumed; } - slider_area.x = state->window_w * (1.f - SLIDER_WIDTH_PERC) / 2; - slider_area.y = state->window_h * (1.f - SLIDER_HEIGHT_PERC) / 2; - slider_area.w = SLIDER_WIDTH_PERC * state->window_w; - slider_area.h = SLIDER_HEIGHT_PERC * state->window_h; - - slider_fill_area = slider_area; - slider_fill_area.w /= 2; - /* Load the SDL library */ if (!SDLTest_CommonInit(state)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); return 1; } + FONT_CHARACTER_SIZE = 16; + filename = GetResourceFilename(filename, "sample.wav"); rc = SDL_LoadWAV(filename, &spec, &audio_buf, &audio_len); SDL_free(filename); @@ -166,12 +419,17 @@ int main(int argc, char *argv[]) return 1; } + init_slider(0, "Speed: %3.2fx", 0x0, 1.0f, 0.2f, 5.0f); + init_slider(1, "Freq: %g", 0x2, (float)spec.freq, 4000.0f, 192000.0f); + init_slider(2, "Channels: %g", 0x3, (float)spec.channels, 1.0f, 8.0f); + for (i = 0; i < state->num_windows; i++) { - SDL_SetWindowTitle(state->windows[i], "Drag the slider: Normal speed"); + SDL_SetWindowTitle(state->windows[i], "Resampler Test"); } stream = SDL_CreateAudioStream(&spec, &spec); - SDL_PutAudioStreamData(stream, audio_buf, audio_len); + SDL_SetAudioStreamGetCallback(stream, our_get_callback, NULL); + SDL_BindAudioStream(state->audio_id, stream); #ifdef __EMSCRIPTEN__ @@ -182,10 +440,10 @@ int main(int argc, char *argv[]) } #endif + SDLTest_CleanupTextDrawing(); SDL_DestroyAudioStream(stream); SDL_free(audio_buf); SDLTest_CommonQuit(state); - SDL_Quit(); return 0; } diff --git a/external/sdl/SDL/test/testautomation_audio.c b/external/sdl/SDL/test/testautomation_audio.c index d7a7c659..829962fe 100644 --- a/external/sdl/SDL/test/testautomation_audio.c +++ b/external/sdl/SDL/test/testautomation_audio.c @@ -176,13 +176,13 @@ static int audio_initOpenCloseQuitAudio(void *arg) case 0: /* Set standard desired spec */ desired.freq = 22050; - desired.format = SDL_AUDIO_S16SYS; + desired.format = SDL_AUDIO_S16; desired.channels = 2; case 1: /* Set custom desired spec */ desired.freq = 48000; - desired.format = SDL_AUDIO_F32SYS; + desired.format = SDL_AUDIO_F32; desired.channels = 2; break; } @@ -261,14 +261,14 @@ static int audio_pauseUnpauseAudio(void *arg) case 0: /* Set standard desired spec */ desired.freq = 22050; - desired.format = SDL_AUDIO_S16SYS; + desired.format = SDL_AUDIO_S16; desired.channels = 2; break; case 1: /* Set custom desired spec */ desired.freq = 48000; - desired.format = SDL_AUDIO_F32SYS; + desired.format = SDL_AUDIO_F32; desired.channels = 2; break; } @@ -441,18 +441,34 @@ static int audio_printCurrentAudioDriver(void *arg) } /* Definition of all formats, channels, and frequencies used to test audio conversions */ -static SDL_AudioFormat g_audioFormats[] = { SDL_AUDIO_S8, SDL_AUDIO_U8, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S16SYS, SDL_AUDIO_S16, - SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S32SYS, SDL_AUDIO_S32, - SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_F32SYS, SDL_AUDIO_F32 }; -static const char *g_audioFormatsVerbose[] = { "SDL_AUDIO_S8", "SDL_AUDIO_U8", "SDL_AUDIO_S16LSB", "SDL_AUDIO_S16MSB", "SDL_AUDIO_S16SYS", "SDL_AUDIO_S16", - "SDL_AUDIO_S32LSB", "SDL_AUDIO_S32MSB", "SDL_AUDIO_S32SYS", "SDL_AUDIO_S32", - "SDL_AUDIO_F32LSB", "SDL_AUDIO_F32MSB", "SDL_AUDIO_F32SYS", "SDL_AUDIO_F32" }; +static SDL_AudioFormat g_audioFormats[] = { + SDL_AUDIO_S8, SDL_AUDIO_U8, + SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, + SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, + SDL_AUDIO_F32LE, SDL_AUDIO_F32BE +}; +static const char *g_audioFormatsVerbose[] = { + "SDL_AUDIO_S8", "SDL_AUDIO_U8", + "SDL_AUDIO_S16LE", "SDL_AUDIO_S16BE", + "SDL_AUDIO_S32LE", "SDL_AUDIO_S32BE", + "SDL_AUDIO_F32LE", "SDL_AUDIO_F32BE" +}; static const int g_numAudioFormats = SDL_arraysize(g_audioFormats); static Uint8 g_audioChannels[] = { 1, 2, 4, 6 }; static const int g_numAudioChannels = SDL_arraysize(g_audioChannels); static int g_audioFrequencies[] = { 11025, 22050, 44100, 48000 }; static const int g_numAudioFrequencies = SDL_arraysize(g_audioFrequencies); +/* Verify the audio formats are laid out as expected */ +SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_U8_FORMAT, SDL_AUDIO_U8 == SDL_AUDIO_BITSIZE(8)); +SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_S8_FORMAT, SDL_AUDIO_S8 == (SDL_AUDIO_BITSIZE(8) | SDL_AUDIO_MASK_SIGNED)); +SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_S16LE_FORMAT, SDL_AUDIO_S16LE == (SDL_AUDIO_BITSIZE(16) | SDL_AUDIO_MASK_SIGNED)); +SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_S16BE_FORMAT, SDL_AUDIO_S16BE == (SDL_AUDIO_S16LE | SDL_AUDIO_MASK_BIG_ENDIAN)); +SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_S32LE_FORMAT, SDL_AUDIO_S32LE == (SDL_AUDIO_BITSIZE(32) | SDL_AUDIO_MASK_SIGNED)); +SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_S32BE_FORMAT, SDL_AUDIO_S32BE == (SDL_AUDIO_S32LE | SDL_AUDIO_MASK_BIG_ENDIAN)); +SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_F32LE_FORMAT, SDL_AUDIO_F32LE == (SDL_AUDIO_BITSIZE(32) | SDL_AUDIO_MASK_FLOAT | SDL_AUDIO_MASK_SIGNED)); +SDL_COMPILE_TIME_ASSERT(SDL_AUDIO_F32BE_FORMAT, SDL_AUDIO_F32BE == (SDL_AUDIO_F32LE | SDL_AUDIO_MASK_BIG_ENDIAN)); + /** * \brief Builds various audio conversion structures * @@ -466,7 +482,7 @@ static int audio_buildAudioStream(void *arg) int i, ii, j, jj, k, kk; /* No conversion needed */ - spec1.format = SDL_AUDIO_S16LSB; + spec1.format = SDL_AUDIO_S16LE; spec1.channels = 2; spec1.freq = 22050; stream = SDL_CreateAudioStream(&spec1, &spec1); @@ -478,7 +494,7 @@ static int audio_buildAudioStream(void *arg) spec1.format = SDL_AUDIO_S8; spec1.channels = 1; spec1.freq = 22050; - spec2.format = SDL_AUDIO_S16LSB; + spec2.format = SDL_AUDIO_S16LE; spec2.channels = 2; spec2.freq = 44100; stream = SDL_CreateAudioStream(&spec1, &spec2); @@ -533,7 +549,7 @@ static int audio_buildAudioStreamNegative(void *arg) spec1.format = SDL_AUDIO_S8; spec1.channels = 1; spec1.freq = 22050; - spec2.format = SDL_AUDIO_S16LSB; + spec2.format = SDL_AUDIO_S16LE; spec2.channels = 2; spec2.freq = 44100; @@ -546,7 +562,7 @@ static int audio_buildAudioStreamNegative(void *arg) spec1.format = SDL_AUDIO_S8; spec1.channels = 1; spec1.freq = 22050; - spec2.format = SDL_AUDIO_S16LSB; + spec2.format = SDL_AUDIO_S16LE; spec2.channels = 2; spec2.freq = 44100; @@ -692,54 +708,65 @@ static int audio_convertAudio(void *arg) } else { Uint8 *dst_buf = NULL, *src_buf = NULL; int dst_len = 0, src_len = 0, real_dst_len = 0; - int l = 64; - int src_samplesize, dst_samplesize; + int l = 64, m; + int src_framesize, dst_framesize; + int src_silence, dst_silence; - src_samplesize = (SDL_AUDIO_BITSIZE(spec1.format) / 8) * spec1.channels; - dst_samplesize = (SDL_AUDIO_BITSIZE(spec2.format) / 8) * spec2.channels; + src_framesize = SDL_AUDIO_FRAMESIZE(spec1); + dst_framesize = SDL_AUDIO_FRAMESIZE(spec2); - - /* Create some random data to convert */ - src_len = l * src_samplesize; + src_len = l * src_framesize; SDLTest_Log("Creating dummy sample buffer of %i length (%i bytes)", l, src_len); src_buf = (Uint8 *)SDL_malloc(src_len); - SDLTest_AssertCheck(dst_buf != NULL, "Check src data buffer to convert is not NULL"); + SDLTest_AssertCheck(src_buf != NULL, "Check src data buffer to convert is not NULL"); if (src_buf == NULL) { return TEST_ABORTED; } - src_len = src_len & ~(src_samplesize - 1); - dst_len = dst_samplesize * (src_len / src_samplesize); - if (spec1.freq < spec2.freq) { - const double mult = ((double)spec2.freq) / ((double)spec1.freq); - dst_len *= (int) SDL_ceil(mult); - } + src_silence = SDL_GetSilenceValueForFormat(spec1.format); + SDL_memset(src_buf, src_silence, src_len); - dst_len = dst_len & ~(dst_samplesize - 1); - dst_buf = (Uint8 *)SDL_calloc(1, dst_len); + dst_len = ((int)((((Sint64)l * spec2.freq) - 1) / spec1.freq) + 1) * dst_framesize; + dst_buf = (Uint8 *)SDL_malloc(dst_len); SDLTest_AssertCheck(dst_buf != NULL, "Check dst data buffer to convert is not NULL"); if (dst_buf == NULL) { return TEST_ABORTED; } + real_dst_len = SDL_GetAudioStreamAvailable(stream); + SDLTest_AssertCheck(0 == real_dst_len, "Verify available (pre-put); expected: %i; got: %i", 0, real_dst_len); + /* Run the audio converter */ if (SDL_PutAudioStreamData(stream, src_buf, src_len) < 0 || SDL_FlushAudioStream(stream) < 0) { return TEST_ABORTED; } + real_dst_len = SDL_GetAudioStreamAvailable(stream); + SDLTest_AssertCheck(dst_len == real_dst_len, "Verify available (post-put); expected: %i; got: %i", dst_len, real_dst_len); + real_dst_len = SDL_GetAudioStreamData(stream, dst_buf, dst_len); - SDLTest_AssertCheck(real_dst_len > 0, "Verify result value; expected: > 0; got: %i", real_dst_len); - if (real_dst_len < 0) { + SDLTest_AssertCheck(dst_len == real_dst_len, "Verify result value; expected: %i; got: %i", dst_len, real_dst_len); + if (dst_len != real_dst_len) { return TEST_ABORTED; } + real_dst_len = SDL_GetAudioStreamAvailable(stream); + SDLTest_AssertCheck(0 == real_dst_len, "Verify available (post-get); expected: %i; got: %i", 0, real_dst_len); + + dst_silence = SDL_GetSilenceValueForFormat(spec2.format); + + for (m = 0; m < dst_len; ++m) { + if (dst_buf[m] != dst_silence) { + SDLTest_LogError("Output buffer is not silent"); + return TEST_ABORTED; + } + } + SDL_DestroyAudioStream(stream); /* Free converted buffer */ SDL_free(src_buf); SDL_free(dst_buf); - - } } } @@ -794,24 +821,30 @@ static int audio_resampleLoss(void *arg) double signal_to_noise; double max_error; } test_specs[] = { - { 50, 440, 0, 44100, 48000, 60, 0.0025 }, - { 50, 5000, SDL_PI_D / 2, 20000, 10000, 65, 0.0010 }, + { 50, 440, 0, 44100, 48000, 80, 0.0009 }, + { 50, 5000, SDL_PI_D / 2, 20000, 10000, 999, 0.0001 }, + { 50, 440, 0, 22050, 96000, 79, 0.0120 }, + { 50, 440, 0, 96000, 22050, 80, 0.0002 }, { 0 } }; int spec_idx = 0; + int min_channels = 1; + int max_channels = 1 /*8*/; + int num_channels = min_channels; - for (spec_idx = 0; test_specs[spec_idx].time > 0; ++spec_idx) { + for (spec_idx = 0; test_specs[spec_idx].time > 0;) { const struct test_spec_t *spec = &test_specs[spec_idx]; const int frames_in = spec->time * spec->rate_in; const int frames_target = spec->time * spec->rate_out; - const int len_in = frames_in * (int)sizeof(float); - const int len_target = frames_target * (int)sizeof(float); + const int len_in = (frames_in * num_channels) * (int)sizeof(float); + const int len_target = (frames_target * num_channels) * (int)sizeof(float); SDL_AudioSpec tmpspec1, tmpspec2; Uint64 tick_beg = 0; Uint64 tick_end = 0; int i = 0; + int j = 0; int ret = 0; SDL_AudioStream *stream = NULL; float *buf_in = NULL; @@ -826,10 +859,10 @@ static int audio_resampleLoss(void *arg) spec->time, spec->freq, spec->phase, spec->rate_in, spec->rate_out); tmpspec1.format = SDL_AUDIO_F32; - tmpspec1.channels = 1; + tmpspec1.channels = num_channels; tmpspec1.freq = spec->rate_in; tmpspec2.format = SDL_AUDIO_F32; - tmpspec2.channels = 1; + tmpspec2.channels = num_channels; tmpspec2.freq = spec->rate_out; stream = SDL_CreateAudioStream(&tmpspec1, &tmpspec2); SDLTest_AssertPass("Call to SDL_CreateAudioStream(SDL_AUDIO_F32, 1, %i, SDL_AUDIO_F32, 1, %i)", spec->rate_in, spec->rate_out); @@ -846,7 +879,10 @@ static int audio_resampleLoss(void *arg) } for (i = 0; i < frames_in; ++i) { - *(buf_in + i) = (float)sine_wave_sample(i, spec->rate_in, spec->freq, spec->phase); + float f = (float)sine_wave_sample(i, spec->rate_in, spec->freq, spec->phase); + for (j = 0; j < num_channels; ++j) { + *(buf_in + (i * num_channels) + j) = f; + } } tick_beg = SDL_GetPerformanceCounter(); @@ -877,9 +913,7 @@ static int audio_resampleLoss(void *arg) len_out = SDL_GetAudioStreamData(stream, buf_out, len_target); SDLTest_AssertPass("Call to SDL_GetAudioStreamData(stream, buf_out, %i)", len_target); - /** !!! FIXME: SDL_AudioStream does not return output of the same length as - ** !!! FIXME: the input even if SDL_FlushAudioStream is called. */ - SDLTest_AssertCheck(len_out <= len_target, "Expected output length to be no larger than %i, got %i.", + SDLTest_AssertCheck(len_out == len_target, "Expected output length to be no larger than %i, got %i.", len_target, len_out); SDL_DestroyAudioStream(stream); if (len_out > len_target) { @@ -890,13 +924,15 @@ static int audio_resampleLoss(void *arg) tick_end = SDL_GetPerformanceCounter(); SDLTest_Log("Resampling used %f seconds.", ((double)(tick_end - tick_beg)) / SDL_GetPerformanceFrequency()); - for (i = 0; i < len_out / (int)sizeof(float); ++i) { - const float output = *(buf_out + i); + for (i = 0; i < frames_target; ++i) { const double target = sine_wave_sample(i, spec->rate_out, spec->freq, spec->phase); - const double error = SDL_fabs(target - output); - max_error = SDL_max(max_error, error); - sum_squared_error += error * error; - sum_squared_value += target * target; + for (j = 0; j < num_channels; ++j) { + const float output = *(buf_out + (i * num_channels) + j); + const double error = SDL_fabs(target - output); + max_error = SDL_max(max_error, error); + sum_squared_error += error * error; + sum_squared_value += target * target; + } } SDL_free(buf_out); signal_to_noise = 10 * SDL_log10(sum_squared_value / sum_squared_error); /* decibel */ @@ -909,10 +945,303 @@ static int audio_resampleLoss(void *arg) signal_to_noise, spec->signal_to_noise); SDLTest_AssertCheck(max_error <= spec->max_error, "Maximum conversion error %f should be no more than %f.", max_error, spec->max_error); + + if (++num_channels > max_channels) { + num_channels = min_channels; + ++spec_idx; + } } return TEST_COMPLETED; } + +/** + * \brief Check accuracy converting between audio formats. + * + * \sa SDL_ConvertAudioSamples + */ +static int audio_convertAccuracy(void *arg) +{ + static SDL_AudioFormat formats[] = { SDL_AUDIO_S8, SDL_AUDIO_U8, SDL_AUDIO_S16, SDL_AUDIO_S32 }; + static const char* format_names[] = { "S8", "U8", "S16", "S32" }; + + int src_num = 65537 + 2048 + 48 + 256 + 100000; + int src_len = src_num * sizeof(float); + float* src_data = SDL_malloc(src_len); + int i, j; + + SDLTest_AssertCheck(src_data != NULL, "Expected source buffer to be created."); + if (src_data == NULL) { + return TEST_ABORTED; + } + + j = 0; + + /* Generate a uniform range of floats between [-1.0, 1.0] */ + for (i = 0; i < 65537; ++i) { + src_data[j++] = ((float)i - 32768.0f) / 32768.0f; + } + + /* Generate floats close to 1.0 */ + const float max_val = 16777216.0f; + + for (i = 0; i < 1024; ++i) { + float f = (max_val + (float)(512 - i)) / max_val; + src_data[j++] = f; + src_data[j++] = -f; + } + + for (i = 0; i < 24; ++i) { + float f = (max_val + (float)(3u << i)) / max_val; + src_data[j++] = f; + src_data[j++] = -f; + } + + /* Generate floats far outside the [-1.0, 1.0] range */ + for (i = 0; i < 128; ++i) { + float f = 2.0f + (float) i; + src_data[j++] = f; + src_data[j++] = -f; + } + + /* Fill the rest with random floats between [-1.0, 1.0] */ + for (i = 0; i < 100000; ++i) { + src_data[j++] = SDLTest_RandomSint32() / 2147483648.0f; + } + + /* Shuffle the data for good measure */ + for (i = src_num - 1; i > 0; --i) { + float f = src_data[i]; + j = SDLTest_RandomIntegerInRange(0, i); + src_data[i] = src_data[j]; + src_data[j] = f; + } + + for (i = 0; i < SDL_arraysize(formats); ++i) { + SDL_AudioSpec src_spec, tmp_spec; + Uint64 convert_begin, convert_end; + Uint8 *tmp_data, *dst_data; + int tmp_len, dst_len; + int ret; + + SDL_AudioFormat format = formats[i]; + const char* format_name = format_names[i]; + + /* Formats with > 23 bits can represent every value exactly */ + float min_delta = 1.0f; + float max_delta = -1.0f; + + /* Subtract 1 bit to account for sign */ + int bits = SDL_AUDIO_BITSIZE(format) - 1; + float target_max_delta = (bits > 23) ? 0.0f : (1.0f / (float)(1 << bits)); + float target_min_delta = -target_max_delta; + + src_spec.format = SDL_AUDIO_F32; + src_spec.channels = 1; + src_spec.freq = 44100; + + tmp_spec.format = format; + tmp_spec.channels = 1; + tmp_spec.freq = 44100; + + convert_begin = SDL_GetPerformanceCounter(); + + tmp_data = NULL; + tmp_len = 0; + ret = SDL_ConvertAudioSamples(&src_spec, (const Uint8*) src_data, src_len, &tmp_spec, &tmp_data, &tmp_len); + SDLTest_AssertCheck(ret == 0, "Expected SDL_ConvertAudioSamples(F32->%s) to succeed", format_name); + if (ret != 0) { + SDL_free(src_data); + return TEST_ABORTED; + } + + dst_data = NULL; + dst_len = 0; + ret = SDL_ConvertAudioSamples(&tmp_spec, tmp_data, tmp_len, &src_spec, &dst_data, &dst_len); + SDLTest_AssertCheck(ret == 0, "Expected SDL_ConvertAudioSamples(%s->F32) to succeed", format_name); + if (ret != 0) { + SDL_free(tmp_data); + SDL_free(src_data); + return TEST_ABORTED; + } + + convert_end = SDL_GetPerformanceCounter(); + SDLTest_Log("Conversion via %s took %f seconds.", format_name, ((double)(convert_end - convert_begin)) / SDL_GetPerformanceFrequency()); + + SDL_free(tmp_data); + + for (j = 0; j < src_num; ++j) { + float x = src_data[j]; + float y = ((float*)dst_data)[j]; + float d = SDL_clamp(x, -1.0f, 1.0f) - y; + + min_delta = SDL_min(min_delta, d); + max_delta = SDL_max(max_delta, d); + } + + SDLTest_AssertCheck(min_delta >= target_min_delta, "%s has min delta of %+f, should be >= %+f", format_name, min_delta, target_min_delta); + SDLTest_AssertCheck(max_delta <= target_max_delta, "%s has max delta of %+f, should be <= %+f", format_name, max_delta, target_max_delta); + + SDL_free(dst_data); + } + + SDL_free(src_data); + + return TEST_COMPLETED; +} + +/** + * \brief Check accuracy when switching between formats + * + * \sa SDL_SetAudioStreamFormat + */ +static int audio_formatChange(void *arg) +{ + int i; + SDL_AudioSpec spec1, spec2, spec3; + int frames_1, frames_2, frames_3; + int length_1, length_2, length_3; + int retval = 0; + int status = TEST_ABORTED; + float* buffer_1 = NULL; + float* buffer_2 = NULL; + float* buffer_3 = NULL; + SDL_AudioStream* stream = NULL; + double max_error = 0; + double sum_squared_error = 0; + double sum_squared_value = 0; + double signal_to_noise = 0; + double target_max_error = 0.02; + double target_signal_to_noise = 75.0; + int sine_freq = 500; + + spec1.format = SDL_AUDIO_F32; + spec1.channels = 1; + spec1.freq = 20000; + + spec2.format = SDL_AUDIO_F32; + spec2.channels = 1; + spec2.freq = 40000; + + spec3.format = SDL_AUDIO_F32; + spec3.channels = 1; + spec3.freq = 80000; + + frames_1 = spec1.freq; + frames_2 = spec2.freq; + frames_3 = spec3.freq * 2; + + length_1 = (int)(frames_1 * sizeof(*buffer_1)); + buffer_1 = (float*) SDL_malloc(length_1); + if (!SDLTest_AssertCheck(buffer_1 != NULL, "Expected buffer_1 to be created.")) { + goto cleanup; + } + + length_2 = (int)(frames_2 * sizeof(*buffer_2)); + buffer_2 = (float*) SDL_malloc(length_2); + if (!SDLTest_AssertCheck(buffer_2 != NULL, "Expected buffer_2 to be created.")) { + goto cleanup; + } + + length_3 = (int)(frames_3 * sizeof(*buffer_3)); + buffer_3 = (float*) SDL_malloc(length_3); + if (!SDLTest_AssertCheck(buffer_3 != NULL, "Expected buffer_3 to be created.")) { + goto cleanup; + } + + for (i = 0; i < frames_1; ++i) { + buffer_1[i] = (float) sine_wave_sample(i, spec1.freq, sine_freq, 0.0f); + } + + for (i = 0; i < frames_2; ++i) { + buffer_2[i] = (float) sine_wave_sample(i, spec2.freq, sine_freq, 0.0f); + } + + stream = SDL_CreateAudioStream(NULL, NULL); + if (!SDLTest_AssertCheck(stream != NULL, "Expected SDL_CreateAudioStream to succeed")) { + goto cleanup; + } + + retval = SDL_SetAudioStreamFormat(stream, &spec1, &spec3); + if (!SDLTest_AssertCheck(retval == 0, "Expected SDL_SetAudioStreamFormat(spec1, spec3) to succeed")) { + goto cleanup; + } + + retval = SDL_GetAudioStreamAvailable(stream); + if (!SDLTest_AssertCheck(retval == 0, "Expected SDL_GetAudioStreamAvailable return 0")) { + goto cleanup; + } + + retval = SDL_PutAudioStreamData(stream, buffer_1, length_1); + if (!SDLTest_AssertCheck(retval == 0, "Expected SDL_PutAudioStreamData(buffer_1) to succeed")) { + goto cleanup; + } + + retval = SDL_FlushAudioStream(stream); + if (!SDLTest_AssertCheck(retval == 0, "Expected SDL_FlushAudioStream to succeed")) { + goto cleanup; + } + + retval = SDL_SetAudioStreamFormat(stream, &spec2, &spec3); + if (!SDLTest_AssertCheck(retval == 0, "Expected SDL_SetAudioStreamFormat(spec2, spec3) to succeed")) { + goto cleanup; + } + + retval = SDL_PutAudioStreamData(stream, buffer_2, length_2); + if (!SDLTest_AssertCheck(retval == 0, "Expected SDL_PutAudioStreamData(buffer_1) to succeed")) { + goto cleanup; + } + + retval = SDL_FlushAudioStream(stream); + if (!SDLTest_AssertCheck(retval == 0, "Expected SDL_FlushAudioStream to succeed")) { + goto cleanup; + } + + retval = SDL_GetAudioStreamAvailable(stream); + if (!SDLTest_AssertCheck(retval == length_3, "Expected SDL_GetAudioStreamAvailable to return %i, got %i", length_3, retval)) { + goto cleanup; + } + + retval = SDL_GetAudioStreamData(stream, buffer_3, length_3); + if (!SDLTest_AssertCheck(retval == length_3, "Expected SDL_GetAudioStreamData to return %i, got %i", length_3, retval)) { + goto cleanup; + } + + retval = SDL_GetAudioStreamAvailable(stream); + if (!SDLTest_AssertCheck(retval == 0, "Expected SDL_GetAudioStreamAvailable to return 0")) { + goto cleanup; + } + + for (i = 0; i < frames_3; ++i) { + const float output = buffer_3[i]; + const float target = (float) sine_wave_sample(i, spec3.freq, sine_freq, 0.0f); + const double error = SDL_fabs(target - output); + max_error = SDL_max(max_error, error); + sum_squared_error += error * error; + sum_squared_value += target * target; + } + + signal_to_noise = 10 * SDL_log10(sum_squared_value / sum_squared_error); /* decibel */ + SDLTest_AssertCheck(isfinite(sum_squared_value), "Sum of squared target should be finite."); + SDLTest_AssertCheck(isfinite(sum_squared_error), "Sum of squared error should be finite."); + /* Infinity is theoretically possible when there is very little to no noise */ + SDLTest_AssertCheck(!isnan(signal_to_noise), "Signal-to-noise ratio should not be NaN."); + SDLTest_AssertCheck(isfinite(max_error), "Maximum conversion error should be finite."); + SDLTest_AssertCheck(signal_to_noise >= target_signal_to_noise, "Conversion signal-to-noise ratio %f dB should be no less than %f dB.", + signal_to_noise, target_signal_to_noise); + SDLTest_AssertCheck(max_error <= target_max_error, "Maximum conversion error %f should be no more than %f.", + max_error, target_max_error); + + status = TEST_COMPLETED; + +cleanup: + SDL_free(buffer_1); + SDL_free(buffer_2); + SDL_free(buffer_3); + SDL_DestroyAudioStream(stream); + + return status; +} /* ================= Test Case References ================== */ /* Audio test cases */ @@ -952,12 +1281,8 @@ static const SDLTest_TestCaseReference audioTest9 = { audio_lockUnlockOpenAudioDevice, "audio_lockUnlockOpenAudioDevice", "Locks and unlocks an open audio device.", TEST_ENABLED }; -/* TODO: enable test when SDL_ConvertAudio segfaults on cygwin have been fixed. - * TODO: re-check, since this was changer to AudioStream */ -/* For debugging, test case can be run manually using --filter audio_convertAudio */ - static const SDLTest_TestCaseReference audioTest10 = { - audio_convertAudio, "audio_convertAudio", "Convert audio using available formats.", TEST_DISABLED + audio_convertAudio, "audio_convertAudio", "Convert audio using available formats.", TEST_ENABLED }; /* TODO: enable test when SDL_AudioDeviceConnected has been implemented. */ @@ -986,11 +1311,20 @@ static const SDLTest_TestCaseReference audioTest16 = { audio_resampleLoss, "audio_resampleLoss", "Check signal-to-noise ratio and maximum error of audio resampling.", TEST_ENABLED }; +static const SDLTest_TestCaseReference audioTest17 = { + audio_convertAccuracy, "audio_convertAccuracy", "Check accuracy converting between audio formats.", TEST_ENABLED +}; + +static const SDLTest_TestCaseReference audioTest18 = { + audio_formatChange, "audio_formatChange", "Check handling of format changes.", TEST_ENABLED +}; + /* Sequence of Audio test cases */ static const SDLTest_TestCaseReference *audioTests[] = { &audioTest1, &audioTest2, &audioTest3, &audioTest4, &audioTest5, &audioTest6, &audioTest7, &audioTest8, &audioTest9, &audioTest10, &audioTest11, - &audioTest12, &audioTest13, &audioTest14, &audioTest15, &audioTest16, NULL + &audioTest12, &audioTest13, &audioTest14, &audioTest15, &audioTest16, + &audioTest17, &audioTest18, NULL }; /* Audio test suite (global) */ diff --git a/external/sdl/SDL/test/testautomation_pixels.c b/external/sdl/SDL/test/testautomation_pixels.c index 0af215d1..9980b561 100644 --- a/external/sdl/SDL/test/testautomation_pixels.c +++ b/external/sdl/SDL/test/testautomation_pixels.c @@ -122,7 +122,7 @@ static int pixels_allocFreeFormat(void *arg) /* Blank/unknown format */ format = 0; - SDLTest_Log("RGB Format: %s (%" SDL_PRIu32 ")", unknownFormat, format); + SDLTest_Log("Pixel Format: %s (%" SDL_PRIu32 ")", unknownFormat, format); /* Allocate format */ result = SDL_CreatePixelFormat(format); @@ -143,7 +143,7 @@ static int pixels_allocFreeFormat(void *arg) /* RGB formats */ for (i = 0; i < g_numAllFormats; i++) { format = g_AllFormats[i]; - SDLTest_Log("RGB Format: %s (%" SDL_PRIu32 ")", g_AllFormatsVerbose[i], format); + SDLTest_Log("Pixel Format: %s (%" SDL_PRIu32 ")", g_AllFormatsVerbose[i], format); /* Allocate format */ result = SDL_CreatePixelFormat(format); @@ -151,11 +151,13 @@ static int pixels_allocFreeFormat(void *arg) SDLTest_AssertCheck(result != NULL, "Verify result is not NULL"); if (result != NULL) { SDLTest_AssertCheck(result->format == format, "Verify value of result.format; expected: %" SDL_PRIu32 ", got %" SDL_PRIu32, format, result->format); - SDLTest_AssertCheck(result->BitsPerPixel > 0, "Verify value of result.BitsPerPixel; expected: >0, got %u", result->BitsPerPixel); - SDLTest_AssertCheck(result->BytesPerPixel > 0, "Verify value of result.BytesPerPixel; expected: >0, got %u", result->BytesPerPixel); - if (result->palette != NULL && !SDL_ISPIXELFORMAT_FOURCC(result->format)) { - masks = result->Rmask | result->Gmask | result->Bmask | result->Amask; - SDLTest_AssertCheck(masks > 0, "Verify value of result.[RGBA]mask combined; expected: >0, got %" SDL_PRIu32, masks); + if (!SDL_ISPIXELFORMAT_FOURCC(format)) { + SDLTest_AssertCheck(result->BitsPerPixel > 0, "Verify value of result.BitsPerPixel; expected: >0, got %u", result->BitsPerPixel); + SDLTest_AssertCheck(result->BytesPerPixel > 0, "Verify value of result.BytesPerPixel; expected: >0, got %u", result->BytesPerPixel); + if (!SDL_ISPIXELFORMAT_INDEXED(format)) { + masks = result->Rmask | result->Gmask | result->Bmask | result->Amask; + SDLTest_AssertCheck(masks > 0, "Verify value of result.[RGBA]mask combined; expected: >0, got %" SDL_PRIu32, masks); + } } /* Deallocate again */ diff --git a/external/sdl/SDL/test/testcontroller.c b/external/sdl/SDL/test/testcontroller.c index fa9e7e7d..dd4b9819 100644 --- a/external/sdl/SDL/test/testcontroller.c +++ b/external/sdl/SDL/test/testcontroller.c @@ -2008,15 +2008,22 @@ int main(int argc, char *argv[]) HandleGamepadRemoved(controllers[0].id); DelController(controllers[0].id); } + SDL_free(controllers); + SDL_free(controller_name); DestroyGamepadImage(image); DestroyGamepadDisplay(gamepad_elements); DestroyGamepadTypeDisplay(gamepad_type); DestroyJoystickDisplay(joystick_elements); + DestroyGamepadButton(setup_mapping_button); + DestroyGamepadButton(done_mapping_button); + DestroyGamepadButton(cancel_button); + DestroyGamepadButton(clear_button); DestroyGamepadButton(copy_button); + DestroyGamepadButton(paste_button); + SDLTest_CleanupTextDrawing(); SDL_DestroyRenderer(screen); SDL_DestroyWindow(window); - SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD); + SDL_Quit(); SDLTest_CommonDestroyState(state); - return 0; } diff --git a/external/sdl/SDL/test/testdropfile.c b/external/sdl/SDL/test/testdropfile.c index acb01612..2e4efedb 100644 --- a/external/sdl/SDL/test/testdropfile.c +++ b/external/sdl/SDL/test/testdropfile.c @@ -10,24 +10,11 @@ freely. */ -#include - #include #include static SDLTest_CommonState *state; -/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ -static void -quit(int rc) -{ - SDLTest_CommonQuit(state); - /* Let 'main()' return normally */ - if (rc != 0) { - exit(rc); - } -} - int main(int argc, char *argv[]) { int i, done; @@ -35,6 +22,7 @@ int main(int argc, char *argv[]) SDL_bool is_hover = SDL_FALSE; float x = 0.0f, y = 0.0f; unsigned int windowID = 0; + int return_code = -1; /* Initialize test framework */ state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO); @@ -58,12 +46,14 @@ int main(int argc, char *argv[]) } if (consumed < 0) { SDLTest_CommonLogUsage(state, argv[0], NULL); - quit(1); + return_code = 1; + goto quit; } i += consumed; } if (!SDLTest_CommonInit(state)) { - quit(2); + return_code = 1; + goto quit; } @@ -114,7 +104,8 @@ int main(int argc, char *argv[]) SDL_Delay(16); } - quit(0); - /* keep the compiler happy ... */ - return 0; + return_code = 0; +quit: + SDLTest_CommonQuit(state); + return return_code; } diff --git a/external/sdl/SDL/test/testfile.c b/external/sdl/SDL/test/testfile.c index 8aa48007..1561f176 100644 --- a/external/sdl/SDL/test/testfile.c +++ b/external/sdl/SDL/test/testfile.c @@ -369,6 +369,7 @@ int main(int argc, char *argv[]) SDL_RWclose(rwops); SDL_Log("test5 OK\n"); cleanup(); + SDL_Quit(); SDLTest_CommonDestroyState(state); return 0; /* all ok */ } diff --git a/external/sdl/SDL/test/testfilesystem_pre.c b/external/sdl/SDL/test/testfilesystem_pre.c new file mode 100644 index 00000000..1a7e0d61 --- /dev/null +++ b/external/sdl/SDL/test/testfilesystem_pre.c @@ -0,0 +1,33 @@ +/* + Copyright (C) 1997-2023 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely. +*/ +/* Call SDL_GetPrefPath to warm the SHGetFolderPathW cache */ + +/** + * We noticed frequent ci timeouts running testfilesystem on 32-bit Windows. + * Internally, this functions calls Shell32.SHGetFolderPathW. + */ + +#include +#include + +int main(int argc, char *argv[]) +{ + Uint64 start; + (void)argc; + (void)argv; + SDL_Init(0); + start = SDL_GetTicks(); + SDL_GetPrefPath("libsdl", "test_filesystem"); + SDL_Log("SDL_GetPrefPath took %" SDL_PRIu64 "ms", SDL_GetTicks() - start); + SDL_Quit(); + return 0; +} diff --git a/external/sdl/SDL/test/testmultiaudio.c b/external/sdl/SDL/test/testmultiaudio.c index b75da946..ecf0316c 100644 --- a/external/sdl/SDL/test/testmultiaudio.c +++ b/external/sdl/SDL/test/testmultiaudio.c @@ -28,7 +28,6 @@ static Uint8 *sound = NULL; /* Pointer to wave data */ static Uint32 soundlen = 0; /* Length of wave data */ /* these have to be in globals so the Emscripten port can see them in the mainloop. :/ */ -static SDL_AudioDeviceID device = 0; static SDL_AudioStream *stream = NULL; @@ -37,7 +36,6 @@ static void loop(void) { if (SDL_GetAudioStreamAvailable(stream) == 0) { SDL_Log("done."); - SDL_CloseAudioDevice(device); SDL_DestroyAudioStream(stream); SDL_free(sound); SDL_Quit(); @@ -65,13 +63,10 @@ test_multi_audio(SDL_AudioDeviceID *devices, int devcount) SDL_Log("Playing on device #%d of %d: id=%u, name='%s'...", i, devcount, (unsigned int) devices[i], devname); - device = SDL_OpenAudioDevice(devices[i], &spec); - if (device == 0) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Open device failed: %s", SDL_GetError()); - } else if ((stream = SDL_CreateAndBindAudioStream(device, &spec)) == NULL) { /* we can reuse these, but we'll just make one each time for now. */ + if ((stream = SDL_OpenAudioDeviceStream(devices[i], &spec, NULL, NULL)) == NULL) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Audio stream creation failed: %s", SDL_GetError()); - SDL_CloseAudioDevice(device); } else { + SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(stream)); SDL_PutAudioStreamData(stream, sound, soundlen); SDL_FlushAudioStream(stream); #ifdef __EMSCRIPTEN__ @@ -87,7 +82,6 @@ test_multi_audio(SDL_AudioDeviceID *devices, int devcount) } #endif SDL_Log("done."); - SDL_CloseAudioDevice(device); SDL_DestroyAudioStream(stream); } SDL_free(devname); @@ -101,29 +95,19 @@ test_multi_audio(SDL_AudioDeviceID *devices, int devcount) SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!"); } else { for (i = 0; i < devcount; i++) { - char *devname = SDL_GetAudioDeviceName(devices[i]); - device = SDL_OpenAudioDevice(devices[i], &spec); - if (device == 0) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Open device %d of %d (id=%u, name='%s') failed: %s\n", i, devcount, (unsigned int) devices[i], devname, SDL_GetError()); - } - SDL_free(devname); - devices[i] = device; /* just replace the physical device ID with the newly-opened logical device ID. */ - if (device) { - SDL_PauseAudioDevice(device); /* hold while we set up all the streams. */ - streams[i] = SDL_CreateAndBindAudioStream(device, &spec); - if (streams[i] == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Audio stream creation failed for device %d of %d: %s", i, devcount, SDL_GetError()); - } else { - SDL_PutAudioStreamData(streams[i], sound, soundlen); - SDL_FlushAudioStream(streams[i]); - } + streams[i] = SDL_OpenAudioDeviceStream(devices[i], &spec, NULL, NULL); + if (streams[i] == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Audio stream creation failed for device %d of %d: %s", i, devcount, SDL_GetError()); + } else { + SDL_PutAudioStreamData(streams[i], sound, soundlen); + SDL_FlushAudioStream(streams[i]); } } /* try to start all the devices about the same time. SDL does not guarantee sync across physical devices. */ for (i = 0; i < devcount; i++) { - if (devices[i]) { - SDL_ResumeAudioDevice(devices[i]); + if (streams[i]) { + SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(streams[i])); } } @@ -143,7 +127,6 @@ test_multi_audio(SDL_AudioDeviceID *devices, int devcount) } for (i = 0; i < devcount; i++) { - SDL_CloseAudioDevice(devices[i]); SDL_DestroyAudioStream(streams[i]); } diff --git a/external/sdl/SDL/test/testnative.c b/external/sdl/SDL/test/testnative.c index ef587ede..166e5d8c 100644 --- a/external/sdl/SDL/test/testnative.c +++ b/external/sdl/SDL/test/testnative.c @@ -212,6 +212,12 @@ int main(int argc, char *argv[]) MoveSprites(renderer, sprite); } + SDL_DestroyTexture(sprite); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_free(positions); + SDL_free(velocities); + quit(0); return 0; /* to prevent compiler warning */ diff --git a/external/sdl/SDL/test/testshape.c b/external/sdl/SDL/test/testshape.c index ae5ee560..07831979 100644 --- a/external/sdl/SDL/test/testshape.c +++ b/external/sdl/SDL/test/testshape.c @@ -388,6 +388,15 @@ ret: /* Destroy the window. */ SDL_DestroyWindow(window); + if (g_bitmap) { + SDL_free(g_bitmap); + g_bitmap = NULL; + } + if (g_shape_surface) { + SDL_DestroySurface(g_shape_surface); + g_shape_surface = NULL; + } + SDL_Quit(); SDLTest_CommonDestroyState(state); diff --git a/external/sdl/SDL/test/testspriteminimal.c b/external/sdl/SDL/test/testspriteminimal.c index b58d21d6..0d89c5e3 100644 --- a/external/sdl/SDL/test/testspriteminimal.c +++ b/external/sdl/SDL/test/testspriteminimal.c @@ -20,7 +20,8 @@ #include #include -#include "testutils.h" + +#include "icon.h" #define WINDOW_WIDTH 640 #define WINDOW_HEIGHT 480 @@ -35,15 +36,23 @@ static int sprite_w, sprite_h; static SDL_Renderer *renderer; static int done; -/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ -static void -quit(int rc) -{ - SDL_Quit(); - /* Let 'main()' return normally */ - if (rc != 0) { - exit(rc); +static SDL_Texture *CreateTexture(SDL_Renderer *r, unsigned char *data, unsigned int len, int *w, int *h) { + SDL_Texture *texture = NULL; + SDL_Surface *surface; + SDL_RWops *src = SDL_RWFromConstMem(data, len); + if (src) { + surface = SDL_LoadBMP_RW(src, SDL_TRUE); + if (surface) { + /* Treat white as transparent */ + SDL_SetSurfaceColorKey(surface, SDL_TRUE, SDL_MapRGB(surface->format, 255, 255, 255)); + + texture = SDL_CreateTextureFromSurface(r, surface); + *w = surface->w; + *h = surface->h; + SDL_DestroySurface(surface); + } } + return texture; } static void MoveSprites(void) @@ -100,7 +109,8 @@ static void loop(void) int main(int argc, char *argv[]) { - SDL_Window *window; + SDL_Window *window = NULL; + int return_code = -1; int i; /* Enable standard application logging */ @@ -108,17 +118,25 @@ int main(int argc, char *argv[]) if (argc > 1) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "USAGE: %s\n", argv[0]); - quit(1); + return_code = 1; + goto quit; } if (SDL_CreateWindowAndRenderer(WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer) < 0) { - quit(2); + return_code = 2; + goto quit; } - sprite = LoadTexture(renderer, "icon.bmp", SDL_TRUE, &sprite_w, &sprite_h); + if (SDL_SetWindowTitle(window, argv[0]) < 0) { + SDL_Log("SDL_SetWindowTitle: %s", SDL_GetError()); + } + + sprite = CreateTexture(renderer, icon_bmp, icon_bmp_len, &sprite_w, &sprite_h); if (sprite == NULL) { - quit(2); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture (%s)", SDL_GetError()); + return_code = 3; + goto quit; } /* Initialize the sprite positions */ @@ -146,7 +164,10 @@ int main(int argc, char *argv[]) loop(); } #endif - quit(0); - - return 0; /* to prevent compiler warning */ + return_code = 0; +quit: + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + return return_code; } diff --git a/external/sdl/SDL/test/testsurround.c b/external/sdl/SDL/test/testsurround.c index c759d073..1edd1faf 100644 --- a/external/sdl/SDL/test/testsurround.c +++ b/external/sdl/SDL/test/testsurround.c @@ -96,7 +96,7 @@ static SDL_bool is_lfe_channel(int channel_index, int channel_count) return (channel_count == 3 && channel_index == 2) || (channel_count >= 6 && channel_index == 3); } -static void SDLCALL fill_buffer(SDL_AudioStream *stream, int len, void *unused) +static void SDLCALL fill_buffer(void *userdata, SDL_AudioStream *stream, int len, int totallen) { const int samples = len / sizeof(Sint16); Sint16 *buffer = NULL; @@ -193,12 +193,11 @@ int main(int argc, char *argv[]) char *devname = SDL_GetAudioDeviceName(devices[i]); int j; SDL_AudioSpec spec; - SDL_AudioDeviceID dev; SDL_Log("Testing audio device: %s\n", devname); SDL_free(devname); - if (SDL_GetAudioDeviceFormat(devices[i], &spec) != 0) { + if (SDL_GetAudioDeviceFormat(devices[i], &spec, NULL) != 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GetAudioDeviceFormat() failed: %s\n", SDL_GetError()); continue; } @@ -206,26 +205,18 @@ int main(int argc, char *argv[]) SDL_Log(" (%d channels)\n", spec.channels); spec.freq = SAMPLE_RATE_HZ; - spec.format = SDL_AUDIO_S16SYS; - - dev = SDL_OpenAudioDevice(devices[i], &spec); - if (dev == 0) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_OpenAudioDevice() failed: %s\n", SDL_GetError()); - continue; - } - - stream = SDL_CreateAndBindAudioStream(dev, &spec); - if (stream == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_CreateAndBindAudioStream() failed: %s\n", SDL_GetError()); - SDL_CloseAudioDevice(dev); - continue; - } + spec.format = SDL_AUDIO_S16; /* These are used by the fill_buffer callback */ total_channels = spec.channels; active_channel = 0; - SDL_SetAudioStreamGetCallback(stream, fill_buffer, NULL); + stream = SDL_OpenAudioDeviceStream(devices[i], &spec, fill_buffer, NULL); + if (stream == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_OpenAudioDeviceStream() failed: %s\n", SDL_GetError()); + continue; + } + SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(stream)); for (j = 0; j < total_channels; j++) { const int sine_freq = is_lfe_channel(j, total_channels) ? LFE_SINE_FREQ_HZ : SINE_FREQ_HZ; @@ -240,7 +231,6 @@ int main(int argc, char *argv[]) } } - SDL_CloseAudioDevice(dev); SDL_DestroyAudioStream(stream); } diff --git a/external/sdl/SDL/test/testwm.c b/external/sdl/SDL/test/testwm.c index 4e4057ec..9116700a 100644 --- a/external/sdl/SDL/test/testwm.c +++ b/external/sdl/SDL/test/testwm.c @@ -10,8 +10,6 @@ freely. */ -#include - #ifdef __EMSCRIPTEN__ #include #endif @@ -42,17 +40,6 @@ static SDL_Cursor *cursor = NULL; static SDL_bool relative_mode = SDL_FALSE; static const SDL_DisplayMode *highlighted_mode = NULL; -/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ -static void -quit(int rc) -{ - SDLTest_CommonQuit(state); - /* Let 'main()' return normally */ - if (rc != 0) { - exit(rc); - } -} - /* Draws the modes menu, and stores the mode index under the mouse in highlighted_mode */ static void draw_modes_menu(SDL_Window *window, SDL_Renderer *renderer, SDL_FRect viewport) @@ -298,7 +285,7 @@ int main(int argc, char *argv[]) #endif SDL_DestroyCursor(cursor); - quit(0); - /* keep the compiler happy ... */ + SDLTest_CleanupTextDrawing(); + SDLTest_CommonQuit(state); return 0; } diff --git a/external/sdl/SDL/test/testyuv.c b/external/sdl/SDL/test/testyuv.c index 7e587b84..afe3cac5 100644 --- a/external/sdl/SDL/test/testyuv.c +++ b/external/sdl/SDL/test/testyuv.c @@ -243,6 +243,7 @@ int main(int argc, char **argv) char *filename = NULL; SDL_Surface *original; SDL_Surface *converted; + SDL_Surface *bmp; SDL_Window *window; SDL_Renderer *renderer; SDL_Texture *output[3]; @@ -368,7 +369,9 @@ int main(int argc, char **argv) } filename = GetResourceFilename(filename, "testyuv.bmp"); - original = SDL_ConvertSurfaceFormat(SDL_LoadBMP(filename), SDL_PIXELFORMAT_RGB24); + bmp = SDL_LoadBMP(filename); + original = SDL_ConvertSurfaceFormat(bmp, SDL_PIXELFORMAT_RGB24); + SDL_DestroySurface(bmp); if (original == NULL) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename, SDL_GetError()); return 3; @@ -481,7 +484,13 @@ int main(int argc, char **argv) SDL_Delay(10); } } + SDL_free(raw_yuv); SDL_free(filename); + SDL_DestroySurface(original); + SDL_DestroySurface(converted); + SDLTest_CleanupTextDrawing(); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); SDL_Quit(); SDLTest_CommonDestroyState(state); return 0;