Squashed 'external/sdl/SDL/' changes from b8d91252c6..ec0042081e

ec0042081e Add .gitattributes file
a5d9db0cd0 cmake: build tests for UWP
b7889a7389 winrt: use windowsio in non-libc mode
ea8757a748 Make testaudiostreamdynamicresample compatible with emscripten
1a7a74fb2e cmake: build emscripten tests as html page
64d570f027 Add minimal http server for emscripten test apps
8e898c4a21 SDL_test does not parse --samples argument
91cd5478be audio: Fix resampler overflowing input buffer.
f290c85b22 testaudiocapture: Make sure we convert captured audio to output format.
b75c751dfc rwlock: Make generic implmentations work on single-threaded platforms.
80850af7ce The controller update complete events are no longer disabled by default
3f486224a9 Fixed refresh rate calculation for KMSDRM
342ec51131 Fix overflow when doing SDL_sscanf("%hd", ...)
9129e1d557 Fixed crash when setting the default cursor twice
8e99a4f4f5 Undo variable rename
be67f0de10 Fixed crashes related to the default cursor on WinRT and KMSDRM
94b3f78c44 Fix out of bound read of 'has_hat' array
94f48f19b0 Use more specific build destinations when creating an xcframework
dabd45997e Back out change supporting multiple names for binding elements
efe15588d5 Relabel back paddles as left or right
be884f0c95 ci: disable visionos.yml by renaming the file
ac094d00f5 ci: add workflow_dispatch event to visionos workflow
9be9e2292b build: Consistently use pathlib APIs in cmake/xxd.py
a9f6950657 Fixed deadlock shutting down Android sensors
d9f09e77f2 Actually make the sensors magical!
690eae7d22 Implement visionOS support
e385d6da0a Fixed build warning
6b93e788fa Improved sensor thread-safety
4ee0e5a984 Fixed thread-safety warnings
12deed91f8 Added information on how to enable thread-safety analysis
5735d2b03b coreaudio: Fixed assertion when device fails/quits mid-iteration.
1022fd6e04 testaudio: the test framework opens an audio device at startup; close it.
0714da37a4 audio: Fix audio stream callback calculations when future buffer has space.
917e036f6f MSVC has __declspec(deprecated)
279ff8909f Changed example code to avoid potential divide by zero
8a1afc9b10 Fixed Android not sending controller event timestamps
463c456b98 Fill the correct member with the joystick ID in SDL_EVENT_JOYSTICK_UPDATE_COMPLETE
55cf1abaa6 test: Don't flag testsurround as suitable for non-interactive use
a2d594269c Fixed pixel format compatibility with SDL2
79a190aa23 Fixed setting invalid bpp for FOURCC formats in SDL_GetMasksForPixelFormatEnum()
8fdebdd3e0 Sync SDL3 wiki -> header
b903ccf945 SDL_rwops read/write functions return size_t again
c03f5b4b69 Fixed rounding up in SDL_PrintFloat
75a020aa6b Only query serial number and firmware versions from Sony PS5 controllers
fa189d302e Added the Victrix Pro FS for PS4/PS5 to the controller list
26205b659d Fixed PS4/PS5 touchpad for third party controllers
6af0448af9 include: fixed a typo in SDL_RenderGetMetalCommandEncoder docs.
f3cb46b083 SDL_thread.h: do not conflict with sdl2-compat::sdl3_include_wrapper.h
080b1dfbdb Revert "Improved fallback for SDL_COMPILE_TIME_ASSERT() (thanks @icculus!)"
9d453daa23 Improved fallback for SDL_COMPILE_TIME_ASSERT() (thanks @icculus!)
1fb2419882 Removed reference to renamed function
e7d56dd0b2 audio: Renamed new API SDL_UnpauseAudioDevice to SDL_ResumeAudioDevice.
2b0c0f5b6b Don't pass NULL to strncmp
778e8185cd Fix size of memcpy in SDL_AudioDeviceFormatChangedAlreadyLocked And add diagnostic that allows to find this kind of issue in clang-tidy
4bb426abad Sync SDL3 wiki -> header
3a752ce650 Reapply "Changed 'freesrc' parameter from int to SDL_bool" to SDL_wave.c
2ba03b4db0 fix build after previous commit.
0026adffd4 apply force_align_arg_pointer attribute to correct version of SDL_RunApp
77446e2029 Unaligned stacks on i686-w64-mingw32 may lead to crashes
d3bcc3f057 Fixed build errors when OpenGL isn't enabled
35ad68e126 Sync SDL3 wiki -> header
70323a8350 Add a function to display the system menu for a window
be5f66c84e testaudio: Fixed soundboard icon, which had a colorkey issue.
c0a88930bf Sync SDL3 wiki -> header
18c59cc969 Merge the SDL3 audio subsystem redesign!
99b0e31788 The Steam Controller D-Pad is only pressed when the button is pressed down
103073d694 Set NSBluetoothAlwaysUsageDescription for testcontroller
ca02bb6c8c We don't need testdropfile-Info.plist
e063f662e9 Enable the controller update complete events
06bea1eb55 Added a gamepad mapping for the G-Shark GS-GP702
5ca3c50bf0 testaudio: Fix compiler warning.
1b1f02c5aa testaudio: Apparently compilers don't like this possibly being NULL now...?
2de9253b6c test: Added testaudio
fb3ab3f113 SDL_video.c: move ngage video before offscreen.
843572d993 Don't mark autorelease keys as virtual
648de4f9b8 Fixed duplicate key press/release events on iOS
a8abe612ed Only pass keypresses up the responder chain when text input is active
c3288d113e Synchronize on-screen keyboard state with text input active state
5fb92ef2f7 Fixed whitespace
f5ea6ae18d Revert "Stop beep when running iOS apps on ARM-based Macs"
546508b9b4 Allow test programs to run at full resolution on iPads
68a4bb01e0 Allocate displays as an array of pointers instead of an array of objects
07578fde3d Fixed crash if a display is enumerated twice
a509771a87 fix ios CI workflow after commit e4460e897f
72ce76905a The scheme isn't always the same as the framework name (e.g. xmp_lite vs xmp-lite)
e4460e897f By default Xcode expects the framework target name to be the name of the project.
ac683773dc Added missing tests to the "All" target
7dd56eaafe Removed unnecessary reference to testoverlay-Info.plist
e1c7f524ef Reduce the number of times SDL3 is duplicated in the xcframework script
65538011ca Make Xcode targets more specific
efe114c300 Revert "Renamed the xcframework target from "SDL.xcframework" to "xcframework""
73ed1d21a9 Renamed the xcframework target from "SDL.xcframework" to "xcframework"
76b4d8a0d8 Build the Framework instead of a static library for iOS and tvOS
d1bf979160 Removed unnecessary setting from the "Create DMG" target
c94cb3a5d8 Simplified the Xcode project to a single Framework target
ea60474c65 cmake: don't build SDL3-static Apple framework
8f00d7856d Sync SDL3 wiki -> header
d4a867a256 Rename SDL_GetPath to SDL_GetUserFolder
71099149b8 Fall back to Xlib if XRandR isn't available
b7f32f74ce Note the removal of the SDL_RENDERER_TARGETTEXTURE flag
0eda582160 testaudiostreamdynamicresample: Load sample.wav correctly.
87eae9a0a1 aaudio: We need a mixbuf on capture devices, too.
fb68e84646 wayland: Fix memory leaks
b0edd23c00 testsurround: Log available audio output devices at the start.
ae3090c387 androidaudio: Move Init/bootstrap code to bottom of source code.
18fc0db9e5 aaudio: Rearranged source code to match other backends.
2507c1d68b aaudio: Disconnect playing devices if error callback fires.
32a3fc3783 aaudio: Use the callback interface.
b49ce86765 audio: Fixed compiler warning on Android NDK.
1c074e8d97 android: Fixed audio device detection.
82ce05ad01 pulseaudio: Be more aggressive with hotplug thread synchronization.
5cbdf1168e androidaudio: Fixed incorrect JNI call (thanks, @madebr!)
660054f3dc include: Correct comment about audio device hotplug events.
ab68428a64 aaudio: Fixed for older SDKs and Android releases.
5ff87c6d4a android: Reworked audio backends for SDL3 audio API.
54af687210 testautomation_audio.c: Patched to compile.  :/
5e82090662 testautomation_audio.c: Apparently we aren't updating test code for C99 atm.
7f4488f625 wasapi: More fixes for Clang warnings.
29a0c689c9 wasapi: Patched to compile with Clang.
4aa95c21bc pspaudio: Patched to compile.
9a2a0a1463 ps2audio: Delete errant character that got inserted before previous commit.
2c578bd0d5 qnxaudio: Rewrite for SDL3 audio APIs.
455eef4cd9 audio: Use AtomicAdd for device counts, don't treat as a refcount.
095ea57f94 pspaudio: Patched to compile.
d7cf63db67 ps2audio: Patched to compile.
027b9e8787 coreaudio: (maybe) patched to compile on iOS.
4836c2db07 pspaudio: Patched to compile.
86ca412436 n3dsaudio: Patched to compile.
66bcee2ca9 testaudiostreamdynamicresample.c: Fixed MSVC compiler warning.
dbf993d358 vitaaudio: patched to compile.
5707e14716 audio: Fix up some things that broke when rebasing the branch against main.
6567285eae SDL_migration.cocci: Fix up SDL_(Pause|Unpause)Audio.
0b6255551e test: Fixed incorrect SDL_OpenAudioDevice call in testautomation.
107fd941cd vitaaudio: Clean up correctly in CloseDevice.
9fa4a6ef87 netbsdaudio: Minor fix.
b0d89868c6 n3dsaudio: Updated (but untested!) for SDL3 audio API.
ba27176106 vitaaudio: Untested attempt to move Vita audio to SDL3's audio API.
0b58e96d9e wasapi: Patched WinRT to compile.
d6b4f48488 visualc: Turn on multiprocessor compilation.
c58d95c343 wasapi: Reworked for new SDL3 audio API, other win32 fixes.
dc04f85646 audio: whoops, that should be an int.
be0dc630b7 audio: Fixed incorrect assertion
77b3fb06ee directsound: First shot at updating for SDL3 audio API.
4399b71715 audio: Generalize how backends can lookup an SDL_AudioDevice.
2fb122fe46 audio: backends now "find" instead of "obtain" devices by handle.
c3f5a5fc72 dummyaudio: SDL3ify style
7d65ff86e2 diskaudio: Adjusted for later SDL3 audio API redesign changes.
4ba9c2eade dummyaudio: Configurable delay, other SDL3 API fixes.
fb395d3ad7 sndio: Updated to the SDL3 audio API.
1a55282051 dsp: Some minor logic fixes
6bc85577d7 netbsdaudio: Updated for SDL3 audio API.
0f6e59312b netbsdaudio: Removed email address from source code.
51ae78c0af haikuaudio: Updated for SDL3 audio API.
fc7ed18ca1 emscriptenaudio: don't forget to finalize the audio thread
4233c41ce2 pulseaudio: Removed unnecessary variable.
a0528cd5ed emscriptenaudio: Updated for SDL3 audio API.
79cc29ba35 wave: Don't check if format->channels > INT_MAX, it's a Uint16.
1bfe97c235 pspaudio: Updated for SDL3 audio API.
121a2dce15 audio: Make sure `device->hidden` is NULL after CloseDevice
3d6ba0cafd ps2audio: Removed free of buffer that hasn't been allocated yet.
00ed6f8827 test: Fixed compiler warnings for unused vars.
6f12f68ec9 ps2audio: SDL3ified the style
4993743a02 ps2audio: Renamed `_this` to `device`
74568cdb2b ps2audio: Updated (but untested) for SDL3 audio API.
c83b68ef26 jack: renamed `_this` to `device`.
3f4f004794 audio: Remove an assertion that no longer makes sense.
86243b2589 jack: Use ProvidesOwnCallbackThread.
18906a32b8 jack: First shot at updating for SDL3 audio API.
a2b488359e dsp: Removed debug logging
6fd71185cd dsp: Updated for new SDL3 audio API.
3482d1215a alsa: Don't ever block in CaptureFromDevice.
65d296ef1a audio: Use SDL_powerof2 instead of reinventing it.
409b544505 alsa: Updated for new SDL3 audio API
0999a090a7 audio: More tweaking of `device->thread_alive`
f94ffd6092 audio: Fixed logic error
4deb2970c9 alsa: Renamed `_this` to `device`
0fb9e4baae audio: Remove no-longer-used SupportsNonPow2Samples
c653e57768 coreaudio: rewritten for SDL3 audio redesign!
533777eff5 audio: SDL_sysaudio.h comment conversion.
8473e522e0 audio: unify device thread naming.
258bc9efed audio: PlayDevice now passes the buffer, too, for convenience.
e518149d14 audio: Fixed locking in SDL_AudioDeviceDisconnected
22afa5735f audio: FreeDeviceHandle should pass the whole device, for convenience.
9e3c5f93e0 coreaudio: Change `_this` to `device`
e969160de0 audio: unset a freed variable to NULL
1fc01b0300 audio: Try to definitely have a default device set up.
b60a56d368 audio: take first reported device if no default was specified.
a8323ebe68 audio: Better handling of ProvidesOwnCallbackThread backends.
1dffb72c1d pipewire: Hooked up default device change notifications.
a93fcf2444 audio: fixed flushed stream reporting bytes but not being able to get them.
ad6c1781fc pulseaudio: Minor cleanups.
cfc8a0d17d pipewire: First shot at moving to the new SDL3 audio interfaces.
13202642a3 aaudio: Fixed capitialization, plus some minor cleanups.
3e9991b535 audio: Make sure we don't write to a NULL pointer.
943351affb pulseaudio: GetDefaultAudioInfo isn't a thing anymore.
11dfc4d737 test: Update testautomation_audio for SDL3 audio API.
29afc2e42b test: Update testresample for SDL3 audio API.
3a02eecced test: Update testsurround for SDL3 audio API.
e1c78718d4 test: testaudiocapture is updated for the SDL3 audio API.
f48cb716c2 pulseaudio: a couple minor tweaks.
dac25fe9eb audio: Seperate audio capture into Wait/Read operations.
3e10c0005d audio: Capture devices should respect logical device pausing.
7e700531c5 audio: Allow SDL_OpenAudioDevice to accept a NULL spec.
bb1cbbd33a test: Update testaudioinfo for SDL3 audio API.
883aee32c5 audio: Let default formats differ for output and capture devices.
62cf24eeb9 pulseaudio: Listen for server events in addition to sources and sinks.
924f370bd7 pulseaudio: Fix deadlock in HotplugThread.
5d4e9e5f80 test: Updated testaudiostreamdynamicresample to SDL3 audio API.
f883b9fc64 test: Updated testaudiohotplug to SDL3 audio API.
2be5f726d4 audio: Removed debug logging.
323ecce123 docs: Added migration note about SDL_AUDIODEVICEREMOVED.
47b0321ebf test: Removed loopwavequeue.c; obsolete in SDL3.
0e5a1d4f29 pulseaudio: Removed debug logging.
f598626e46 test: loopwave shouldn't use an audiostream callback.
eee407caf8 docs: migration guide note that SDL_LoadWAV has a different return type.
b03c493fc4 test: Updated testmultiaudio to new SDL3 audio API
fe1daf6fb5 audio: Mark disconnected default devices as "zombies".
cdd2ba81de audio: Fixed adding new physical devices to a double-linked list.
db39cbf208 audio: Allow SDL_GetAudioDeviceFormat() to query the default devices.
ee10bab3cd audio: An enormous amount of work on managing default devices.
c7a44eea83 audio: Fixed logic error.
089cd87cb5 audio: Make sure device count stays correct as hardware disconnects.
e50cb72eb6 docs: Note that audio opening doesn't implicitly init SDL now.
97b2f747d0 docs: Corrections to audio section of README-migration.md
464640440f audio: Added SDL_GetAudioStreamBinding.
01f7b53865 audio: Readded (logical) device pausing.
fd4c9f4e11 audio: documentation improvements.
4b78b789a7 audio: Switch SDL_audio.c and SDL_audiocvt.c to C99-ish syntax.
d96a1db7d7 audio: Opening via a logical device ID should also track default device.
b2e020958f audio: Wrap device access in opening of logical devices.
7ee2459927 audio: Check for unlikely failure case in WAV loaded.
3d65a2cefe audio: Made SDL_LoadWAV a real function, not just a macro.
26525f5fd3 audio: Readd SDL_AudioSpec, but just with format/channels/freq fields.
e6aaed7d79 include: Audio is not, and has not been, a raw mixing buffer for a long time.
56b1bc2198 audio: SDL_AudioStream now has callbacks for Get and Put operations.
905c4fff5b audio: First shot at the SDL3 audio subsystem redesign!
b221b59995 cmake: add SDL_REVISION option
0500fca00c Add missing break
d3f2de7f29 fixed typo in prev. patch.
12b35c6a46 test/testnativecocoa.m: fixed deprecation warnings.
e24b3e2fa4 cmake: rename SDL_TEST -> SDL_TEST_LIBRARY
da5016d336 cmake: use pkg-config + test compile instead of Find module for detecting rpi
deec574ff6 cmake: fix SDL_HIDAPI_LIBUSB
f2ae00c1ad Sync SDL3 wiki -> header
41a96c8133 doc: document building of SDL tests with CMake
3174d0b970 Sorted controller list
27b8abb056 Add Steam Deck controller mapping to database.
41d436f0fe Use SetWindowPos to show windows when SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN is set to avoid activating the parent window when showing a child window
0dc85f3078 Improved the documentation for the gamepad paddle buttons
2fff999a41 Try to create the dummy mouse cursor after video backend initialization
d086d9874d Sync SDL3 wiki -> header
bce598addd SDL_pixels.c: Fixed compiler warning on Android NDK.
ad0c0d3cde Sync SDL3 wiki -> header
f8e8dff7ee tests: Fix automated window grab and positioning tests under Wayland/XWayland
4cffbc3644 Add VS code directory to gitignore
666f81bace Add more endian-specific aliases for 32 bit pixelformats
4749df0a63 Just disable the 4214 warning instead of trying to change the structure definition

git-subtree-dir: external/sdl/SDL
git-subtree-split: ec0042081ea104d5dd0ee291105210e00a4fe3d9
This commit is contained in:
Green Sky 2023-08-12 20:17:29 +02:00
parent dec0d4ec41
commit 4d48f9d237
215 changed files with 12672 additions and 17114 deletions

View File

@ -25,6 +25,7 @@ Checks: >
clang-analyzer-core.*, clang-analyzer-core.*,
clang-analyzer-valist.*, clang-analyzer-valist.*,
clang-analyzer-unix.Malloc, clang-analyzer-unix.Malloc,
clang-diagnostic-*,
google-readability-casting, google-readability-casting,
misc-misleading-bidirectional, misc-misleading-bidirectional,
misc-misleading-identifier, misc-misleading-identifier,

12
.gitattributes vendored Normal file
View File

@ -0,0 +1,12 @@
*.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

View File

@ -15,8 +15,8 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
platform: platform:
- { name: iOS, target: Static Library-iOS, sdk: iphoneos } - { name: iOS, target: SDL3, sdk: iphoneos }
- { name: tvOS, target: Static Library-tvOS, sdk: appletvos } - { name: tvOS, target: SDL3, sdk: appletvos }
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

View File

@ -30,7 +30,7 @@ jobs:
- { name: Intel Compiler (Ubuntu 20.04), os: ubuntu-20.04, shell: bash, artifact: 'SDL-ubuntu20.04-icc', intel: true, cmake: '-DSDL_CLANG_TIDY=OFF', - { name: Intel Compiler (Ubuntu 20.04), os: ubuntu-20.04, shell: bash, artifact: 'SDL-ubuntu20.04-icc', intel: true, cmake: '-DSDL_CLANG_TIDY=OFF',
source_cmd: 'source /opt/intel/oneapi/setvars.sh; export CC=icc; export CXX=icpc; export CFLAGS=-diag-disable=10441; export CXXFLAGS=-diag-disable=10441; '} source_cmd: 'source /opt/intel/oneapi/setvars.sh; export CC=icc; export CXX=icpc; export CFLAGS=-diag-disable=10441; export CXXFLAGS=-diag-disable=10441; '}
- { name: Ubuntu 22.04, os: ubuntu-22.04, shell: sh, artifact: 'SDL-ubuntu22.04' } - { name: Ubuntu 22.04, os: ubuntu-22.04, shell: sh, artifact: 'SDL-ubuntu22.04' }
- { name: MacOS (Framework), os: macos-latest, shell: sh, cmake: '-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DSDL_FRAMEWORK=ON -DSDL_CLANG_TIDY=OFF', skip_test_pkgconfig: true, artifact: 'SDL-macos-framework' } - { name: MacOS (Framework), os: macos-latest, shell: sh, cmake: '-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DSDL_FRAMEWORK=ON -DSDL_CLANG_TIDY=OFF', skip_test_pkgconfig: true, artifact: 'SDL-macos-framework', no-static: true }
- { name: MacOS (GNU prefix), os: macos-latest, shell: sh, cmake: '-DCMAKE_OSX_ARCHITECTURES="x86_64" -DCLANG_TIDY_BINARY="$(brew --prefix llvm)/bin/clang-tidy"', artifact: 'SDL-macos-gnu' } - { name: MacOS (GNU prefix), os: macos-latest, shell: sh, cmake: '-DCMAKE_OSX_ARCHITECTURES="x86_64" -DCLANG_TIDY_BINARY="$(brew --prefix llvm)/bin/clang-tidy"', artifact: 'SDL-macos-gnu' }
steps: steps:
@ -130,7 +130,7 @@ jobs:
${{ matrix.platform.source_cmd }} ${{ matrix.platform.source_cmd }}
cmake -S cmake/test -B cmake_config_build -G Ninja \ cmake -S cmake/test -B cmake_config_build -G Ninja \
-DTEST_SHARED=ON \ -DTEST_SHARED=ON \
-DTEST_STATIC=ON \ -DTEST_STATIC=${{ !matrix.platform.no-static }} \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=$(echo "${{ github.workspace }}/cmake_prefix" | sed -e 's#\\#/#g') -DCMAKE_PREFIX_PATH=$(echo "${{ github.workspace }}/cmake_prefix" | sed -e 's#\\#/#g')
cmake --build cmake_config_build --verbose cmake --build cmake_config_build --verbose

View File

@ -23,7 +23,7 @@ jobs:
- { name: Windows (clang-cl x86), flags: -T ClangCL -A Win32, artifact: 'SDL-clang-cl-x86' } - { name: Windows (clang-cl x86), flags: -T ClangCL -A Win32, artifact: 'SDL-clang-cl-x86' }
- { name: Windows (ARM), flags: -A ARM, artifact: 'SDL-VC-arm32' } - { name: Windows (ARM), flags: -A ARM, artifact: 'SDL-VC-arm32' }
- { name: Windows (ARM64), flags: -A ARM64, artifact: 'SDL-VC-arm64' } - { name: Windows (ARM64), flags: -A ARM64, artifact: 'SDL-VC-arm64' }
- { name: UWP (x64), flags: -A x64 -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0" -DSDL_TESTS=OFF, nowerror: true, - { name: UWP (x64), flags: -A x64 -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0", nowerror: true,
project: VisualC-WinRT/SDL-UWP.sln, projectflags: '/p:Platform=x64 /p:WindowsTargetPlatformVersion=10.0.17763.0', artifact: 'SDL-VC-UWP' } project: VisualC-WinRT/SDL-UWP.sln, projectflags: '/p:Platform=x64 /p:WindowsTargetPlatformVersion=10.0.17763.0', artifact: 'SDL-VC-UWP' }
steps: steps:
@ -70,7 +70,6 @@ jobs:
run: | run: |
cmake --build build/ --config Release --target PACKAGE cmake --build build/ --config Release --target PACKAGE
- name: Verify CMake configuration files - name: Verify CMake configuration files
if: ${{ !contains(matrix.platform.name, 'UWP') }} # FIXME: cmake/test/CMakeLists.txt should support UWP
run: | run: |
cmake -S cmake/test -B cmake_config_build ` cmake -S cmake/test -B cmake_config_build `
-DCMAKE_PREFIX_PATH=${{ env.SDL3_DIR }} ` -DCMAKE_PREFIX_PATH=${{ env.SDL3_DIR }} `

22
.github/workflows/visionos.yml.disabled vendored Normal file
View File

@ -0,0 +1,22 @@
name: Build (visionOS)
# FIXME: Enable this workflow once CMake 3.28 becomes available on GitHub
on: [push, pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
jobs:
Build:
name: visionOS
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Configure
run: |
cmake -B build -GXcode -DCMAKE_SYSTEM_NAME=visionOS
- name: Build
run: |
cmake --build build

3
.gitignore vendored
View File

@ -50,6 +50,9 @@ cmake-build-*
xcuserdata xcuserdata
*.xcworkspace *.xcworkspace
# for Visual Studio Code
.vscode/
# for Visual C++ # for Visual C++
.vs .vs
Debug Debug

View File

@ -131,7 +131,7 @@ endif()
# so we'll just use libusb when it's available. libusb does not support iOS, # so we'll just use libusb when it's available. libusb does not support iOS,
# so we default to yes on iOS. # so we default to yes on iOS.
# TODO: Windows can support libusb, the hid.c file just depends on Unix APIs # TODO: Windows can support libusb, the hid.c file just depends on Unix APIs
if((WINDOWS AND NOT WINDOWS_STORE) OR IOS OR TVOS OR ANDROID) if((WINDOWS AND NOT WINDOWS_STORE) OR IOS OR TVOS OR VISIONOS OR ANDROID)
set(SDL_HIDAPI_LIBUSB_AVAILABLE FALSE) set(SDL_HIDAPI_LIBUSB_AVAILABLE FALSE)
else() else()
set(SDL_HIDAPI_LIBUSB_AVAILABLE TRUE) set(SDL_HIDAPI_LIBUSB_AVAILABLE TRUE)
@ -198,6 +198,7 @@ set(SDL_SHARED_DEFAULT ON)
set(SDL_STATIC_DEFAULT ON) set(SDL_STATIC_DEFAULT ON)
set(SDL_SHARED_AVAILABLE ON) set(SDL_SHARED_AVAILABLE ON)
set(SDL_STATIC_AVAILABLE ON)
# All these *_DEFAULT vars will default to ON if not specified, # All these *_DEFAULT vars will default to ON if not specified,
# so you only need to override them if they need to be disabled. # so you only need to override them if they need to be disabled.
@ -263,6 +264,11 @@ foreach(_SUB IN LISTS SDL_SUBSYSTEMS)
option(SDL_${_OPT} "Enable the ${_SUB} subsystem" ${SDL_${_OPT}_DEFAULT}) option(SDL_${_OPT} "Enable the ${_SUB} subsystem" ${SDL_${_OPT}_DEFAULT})
endforeach() endforeach()
cmake_dependent_option(SDL_FRAMEWORK "Build SDL libraries as Apple Framework" OFF "APPLE" OFF)
if(SDL_FRAMEWORK)
set(SDL_STATIC_AVAILABLE FALSE)
endif()
# Allow some projects to be built conditionally. # Allow some projects to be built conditionally.
set_option(SDL_DISABLE_INSTALL "Disable installation of SDL3" ${SDL3_SUBPROJECT}) set_option(SDL_DISABLE_INSTALL "Disable installation of SDL3" ${SDL3_SUBPROJECT})
cmake_dependent_option(SDL_DISABLE_INSTALL_CPACK "Create binary SDL3 archive using CPack" ${SDL3_SUBPROJECT} "NOT SDL_DISABLE_INSTALL" ON) cmake_dependent_option(SDL_DISABLE_INSTALL_CPACK "Create binary SDL3 archive using CPack" ${SDL3_SUBPROJECT} "NOT SDL_DISABLE_INSTALL" ON)
@ -297,8 +303,8 @@ set_option(SDL_DISKAUDIO "Support the disk writer audio driver" ON)
set_option(SDL_DUMMYAUDIO "Support the dummy audio driver" ON) set_option(SDL_DUMMYAUDIO "Support the dummy audio driver" ON)
set_option(SDL_DUMMYVIDEO "Use dummy video driver" ON) set_option(SDL_DUMMYVIDEO "Use dummy video driver" ON)
dep_option(SDL_IBUS "Enable IBus support" ON ${UNIX_SYS} OFF) dep_option(SDL_IBUS "Enable IBus support" ON ${UNIX_SYS} OFF)
set_option(SDL_OPENGL "Include OpenGL support" ON) dep_option(SDL_OPENGL "Include OpenGL support" ON "NOT VISIONOS" OFF)
set_option(SDL_OPENGLES "Include OpenGL ES support" ON) dep_option(SDL_OPENGLES "Include OpenGL ES support" ON "NOT VISIONOS" OFF)
set_option(SDL_PTHREADS "Use POSIX threads for multi-threading" ${SDL_PTHREADS_DEFAULT}) set_option(SDL_PTHREADS "Use POSIX threads for multi-threading" ${SDL_PTHREADS_DEFAULT})
dep_option(SDL_PTHREADS_SEM "Use pthread semaphores" ON "SDL_PTHREADS" OFF) dep_option(SDL_PTHREADS_SEM "Use pthread semaphores" ON "SDL_PTHREADS" OFF)
dep_option(SDL_OSS "Support the OSS audio API" ON "UNIX_SYS OR RISCOS" OFF) dep_option(SDL_OSS "Support the OSS audio API" ON "UNIX_SYS OR RISCOS" OFF)
@ -342,7 +348,7 @@ dep_option(SDL_KMSDRM_SHARED "Dynamically load KMS DRM support" ON "SDL_KM
set_option(SDL_OFFSCREEN "Use offscreen video driver" ON) 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_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) option_string(SDL_FOREGROUNDING_SIGNAL "number to use for magic foregrounding signal or 'OFF'" OFF)
set_option(SDL_HIDAPI "Enable the HIDAPI subsystem" ON) 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" ${SDL_HIDAPI_LIBUSB_DEFAULT} "SDL_HIDAPI;${SDL_HIDAPI_LIBUSB_AVAILABLE}" OFF)
dep_option(SDL_HIDAPI_JOYSTICK "Use HIDAPI for low level joystick drivers" ON SDL_HIDAPI 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) dep_option(SDL_VIRTUAL_JOYSTICK "Enable the virtual-joystick driver" ON SDL_HIDAPI OFF)
@ -354,15 +360,12 @@ set_option(SDL_CLANG_TIDY "Run clang-tidy static analysis" OFF)
set(SDL_VENDOR_INFO "" CACHE STRING "Vendor name and/or version to add to SDL_REVISION") set(SDL_VENDOR_INFO "" CACHE STRING "Vendor name and/or version to add to SDL_REVISION")
cmake_dependent_option(SDL_SHARED "Build a shared version of the library" ${SDL_SHARED_DEFAULT} ${SDL_SHARED_AVAILABLE} OFF) cmake_dependent_option(SDL_SHARED "Build a shared version of the library" ${SDL_SHARED_DEFAULT} ${SDL_SHARED_AVAILABLE} OFF)
option(SDL_STATIC "Build a static version of the library" ${SDL_STATIC_DEFAULT}) cmake_dependent_option(SDL_STATIC "Build a static version of the library" ${SDL_STATIC_DEFAULT} ${SDL_STATIC_AVAILABLE} OFF)
option(SDL_TEST "Build the SDL3_test library" ON) option(SDL_TEST_LIBRARY "Build the SDL3_test library" ON)
# Apple Frameworks NEED a (shared) SDL3.framework for `#include <SDL3/xxx.h>` to work
cmake_dependent_option(SDL_FRAMEWORK "Build SDL libraries as Apple Framework" OFF "SDL_SHARED;APPLE" OFF)
dep_option(SDL_STATIC_PIC "Static version of the library should be built with Position Independent Code" "${CMAKE_POSITION_INDEPENDENT_CODE}" "SDL_STATIC" OFF) dep_option(SDL_STATIC_PIC "Static version of the library should be built with Position Independent Code" "${CMAKE_POSITION_INDEPENDENT_CODE}" "SDL_STATIC" OFF)
dep_option(SDL_TESTS "Build the test directory" OFF SDL_TEST OFF) dep_option(SDL_TESTS "Build the test directory" OFF SDL_TEST_LIBRARY OFF)
dep_option(SDL_INSTALL_TESTS "Install test-cases" OFF "NOT SDL_DISABLE_INSTALL;NOT SDL_FRAMEWORK" OFF) dep_option(SDL_INSTALL_TESTS "Install test-cases" OFF "NOT SDL_DISABLE_INSTALL;NOT SDL_FRAMEWORK;NOT WINDOWS_STORE" OFF)
dep_option(SDL_TESTS_LINK_SHARED "link tests to shared SDL library" "${SDL_SHARED}" "SDL_SHARED;SDL_STATIC" "${SDL_SHARED}") dep_option(SDL_TESTS_LINK_SHARED "link tests to shared SDL library" "${SDL_SHARED}" "SDL_SHARED;SDL_STATIC" "${SDL_SHARED}")
set(SDL_TESTS_TIMEOUT_MULTIPLIER "1" CACHE STRING "Timeout multiplier to account for really slow machines") set(SDL_TESTS_TIMEOUT_MULTIPLIER "1" CACHE STRING "Timeout multiplier to account for really slow machines")
@ -391,7 +394,7 @@ if(SDL_STATIC)
target_compile_features(SDL3-static PRIVATE c_std_99) target_compile_features(SDL3-static PRIVATE c_std_99)
endif() endif()
if(SDL_TEST) if(SDL_TEST_LIBRARY)
add_library(SDL3_test STATIC) add_library(SDL3_test STATIC)
add_library(SDL3::SDL3_test ALIAS SDL3_test) add_library(SDL3::SDL3_test ALIAS SDL3_test)
SDL_AddCommonCompilerFlags(SDL3_test) SDL_AddCommonCompilerFlags(SDL3_test)
@ -2028,7 +2031,7 @@ elseif(APPLE)
endif() endif()
if(SDL_MISC) if(SDL_MISC)
if(IOS OR TVOS) if(IOS OR TVOS OR VISIONOS)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/misc/ios/*.m") sdl_glob_sources("${SDL3_SOURCE_DIR}/src/misc/ios/*.m")
else() else()
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/misc/macos/*.m") sdl_glob_sources("${SDL3_SOURCE_DIR}/src/misc/macos/*.m")
@ -2051,10 +2054,10 @@ elseif(APPLE)
if(SDL_JOYSTICK) if(SDL_JOYSTICK)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/apple/*.m") sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/apple/*.m")
if(IOS OR TVOS) if(IOS OR TVOS OR VISIONOS)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/steam/*.c") sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/steam/*.c")
set(SDL_JOYSTICK_MFI 1) set(SDL_JOYSTICK_MFI 1)
if(IOS) if(IOS OR VISIONOS)
set(SDL_FRAMEWORK_COREMOTION 1) set(SDL_FRAMEWORK_COREMOTION 1)
endif() endif()
set(SDL_FRAMEWORK_GAMECONTROLLER 1) set(SDL_FRAMEWORK_GAMECONTROLLER 1)
@ -2086,15 +2089,17 @@ elseif(APPLE)
set(SDL_FRAMEWORK_GAMECONTROLLER 1) set(SDL_FRAMEWORK_GAMECONTROLLER 1)
set(SDL_FRAMEWORK_COREHAPTICS 1) set(SDL_FRAMEWORK_COREHAPTICS 1)
endif() endif()
if(NOT VISIONOS)
set(SDL_JOYSTICK_IOKIT 1) set(SDL_JOYSTICK_IOKIT 1)
set(SDL_FRAMEWORK_IOKIT 1) set(SDL_FRAMEWORK_IOKIT 1)
endif()
set(SDL_FRAMEWORK_FF 1) set(SDL_FRAMEWORK_FF 1)
endif() endif()
set(HAVE_SDL_JOYSTICK TRUE) set(HAVE_SDL_JOYSTICK TRUE)
endif() endif()
if(SDL_HAPTIC) if(SDL_HAPTIC)
if (IOS OR TVOS) if (IOS OR TVOS OR VISIONOS)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/haptic/dummy/*.c") sdl_glob_sources("${SDL3_SOURCE_DIR}/src/haptic/dummy/*.c")
set(SDL_HAPTIC_DUMMY 1) set(SDL_HAPTIC_DUMMY 1)
else() else()
@ -2107,7 +2112,7 @@ elseif(APPLE)
endif() endif()
if(SDL_POWER) if(SDL_POWER)
if (IOS OR TVOS) if (IOS OR TVOS OR VISIONOS)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/power/uikit/*.m") sdl_glob_sources("${SDL3_SOURCE_DIR}/src/power/uikit/*.m")
set(SDL_POWER_UIKIT 1) set(SDL_POWER_UIKIT 1)
else() else()
@ -2136,7 +2141,7 @@ elseif(APPLE)
endif() endif()
if(SDL_SENSOR) if(SDL_SENSOR)
if(IOS) if(IOS OR VISIONOS)
set(SDL_SENSOR_COREMOTION 1) set(SDL_SENSOR_COREMOTION 1)
set(HAVE_SDL_SENSORS TRUE) set(HAVE_SDL_SENSORS TRUE)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/sensor/coremotion/*.m") sdl_glob_sources("${SDL3_SOURCE_DIR}/src/sensor/coremotion/*.m")
@ -2145,7 +2150,7 @@ elseif(APPLE)
# iOS hack needed - http://code.google.com/p/ios-cmake/ ? # iOS hack needed - http://code.google.com/p/ios-cmake/ ?
if(SDL_VIDEO) if(SDL_VIDEO)
if (IOS OR TVOS) if (IOS OR TVOS OR VISIONOS)
set(SDL_VIDEO_DRIVER_UIKIT 1) set(SDL_VIDEO_DRIVER_UIKIT 1)
set(SDL_FRAMEWORK_COREGRAPHICS 1) set(SDL_FRAMEWORK_COREGRAPHICS 1)
set(SDL_FRAMEWORK_QUARTZCORE 1) set(SDL_FRAMEWORK_QUARTZCORE 1)
@ -2165,7 +2170,7 @@ elseif(APPLE)
endif() endif()
if(SDL_OPENGLES) if(SDL_OPENGLES)
if(IOS OR TVOS) if(IOS OR TVOS OR VISIONOS)
set(SDL_FRAMEWORK_OPENGLES 1) set(SDL_FRAMEWORK_OPENGLES 1)
set(SDL_VIDEO_OPENGL_ES 1) set(SDL_VIDEO_OPENGL_ES 1)
else() else()
@ -2250,7 +2255,7 @@ elseif(APPLE)
endif() endif()
endif() endif()
if(SDL_FRAMEWORK_METAL) if(SDL_FRAMEWORK_METAL)
if(IOS OR TVOS) if(IOS OR TVOS OR VISIONOS)
sdl_link_dependency(metal LINK_OPTIONS "-Wl,-framework,Metal") sdl_link_dependency(metal LINK_OPTIONS "-Wl,-framework,Metal")
else() else()
sdl_link_dependency(metal LINK_OPTIONS "-Wl,-weak_framework,Metal") sdl_link_dependency(metal LINK_OPTIONS "-Wl,-weak_framework,Metal")
@ -2260,7 +2265,7 @@ elseif(APPLE)
sdl_link_dependency(opengles LINK_OPTIONS "-Wl,-framework,OpenGLES") sdl_link_dependency(opengles LINK_OPTIONS "-Wl,-framework,OpenGLES")
endif() endif()
if(SDL_FRAMEWORK_QUARTZCORE) if(SDL_FRAMEWORK_QUARTZCORE)
if(IOS OR TVOS) if(IOS OR TVOS OR VISIONOS)
sdl_link_dependency(quartz_core LINK_OPTIONS "-Wl,-framework,QuartzCore") sdl_link_dependency(quartz_core LINK_OPTIONS "-Wl,-framework,QuartzCore")
else() else()
sdl_link_dependency(metal LINK_OPTIONS "-Wl,-weak_framework,QuartzCore") sdl_link_dependency(metal LINK_OPTIONS "-Wl,-weak_framework,QuartzCore")
@ -2796,6 +2801,8 @@ foreach(_hdr IN LISTS SDL3_INCLUDE_FILES)
endif() endif()
endforeach() endforeach()
set(SDL_REVISION "" CACHE STRING "Custom SDL revision (overrides SDL_REVISION_SUFFIX)")
if(NOT SDL_REVISION)
set(SDL_REVISION_SUFFIX "" CACHE STRING "Suffix for the SDL revision") set(SDL_REVISION_SUFFIX "" CACHE STRING "Suffix for the SDL revision")
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION.txt") if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION.txt")
# If VERSION exists, it contains the SDL version # If VERSION exists, it contains the SDL version
@ -2809,6 +2816,7 @@ else()
endif() endif()
endif() endif()
set(SDL_REVISION "SDL-${SDL_REVISION_CENTER}${SDL_REVISION_SUFFIX}") set(SDL_REVISION "SDL-${SDL_REVISION_CENTER}${SDL_REVISION_SUFFIX}")
endif()
execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${SDL3_BINARY_DIR}/include/SDL3") execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${SDL3_BINARY_DIR}/include/SDL3")
configure_file(include/build_config/SDL_revision.h.cmake include/SDL3/SDL_revision.h @ONLY) configure_file(include/build_config/SDL_revision.h.cmake include/SDL3/SDL_revision.h @ONLY)
@ -3094,18 +3102,6 @@ if(SDL_STATIC)
OUTPUT_NAME "${sdl_static_libname}" OUTPUT_NAME "${sdl_static_libname}"
POSITION_INDEPENDENT_CODE "${SDL_STATIC_PIC}" POSITION_INDEPENDENT_CODE "${SDL_STATIC_PIC}"
) )
if(APPLE)
set_target_properties(SDL3-static PROPERTIES
FRAMEWORK "${SDL_FRAMEWORK}"
)
if(SDL_FRAMEWORK)
set_target_properties(SDL3-static PROPERTIES
FRAMEWORK_VERSION "${SDL_FRAMEWORK_VERSION}"
MACOSX_FRAMEWORK_IDENTIFIER "org.libsdl.SDL3-static"
RESOURCE "${SDL_FRAMEWORK_RESOURCES}"
)
endif()
endif()
target_compile_definitions(SDL3-static PRIVATE SDL_STATIC_LIB) target_compile_definitions(SDL3-static PRIVATE SDL_STATIC_LIB)
target_link_libraries(SDL3-static PRIVATE ${SDL_CMAKE_DEPENDS}) target_link_libraries(SDL3-static PRIVATE ${SDL_CMAKE_DEPENDS})
target_include_directories(SDL3-static target_include_directories(SDL3-static
@ -3135,7 +3131,7 @@ sdl_compile_definitions(
##### Tests ##### ##### Tests #####
if(SDL_TEST) if(SDL_TEST_LIBRARY)
file(GLOB TEST_SOURCES "${SDL3_SOURCE_DIR}/src/test/*.c") file(GLOB TEST_SOURCES "${SDL3_SOURCE_DIR}/src/test/*.c")
target_sources(SDL3_test PRIVATE ${TEST_SOURCES}) target_sources(SDL3_test PRIVATE ${TEST_SOURCES})
if(APPLE) if(APPLE)
@ -3193,12 +3189,6 @@ if(SDL_FRAMEWORK)
set(SDL_SDL_INSTALL_REAL_RESOURCEDIR "SDL3.framework/Versions/${SDL_FRAMEWORK_VERSION}/Resources") set(SDL_SDL_INSTALL_REAL_RESOURCEDIR "SDL3.framework/Versions/${SDL_FRAMEWORK_VERSION}/Resources")
set(SDL_SDL_INSTALL_REAL_CMAKEDIR "${SDL_SDL_INSTALL_REAL_RESOURCEDIR}/CMake") set(SDL_SDL_INSTALL_REAL_CMAKEDIR "${SDL_SDL_INSTALL_REAL_RESOURCEDIR}/CMake")
# - Install other SDL3*Config.cmake files in SDL3*.framework/Resources/CMake
# - The *_RELATIVE_CMAKEDIR variables are the symlinked folders visible from outside
set(SDL_SDLstatic_INSTALL_RESOURCEDIR "SDL3-static.framework/Resources")
set(SDL_SDLstatic_INSTALL_CMAKEDIR "${SDL_SDLstatic_INSTALL_RESOURCEDIR}/CMake")
set(SDL_SDLstatic_INSTALL_CMAKEFILENAME "SDL3-staticConfig.cmake")
set(SDL_SDLtest_INSTALL_RESOURCEDIR "SDL3_test.framework/Resources") set(SDL_SDLtest_INSTALL_RESOURCEDIR "SDL3_test.framework/Resources")
set(SDL_SDLtest_INSTALL_CMAKEDIR "${SDL_SDLtest_INSTALL_RESOURCEDIR}/CMake") set(SDL_SDLtest_INSTALL_CMAKEDIR "${SDL_SDLtest_INSTALL_RESOURCEDIR}/CMake")
set(SDL_SDLtest_INSTALL_CMAKEFILENAME "SDL3_testConfig.cmake") set(SDL_SDLtest_INSTALL_CMAKEFILENAME "SDL3_testConfig.cmake")
@ -3227,7 +3217,7 @@ if(SDL_STATIC)
export(TARGETS SDL3-static NAMESPACE "SDL3::" FILE "SDL3staticTargets.cmake") export(TARGETS SDL3-static NAMESPACE "SDL3::" FILE "SDL3staticTargets.cmake")
endif() endif()
if(SDL_TEST) if(SDL_TEST_LIBRARY)
export(TARGETS SDL3_test NAMESPACE "SDL3::" FILE "SDL3testTargets.cmake") export(TARGETS SDL3_test NAMESPACE "SDL3::" FILE "SDL3testTargets.cmake")
endif() endif()
@ -3285,7 +3275,7 @@ if(NOT SDL_DISABLE_INSTALL)
) )
endif() endif()
if(SDL_TEST) if(SDL_TEST_LIBRARY)
install(TARGETS SDL3_test EXPORT SDL3testTargets install(TARGETS SDL3_test EXPORT SDL3testTargets
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
FRAMEWORK DESTINATION "." FRAMEWORK DESTINATION "."
@ -3317,7 +3307,7 @@ if(NOT SDL_DISABLE_INSTALL)
) )
endif() endif()
if(SDL_TEST) if(SDL_TEST_LIBRARY)
install(EXPORT SDL3testTargets install(EXPORT SDL3testTargets
FILE "${SDL_SDLtest_INSTALL_CMAKEFILENAME}" FILE "${SDL_SDLtest_INSTALL_CMAKEFILENAME}"
NAMESPACE SDL3:: NAMESPACE SDL3::
@ -3336,7 +3326,7 @@ if(NOT SDL_DISABLE_INSTALL)
install(FILES ${SDL3_INCLUDE_FILES} install(FILES ${SDL3_INCLUDE_FILES}
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/SDL3" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/SDL3"
) )
if(SDL_TEST) if(SDL_TEST_LIBRARY)
install(FILES ${SDL3_TEST_INCLUDE_FILES} install(FILES ${SDL3_TEST_INCLUDE_FILES}
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/SDL3" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/SDL3"
) )

View File

@ -122,6 +122,7 @@
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>SDL_internal.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>SDL_internal.h</PrecompiledHeaderFile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile> </ClCompile>
<ResourceCompile> <ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -153,6 +154,7 @@
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>SDL_internal.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>SDL_internal.h</PrecompiledHeaderFile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile> </ClCompile>
<ResourceCompile> <ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -188,6 +190,7 @@
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>SDL_internal.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>SDL_internal.h</PrecompiledHeaderFile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile> </ClCompile>
<ResourceCompile> <ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -220,6 +223,7 @@
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>SDL_internal.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>SDL_internal.h</PrecompiledHeaderFile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile> </ClCompile>
<ResourceCompile> <ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>

View File

@ -100,6 +100,7 @@
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>OldStyle</DebugInformationFormat> <DebugInformationFormat>OldStyle</DebugInformationFormat>
<OmitDefaultLibName>true</OmitDefaultLibName> <OmitDefaultLibName>true</OmitDefaultLibName>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile> </ClCompile>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -116,6 +117,7 @@
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>OldStyle</DebugInformationFormat> <DebugInformationFormat>OldStyle</DebugInformationFormat>
<OmitDefaultLibName>true</OmitDefaultLibName> <OmitDefaultLibName>true</OmitDefaultLibName>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile> </ClCompile>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
@ -134,6 +136,7 @@
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>OldStyle</DebugInformationFormat> <DebugInformationFormat>OldStyle</DebugInformationFormat>
<OmitDefaultLibName>true</OmitDefaultLibName> <OmitDefaultLibName>true</OmitDefaultLibName>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile> </ClCompile>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -150,6 +153,7 @@
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>OldStyle</DebugInformationFormat> <DebugInformationFormat>OldStyle</DebugInformationFormat>
<OmitDefaultLibName>true</OmitDefaultLibName> <OmitDefaultLibName>true</OmitDefaultLibName>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile> </ClCompile>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>public.data</string>
</array>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>org.libsdl.test-dropfile</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSMinimumSystemVersion</key>
<string>10.7</string>
</dict>
</plist>

View File

@ -7,8 +7,8 @@
// https://help.apple.com/xcode/#/dev745c5c974 // https://help.apple.com/xcode/#/dev745c5c974
// Include any optional config for this build // Include any optional config for this build
// This allows you to set DEVELOPMENT_TEAM for all targets, for example.
#include? "build.xcconfig" #include? "build.xcconfig"
CONFIG_FRAMEWORK_LDFLAGS[sdk=macos*] = $(inherited) -framework SDL3 -framework AudioToolbox -framework Carbon -framework Cocoa -framework CoreAudio -framework CoreHaptics -framework CoreVideo -framework ForceFeedback -framework GameController -framework IOKit -framework Metal CONFIG_FRAMEWORK_LDFLAGS = -lSDL3_test
CONFIG_FRAMEWORK_LDFLAGS[sdk=iphone*] = $(inherited) -framework SDL3 -framework AVFoundation -framework AudioToolbox -framework CoreGraphics -framework CoreHaptics -framework CoreMotion -framework Foundation -framework GameController -framework Metal -framework OpenGLES -framework QuartzCore -framework UIKit
CONFIG_FRAMEWORK_LDFLAGS[sdk=appletv*] = $(inherited) -framework SDL3 -framework AVFoundation -framework AudioToolbox -framework CoreGraphics -framework CoreHaptics -framework Foundation -framework GameController -framework Metal -framework OpenGLES -framework QuartzCore -framework UIKit

View File

@ -21,8 +21,6 @@ public class SDLAudioManager {
protected static AudioRecord mAudioRecord; protected static AudioRecord mAudioRecord;
protected static Context mContext; protected static Context mContext;
private static final int[] NO_DEVICES = {};
private static AudioDeviceCallback mAudioDeviceCallback; private static AudioDeviceCallback mAudioDeviceCallback;
public static void initialize() { public static void initialize() {
@ -36,7 +34,7 @@ public class SDLAudioManager {
@Override @Override
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) { public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
for (AudioDeviceInfo deviceInfo : addedDevices) { for (AudioDeviceInfo deviceInfo : addedDevices) {
addAudioDevice(deviceInfo.isSink(), deviceInfo.getId()); addAudioDevice(deviceInfo.isSink(), deviceInfo.getProductName().toString(), deviceInfo.getId());
} }
} }
@ -52,13 +50,10 @@ public class SDLAudioManager {
public static void setContext(Context context) { public static void setContext(Context context) {
mContext = context; mContext = context;
if (context != null) {
registerAudioDeviceCallback();
}
} }
public static void release(Context context) { public static void release(Context context) {
unregisterAudioDeviceCallback(context); // no-op atm
} }
// Audio // Audio
@ -311,65 +306,30 @@ public class SDLAudioManager {
return null; return null;
} }
private static void registerAudioDeviceCallback() { public static void registerAudioDeviceCallback() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
// get an initial list now, before hotplug callbacks fire.
for (AudioDeviceInfo dev : audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) {
if (dev.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
continue; // Device cannot be opened
}
addAudioDevice(dev.isSink(), dev.getProductName().toString(), dev.getId());
}
for (AudioDeviceInfo dev : audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) {
addAudioDevice(dev.isSink(), dev.getProductName().toString(), dev.getId());
}
audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null); audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
} }
} }
private static void unregisterAudioDeviceCallback(Context context) { public static void unregisterAudioDeviceCallback() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback); audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
} }
} }
private static int[] ArrayListToArray(ArrayList<Integer> integers)
{
int[] ret = new int[integers.size()];
for (int i=0; i < ret.length; i++) {
ret[i] = integers.get(i).intValue();
}
return ret;
}
/**
* This method is called by SDL using JNI.
*/
public static int[] getAudioOutputDevices() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
ArrayList<Integer> arrlist = new ArrayList<Integer>();
for (AudioDeviceInfo dev : audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) {
/* Device cannot be opened */
if (dev.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
continue;
}
arrlist.add(dev.getId());
}
return ArrayListToArray(arrlist);
} else {
return NO_DEVICES;
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] getAudioInputDevices() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
ArrayList<Integer> arrlist = new ArrayList<Integer>();
for (AudioDeviceInfo dev : audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) {
arrlist.add(dev.getId());
}
return ArrayListToArray(arrlist);
} else {
return NO_DEVICES;
}
}
/** /**
* This method is called by SDL using JNI. * This method is called by SDL using JNI.
*/ */
@ -535,6 +495,6 @@ public class SDLAudioManager {
public static native void removeAudioDevice(boolean isCapture, int deviceId); public static native void removeAudioDevice(boolean isCapture, int deviceId);
public static native void addAudioDevice(boolean isCapture, int deviceId); public static native void addAudioDevice(boolean isCapture, String name, int deviceId);
} }

View File

@ -308,10 +308,10 @@ expression e;
+ SDL_PauseAudioDevice(e) + SDL_PauseAudioDevice(e)
| |
- SDL_PauseAudioDevice(e, 0) - SDL_PauseAudioDevice(e, 0)
+ SDL_PlayAudioDevice(e) + SDL_ResumeAudioDevice(e)
| |
- SDL_PauseAudioDevice(e, SDL_FALSE) - SDL_PauseAudioDevice(e, SDL_FALSE)
+ SDL_PlayAudioDevice(e) + SDL_ResumeAudioDevice(e)
) )
@@ @@
@ -321,7 +321,7 @@ expression e, pause_on;
+ if (pause_on) { + if (pause_on) {
+ SDL_PauseAudioDevice(e); + SDL_PauseAudioDevice(e);
+ } else { + } else {
+ SDL_PlayAudioDevice(e); + SDL_ResumeAudioDevice(e);
+ } + }
@ -940,19 +940,19 @@ typedef SDL_ControllerTouchpadEvent, SDL_GamepadTouchpadEvent;
@@ @@
@@ @@
- SDL_CONTROLLER_BUTTON_PADDLE1 - SDL_CONTROLLER_BUTTON_PADDLE1
+ SDL_GAMEPAD_BUTTON_PADDLE1 + SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1
@@ @@
@@ @@
- SDL_CONTROLLER_BUTTON_PADDLE2 - SDL_CONTROLLER_BUTTON_PADDLE2
+ SDL_GAMEPAD_BUTTON_PADDLE2 + SDL_GAMEPAD_BUTTON_LEFT_PADDLE1
@@ @@
@@ @@
- SDL_CONTROLLER_BUTTON_PADDLE3 - SDL_CONTROLLER_BUTTON_PADDLE3
+ SDL_GAMEPAD_BUTTON_PADDLE3 + SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2
@@ @@
@@ @@
- SDL_CONTROLLER_BUTTON_PADDLE4 - SDL_CONTROLLER_BUTTON_PADDLE4
+ SDL_GAMEPAD_BUTTON_PADDLE4 + SDL_GAMEPAD_BUTTON_LEFT_PADDLE2
@@ @@
@@ @@
- SDL_CONTROLLER_BUTTON_RIGHTSHOULDER - SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
@ -2690,3 +2690,63 @@ typedef SDL_cond, SDL_Condition;
- SDL_strtokr - SDL_strtokr
+ SDL_strtok_r + SDL_strtok_r
(...) (...)
@@
@@
- SDL_ReadLE16
+ SDL_ReadU16LE
(...)
@@
@@
- SDL_ReadLE32
+ SDL_ReadU32LE
(...)
@@
@@
- SDL_ReadBE32
+ SDL_ReadU32BE
(...)
@@
@@
- SDL_ReadBE16
+ SDL_ReadU16BE
(...)
@@
@@
- SDL_ReadLE64
+ SDL_ReadU64LE
(...)
@@
@@
- SDL_ReadBE64
+ SDL_ReadU64BE
(...)
@@
@@
- SDL_WriteLE16
+ SDL_WriteU16LE
(...)
@@
@@
- SDL_WriteBE16
+ SDL_WriteU16BE
(...)
@@
@@
- SDL_WriteLE32
+ SDL_WriteU32LE
(...)
@@
@@
- SDL_WriteBE32
+ SDL_WriteU32BE
(...)
@@
@@
- SDL_WriteLE64
+ SDL_WriteU64LE
(...)
@@
@@
- SDL_WriteBE64
+ SDL_WriteU64BE
(...)

View File

@ -1,75 +0,0 @@
include(FeatureSummary)
set_package_properties(RPi_BcmHost PROPERTIES
URL "https://github.com/raspberrypi/firmware"
DESCRIPTION "Broadcom VideoCore host API library"
)
set(RPi_BcmHost_PKG_CONFIG_SPEC bcm_host)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_RPi_BcmHost QUIET ${RPi_BcmHost_PKG_CONFIG_SPEC})
find_library(RPi_BcmHost_bcm_host_LIBRARY
NAMES bcm_host
HINTS
${PC_RPi_BcmHost_LIBRARY_DIRS}
/opt/vc/lib
)
find_path(RPi_BcmHost_bcm_host_h_PATH
NAMES bcm_host.h
HINTS
${PC_RPi_BcmHost_INCLUDE_DIRS}
/opt/vc/include
)
if(PC_RPi_BcmHost_FOUND)
include("${CMAKE_CURRENT_LIST_DIR}/PkgConfigHelper.cmake")
get_flags_from_pkg_config("${RPi_BcmHost_bcm_host_LIBRARY}" "PC_RPi_BcmHost" "_RPi_BcmHost")
else()
set(_RPi_BcmHost_include_dirs
/opt/vc/include
/opt/vc/include/interface/vcos/pthreads
/opt/vc/include/interface/vmcs_host/linux
)
set(_RPi_BcmHost_compile_options
-DUSE_VCHIQ_ARM
)
set(_RPi_BcmHost_link_libraries
-lvcos -lvchiq_arm
)
set(_RPi_BcmHost_link_options
-pthread
)
set(_RPi_BcmHost_link_directories
/opt/vc/lib
)
endif()
set(RPi_BcmHost_INCLUDE_DIRS "${_RPi_BcmHost_include_dirs}" CACHE STRING "Extra include dirs of bcm_host")
set(RPi_BcmHost_COMPILE_OPTIONS "${_RPi_BcmHost_compile_options}" CACHE STRING "Extra compile options of bcm_host")
set(RPi_BcmHost_LINK_LIBRARIES "${_RPi_BcmHost_link_libraries}" CACHE STRING "Extra link libraries of bcm_host")
set(RPi_BcmHost_LINK_OPTIONS "${_RPi_BcmHost_link_options}" CACHE STRING "Extra link flags of bcm_host")
set(RPi_BcmHost_LINK_DIRECTORIES "${_RPi_BcmHost_link_directories}" CACHE PATH "Extra link directories of bcm_host")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(RPi_BcmHost
REQUIRED_VARS RPi_BcmHost_bcm_host_LIBRARY RPi_BcmHost_bcm_host_h_PATH
)
if(RPi_BcmHost_FOUND)
if(NOT TARGET RPi_BcmHost::RPi_BcmHost)
add_library(RPi_BcmHost::RPi_BcmHost INTERFACE IMPORTED)
set_target_properties(RPi_BcmHost::RPi_BcmHost PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${RPi_BcmHost_INCLUDE_DIRS}"
INTERFACE_COMPILE_OPTIONS "${RPi_BcmHost_COMPILE_OPTIONS}"
INTERFACE_LINK_LIBRARIES "${RPi_BcmHost_LINK_LIBRARIES}"
INTERFACE_LINK_OPTIONS "${RPi_BcmHost_LINK_OPTIONS}"
INTERFACE_LINK_DIRECTORIES "${RPi_BcmHost_LINK_DIRECTORIES}"
)
endif()
endif()

View File

@ -1,59 +0,0 @@
include(FeatureSummary)
set_package_properties(RPi_BrcmEGL PROPERTIES
URL "https://github.com/raspberrypi/firmware"
DESCRIPTION "Fake brcmEGL package for RPi"
)
set(RPi_BrcmEGL_PKG_CONFIG_SPEC brcmegl)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_RPi_BrcmEGL QUIET ${RPi_BrcmEGL_PKG_CONFIG_SPEC})
find_package(RPi_BcmHost)
find_library(RPi_BrcmEGL_brcmEGL_LIBRARY
NAMES brcmEGL
HINTS
${PC_RPi_BrcmEGL_LIBRARY_DIRS}
/opt/vc/lib
)
find_path(RPi_BrcmEGL_EGL_eglplatform_h_PATH
NAMES EGL/eglplatform.h
HINTS
${PC_RPi_BrcmEGL_INCLUDE_DIRS}
/opt/vc/include
)
if(PC_RPi_BrcmEGL_FOUND)
include("${CMAKE_CURRENT_LIST_DIR}/PkgConfigHelper.cmake")
get_flags_from_pkg_config("${RPi_BrcmEGL_brcmEGL_LIBRARY}" "PC_RPi_BrcmEGL" "_RPi_BrcmEGL")
endif()
set(RPi_BrcmEGL_INCLUDE_DIRS "${_RPi_BrcmEGL_include_dirs}" CACHE STRING "Extra include dirs of brcmEGL")
set(RPi_BrcmEGL_COMPILE_OPTIONS "${_RPi_BrcmEGL_compile_options}" CACHE STRING "Extra compile options of brcmEGL")
set(RPi_BrcmEGL_LINK_LIBRARIES "${_RPi_BrcmEGL_link_libraries}" CACHE STRING "Extra link libraries of brcmEGL")
set(RPi_BrcmEGL_LINK_OPTIONS "${_RPi_BrcmEGL_link_options}" CACHE STRING "Extra link flags of brcmEGL")
set(RPi_BrcmEGL_LINK_DIRECTORIES "${_RPi_BrcmEGL_link_directories}" CACHE PATH "Extra link directories of brcmEGL")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(RPi_BrcmEGL
REQUIRED_VARS RPi_BrcmEGL_brcmEGL_LIBRARY RPi_BrcmEGL_EGL_eglext_brcm_h_PATH RPi_BcmHost_FOUND
)
if(RPi_BrcmEGL_FOUND)
if(NOT TARGET RPi_BcmHost::RPi_BcmHost)
add_library(RPi_BcmHost::RPi_BcmHost INTERFACE IMPORTED)
set_target_properties(RPi_BcmHost::RPi_BcmHost PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${RPi_BrcmEGL_INCLUDE_DIRS}"
INTERFACE_COMPILE_OPTIONS "${RPi_BrcmEGL_COMPILE_OPTIONS}"
INTERFACE_LINK_LIBRARIES "${RPi_BrcmEGL_LINK_LIBRARIES};RPi_BcmHost::RPi_BcmHost"
INTERFACE_LINK_OPTIONS "${RPi_BrcmEGL_LINK_OPTIONS}"
INTERFACE_LINK_DIRECTORIES "${RPi_BrcmEGL_LINK_DIRECTORIES}"
)
endif()
endif()

View File

@ -1028,7 +1028,7 @@ macro(CheckHIDAPI)
pkg_check_modules(PC_LIBUSB IMPORTED_TARGET ${LibUSB_PKG_CONFIG_SPEC}) pkg_check_modules(PC_LIBUSB IMPORTED_TARGET ${LibUSB_PKG_CONFIG_SPEC})
if(PC_LIBUSB_FOUND) if(PC_LIBUSB_FOUND)
cmake_push_check_state() cmake_push_check_state()
list(APPEND CMAKE_REQUIRED_INCLUDES ${LibUSB_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_INCLUDES ${PC_LIBUSB_INCLUDE_DIRS})
check_include_file(libusb.h HAVE_LIBUSB_H) check_include_file(libusb.h HAVE_LIBUSB_H)
cmake_pop_check_state() cmake_pop_check_state()
if(HAVE_LIBUSB_H) if(HAVE_LIBUSB_H)
@ -1041,7 +1041,7 @@ macro(CheckHIDAPI)
if(USB_1.0_LIB) if(USB_1.0_LIB)
set(SDL_LIBUSB_DYNAMIC "\"${USB_1.0_LIB_SONAME}\"") set(SDL_LIBUSB_DYNAMIC "\"${USB_1.0_LIB_SONAME}\"")
endif() endif()
sdl_link_dependency(hidapi INCLUDES $<TARGET_PROPERTY:LibUSB::LibUSB,INTERFACE_INCLUDE_DIRECTORIES>) sdl_link_dependency(hidapi INCLUDES $<TARGET_PROPERTY:PkgConfig::PC_LIBUSB,INTERFACE_INCLUDE_DIRECTORIES>)
endif() endif()
endif() endif()
endif() endif()
@ -1079,17 +1079,22 @@ endmacro()
# - n/a # - n/a
macro(CheckRPI) macro(CheckRPI)
if(SDL_RPI) if(SDL_RPI)
# presence of bcm_host means raspberry pi set(BCM_HOST_PKG_CONFIG_SPEC bcm_host)
find_package(RPi_BcmHost) set(BRCMEGL_PKG_CONFIG_SPEC brcmegl)
if(RPi_BcmHost_FOUND)
set(original_PKG_CONFIG_PATH $ENV{PKG_CONFIG_PATH})
set(ENV{PKG_CONFIG_PATH} "${original_PKG_CONFIG_PATH}:/opt/vc/lib/pkgconfig")
pkg_check_modules(PC_BCM_HOST IMPORTED_TARGET QUIET ${BCM_HOST_PKG_CONFIG_SPEC})
pkg_check_modules(PC_BRCMEGL IMPORTED_TARGET QUIET ${BRCMEGL_PKG_CONFIG_SPEC})
set(ENV{PKG_CONFIG_PATH} "${original_PKG_CONFIG_PATH}")
if(TARGET PkgConfig::PC_BCM_HOST AND TARGET PkgConfig::PC_BRCMEGL)
set(HAVE_RPI TRUE) set(HAVE_RPI TRUE)
sdl_link_dependency(rpi LIBS RPi_BcmHost::RPi_BcmHost CMAKE_MODULE RPi_BcmHost PKG_CONFIG_SPECS ${RPi_BcmHost_PKG_CONFIG_SPEC}) if(SDL_VIDEO)
find_package(RPi_BrcmEGL)
if(SDL_VIDEO AND RPi_BrcmEGL_FOUND)
set(HAVE_SDL_VIDEO TRUE) set(HAVE_SDL_VIDEO TRUE)
set(SDL_VIDEO_DRIVER_RPI 1) set(SDL_VIDEO_DRIVER_RPI 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/raspberry/*.c") sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/raspberry/*.c")
sdl_link_dependency(rpi-video LIBS RPi_BrcmEGL::RPi_BrcmEGL CMAKE_MODULE RPi_BrcmEGL PKG_CONFIG_SPECS ${RPi_BrcmEGL_PKG_CONFIG_SPEC}) sdl_link_dependency(rpi-video LIBS PkgConfig::PC_BCM_HOST PKG_CONFIG_PREFIX PC_BCM_HOST PKG_CONFIG_SPECS ${BCM_HOST_PKG_CONFIG_SPEC})
endif() endif()
endif() endif()
endif() endif()

View File

@ -14,6 +14,9 @@ macro(SDL_DetectCMakePlatform)
set(SDL_CMAKE_PLATFORM tvOS) set(SDL_CMAKE_PLATFORM tvOS)
elseif(CMAKE_SYSTEM_NAME MATCHES ".*iOS.*") elseif(CMAKE_SYSTEM_NAME MATCHES ".*iOS.*")
set(SDL_CMAKE_PLATFORM iOS) set(SDL_CMAKE_PLATFORM iOS)
elseif (CMAKE_SYSTEM_NAME MATCHES "visionOS")
set(SDL_CMAKE_PLATFORM visionOS)
set(VISIONOS ON) # CMAKE does not set this automatically yet
endif() endif()
elseif(CMAKE_SYSTEM_NAME MATCHES "Haiku.*") elseif(CMAKE_SYSTEM_NAME MATCHES "Haiku.*")
set(SDL_CMAKE_PLATFORM Haiku) set(SDL_CMAKE_PLATFORM Haiku)

View File

@ -2,6 +2,11 @@
cmake_minimum_required(VERSION 3.12) cmake_minimum_required(VERSION 3.12)
project(sdl_test LANGUAGES C) project(sdl_test LANGUAGES C)
if(WINDOWS_STORE)
enable_language(CXX)
add_compile_options(/ZW)
set_source_files_properties(ain_cli.c main_gui.c PROPERTIES LANGUAGE CXX)
endif()
include(GenerateExportHeader) include(GenerateExportHeader)

View File

@ -17,7 +17,7 @@ def main():
binary_data = args.input.open("rb").read() binary_data = args.input.open("rb").read()
with open(args.output, "w") as fout: with args.output.open("w") as fout:
fout.write("unsigned char {}[] = {{\n".format(varname)) fout.write("unsigned char {}[] = {{\n".format(varname))
bytes_written = 0 bytes_written = 0
while bytes_written < len(binary_data): while bytes_written < len(binary_data):

View File

@ -35,6 +35,14 @@ cmake --install ~/build --prefix /usr/local # '--install' requires CMake
This will install SDL to /usr/local. This will install SDL to /usr/local.
### Building SDL tests
You can build the SDL test programs by adding `-DSDL_TESTS=ON` to the first cmake command above:
```sh
cmake -S ~/sdl -B ~/build -DSDL_TEST_LIBRARY=ON -DSDL_TESTS=ON
```
and then building normally. In this example, the test programs will be built and can be run from `~/build/tests/`.
## Including SDL in your project ## Including SDL in your project
SDL can be included in your project in 2 major ways: SDL can be included in your project in 2 major ways:
@ -197,6 +205,22 @@ To use, set the following CMake variables when running CMake's configuration sta
cmake ~/sdl -DCMAKE_TOOLCHAIN_FILE=~/sdl/build-scripts/cmake-toolchain-qnx-aarch64le.cmake -DSDL_X11=0 cmake ~/sdl -DCMAKE_TOOLCHAIN_FILE=~/sdl/build-scripts/cmake-toolchain-qnx-aarch64le.cmake -DSDL_X11=0
``` ```
## SDL-specific CMake options
SDL can be customized through (platform-specific) CMake options.
The following table shows generic options that are available for most platforms.
At the end of SDL CMake configuration, a table shows all CMake options along with its detected value.
| CMake option | Valid values | Description |
|-------------------------------|--------------|-----------------------------------------------------------------------------------------------------|
| `-DSDL_SHARED=` | `ON`/`OFF` | Build SDL shared library (not all platforms support this) (`libSDL3.so`/`libSDL3.dylib`/`SDL3.dll`) |
| `-DSDL_STATIC=` | `ON`/`OFF` | Build SDL static library (`libSDL3.a`/`SDL3-static.lib`) |
| `-DSDL_TEST_LIBRARY=` | `ON`/`OFF` | Build SDL test library (`libSDL3_test.a`/`SDL3_test.lib`) |
| `-DSDL_TESTS=` | `ON`/`OFF` | Build SDL test programs (**requires `-DSDL_TEST_LIBRARY=ON`**) |
| `-DSDL_DISABLE_INSTALL=` | `ON`/`OFF` | Don't create a SDL install target |
| `-DSDL_DISABLE_INSTALL_DOCS=` | `ON`/`OFF` | Don't install the SDL documentation |
| `-DSDL_INSTALL_TESTS=` | `ON`/`OFF` | Install the SDL test programs |
## Help, it doesn't work! ## Help, it doesn't work!
Below, a SDL3 CMake project can be found that builds 99.9% of time (assuming you have internet connectivity). Below, a SDL3 CMake project can be found that builds 99.9% of time (assuming you have internet connectivity).

View File

@ -53,13 +53,128 @@ The following structures have been renamed:
## SDL_audio.h ## 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 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:
In SDL2, you might have done something like this to play audio:
```c
void SDLCALL MyAudioCallback(void *userdata, Uint8 * stream, int len)
{
/* calculate a little more audio here, maybe using `userdata`, write it to `stream` */
}
/* ...somewhere near startup... */
SDL_AudioSpec my_desired_audio_format;
SDL_zero(my_desired_audio_format);
my_desired_audio_format.format = AUDIO_S16;
my_desired_audio_format.channels = 2;
my_desired_audio_format.freq = 44100;
my_desired_audio_format.samples = 1024;
my_desired_audio_format.callback = MyAudioCallback;
my_desired_audio_format.userdata = &my_audio_callback_user_data;
SDL_AudioDeviceID my_audio_device = SDL_OpenAudioDevice(NULL, 0, &my_desired_audio_format, NULL, 0);
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:
```c
void SDLCALL MyAudioCallback(SDL_AudioStream *stream, int len, void *userdata)
{
/* calculate a little more audio here, maybe using `userdata`, write it to `stream` */
SDL_PutAudioStreamData(stream, newdata, len);
}
/* ...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. */
```
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. 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.
SDL_PauseAudioDevice() is only used to pause audio playback. Use SDL_PlayAudioDevice() to start playing audio. 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.
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.
Devices are not opened by an arbitrary string name anymore, but by device instance ID (or magic numbers to request a reasonable default, like a NULL string in SDL2). In SDL2, the string was used to open both a standard list of system devices, but also allowed for arbitrary devices, such as hostnames of network sound servers. In SDL3, many of the backends that supported arbitrary device names are obsolete and have been removed; of those that remain, arbitrary devices will be opened with a default device ID and an SDL_hint, so specific end-users can set an environment variable to fit their needs and apps don't have to concern themselves with it.
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:
```c
{
if (SDL_InitSubSystem(SDL_INIT_AUDIO) == 0) {
int i, num_devices;
SDL_AudioDeviceID *devices = SDL_GetAudioDevices(/*iscapture=*/SDL_FALSE, &num_devices);
if (devices) {
for (i = 0; i < num_devices; ++i) {
SDL_AudioDeviceID instance_id = devices[i];
char *name = SDL_GetAudioDeviceName(instance_id);
SDL_Log("AudioDevice %" SDL_PRIu32 ": %s\n", instance_id, name);
SDL_free(name);
}
SDL_free(devices);
}
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
}
```
SDL_LockAudioDevice() and SDL_UnlockAudioDevice() have been removed, since there is no callback in another thread to protect. SDL's audio subsystem and SDL_AudioStream maintain their own locks internally, so audio streams are safe to use from any thread. If the app assigns a callback to a specific stream, it can use the stream's lock through SDL_LockAudioStream() if necessary.
SDL_PauseAudioDevice() no longer takes a second argument; it always pauses the device. To unpause, use SDL_ResumeAudioDevice().
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_QueueAudio(), SDL_DequeueAudio, and SDL_ClearQueuedAudio and SDL_GetQueuedAudioSize() have been removed; an SDL_AudioStream bound to a device provides the exact same functionality.
APIs that use channel counts used to use a Uint8 for the channel; now they use int.
SDL_AudioSpec has been reduced; now it only holds format, channel, and sample rate. SDL_GetSilenceValueForFormat() can provide the information from the SDL_AudioSpec's `silence` field. The other SDL2 SDL_AudioSpec fields aren't relevant anymore.
SDL_GetAudioDeviceSpec() is removed; use SDL_GetAudioDeviceFormat() instead.
SDL_GetDefaultAudioInfo() is removed; SDL_GetAudioDeviceFormat() with SDL_AUDIO_DEVICE_DEFAULT_OUTPUT or SDL_AUDIO_DEVICE_DEFAULT_CAPTURE. There is no replacement for querying the default device name; the string is no longer used to open devices, and SDL3 will migrate between physical devices on the fly if the system default changes, so if you must show this to the user, a generic name like "System default" is recommended.
SDL_MixAudio() has been removed, as it relied on legacy SDL 1.2 quirks; SDL_MixAudioFormat() remains and offers the same functionality.
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.
SDL_FreeWAV has been removed and calls can be replaced with SDL_free. SDL_FreeWAV has been removed and calls can be replaced with SDL_free.
SDL_AudioCVT interface is removed, SDL_AudioStream interface or SDL_ConvertAudioSamples() helper function can be used. SDL_LoadWAV() is a proper function now and no longer a macro (but offers the same functionality otherwise).
SDL_LoadWAV_RW() and SDL_LoadWAV() return an int now: zero on success, -1 on error, like most of SDL. They no longer return a pointer to an SDL_AudioSpec.
SDL_AudioCVT interface has been removed, the SDL_AudioStream interface (for audio supplied in pieces) or the new SDL_ConvertAudioSamples() function (for converting a complete audio buffer in one call) can be used instead.
Code that used to look like this: Code that used to look like this:
```c ```c
@ -75,8 +190,9 @@ should be changed to:
```c ```c
Uint8 *dst_data = NULL; Uint8 *dst_data = NULL;
int dst_len = 0; int dst_len = 0;
if (SDL_ConvertAudioSamples(src_format, src_channels, src_rate, src_data, src_len const SDL_AudioSpec src_spec = { src_format, src_channels, src_rate };
dst_format, dst_channels, dst_rate, &dst_data, &dst_len) < 0) { const SDL_AudioSpec dst_spec = { dst_format, dst_channels, dst_rate };
if (SDL_ConvertAudioSamples(&src_spec, src_data, src_len, &dst_spec, &dst_data, &dst_len) < 0) {
/* error */ /* error */
} }
do_something(dst_data, dst_len); do_something(dst_data, dst_len);
@ -103,9 +219,13 @@ If you need to convert U16 audio data to a still-supported format at runtime, th
} }
``` ```
In SDL2, SDL_AudioStream would convert/resample audio data during input (via SDL_AudioStreamPut). In SDL3, it does this work when requesting audio (via SDL_GetAudioStreamData, which would have been SDL_AudioStreamPut in SDL2. The way you use an AudioStream is roughly the same, just be aware that the workload moved to a different phase. All remaining `AUDIO_*` symbols have been renamed to `SDL_AUDIO_*` for API consistency, but othewise are identical in value and usage.
In SDL2, SDL_AudioStream would convert/resample audio data during input (via SDL_AudioStreamPut). In SDL3, it does this work when requesting audio (via SDL_GetAudioStreamData, which would have been SDL_AudioStreamGet in SDL2). The way you use an AudioStream is roughly the same, just be aware that the workload moved to a different phase.
In SDL2, SDL_AudioStreamAvailable() returns 0 if passed a NULL stream. In SDL3, the equivalent SDL_GetAudioStreamAvailable() call returns -1 and sets an error string, which matches other audiostream APIs' behavior. In SDL2, SDL_AudioStreamAvailable() returns 0 if passed a NULL stream. In SDL3, the equivalent SDL_GetAudioStreamAvailable() call returns -1 and sets an error string, which matches other audiostream APIs' behavior.
In SDL2, SDL_AUDIODEVICEREMOVED events would fire for open devices with the `which` field set to the SDL_AudioDeviceID of the lost device, and in later SDL2 releases, would also fire this event with a `which` field of zero for unopened devices, to signify that the app might want to refresh the available device list. In SDL3, this event works the same, except it won't ever fire with a zero; in this case it'll return the physical device's SDL_AudioDeviceID. Any still-open SDL_AudioDeviceIDs generated from this device with SDL_OpenAudioDevice() will also fire a separate event.
The following functions have been renamed: The following functions have been renamed:
* SDL_AudioStreamAvailable() => SDL_GetAudioStreamAvailable() * SDL_AudioStreamAvailable() => SDL_GetAudioStreamAvailable()
@ -118,17 +238,25 @@ The following functions have been renamed:
The following functions have been removed: The following functions have been removed:
* SDL_GetNumAudioDevices()
* SDL_GetAudioDeviceSpec()
* SDL_ConvertAudio() * SDL_ConvertAudio()
* SDL_BuildAudioCVT() * SDL_BuildAudioCVT()
* SDL_OpenAudio() * SDL_OpenAudio()
* SDL_CloseAudio() * SDL_CloseAudio()
* SDL_PauseAudio() * SDL_PauseAudio()
* SDL_GetAudioStatus() * SDL_GetAudioStatus()
* SDL_GetAudioDeviceStatus()
* SDL_GetDefaultAudioInfo()
* SDL_LockAudio() * SDL_LockAudio()
* SDL_LockAudioDevice()
* SDL_UnlockAudio() * SDL_UnlockAudio()
* SDL_UnlockAudioDevice()
* SDL_MixAudio() * SDL_MixAudio()
* SDL_QueueAudio()
Use the SDL_AudioDevice functions instead. * SDL_DequeueAudio()
* SDL_ClearAudioQueue()
* SDL_GetQueuedAudioSize()
The following symbols have been renamed: The following symbols have been renamed:
* AUDIO_F32 => SDL_AUDIO_F32 * AUDIO_F32 => SDL_AUDIO_F32
@ -380,10 +508,10 @@ The following symbols have been renamed:
* SDL_CONTROLLER_BUTTON_LEFTSTICK => SDL_GAMEPAD_BUTTON_LEFT_STICK * SDL_CONTROLLER_BUTTON_LEFTSTICK => SDL_GAMEPAD_BUTTON_LEFT_STICK
* SDL_CONTROLLER_BUTTON_MAX => SDL_GAMEPAD_BUTTON_MAX * SDL_CONTROLLER_BUTTON_MAX => SDL_GAMEPAD_BUTTON_MAX
* SDL_CONTROLLER_BUTTON_MISC1 => SDL_GAMEPAD_BUTTON_MISC1 * SDL_CONTROLLER_BUTTON_MISC1 => SDL_GAMEPAD_BUTTON_MISC1
* SDL_CONTROLLER_BUTTON_PADDLE1 => SDL_GAMEPAD_BUTTON_PADDLE1 * SDL_CONTROLLER_BUTTON_PADDLE1 => SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1
* SDL_CONTROLLER_BUTTON_PADDLE2 => SDL_GAMEPAD_BUTTON_PADDLE2 * SDL_CONTROLLER_BUTTON_PADDLE2 => SDL_GAMEPAD_BUTTON_LEFT_PADDLE1
* SDL_CONTROLLER_BUTTON_PADDLE3 => SDL_GAMEPAD_BUTTON_PADDLE3 * SDL_CONTROLLER_BUTTON_PADDLE3 => SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2
* SDL_CONTROLLER_BUTTON_PADDLE4 => SDL_GAMEPAD_BUTTON_PADDLE4 * SDL_CONTROLLER_BUTTON_PADDLE4 => SDL_GAMEPAD_BUTTON_LEFT_PADDLE2
* SDL_CONTROLLER_BUTTON_RIGHTSHOULDER => SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER * SDL_CONTROLLER_BUTTON_RIGHTSHOULDER => SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER
* SDL_CONTROLLER_BUTTON_RIGHTSTICK => SDL_GAMEPAD_BUTTON_RIGHT_STICK * SDL_CONTROLLER_BUTTON_RIGHTSTICK => SDL_GAMEPAD_BUTTON_RIGHT_STICK
* SDL_CONTROLLER_BUTTON_START => SDL_GAMEPAD_BUTTON_START * SDL_CONTROLLER_BUTTON_START => SDL_GAMEPAD_BUTTON_START
@ -698,6 +826,8 @@ which index is the "opengl" or whatnot driver, you can just pass that string dir
here, now. Passing NULL is the same as passing -1 here in SDL2, to signify you want SDL here, now. Passing NULL is the same as passing -1 here in SDL2, to signify you want SDL
to decide for you. to decide for you.
The SDL_RENDERER_TARGETTEXTURE flag has been removed, all current renderers support target texture functionality.
When a renderer is created, it will automatically set the logical size to the size of When a renderer is created, it will automatically set the logical size to the size of
the window in points. For high DPI displays, this will set up scaling from points to the window in points. For high DPI displays, this will set up scaling from points to
pixels. You can disable this scaling with: pixels. You can disable this scaling with:
@ -780,33 +910,28 @@ size_t SDL_RWwrite(SDL_RWops *context, const void *ptr, size_t size, size_t maxn
But now they look more like POSIX: But now they look more like POSIX:
```c ```c
Sint64 SDL_RWread(SDL_RWops *context, void *ptr, Sint64 size); size_t SDL_RWread(SDL_RWops *context, void *ptr, size_t size);
Sint64 SDL_RWwrite(SDL_RWops *context, const void *ptr, Sint64 size); size_t SDL_RWwrite(SDL_RWops *context, const void *ptr, size_t size);
``` ```
SDL_RWread() previously returned 0 at end of file or other error. Now it returns the number of bytes read, 0 for end of file, -1 for another error, or -2 for data not ready (in the case of a non-blocking context).
Code that used to look like this: Code that used to look like this:
``` ```
size_t custom_read(void *ptr, size_t size, size_t nitems, SDL_RWops *stream) size_t custom_read(void *ptr, size_t size, size_t nitems, SDL_RWops *stream)
{ {
return (size_t)SDL_RWread(stream, ptr, size, nitems); return SDL_RWread(stream, ptr, size, nitems);
} }
``` ```
should be changed to: should be changed to:
``` ```
size_t custom_read(void *ptr, size_t size, size_t nitems, SDL_RWops *stream) size_t custom_read(void *ptr, size_t size, size_t nitems, SDL_RWops *stream)
{ {
Sint64 amount = SDL_RWread(stream, ptr, size * nitems); if (size > 0 && nitems > 0) {
if (amount <= 0) { return SDL_RWread(stream, ptr, size * nitems) / size;
}
return 0; return 0;
} }
return (size_t)(amount / size);
}
``` ```
Similarly, SDL_RWwrite() can return -2 for data not ready in the case of a non-blocking context. There is currently no way to create a non-blocking context, we have simply defined the semantic for your own custom SDL_RWops object.
SDL_RWFromFP has been removed from the API, due to issues when the SDL library uses a different C runtime from the application. SDL_RWFromFP has been removed from the API, due to issues when the SDL library uses a different C runtime from the application.
You can implement this in your own code easily: You can implement this in your own code easily:
@ -814,23 +939,7 @@ You can implement this in your own code easily:
#include <stdio.h> #include <stdio.h>
static Sint64 SDLCALL static Sint64 SDLCALL stdio_seek(SDL_RWops *context, Sint64 offset, int whence)
stdio_size(SDL_RWops * context)
{
Sint64 pos, size;
pos = SDL_RWseek(context, 0, SDL_RW_SEEK_CUR);
if (pos < 0) {
return -1;
}
size = SDL_RWseek(context, 0, SDL_RW_SEEK_END);
SDL_RWseek(context, pos, SDL_RW_SEEK_SET);
return size;
}
static Sint64 SDLCALL
stdio_seek(SDL_RWops * context, Sint64 offset, int whence)
{ {
int stdiowhence; int stdiowhence;
@ -858,54 +967,46 @@ stdio_seek(SDL_RWops * context, Sint64 offset, int whence)
return SDL_Error(SDL_EFSEEK); return SDL_Error(SDL_EFSEEK);
} }
static Sint64 SDLCALL static size_t SDLCALL stdio_read(SDL_RWops *context, void *ptr, size_t size)
stdio_read(SDL_RWops * context, void *ptr, Sint64 size)
{ {
size_t nread; size_t bytes;
nread = fread(ptr, 1, (size_t) size, (FILE *)context->hidden.stdio.fp); bytes = fread(ptr, 1, size, (FILE *)context->hidden.stdio.fp);
if (nread == 0 && ferror((FILE *)context->hidden.stdio.fp)) { if (bytes == 0 && ferror((FILE *)context->hidden.stdio.fp)) {
return SDL_Error(SDL_EFREAD); SDL_Error(SDL_EFREAD);
} }
return (Sint64) nread; return bytes;
} }
static Sint64 SDLCALL static size_t SDLCALL stdio_write(SDL_RWops *context, const void *ptr, size_t size)
stdio_write(SDL_RWops * context, const void *ptr, Sint64 size)
{ {
size_t nwrote; size_t bytes;
nwrote = fwrite(ptr, 1, (size_t) size, (FILE *)context->hidden.stdio.fp); bytes = fwrite(ptr, 1, size, (FILE *)context->hidden.stdio.fp);
if (nwrote == 0 && ferror((FILE *)context->hidden.stdio.fp)) { if (bytes == 0 && ferror((FILE *)context->hidden.stdio.fp)) {
return SDL_Error(SDL_EFWRITE); SDL_Error(SDL_EFWRITE);
} }
return (Sint64) nwrote; return bytes;
} }
static int SDLCALL static int SDLCALL stdio_close(SDL_RWops *context)
stdio_close(SDL_RWops * context)
{ {
int status = 0; int status = 0;
if (context) {
if (context->hidden.stdio.autoclose) { if (context->hidden.stdio.autoclose) {
/* WARNING: Check the return value here! */
if (fclose((FILE *)context->hidden.stdio.fp) != 0) { if (fclose((FILE *)context->hidden.stdio.fp) != 0) {
status = SDL_Error(SDL_EFWRITE); status = SDL_Error(SDL_EFWRITE);
} }
} }
SDL_DestroyRW(context); SDL_DestroyRW(context);
}
return status; return status;
} }
SDL_RWops * SDL_RWops *SDL_RWFromFP(void *fp, SDL_bool autoclose)
SDL_RWFromFP(void *fp, SDL_bool autoclose)
{ {
SDL_RWops *rwops = NULL; SDL_RWops *rwops = NULL;
rwops = SDL_CreateRW(); rwops = SDL_CreateRW();
if (rwops != NULL) { if (rwops != NULL) {
rwops->size = stdio_size;
rwops->seek = stdio_seek; rwops->seek = stdio_seek;
rwops->read = stdio_read; rwops->read = stdio_read;
rwops->write = stdio_write; rwops->write = stdio_write;
@ -918,10 +1019,23 @@ SDL_RWFromFP(void *fp, SDL_bool autoclose)
} }
``` ```
The functions SDL_ReadU8(), SDL_ReadU16LE(), SDL_ReadU16BE(), SDL_ReadU32LE(), SDL_ReadU32BE(), SDL_ReadU64LE(), and SDL_ReadU64BE() now return SDL_TRUE if the read succeeded and SDL_FALSE if it didn't, and store the data in a pointer passed in as a parameter.
The following functions have been renamed: The following functions have been renamed:
* SDL_AllocRW() => SDL_CreateRW() * SDL_AllocRW() => SDL_CreateRW()
* SDL_FreeRW() => SDL_DestroyRW() * SDL_FreeRW() => SDL_DestroyRW()
* SDL_ReadBE16() => SDL_ReadU16BE()
* SDL_ReadBE32() => SDL_ReadU32BE()
* SDL_ReadBE64() => SDL_ReadU64BE()
* SDL_ReadLE16() => SDL_ReadU16LE()
* SDL_ReadLE32() => SDL_ReadU32LE()
* SDL_ReadLE64() => SDL_ReadU64LE()
* SDL_WriteBE16() => SDL_WriteU16BE()
* SDL_WriteBE32() => SDL_WriteU32BE()
* SDL_WriteBE64() => SDL_WriteU64BE()
* SDL_WriteLE16() => SDL_WriteU16LE()
* SDL_WriteLE32() => SDL_WriteU32LE()
* SDL_WriteLE64() => SDL_WriteU64LE()
## SDL_sensor.h ## SDL_sensor.h

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,8 @@
#ifndef SDL_DEPRECATED #ifndef SDL_DEPRECATED
# if defined(__GNUC__) && (__GNUC__ >= 4) /* technically, this arrived in gcc 3.1, but oh well. */ # if defined(__GNUC__) && (__GNUC__ >= 4) /* technically, this arrived in gcc 3.1, but oh well. */
# define SDL_DEPRECATED __attribute__((deprecated)) # define SDL_DEPRECATED __attribute__((deprecated))
# elif defined(_MSC_VER)
# define SDL_DEPRECATED __declspec(deprecated)
# else # else
# define SDL_DEPRECATED # define SDL_DEPRECATED
# endif # endif

View File

@ -152,7 +152,7 @@ typedef enum
SDL_EVENT_JOYSTICK_ADDED, /**< A new joystick has been inserted into the system */ SDL_EVENT_JOYSTICK_ADDED, /**< A new joystick has been inserted into the system */
SDL_EVENT_JOYSTICK_REMOVED, /**< An opened joystick has been removed */ SDL_EVENT_JOYSTICK_REMOVED, /**< An opened joystick has been removed */
SDL_EVENT_JOYSTICK_BATTERY_UPDATED, /**< Joystick battery level change */ SDL_EVENT_JOYSTICK_BATTERY_UPDATED, /**< Joystick battery level change */
SDL_EVENT_JOYSTICK_UPDATE_COMPLETE, /**< Joystick update is complete (disabled by default) */ SDL_EVENT_JOYSTICK_UPDATE_COMPLETE, /**< Joystick update is complete */
/* Gamepad events */ /* Gamepad events */
SDL_EVENT_GAMEPAD_AXIS_MOTION = 0x650, /**< Gamepad axis motion */ SDL_EVENT_GAMEPAD_AXIS_MOTION = 0x650, /**< Gamepad axis motion */
@ -165,7 +165,7 @@ typedef enum
SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION, /**< Gamepad touchpad finger was moved */ SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION, /**< Gamepad touchpad finger was moved */
SDL_EVENT_GAMEPAD_TOUCHPAD_UP, /**< Gamepad touchpad finger was lifted */ SDL_EVENT_GAMEPAD_TOUCHPAD_UP, /**< Gamepad touchpad finger was lifted */
SDL_EVENT_GAMEPAD_SENSOR_UPDATE, /**< Gamepad sensor was updated */ SDL_EVENT_GAMEPAD_SENSOR_UPDATE, /**< Gamepad sensor was updated */
SDL_EVENT_GAMEPAD_UPDATE_COMPLETE, /**< Gamepad update is complete (disabled by default) */ SDL_EVENT_GAMEPAD_UPDATE_COMPLETE, /**< Gamepad update is complete */
/* Touch events */ /* Touch events */
SDL_EVENT_FINGER_DOWN = 0x700, SDL_EVENT_FINGER_DOWN = 0x700,
@ -493,7 +493,7 @@ 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 */
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
SDL_AudioDeviceID which; /**< The audio device index for the ADDED event (valid until next SDL_GetNumAudioDevices() call), SDL_AudioDeviceID for the REMOVED event */ SDL_AudioDeviceID which; /**< SDL_AudioDeviceID for the device being added or removed */
Uint8 iscapture; /**< zero if an output device, non-zero if a capture device. */ Uint8 iscapture; /**< zero if an output device, non-zero if a capture device. */
Uint8 padding1; Uint8 padding1;
Uint8 padding2; Uint8 padding2;

View File

@ -159,11 +159,11 @@ extern DECLSPEC char *SDLCALL SDL_GetPrefPath(const char *org, const char *app);
* | SAVEDGAMES | Vista+ | | | | | | * | SAVEDGAMES | Vista+ | | | | | |
* | SCREENSHOTS | Vista+ | | | | | | * | SCREENSHOTS | Vista+ | | | | | |
* | TEMPLATES | X | X | | X | | | * | TEMPLATES | X | X | | X | | |
* | VIDEOS | X | X | | X | | | * | VIDEOS | X | X* | | X | | |
* *
* Note that on macOS/iOS, the Videos folder is called "Movies". * * Note that on macOS/iOS, the Videos folder is called "Movies".
* *
* \sa SDL_GetPath * \sa SDL_GetUserFolder
*/ */
typedef enum typedef enum
{ {
@ -206,13 +206,18 @@ typedef enum
} SDL_Folder; } SDL_Folder;
/** /**
* Finds the most suitable OS-provided folder for @p folder, and returns its * Finds the most suitable user folder for @p purpose, and returns its path in
* path in OS-specific notation. * OS-specific notation.
* *
* Many OSes provide certain standard folders for certain purposes, such as * Many OSes provide certain standard folders for certain purposes, such as
* storing pictures, music or videos for a certain user. This function gives * storing pictures, music or videos for a certain user. This function gives
* the path for many of those special locations. * the path for many of those special locations.
* *
* This function is specifically for _user_ folders, which are meant for the
* user to access and manage. For application-specific folders, meant to hold
* data for the application to manage, see SDL_GetBasePath() and
* SDL_GetPrefPath().
*
* Note that the function is expensive, and should be called once at the * Note that the function is expensive, and should be called once at the
* beginning of the execution and kept for as long as needed. * beginning of the execution and kept for as long as needed.
* *
@ -229,7 +234,7 @@ typedef enum
* *
* \sa SDL_Folder * \sa SDL_Folder
*/ */
extern DECLSPEC char *SDLCALL SDL_GetPath(SDL_Folder folder); extern DECLSPEC char *SDLCALL SDL_GetUserFolder(SDL_Folder folder);
/* Ends C function definitions when using C++ */ /* Ends C function definitions when using C++ */
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -95,11 +95,11 @@ typedef enum
SDL_GAMEPAD_BUTTON_DPAD_DOWN, SDL_GAMEPAD_BUTTON_DPAD_DOWN,
SDL_GAMEPAD_BUTTON_DPAD_LEFT, SDL_GAMEPAD_BUTTON_DPAD_LEFT,
SDL_GAMEPAD_BUTTON_DPAD_RIGHT, SDL_GAMEPAD_BUTTON_DPAD_RIGHT,
SDL_GAMEPAD_BUTTON_MISC1, /* Xbox Series X share button, PS5 microphone button, Nintendo Switch Pro capture button, Amazon Luna microphone button */ SDL_GAMEPAD_BUTTON_MISC1, /* Additional button (e.g. Xbox Series X share button, PS5 microphone button, Nintendo Switch Pro capture button, Amazon Luna microphone button) */
SDL_GAMEPAD_BUTTON_PADDLE1, /* Xbox Elite paddle P1 (upper left, facing the back) */ SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1, /* Upper or primary paddle, under your right hand (e.g. Xbox Elite paddle P1) */
SDL_GAMEPAD_BUTTON_PADDLE2, /* Xbox Elite paddle P3 (upper right, facing the back) */ SDL_GAMEPAD_BUTTON_LEFT_PADDLE1, /* Upper or primary paddle, under your left hand (e.g. Xbox Elite paddle P3) */
SDL_GAMEPAD_BUTTON_PADDLE3, /* Xbox Elite paddle P2 (lower left, facing the back) */ SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2, /* Lower or secondary paddle, under your right hand (e.g. Xbox Elite paddle P2) */
SDL_GAMEPAD_BUTTON_PADDLE4, /* Xbox Elite paddle P4 (lower right, facing the back) */ SDL_GAMEPAD_BUTTON_LEFT_PADDLE2, /* Lower or secondary paddle, under your left hand (e.g. Xbox Elite paddle P4) */
SDL_GAMEPAD_BUTTON_TOUCHPAD, /* PS4/PS5 touchpad button */ SDL_GAMEPAD_BUTTON_TOUCHPAD, /* PS4/PS5 touchpad button */
SDL_GAMEPAD_BUTTON_MAX SDL_GAMEPAD_BUTTON_MAX
} SDL_GamepadButton; } SDL_GamepadButton;
@ -176,7 +176,8 @@ extern DECLSPEC int SDLCALL SDL_AddGamepadMapping(const char *mapping);
* constrained environment. * constrained environment.
* *
* \param src the data stream for the mappings to be added * \param src the data stream for the mappings to be added
* \param freesrc non-zero to close the stream after being read * \param freesrc if SDL_TRUE, calls SDL_RWclose() on `src` before returning,
* even in the case of an error
* \returns the number of mappings added or -1 on error; call SDL_GetError() * \returns the number of mappings added or -1 on error; call SDL_GetError()
* for more information. * for more information.
* *
@ -186,7 +187,7 @@ extern DECLSPEC int SDLCALL SDL_AddGamepadMapping(const char *mapping);
* \sa SDL_AddGamepadMappingsFromFile * \sa SDL_AddGamepadMappingsFromFile
* \sa SDL_GetGamepadMappingForGUID * \sa SDL_GetGamepadMappingForGUID
*/ */
extern DECLSPEC int SDLCALL SDL_AddGamepadMappingsFromRW(SDL_RWops *src, int freesrc); extern DECLSPEC int SDLCALL SDL_AddGamepadMappingsFromRW(SDL_RWops *src, SDL_bool freesrc);
/** /**
* Load a set of gamepad mappings from a file. * Load a set of gamepad mappings from a file.
@ -321,7 +322,6 @@ extern DECLSPEC SDL_JoystickID *SDLCALL SDL_GetGamepads(int *count);
* *
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
* *
* \sa SDL_GetGamepadNameForIndex
* \sa SDL_OpenGamepad * \sa SDL_OpenGamepad
*/ */
extern DECLSPEC SDL_bool SDLCALL SDL_IsGamepad(SDL_JoystickID instance_id); extern DECLSPEC SDL_bool SDLCALL SDL_IsGamepad(SDL_JoystickID instance_id);
@ -478,7 +478,6 @@ extern DECLSPEC char *SDLCALL SDL_GetGamepadInstanceMapping(SDL_JoystickID insta
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
* *
* \sa SDL_CloseGamepad * \sa SDL_CloseGamepad
* \sa SDL_GetGamepadNameForIndex
* \sa SDL_IsGamepad * \sa SDL_IsGamepad
*/ */
extern DECLSPEC SDL_Gamepad *SDLCALL SDL_OpenGamepad(SDL_JoystickID instance_id); extern DECLSPEC SDL_Gamepad *SDLCALL SDL_OpenGamepad(SDL_JoystickID instance_id);
@ -532,7 +531,7 @@ extern DECLSPEC SDL_JoystickID SDLCALL SDL_GetGamepadInstanceID(SDL_Gamepad *gam
* *
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
* *
* \sa SDL_GetGamepadNameForIndex * \sa SDL_GetGamepadInstanceName
* \sa SDL_OpenGamepad * \sa SDL_OpenGamepad
*/ */
extern DECLSPEC const char *SDLCALL SDL_GetGamepadName(SDL_Gamepad *gamepad); extern DECLSPEC const char *SDLCALL SDL_GetGamepadName(SDL_Gamepad *gamepad);

View File

@ -34,6 +34,10 @@
/******************************************************************************/ /******************************************************************************/
/* Enable thread safety attributes only with clang. /* Enable thread safety attributes only with clang.
* The attributes can be safely erased when compiling with other compilers. * The attributes can be safely erased when compiling with other compilers.
*
* To enable analysis, set these environment variables before running cmake:
* export CC=clang
* export CFLAGS="-DSDL_THREAD_SAFETY_ANALYSIS -Wthread-safety"
*/ */
#if defined(SDL_THREAD_SAFETY_ANALYSIS) && \ #if defined(SDL_THREAD_SAFETY_ANALYSIS) && \
defined(__clang__) && (!defined(SWIG)) defined(__clang__) && (!defined(SWIG))

View File

@ -174,10 +174,10 @@
#define SDL_CONTROLLER_BUTTON_LEFTSTICK SDL_GAMEPAD_BUTTON_LEFT_STICK #define SDL_CONTROLLER_BUTTON_LEFTSTICK SDL_GAMEPAD_BUTTON_LEFT_STICK
#define SDL_CONTROLLER_BUTTON_MAX SDL_GAMEPAD_BUTTON_MAX #define SDL_CONTROLLER_BUTTON_MAX SDL_GAMEPAD_BUTTON_MAX
#define SDL_CONTROLLER_BUTTON_MISC1 SDL_GAMEPAD_BUTTON_MISC1 #define SDL_CONTROLLER_BUTTON_MISC1 SDL_GAMEPAD_BUTTON_MISC1
#define SDL_CONTROLLER_BUTTON_PADDLE1 SDL_GAMEPAD_BUTTON_PADDLE1 #define SDL_CONTROLLER_BUTTON_PADDLE1 SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1
#define SDL_CONTROLLER_BUTTON_PADDLE2 SDL_GAMEPAD_BUTTON_PADDLE2 #define SDL_CONTROLLER_BUTTON_PADDLE2 SDL_GAMEPAD_BUTTON_LEFT_PADDLE1
#define SDL_CONTROLLER_BUTTON_PADDLE3 SDL_GAMEPAD_BUTTON_PADDLE3 #define SDL_CONTROLLER_BUTTON_PADDLE3 SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2
#define SDL_CONTROLLER_BUTTON_PADDLE4 SDL_GAMEPAD_BUTTON_PADDLE4 #define SDL_CONTROLLER_BUTTON_PADDLE4 SDL_GAMEPAD_BUTTON_LEFT_PADDLE2
#define SDL_CONTROLLER_BUTTON_RIGHTSHOULDER SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER #define SDL_CONTROLLER_BUTTON_RIGHTSHOULDER SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER
#define SDL_CONTROLLER_BUTTON_RIGHTSTICK SDL_GAMEPAD_BUTTON_RIGHT_STICK #define SDL_CONTROLLER_BUTTON_RIGHTSTICK SDL_GAMEPAD_BUTTON_RIGHT_STICK
#define SDL_CONTROLLER_BUTTON_START SDL_GAMEPAD_BUTTON_START #define SDL_CONTROLLER_BUTTON_START SDL_GAMEPAD_BUTTON_START
@ -423,6 +423,18 @@
#define RW_SEEK_SET SDL_RW_SEEK_SET #define RW_SEEK_SET SDL_RW_SEEK_SET
#define SDL_AllocRW SDL_CreateRW #define SDL_AllocRW SDL_CreateRW
#define SDL_FreeRW SDL_DestroyRW #define SDL_FreeRW SDL_DestroyRW
#define SDL_ReadBE16 SDL_ReadU16BE
#define SDL_ReadBE32 SDL_ReadU32BE
#define SDL_ReadBE64 SDL_ReadU64BE
#define SDL_ReadLE16 SDL_ReadU16LE
#define SDL_ReadLE32 SDL_ReadU32LE
#define SDL_ReadLE64 SDL_ReadU64LE
#define SDL_WriteBE16 SDL_WriteU16BE
#define SDL_WriteBE32 SDL_WriteU32BE
#define SDL_WriteBE64 SDL_WriteU64BE
#define SDL_WriteLE16 SDL_WriteU16LE
#define SDL_WriteLE32 SDL_WriteU32LE
#define SDL_WriteLE64 SDL_WriteU64LE
/* ##SDL_sensor.h */ /* ##SDL_sensor.h */
#define SDL_SensorClose SDL_CloseSensor #define SDL_SensorClose SDL_CloseSensor
@ -613,10 +625,10 @@
#define SDL_CONTROLLER_BUTTON_LEFTSTICK SDL_CONTROLLER_BUTTON_LEFTSTICK_renamed_SDL_GAMEPAD_BUTTON_LEFT_STICK #define SDL_CONTROLLER_BUTTON_LEFTSTICK SDL_CONTROLLER_BUTTON_LEFTSTICK_renamed_SDL_GAMEPAD_BUTTON_LEFT_STICK
#define SDL_CONTROLLER_BUTTON_MAX SDL_CONTROLLER_BUTTON_MAX_renamed_SDL_GAMEPAD_BUTTON_MAX #define SDL_CONTROLLER_BUTTON_MAX SDL_CONTROLLER_BUTTON_MAX_renamed_SDL_GAMEPAD_BUTTON_MAX
#define SDL_CONTROLLER_BUTTON_MISC1 SDL_CONTROLLER_BUTTON_MISC1_renamed_SDL_GAMEPAD_BUTTON_MISC1 #define SDL_CONTROLLER_BUTTON_MISC1 SDL_CONTROLLER_BUTTON_MISC1_renamed_SDL_GAMEPAD_BUTTON_MISC1
#define SDL_CONTROLLER_BUTTON_PADDLE1 SDL_CONTROLLER_BUTTON_PADDLE1_renamed_SDL_GAMEPAD_BUTTON_PADDLE1 #define SDL_CONTROLLER_BUTTON_PADDLE1 SDL_CONTROLLER_BUTTON_PADDLE1_renamed_SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1
#define SDL_CONTROLLER_BUTTON_PADDLE2 SDL_CONTROLLER_BUTTON_PADDLE2_renamed_SDL_GAMEPAD_BUTTON_PADDLE2 #define SDL_CONTROLLER_BUTTON_PADDLE2 SDL_CONTROLLER_BUTTON_PADDLE2_renamed_SDL_GAMEPAD_BUTTON_LEFT_PADDLE1
#define SDL_CONTROLLER_BUTTON_PADDLE3 SDL_CONTROLLER_BUTTON_PADDLE3_renamed_SDL_GAMEPAD_BUTTON_PADDLE3 #define SDL_CONTROLLER_BUTTON_PADDLE3 SDL_CONTROLLER_BUTTON_PADDLE3_renamed_SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2
#define SDL_CONTROLLER_BUTTON_PADDLE4 SDL_CONTROLLER_BUTTON_PADDLE4_renamed_SDL_GAMEPAD_BUTTON_PADDLE4 #define SDL_CONTROLLER_BUTTON_PADDLE4 SDL_CONTROLLER_BUTTON_PADDLE4_renamed_SDL_GAMEPAD_BUTTON_LEFT_PADDLE2
#define SDL_CONTROLLER_BUTTON_RIGHTSHOULDER SDL_CONTROLLER_BUTTON_RIGHTSHOULDER_renamed_SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER #define SDL_CONTROLLER_BUTTON_RIGHTSHOULDER SDL_CONTROLLER_BUTTON_RIGHTSHOULDER_renamed_SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER
#define SDL_CONTROLLER_BUTTON_RIGHTSTICK SDL_CONTROLLER_BUTTON_RIGHTSTICK_renamed_SDL_GAMEPAD_BUTTON_RIGHT_STICK #define SDL_CONTROLLER_BUTTON_RIGHTSTICK SDL_CONTROLLER_BUTTON_RIGHTSTICK_renamed_SDL_GAMEPAD_BUTTON_RIGHT_STICK
#define SDL_CONTROLLER_BUTTON_START SDL_CONTROLLER_BUTTON_START_renamed_SDL_GAMEPAD_BUTTON_START #define SDL_CONTROLLER_BUTTON_START SDL_CONTROLLER_BUTTON_START_renamed_SDL_GAMEPAD_BUTTON_START
@ -863,6 +875,18 @@
#define RW_SEEK_SET RW_SEEK_SET_renamed_SDL_RW_SEEK_SET #define RW_SEEK_SET RW_SEEK_SET_renamed_SDL_RW_SEEK_SET
#define SDL_AllocRW SDL_AllocRW_renamed_SDL_CreateRW #define SDL_AllocRW SDL_AllocRW_renamed_SDL_CreateRW
#define SDL_FreeRW SDL_FreeRW_renamed_SDL_DestroyRW #define SDL_FreeRW SDL_FreeRW_renamed_SDL_DestroyRW
#define SDL_ReadBE16 SDL_ReadBE16_renamed_SDL_ReadU16BE
#define SDL_ReadBE32 SDL_ReadBE32_renamed_SDL_ReadU32BE
#define SDL_ReadBE64 SDL_ReadBE64_renamed_SDL_ReadU64BE
#define SDL_ReadLE16 SDL_ReadLE16_renamed_SDL_ReadU16LE
#define SDL_ReadLE32 SDL_ReadLE32_renamed_SDL_ReadU32LE
#define SDL_ReadLE64 SDL_ReadLE64_renamed_SDL_ReadU64LE
#define SDL_WriteBE16 SDL_WriteBE16_renamed_SDL_WriteU16BE
#define SDL_WriteBE32 SDL_WriteBE32_renamed_SDL_WriteU32BE
#define SDL_WriteBE64 SDL_WriteBE64_renamed_SDL_WriteU64BE
#define SDL_WriteLE16 SDL_WriteLE16_renamed_SDL_WriteU16LE
#define SDL_WriteLE32 SDL_WriteLE32_renamed_SDL_WriteU32LE
#define SDL_WriteLE64 SDL_WriteLE64_renamed_SDL_WriteU64LE
/* ##SDL_sensor.h */ /* ##SDL_sensor.h */
#define SDL_SensorClose SDL_SensorClose_renamed_SDL_CloseSensor #define SDL_SensorClose SDL_SensorClose_renamed_SDL_CloseSensor

View File

@ -91,6 +91,8 @@ typedef enum
{ {
SDL_ARRAYORDER_NONE, SDL_ARRAYORDER_NONE,
SDL_ARRAYORDER_RGB, SDL_ARRAYORDER_RGB,
SDL_ARRAYORDER_UNUSED1, /* Left for compatibility with SDL2 */
SDL_ARRAYORDER_UNUSED2, /* Left for compatibility with SDL2 */
SDL_ARRAYORDER_BGR SDL_ARRAYORDER_BGR
} SDL_ArrayOrder; } SDL_ArrayOrder;
@ -263,11 +265,19 @@ typedef enum
SDL_PIXELFORMAT_ARGB32 = SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORMAT_ARGB32 = SDL_PIXELFORMAT_ARGB8888,
SDL_PIXELFORMAT_BGRA32 = SDL_PIXELFORMAT_BGRA8888, SDL_PIXELFORMAT_BGRA32 = SDL_PIXELFORMAT_BGRA8888,
SDL_PIXELFORMAT_ABGR32 = SDL_PIXELFORMAT_ABGR8888, SDL_PIXELFORMAT_ABGR32 = SDL_PIXELFORMAT_ABGR8888,
SDL_PIXELFORMAT_RGBX32 = SDL_PIXELFORMAT_RGBX8888,
SDL_PIXELFORMAT_XRGB32 = SDL_PIXELFORMAT_XRGB8888,
SDL_PIXELFORMAT_BGRX32 = SDL_PIXELFORMAT_BGRX8888,
SDL_PIXELFORMAT_XBGR32 = SDL_PIXELFORMAT_XBGR8888,
#else #else
SDL_PIXELFORMAT_RGBA32 = SDL_PIXELFORMAT_ABGR8888, SDL_PIXELFORMAT_RGBA32 = SDL_PIXELFORMAT_ABGR8888,
SDL_PIXELFORMAT_ARGB32 = SDL_PIXELFORMAT_BGRA8888, SDL_PIXELFORMAT_ARGB32 = SDL_PIXELFORMAT_BGRA8888,
SDL_PIXELFORMAT_BGRA32 = SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORMAT_BGRA32 = SDL_PIXELFORMAT_ARGB8888,
SDL_PIXELFORMAT_ABGR32 = SDL_PIXELFORMAT_RGBA8888, SDL_PIXELFORMAT_ABGR32 = SDL_PIXELFORMAT_RGBA8888,
SDL_PIXELFORMAT_RGBX32 = SDL_PIXELFORMAT_XBGR8888,
SDL_PIXELFORMAT_XRGB32 = SDL_PIXELFORMAT_BGRX8888,
SDL_PIXELFORMAT_BGRX32 = SDL_PIXELFORMAT_XRGB8888,
SDL_PIXELFORMAT_XBGR32 = SDL_PIXELFORMAT_RGBX8888,
#endif #endif
SDL_PIXELFORMAT_YV12 = /**< Planar mode: Y + V + U (3 planes) */ SDL_PIXELFORMAT_YV12 = /**< Planar mode: Y + V + U (3 planes) */

View File

@ -94,6 +94,9 @@
#ifndef TARGET_OS_SIMULATOR #ifndef TARGET_OS_SIMULATOR
#define TARGET_OS_SIMULATOR 0 #define TARGET_OS_SIMULATOR 0
#endif #endif
#ifndef TARGET_OS_XR
#define TARGET_OS_XR 0
#endif
#if TARGET_OS_TV #if TARGET_OS_TV
#undef __TVOS__ #undef __TVOS__

View File

@ -778,10 +778,6 @@ extern DECLSPEC void SDLCALL SDL_UnlockTexture(SDL_Texture *texture);
/** /**
* Set a texture as the current rendering target. * Set a texture as the current rendering target.
* *
* Before using this function, you should check the
* `SDL_RENDERER_TARGETTEXTURE` bit in the flags of SDL_RendererInfo to see if
* render targets are supported.
*
* The default render target is the window for which the renderer was created. * The default render target is the window for which the renderer was created.
* To stop rendering to a texture and render to the window again, call this * To stop rendering to a texture and render to the window again, call this
* function with a NULL `texture`. * function with a NULL `texture`.
@ -1558,7 +1554,7 @@ extern DECLSPEC void *SDLCALL SDL_GetRenderMetalLayer(SDL_Renderer *renderer);
* Note that as of SDL 2.0.18, this will return NULL if Metal refuses to give * Note that as of SDL 2.0.18, this will return NULL if Metal refuses to give
* SDL a drawable to render to, which might happen if the window is * SDL a drawable to render to, which might happen if the window is
* hidden/minimized/offscreen. This doesn't apply to command encoders for * hidden/minimized/offscreen. This doesn't apply to command encoders for
* render targets, just the window's backbacker. Check your return values! * render targets, just the window's backbuffer. Check your return values!
* *
* \param renderer The renderer to query * \param renderer The renderer to query
* \returns an `id<MTLRenderCommandEncoder>` on success, or NULL if the * \returns an `id<MTLRenderCommandEncoder>` on success, or NULL if the

View File

@ -38,13 +38,21 @@
extern "C" { extern "C" {
#endif #endif
/* RWops Types */ /* RWops types */
#define SDL_RWOPS_UNKNOWN 0U /**< Unknown stream type */ #define SDL_RWOPS_UNKNOWN 0 /**< Unknown stream type */
#define SDL_RWOPS_WINFILE 1U /**< Win32 file */ #define SDL_RWOPS_WINFILE 1 /**< Win32 file */
#define SDL_RWOPS_STDFILE 2U /**< Stdio file */ #define SDL_RWOPS_STDFILE 2 /**< Stdio file */
#define SDL_RWOPS_JNIFILE 3U /**< Android asset */ #define SDL_RWOPS_JNIFILE 3 /**< Android asset */
#define SDL_RWOPS_MEMORY 4U /**< Memory stream */ #define SDL_RWOPS_MEMORY 4 /**< Memory stream */
#define SDL_RWOPS_MEMORY_RO 5U /**< Read-Only memory stream */ #define SDL_RWOPS_MEMORY_RO 5 /**< Read-Only memory stream */
/* RWops status, set by a read or write operation */
#define SDL_RWOPS_STATUS_READY 0 /**< Everything is ready */
#define SDL_RWOPS_STATUS_ERROR 1 /**< Read or write I/O error */
#define SDL_RWOPS_STATUS_EOF 2 /**< End of file */
#define SDL_RWOPS_STATUS_NOT_READY 3 /**< Non blocking I/O, not ready */
#define SDL_RWOPS_STATUS_READONLY 4 /**< Tried to write a read-only buffer */
#define SDL_RWOPS_STATUS_WRITEONLY 5 /**< Tried to read a write-only buffer */
/** /**
* This is the read/write operation structure -- very basic. * This is the read/write operation structure -- very basic.
@ -52,7 +60,9 @@ extern "C" {
typedef struct SDL_RWops typedef struct SDL_RWops
{ {
/** /**
* Return the size of the file in this rwops, or -1 if unknown * Return the number of bytes in this rwops
*
* \return the total size of the data stream, or -1 on error.
*/ */
Sint64 (SDLCALL *size)(struct SDL_RWops *context); Sint64 (SDLCALL *size)(struct SDL_RWops *context);
@ -62,36 +72,23 @@ typedef struct SDL_RWops
* *
* \return the final offset in the data stream, or -1 on error. * \return the final offset in the data stream, or -1 on error.
*/ */
Sint64 (SDLCALL * seek) (struct SDL_RWops * context, Sint64 offset, Sint64 (SDLCALL *seek)(struct SDL_RWops *context, Sint64 offset, int whence);
int whence);
/** /**
* Read up to \c size bytes from the data stream to the area pointed * Read up to \c size bytes from the data stream to the area pointed
* at by \c ptr. * at by \c ptr.
* *
* It is an error to use a negative \c size, but this parameter is * \return the number of bytes read
* signed so you definitely cannot overflow the return value on a
* successful run with enormous amounts of data.
*
* \return the number of objects read, or 0 on end of file, or -1 on error.
*/ */
Sint64 (SDLCALL * read) (struct SDL_RWops * context, void *ptr, size_t (SDLCALL *read)(struct SDL_RWops *context, void *ptr, size_t size);
Sint64 size);
/** /**
* Write exactly \c size bytes from the area pointed at by \c ptr * Write exactly \c size bytes from the area pointed at by \c ptr
* to data stream. May write less than requested (error, non-blocking i/o, * to data stream.
* etc). Returns -1 on error when nothing was written.
* *
* It is an error to use a negative \c size, but this parameter is * \return the number of bytes written
* signed so you definitely cannot overflow the return value on a
* successful run with enormous amounts of data.
*
* \return the number of bytes written, which might be less than \c size,
* and -1 on error.
*/ */
Sint64 (SDLCALL * write) (struct SDL_RWops * context, const void *ptr, size_t (SDLCALL *write)(struct SDL_RWops *context, const void *ptr, size_t size);
Sint64 size);
/** /**
* Close and free an allocated SDL_RWops structure. * Close and free an allocated SDL_RWops structure.
@ -101,6 +98,7 @@ typedef struct SDL_RWops
int (SDLCALL *close)(struct SDL_RWops *context); int (SDLCALL *close)(struct SDL_RWops *context);
Uint32 type; Uint32 type;
Uint32 status;
union union
{ {
#ifdef __ANDROID__ #ifdef __ANDROID__
@ -109,7 +107,7 @@ typedef struct SDL_RWops
void *asset; void *asset;
} androidio; } androidio;
#elif defined(__WIN32__) || defined(__GDK__) #elif defined(__WIN32__) || defined(__GDK__) || defined(__WINRT__)
struct struct
{ {
SDL_bool append; SDL_bool append;
@ -213,8 +211,7 @@ typedef struct SDL_RWops
* \sa SDL_RWtell * \sa SDL_RWtell
* \sa SDL_RWwrite * \sa SDL_RWwrite
*/ */
extern DECLSPEC SDL_RWops *SDLCALL SDL_RWFromFile(const char *file, extern DECLSPEC SDL_RWops *SDLCALL SDL_RWFromFile(const char *file, const char *mode);
const char *mode);
/** /**
* Use this function to prepare a read-write memory buffer for use with * Use this function to prepare a read-write memory buffer for use with
@ -279,8 +276,7 @@ extern DECLSPEC SDL_RWops *SDLCALL SDL_RWFromMem(void *mem, size_t size);
* \sa SDL_RWseek * \sa SDL_RWseek
* \sa SDL_RWtell * \sa SDL_RWtell
*/ */
extern DECLSPEC SDL_RWops *SDLCALL SDL_RWFromConstMem(const void *mem, extern DECLSPEC SDL_RWops *SDLCALL SDL_RWFromConstMem(const void *mem, size_t size);
size_t size);
/* @} *//* RWFrom functions */ /* @} *//* RWFrom functions */
@ -289,7 +285,7 @@ extern DECLSPEC SDL_RWops *SDLCALL SDL_RWFromConstMem(const void *mem,
* Use this function to allocate an empty, unpopulated SDL_RWops structure. * Use this function to allocate an empty, unpopulated SDL_RWops structure.
* *
* Applications do not need to use this function unless they are providing * Applications do not need to use this function unless they are providing
* their own SDL_RWops implementation. If you just need a SDL_RWops to * their own SDL_RWops implementation. If you just need an SDL_RWops to
* read/write a common data source, you should use the built-in * read/write a common data source, you should use the built-in
* implementations in SDL, like SDL_RWFromFile() or SDL_RWFromMem(), etc. * implementations in SDL, like SDL_RWFromFile() or SDL_RWFromMem(), etc.
* *
@ -315,7 +311,7 @@ extern DECLSPEC SDL_RWops *SDLCALL SDL_CreateRW(void);
* SDL_CreateRW(). * SDL_CreateRW().
* *
* Applications do not need to use this function unless they are providing * Applications do not need to use this function unless they are providing
* their own SDL_RWops implementation. If you just need a SDL_RWops to * their own SDL_RWops implementation. If you just need an SDL_RWops to
* read/write a common data source, you should use the built-in * read/write a common data source, you should use the built-in
* implementations in SDL, like SDL_RWFromFile() or SDL_RWFromMem(), etc, and * implementations in SDL, like SDL_RWFromFile() or SDL_RWFromMem(), etc, and
* call the **close** method on those SDL_RWops pointers when you are done * call the **close** method on those SDL_RWops pointers when you are done
@ -327,13 +323,13 @@ extern DECLSPEC SDL_RWops *SDLCALL SDL_CreateRW(void);
* the programmer must be responsible for managing that memory in their * the programmer must be responsible for managing that memory in their
* **close** method. * **close** method.
* *
* \param area the SDL_RWops structure to be freed * \param context the SDL_RWops structure to be freed
* *
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
* *
* \sa SDL_CreateRW * \sa SDL_CreateRW
*/ */
extern DECLSPEC void SDLCALL SDL_DestroyRW(SDL_RWops * area); extern DECLSPEC void SDLCALL SDL_DestroyRW(SDL_RWops *context);
#define SDL_RW_SEEK_SET 0 /**< Seek from the beginning of data */ #define SDL_RW_SEEK_SET 0 /**< Seek from the beginning of data */
#define SDL_RW_SEEK_CUR 1 /**< Seek relative to current read point */ #define SDL_RW_SEEK_CUR 1 /**< Seek relative to current read point */
@ -342,12 +338,10 @@ extern DECLSPEC void SDLCALL SDL_DestroyRW(SDL_RWops * area);
/** /**
* Use this function to get the size of the data stream in an SDL_RWops. * Use this function to get the size of the data stream in an SDL_RWops.
* *
* Prior to SDL 2.0.10, this function was a macro.
*
* \param context the SDL_RWops to get the size of the data stream from * \param context the SDL_RWops to get the size of the data stream from
* \returns the size of the data stream in the SDL_RWops on success, -1 if * \returns the size of the data stream in the SDL_RWops on success or a
* unknown or a negative error code on failure; call SDL_GetError() * negative error code on failure; call SDL_GetError() for more
* for more information. * information.
* *
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
*/ */
@ -369,14 +363,13 @@ extern DECLSPEC Sint64 SDLCALL SDL_RWsize(SDL_RWops *context);
* SDL_RWseek() is actually a wrapper function that calls the SDL_RWops's * SDL_RWseek() is actually a wrapper function that calls the SDL_RWops's
* `seek` method appropriately, to simplify application development. * `seek` method appropriately, to simplify application development.
* *
* Prior to SDL 2.0.10, this function was a macro.
*
* \param context a pointer to an SDL_RWops structure * \param context a pointer to an SDL_RWops structure
* \param offset an offset in bytes, relative to **whence** location; can be * \param offset an offset in bytes, relative to **whence** location; can be
* negative * negative
* \param whence any of `SDL_RW_SEEK_SET`, `SDL_RW_SEEK_CUR`, * \param whence any of `SDL_RW_SEEK_SET`, `SDL_RW_SEEK_CUR`,
* `SDL_RW_SEEK_END` * `SDL_RW_SEEK_END`
* \returns the final offset in the data stream after the seek or -1 on error. * \returns the final offset in the data stream after the seek or a negative
* error code on failure; call SDL_GetError() for more information.
* *
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
* *
@ -388,8 +381,7 @@ extern DECLSPEC Sint64 SDLCALL SDL_RWsize(SDL_RWops *context);
* \sa SDL_RWtell * \sa SDL_RWtell
* \sa SDL_RWwrite * \sa SDL_RWwrite
*/ */
extern DECLSPEC Sint64 SDLCALL SDL_RWseek(SDL_RWops *context, extern DECLSPEC Sint64 SDLCALL SDL_RWseek(SDL_RWops *context, Sint64 offset, int whence);
Sint64 offset, int whence);
/** /**
* Determine the current read/write offset in an SDL_RWops data stream. * Determine the current read/write offset in an SDL_RWops data stream.
@ -398,10 +390,8 @@ extern DECLSPEC Sint64 SDLCALL SDL_RWseek(SDL_RWops *context,
* method, with an offset of 0 bytes from `SDL_RW_SEEK_CUR`, to simplify * method, with an offset of 0 bytes from `SDL_RW_SEEK_CUR`, to simplify
* application development. * application development.
* *
* Prior to SDL 2.0.10, this function was a macro. * \param context an SDL_RWops data stream object from which to get the
* * current offset
* \param context a SDL_RWops data stream object from which to get the current
* offset
* \returns the current offset in the stream, or -1 if the information can not * \returns the current offset in the stream, or -1 if the information can not
* be determined. * be determined.
* *
@ -438,8 +428,7 @@ extern DECLSPEC Sint64 SDLCALL SDL_RWtell(SDL_RWops *context);
* \param context a pointer to an SDL_RWops structure * \param context a pointer to an SDL_RWops structure
* \param ptr a pointer to a buffer to read data into * \param ptr a pointer to a buffer to read data into
* \param size the number of bytes to read from the data source. * \param size the number of bytes to read from the data source.
* \returns the number of bytes read, 0 at end of file, -1 on error, and -2 * \returns the number of bytes read, or 0 on end of file or other error.
* for data not ready with a non-blocking context.
* *
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
* *
@ -450,7 +439,7 @@ extern DECLSPEC Sint64 SDLCALL SDL_RWtell(SDL_RWops *context);
* \sa SDL_RWseek * \sa SDL_RWseek
* \sa SDL_RWwrite * \sa SDL_RWwrite
*/ */
extern DECLSPEC Sint64 SDLCALL SDL_RWread(SDL_RWops *context, void *ptr, Sint64 size); extern DECLSPEC size_t SDLCALL SDL_RWread(SDL_RWops *context, void *ptr, size_t size);
/** /**
* Write to an SDL_RWops data stream. * Write to an SDL_RWops data stream.
@ -488,8 +477,7 @@ extern DECLSPEC Sint64 SDLCALL SDL_RWread(SDL_RWops *context, void *ptr, Sint64
* \sa SDL_RWread * \sa SDL_RWread
* \sa SDL_RWseek * \sa SDL_RWseek
*/ */
extern DECLSPEC Sint64 SDLCALL SDL_RWwrite(SDL_RWops *context, extern DECLSPEC size_t SDLCALL SDL_RWwrite(SDL_RWops *context, const void *ptr, size_t size);
const void *ptr, Sint64 size);
/** /**
* Close and free an allocated SDL_RWops structure. * Close and free an allocated SDL_RWops structure.
@ -502,8 +490,6 @@ extern DECLSPEC Sint64 SDLCALL SDL_RWwrite(SDL_RWops *context,
* Note that if this fails to flush the stream to disk, this function reports * Note that if this fails to flush the stream to disk, this function reports
* an error, but the SDL_RWops is still invalid once this function returns. * an error, but the SDL_RWops is still invalid once this function returns.
* *
* Prior to SDL 2.0.10, this function was a macro.
*
* \param context SDL_RWops structure to close * \param context SDL_RWops structure to close
* \returns 0 on success or a negative error code on failure; call * \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information. * SDL_GetError() for more information.
@ -536,9 +522,7 @@ extern DECLSPEC int SDLCALL SDL_RWclose(SDL_RWops *context);
* *
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
*/ */
extern DECLSPEC void *SDLCALL SDL_LoadFile_RW(SDL_RWops *src, extern DECLSPEC void *SDLCALL SDL_LoadFile_RW(SDL_RWops *src, size_t *datasize, SDL_bool freesrc);
size_t *datasize,
SDL_bool freesrc);
/** /**
* Load all the data from a file path. * Load all the data from a file path.
@ -549,9 +533,6 @@ extern DECLSPEC void *SDLCALL SDL_LoadFile_RW(SDL_RWops *src,
* *
* The data should be freed with SDL_free(). * The data should be freed with SDL_free().
* *
* Prior to SDL 2.0.10, this function was a macro wrapping around
* SDL_LoadFile_RW.
*
* \param file the path to read all available data from * \param file the path to read all available data from
* \param datasize if not NULL, will store the number of bytes read * \param datasize if not NULL, will store the number of bytes read
* \returns the data, or NULL if there was an error. * \returns the data, or NULL if there was an error.
@ -571,14 +552,13 @@ extern DECLSPEC void *SDLCALL SDL_LoadFile(const char *file, size_t *datasize);
* Use this function to read a byte from an SDL_RWops. * Use this function to read a byte from an SDL_RWops.
* *
* \param src the SDL_RWops to read from * \param src the SDL_RWops to read from
* \returns the read byte on success or 0 on failure; call SDL_GetError() for * \param value a pointer filled in with the data read
* more information. * \returns SDL_TRUE on success or SDL_FALSE on failure; call SDL_GetError()
* for more information.
* *
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
*
* \sa SDL_WriteU8
*/ */
extern DECLSPEC Uint8 SDLCALL SDL_ReadU8(SDL_RWops * src); extern DECLSPEC SDL_bool SDLCALL SDL_ReadU8(SDL_RWops *src, Uint8 *value);
/** /**
* Use this function to read 16 bits of little-endian data from an SDL_RWops * Use this function to read 16 bits of little-endian data from an SDL_RWops
@ -588,13 +568,29 @@ extern DECLSPEC Uint8 SDLCALL SDL_ReadU8(SDL_RWops * src);
* the native byte order. * the native byte order.
* *
* \param src the stream from which to read data * \param src the stream from which to read data
* \returns 16 bits of data in the native byte order of the platform. * \param value a pointer filled in with the data read
* \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. * \since This function is available since SDL 3.0.0.
*
* \sa SDL_ReadBE16
*/ */
extern DECLSPEC Uint16 SDLCALL SDL_ReadLE16(SDL_RWops * src); extern DECLSPEC SDL_bool SDLCALL SDL_ReadU16LE(SDL_RWops *src, Uint16 *value);
/**
* Use this function to read 16 bits of little-endian data from an SDL_RWops
* and return in native format.
*
* SDL byteswaps the data only if necessary, so the data returned will be in
* the native byte order.
*
* \param src the stream from which to read data
* \param value a pointer filled in with the data read
* \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_ReadS16LE(SDL_RWops *src, Sint16 *value);
/** /**
* Use this function to read 16 bits of big-endian data from an SDL_RWops and * Use this function to read 16 bits of big-endian data from an SDL_RWops and
@ -604,13 +600,29 @@ extern DECLSPEC Uint16 SDLCALL SDL_ReadLE16(SDL_RWops * src);
* the native byte order. * the native byte order.
* *
* \param src the stream from which to read data * \param src the stream from which to read data
* \returns 16 bits of data in the native byte order of the platform. * \param value a pointer filled in with the data read
* \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. * \since This function is available since SDL 3.0.0.
*
* \sa SDL_ReadLE16
*/ */
extern DECLSPEC Uint16 SDLCALL SDL_ReadBE16(SDL_RWops * src); extern DECLSPEC SDL_bool SDLCALL SDL_ReadU16BE(SDL_RWops *src, Uint16 *value);
/**
* Use this function to read 16 bits of big-endian data from an SDL_RWops and
* return in native format.
*
* SDL byteswaps the data only if necessary, so the data returned will be in
* the native byte order.
*
* \param src the stream from which to read data
* \param value a pointer filled in with the data read
* \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_ReadS16BE(SDL_RWops *src, Sint16 *value);
/** /**
* Use this function to read 32 bits of little-endian data from an SDL_RWops * Use this function to read 32 bits of little-endian data from an SDL_RWops
@ -620,13 +632,29 @@ extern DECLSPEC Uint16 SDLCALL SDL_ReadBE16(SDL_RWops * src);
* the native byte order. * the native byte order.
* *
* \param src the stream from which to read data * \param src the stream from which to read data
* \returns 32 bits of data in the native byte order of the platform. * \param value a pointer filled in with the data read
* \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. * \since This function is available since SDL 3.0.0.
*
* \sa SDL_ReadBE32
*/ */
extern DECLSPEC Uint32 SDLCALL SDL_ReadLE32(SDL_RWops * src); extern DECLSPEC SDL_bool SDLCALL SDL_ReadU32LE(SDL_RWops *src, Uint32 *value);
/**
* Use this function to read 32 bits of little-endian data from an SDL_RWops
* and return in native format.
*
* SDL byteswaps the data only if necessary, so the data returned will be in
* the native byte order.
*
* \param src the stream from which to read data
* \param value a pointer filled in with the data read
* \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_ReadS32LE(SDL_RWops *src, Sint32 *value);
/** /**
* Use this function to read 32 bits of big-endian data from an SDL_RWops and * Use this function to read 32 bits of big-endian data from an SDL_RWops and
@ -636,13 +664,29 @@ extern DECLSPEC Uint32 SDLCALL SDL_ReadLE32(SDL_RWops * src);
* the native byte order. * the native byte order.
* *
* \param src the stream from which to read data * \param src the stream from which to read data
* \returns 32 bits of data in the native byte order of the platform. * \param value a pointer filled in with the data read
* \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. * \since This function is available since SDL 3.0.0.
*
* \sa SDL_ReadLE32
*/ */
extern DECLSPEC Uint32 SDLCALL SDL_ReadBE32(SDL_RWops * src); extern DECLSPEC SDL_bool SDLCALL SDL_ReadU32BE(SDL_RWops *src, Uint32 *value);
/**
* Use this function to read 32 bits of big-endian data from an SDL_RWops and
* return in native format.
*
* SDL byteswaps the data only if necessary, so the data returned will be in
* the native byte order.
*
* \param src the stream from which to read data
* \param value a pointer filled in with the data read
* \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_ReadS32BE(SDL_RWops *src, Sint32 *value);
/** /**
* Use this function to read 64 bits of little-endian data from an SDL_RWops * Use this function to read 64 bits of little-endian data from an SDL_RWops
@ -652,13 +696,29 @@ extern DECLSPEC Uint32 SDLCALL SDL_ReadBE32(SDL_RWops * src);
* the native byte order. * the native byte order.
* *
* \param src the stream from which to read data * \param src the stream from which to read data
* \returns 64 bits of data in the native byte order of the platform. * \param value a pointer filled in with the data read
* \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. * \since This function is available since SDL 3.0.0.
*
* \sa SDL_ReadBE64
*/ */
extern DECLSPEC Uint64 SDLCALL SDL_ReadLE64(SDL_RWops * src); extern DECLSPEC SDL_bool SDLCALL SDL_ReadU64LE(SDL_RWops *src, Uint64 *value);
/**
* Use this function to read 64 bits of little-endian data from an SDL_RWops
* and return in native format.
*
* SDL byteswaps the data only if necessary, so the data returned will be in
* the native byte order.
*
* \param src the stream from which to read data
* \param value a pointer filled in with the data read
* \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_ReadS64LE(SDL_RWops *src, Sint64 *value);
/** /**
* Use this function to read 64 bits of big-endian data from an SDL_RWops and * Use this function to read 64 bits of big-endian data from an SDL_RWops and
@ -668,13 +728,29 @@ extern DECLSPEC Uint64 SDLCALL SDL_ReadLE64(SDL_RWops * src);
* the native byte order. * the native byte order.
* *
* \param src the stream from which to read data * \param src the stream from which to read data
* \returns 64 bits of data in the native byte order of the platform. * \param value a pointer filled in with the data read
* \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. * \since This function is available since SDL 3.0.0.
*
* \sa SDL_ReadLE64
*/ */
extern DECLSPEC Uint64 SDLCALL SDL_ReadBE64(SDL_RWops * src); extern DECLSPEC SDL_bool SDLCALL SDL_ReadU64BE(SDL_RWops *src, Uint64 *value);
/**
* Use this function to read 64 bits of big-endian data from an SDL_RWops and
* return in native format.
*
* SDL byteswaps the data only if necessary, so the data returned will be in
* the native byte order.
*
* \param src the stream from which to read data
* \param value a pointer filled in with the data read
* \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_ReadS64BE(SDL_RWops *src, Sint64 *value);
/* @} *//* Read endian functions */ /* @} *//* Read endian functions */
/** /**
@ -689,17 +765,15 @@ extern DECLSPEC Uint64 SDLCALL SDL_ReadBE64(SDL_RWops * src);
* *
* \param dst the SDL_RWops to write to * \param dst the SDL_RWops to write to
* \param value the byte value to write * \param value the byte value to write
* \returns 1 on success or 0 on failure; call SDL_GetError() for more * \returns SDL_TRUE on successful write, SDL_FALSE on failure; call
* information. * SDL_GetError() for more information.
* *
* \since This function is available since SDL 3.0.0. * \since This function is available since SDL 3.0.0.
*
* \sa SDL_ReadU8
*/ */
extern DECLSPEC size_t SDLCALL SDL_WriteU8(SDL_RWops * dst, Uint8 value); extern DECLSPEC SDL_bool SDLCALL SDL_WriteU8(SDL_RWops *dst, Uint8 value);
/** /**
* Use this function to write 16 bits in native format to a SDL_RWops as * Use this function to write 16 bits in native format to an SDL_RWops as
* little-endian data. * little-endian data.
* *
* SDL byteswaps the data only if necessary, so the application always * SDL byteswaps the data only if necessary, so the application always
@ -708,33 +782,15 @@ extern DECLSPEC size_t SDLCALL SDL_WriteU8(SDL_RWops * dst, Uint8 value);
* *
* \param dst the stream to which data will be written * \param dst the stream to which data will be written
* \param value the data to be written, in native format * \param value the data to be written, in native format
* \returns 1 on successful write, 0 on error. * \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. * \since This function is available since SDL 3.0.0.
*
* \sa SDL_WriteBE16
*/ */
extern DECLSPEC size_t SDLCALL SDL_WriteLE16(SDL_RWops * dst, Uint16 value); extern DECLSPEC SDL_bool SDLCALL SDL_WriteU16LE(SDL_RWops *dst, Uint16 value);
/** /**
* Use this function to write 16 bits in native format to a SDL_RWops as * Use this function to write 16 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 1 on successful write, 0 on error.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_WriteLE16
*/
extern DECLSPEC size_t SDLCALL SDL_WriteBE16(SDL_RWops * dst, Uint16 value);
/**
* Use this function to write 32 bits in native format to a SDL_RWops as
* little-endian data. * little-endian data.
* *
* SDL byteswaps the data only if necessary, so the application always * SDL byteswaps the data only if necessary, so the application always
@ -743,16 +799,15 @@ extern DECLSPEC size_t SDLCALL SDL_WriteBE16(SDL_RWops * dst, Uint16 value);
* *
* \param dst the stream to which data will be written * \param dst the stream to which data will be written
* \param value the data to be written, in native format * \param value the data to be written, in native format
* \returns 1 on successful write, 0 on error. * \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. * \since This function is available since SDL 3.0.0.
*
* \sa SDL_WriteBE32
*/ */
extern DECLSPEC size_t SDLCALL SDL_WriteLE32(SDL_RWops * dst, Uint32 value); extern DECLSPEC SDL_bool SDLCALL SDL_WriteS16LE(SDL_RWops *dst, Sint16 value);
/** /**
* Use this function to write 32 bits in native format to a SDL_RWops as * Use this function to write 16 bits in native format to an SDL_RWops as
* big-endian data. * big-endian data.
* *
* SDL byteswaps the data only if necessary, so the application always * SDL byteswaps the data only if necessary, so the application always
@ -760,16 +815,31 @@ extern DECLSPEC size_t SDLCALL SDL_WriteLE32(SDL_RWops * dst, Uint32 value);
* *
* \param dst the stream to which data will be written * \param dst the stream to which data will be written
* \param value the data to be written, in native format * \param value the data to be written, in native format
* \returns 1 on successful write, 0 on error. * \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. * \since This function is available since SDL 3.0.0.
*
* \sa SDL_WriteLE32
*/ */
extern DECLSPEC size_t SDLCALL SDL_WriteBE32(SDL_RWops * dst, Uint32 value); extern DECLSPEC SDL_bool SDLCALL SDL_WriteU16BE(SDL_RWops *dst, Uint16 value);
/** /**
* Use this function to write 64 bits in native format to a SDL_RWops as * Use this function to write 16 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_WriteS16BE(SDL_RWops *dst, Sint16 value);
/**
* Use this function to write 32 bits in native format to an SDL_RWops as
* little-endian data. * little-endian data.
* *
* SDL byteswaps the data only if necessary, so the application always * SDL byteswaps the data only if necessary, so the application always
@ -778,16 +848,32 @@ extern DECLSPEC size_t SDLCALL SDL_WriteBE32(SDL_RWops * dst, Uint32 value);
* *
* \param dst the stream to which data will be written * \param dst the stream to which data will be written
* \param value the data to be written, in native format * \param value the data to be written, in native format
* \returns 1 on successful write, 0 on error. * \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. * \since This function is available since SDL 3.0.0.
*
* \sa SDL_WriteBE64
*/ */
extern DECLSPEC size_t SDLCALL SDL_WriteLE64(SDL_RWops * dst, Uint64 value); extern DECLSPEC SDL_bool SDLCALL SDL_WriteU32LE(SDL_RWops *dst, Uint32 value);
/** /**
* Use this function to write 64 bits in native format to a SDL_RWops as * Use this function to write 32 bits in native format to an SDL_RWops as
* little-endian data.
*
* SDL byteswaps the data only if necessary, so the application always
* specifies native format, and the data written will be in little-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_WriteS32LE(SDL_RWops *dst, Sint32 value);
/**
* Use this function to write 32 bits in native format to an SDL_RWops as
* big-endian data. * big-endian data.
* *
* SDL byteswaps the data only if necessary, so the application always * SDL byteswaps the data only if necessary, so the application always
@ -795,13 +881,80 @@ extern DECLSPEC size_t SDLCALL SDL_WriteLE64(SDL_RWops * dst, Uint64 value);
* *
* \param dst the stream to which data will be written * \param dst the stream to which data will be written
* \param value the data to be written, in native format * \param value the data to be written, in native format
* \returns 1 on successful write, 0 on error. * \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. * \since This function is available since SDL 3.0.0.
*
* \sa SDL_WriteLE64
*/ */
extern DECLSPEC size_t SDLCALL SDL_WriteBE64(SDL_RWops * dst, Uint64 value); extern DECLSPEC SDL_bool SDLCALL SDL_WriteU32BE(SDL_RWops *dst, Uint32 value);
/**
* Use this function to write 32 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_WriteS32BE(SDL_RWops *dst, Sint32 value);
/**
* Use this function to write 64 bits in native format to an SDL_RWops as
* little-endian data.
*
* SDL byteswaps the data only if necessary, so the application always
* specifies native format, and the data written will be in little-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_WriteU64LE(SDL_RWops *dst, Uint64 value);
/**
* Use this function to write 64 bits in native format to an SDL_RWops as
* little-endian data.
*
* SDL byteswaps the data only if necessary, so the application always
* specifies native format, and the data written will be in little-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_WriteS64LE(SDL_RWops *dst, Sint64 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_WriteU64BE(SDL_RWops *dst, Uint64 value);
extern DECLSPEC SDL_bool SDLCALL SDL_WriteU64BE(SDL_RWops *dst, Uint64 value);
/* @} *//* Write endian functions */ /* @} *//* Write endian functions */
/* Ends C function definitions when using C++ */ /* Ends C function definitions when using C++ */

View File

@ -131,12 +131,15 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_CreateSurface
(int width, int height, Uint32 format); (int width, int height, Uint32 format);
/** /**
* Allocate a new RGB surface with with a specific pixel format and existing * Allocate a new RGB surface with a specific pixel format and existing pixel
* pixel data. * data.
* *
* No copy is made of the pixel data. Pixel data is not managed automatically; * No copy is made of the pixel data. Pixel data is not managed automatically;
* you must free the surface before you free the pixel data. * you must free the surface before you free the pixel data.
* *
* Pitch is the offset in bytes from one row of pixels to the next, e.g.
* `width*4` for `SDL_PIXELFORMAT_RGBA8888`.
*
* You may pass NULL for pixels and 0 for pitch to create a surface that you * You may pass NULL for pixels and 0 for pitch to create a surface that you
* will fill in with valid values later. * will fill in with valid values later.
* *
@ -270,7 +273,8 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_LoadBMP(const char *file);
* *
* \param surface the SDL_Surface structure containing the image to be saved * \param surface the SDL_Surface structure containing the image to be saved
* \param dst a data stream to save to * \param dst a data stream to save to
* \param freedst non-zero to close the stream after being written * \param freedst if SDL_TRUE, calls SDL_RWclose() on `dst` before returning,
* even in the case of an error
* \returns 0 on success or a negative error code on failure; call * \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information. * SDL_GetError() for more information.
* *
@ -279,7 +283,7 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_LoadBMP(const char *file);
* \sa SDL_LoadBMP_RW * \sa SDL_LoadBMP_RW
* \sa SDL_SaveBMP * \sa SDL_SaveBMP
*/ */
extern DECLSPEC int SDLCALL SDL_SaveBMP_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst); extern DECLSPEC int SDLCALL SDL_SaveBMP_RW(SDL_Surface *surface, SDL_RWops *dst, SDL_bool freedst);
/** /**
* Save a surface to a file. * Save a surface to a file.

View File

@ -97,7 +97,9 @@ typedef struct
/* Audio info */ /* Audio info */
const char *audiodriver; const char *audiodriver;
SDL_AudioSpec audiospec; SDL_AudioFormat audio_format;
int audio_channels;
int audio_freq;
SDL_AudioDeviceID audio_id; SDL_AudioDeviceID audio_id;
/* GL settings */ /* GL settings */

View File

@ -155,7 +155,7 @@ SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn,
pfnSDL_CurrentBeginThread pfnBeginThread, pfnSDL_CurrentBeginThread pfnBeginThread,
pfnSDL_CurrentEndThread pfnEndThread); pfnSDL_CurrentEndThread pfnEndThread);
#if !defined(__BUILDING_SDL2_COMPAT__) /* do not conflict with sdl2-compat::sdl3_include_wrapper.h */
#if defined(SDL_CreateThread) && SDL_DYNAMIC_API #if defined(SDL_CreateThread) && SDL_DYNAMIC_API
#undef SDL_CreateThread #undef SDL_CreateThread
#define SDL_CreateThread(fn, name, data) SDL_CreateThread_REAL(fn, name, data, (pfnSDL_CurrentBeginThread)SDL_beginthread, (pfnSDL_CurrentEndThread)SDL_endthread) #define SDL_CreateThread(fn, name, data) SDL_CreateThread_REAL(fn, name, data, (pfnSDL_CurrentBeginThread)SDL_beginthread, (pfnSDL_CurrentEndThread)SDL_endthread)
@ -165,6 +165,7 @@ SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn,
#define SDL_CreateThread(fn, name, data) SDL_CreateThread(fn, name, data, (pfnSDL_CurrentBeginThread)SDL_beginthread, (pfnSDL_CurrentEndThread)SDL_endthread) #define SDL_CreateThread(fn, name, data) SDL_CreateThread(fn, name, data, (pfnSDL_CurrentBeginThread)SDL_beginthread, (pfnSDL_CurrentEndThread)SDL_endthread)
#define SDL_CreateThreadWithStackSize(fn, name, stacksize, data) SDL_CreateThreadWithStackSize(fn, name, stacksize, data, (pfnSDL_CurrentBeginThread)SDL_beginthread, (pfnSDL_CurrentEndThread)SDL_endthread) #define SDL_CreateThreadWithStackSize(fn, name, stacksize, data) SDL_CreateThreadWithStackSize(fn, name, stacksize, data, (pfnSDL_CurrentBeginThread)SDL_beginthread, (pfnSDL_CurrentEndThread)SDL_endthread)
#endif #endif
#endif /* !__BUILDING_SDL2_COMPAT__ */
#else #else

View File

@ -118,6 +118,7 @@ typedef enum
* \sa SDL_SetWindowResizable() * \sa SDL_SetWindowResizable()
* \sa SDL_SetWindowTitle() * \sa SDL_SetWindowTitle()
* \sa SDL_ShowWindow() * \sa SDL_ShowWindow()
* \sa SDL_ShowWindowSystemMenu()
*/ */
typedef struct SDL_Window SDL_Window; typedef struct SDL_Window SDL_Window;
@ -1675,6 +1676,29 @@ extern DECLSPEC int SDLCALL SDL_SetWindowModalFor(SDL_Window *modal_window, SDL_
*/ */
extern DECLSPEC int SDLCALL SDL_SetWindowInputFocus(SDL_Window *window); extern DECLSPEC int SDLCALL SDL_SetWindowInputFocus(SDL_Window *window);
/**
* Display the system-level window menu.
*
* This default window menu is provided by the system and on some platforms
* provides functionality for setting or changing privileged state on the
* window, such as moving it between workspaces or displays, or toggling the
* always-on-top property.
*
* On platforms or desktops where this is unsupported, this function does
* nothing.
*
* \param window the window for which the menu will be displayed
* \param x the x coordinate of the menu, relative to the origin (top-left) of
* the client area
* \param y the y coordinate of the menu, relative to the origin (top-left) of
* the client area
* \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_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
/** /**
* Possible return values from the SDL_HitTest callback. * Possible return values from the SDL_HitTest callback.
* *

View File

@ -632,9 +632,9 @@ SDL_bool SDL_IsTablet(void)
#ifdef __WIN32__ #ifdef __WIN32__
#if (!defined(HAVE_LIBC) || defined(__WATCOMC__)) && !defined(SDL_STATIC_LIB) #if (!defined(HAVE_LIBC) || defined(__WATCOMC__)) && !defined(SDL_STATIC_LIB)
/* Need to include DllMain() on Watcom C for some reason.. */ /* FIXME: Still need to include DllMain() on Watcom C ? */
BOOL APIENTRY _DllMainCRTStartup(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) BOOL APIENTRY MINGW32_FORCEALIGN _DllMainCRTStartup(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{ {
switch (ul_reason_for_call) { switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH: case DLL_PROCESS_ATTACH:

File diff suppressed because it is too large Load Diff

View File

@ -22,59 +22,8 @@
#ifndef SDL_audio_c_h_ #ifndef SDL_audio_c_h_
#define SDL_audio_c_h_ #define SDL_audio_c_h_
#include "SDL_internal.h" /* !!! FIXME: remove this header and have things just include SDL_sysaudio.h directly. */
#define DEBUG_AUDIOSTREAM 0 #include "SDL_sysaudio.h"
#define DEBUG_AUDIO_CONVERT 0
#if DEBUG_AUDIO_CONVERT
#define LOG_DEBUG_AUDIO_CONVERT(from, to) SDL_Log("SDL_AUDIO_CONVERT: Converting %s to %s.\n", from, to);
#else
#define LOG_DEBUG_AUDIO_CONVERT(from, to)
#endif
/* Functions and variables exported from SDL_audio.c for SDL_sysaudio.c */
/* 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);
/* Function to calculate the size and silence for a SDL_AudioSpec */
extern Uint8 SDL_GetSilenceValueForFormat(const SDL_AudioFormat format);
extern void SDL_CalculateAudioSpec(SDL_AudioSpec *spec);
/* Must be called at least once before using converters (SDL_CreateAudioStream will call it). */
extern void SDL_ChooseAudioConverters(void);
/* These pointers get set during SDL_ChooseAudioConverters() to various SIMD implementations. */
extern void (*SDL_Convert_S8_to_F32)(float *dst, const Sint8 *src, int num_samples);
extern void (*SDL_Convert_U8_to_F32)(float *dst, const Uint8 *src, int num_samples);
extern void (*SDL_Convert_S16_to_F32)(float *dst, const Sint16 *src, int num_samples);
extern void (*SDL_Convert_S32_to_F32)(float *dst, const Sint32 *src, int num_samples);
extern void (*SDL_Convert_F32_to_S8)(Sint8 *dst, const float *src, int num_samples);
extern void (*SDL_Convert_F32_to_U8)(Uint8 *dst, const float *src, int num_samples);
extern void (*SDL_Convert_F32_to_S16)(Sint16 *dst, const float *src, int num_samples);
extern void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_samples);
/**
* Use this function to initialize a particular audio driver.
*
* This function is used internally, and should not be used unless you have a
* specific need to designate the audio driver you want to use. You should
* normally use SDL_Init() or SDL_InitSubSystem().
*
* \param driver_name the name of the desired audio driver
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*/
extern int SDL_InitAudio(const char *driver_name);
/**
* Use this function to shut down audio if you initialized it with SDL_InitAudio().
*
* This function is used internally, and should not be used unless you have a
* specific need to specify the audio driver you want to use. You should
* normally use SDL_Quit() or SDL_QuitSubSystem().
*/
extern void SDL_QuitAudio(void);
#endif /* SDL_audio_c_h_ */ #endif /* SDL_audio_c_h_ */

File diff suppressed because it is too large Load Diff

View File

@ -45,13 +45,13 @@
#define SDL_PATH_DEV_AUDIO "/dev/audio" #define SDL_PATH_DEV_AUDIO "/dev/audio"
#endif #endif
static void test_device(const int iscapture, const char *fname, int flags, int (*test)(int fd)) static void test_device(const SDL_bool iscapture, const char *fname, int flags, SDL_bool (*test)(int fd))
{ {
struct stat sb; struct stat sb;
if ((stat(fname, &sb) == 0) && (S_ISCHR(sb.st_mode))) { if ((stat(fname, &sb) == 0) && (S_ISCHR(sb.st_mode))) {
const int audio_fd = open(fname, flags | O_CLOEXEC, 0); const int audio_fd = open(fname, flags | O_CLOEXEC, 0);
if (audio_fd >= 0) { if (audio_fd >= 0) {
const int okay = test(audio_fd); const SDL_bool okay = test(audio_fd);
close(audio_fd); close(audio_fd);
if (okay) { if (okay) {
static size_t dummyhandle = 0; static size_t dummyhandle = 0;
@ -69,12 +69,12 @@ static void test_device(const int iscapture, const char *fname, int flags, int (
} }
} }
static int test_stub(int fd) static SDL_bool test_stub(int fd)
{ {
return 1; return SDL_TRUE;
} }
static void SDL_EnumUnixAudioDevices_Internal(const int iscapture, const int classic, int (*test)(int)) static void SDL_EnumUnixAudioDevices_Internal(const SDL_bool iscapture, const SDL_bool classic, SDL_bool (*test)(int))
{ {
const int flags = iscapture ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT; const int flags = iscapture ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT;
const char *audiodev; const char *audiodev;
@ -116,7 +116,7 @@ static void SDL_EnumUnixAudioDevices_Internal(const int iscapture, const int cla
} }
} }
void SDL_EnumUnixAudioDevices(const int classic, int (*test)(int)) void SDL_EnumUnixAudioDevices(const SDL_bool classic, SDL_bool (*test)(int))
{ {
SDL_EnumUnixAudioDevices_Internal(SDL_TRUE, classic, test); SDL_EnumUnixAudioDevices_Internal(SDL_TRUE, classic, test);
SDL_EnumUnixAudioDevices_Internal(SDL_FALSE, classic, test); SDL_EnumUnixAudioDevices_Internal(SDL_FALSE, classic, test);

View File

@ -36,6 +36,6 @@
#define OPEN_FLAGS_INPUT (O_RDONLY | O_NONBLOCK) #define OPEN_FLAGS_INPUT (O_RDONLY | O_NONBLOCK)
#endif #endif
extern void SDL_EnumUnixAudioDevices(const int classic, int (*test)(int)); extern void SDL_EnumUnixAudioDevices(const SDL_bool classic, SDL_bool (*test)(int));
#endif /* SDL_audiodev_c_h_ */ #endif /* SDL_audiodev_c_h_ */

View File

@ -18,151 +18,269 @@
misrepresented as being the original software. misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
*/ */
#include "SDL_internal.h" #include "SDL_internal.h"
#ifndef SDL_sysaudio_h_ #ifndef SDL_sysaudio_h_
#define SDL_sysaudio_h_ #define SDL_sysaudio_h_
#include "../SDL_dataqueue.h" #include "../SDL_dataqueue.h"
#include "./SDL_audio_c.h"
/* !!! FIXME: These are wordy and unlocalized... */ #define DEBUG_AUDIOSTREAM 0
#define DEBUG_AUDIO_CONVERT 0
#if DEBUG_AUDIO_CONVERT
#define LOG_DEBUG_AUDIO_CONVERT(from, to) SDL_Log("SDL_AUDIO_CONVERT: Converting %s to %s.\n", from, to);
#else
#define LOG_DEBUG_AUDIO_CONVERT(from, to)
#endif
// These pointers get set during SDL_ChooseAudioConverters() to various SIMD implementations.
extern void (*SDL_Convert_S8_to_F32)(float *dst, const Sint8 *src, int num_samples);
extern void (*SDL_Convert_U8_to_F32)(float *dst, const Uint8 *src, int num_samples);
extern void (*SDL_Convert_S16_to_F32)(float *dst, const Sint16 *src, int num_samples);
extern void (*SDL_Convert_S32_to_F32)(float *dst, const Sint32 *src, int num_samples);
extern void (*SDL_Convert_F32_to_S8)(Sint8 *dst, const float *src, int num_samples);
extern void (*SDL_Convert_F32_to_U8)(Uint8 *dst, const float *src, int num_samples);
extern void (*SDL_Convert_F32_to_S16)(Sint16 *dst, const float *src, int num_samples);
extern void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_samples);
// !!! FIXME: These are wordy and unlocalized...
#define DEFAULT_OUTPUT_DEVNAME "System audio output device" #define DEFAULT_OUTPUT_DEVNAME "System audio output device"
#define DEFAULT_INPUT_DEVNAME "System audio capture device" #define DEFAULT_INPUT_DEVNAME "System audio capture device"
/* The SDL audio driver */ // these are used when no better specifics are known. We default to CD audio quality.
typedef struct SDL_AudioDevice SDL_AudioDevice; #define DEFAULT_AUDIO_OUTPUT_FORMAT SDL_AUDIO_S16
#define DEFAULT_AUDIO_OUTPUT_CHANNELS 2
#define DEFAULT_AUDIO_OUTPUT_FREQUENCY 44100
/* Audio targets should call this as devices are added to the system (such as #define DEFAULT_AUDIO_CAPTURE_FORMAT SDL_AUDIO_S16
#define DEFAULT_AUDIO_CAPTURE_CHANNELS 1
#define DEFAULT_AUDIO_CAPTURE_FREQUENCY 44100
typedef struct SDL_AudioDevice SDL_AudioDevice;
typedef struct SDL_LogicalAudioDevice SDL_LogicalAudioDevice;
// Used by src/SDL.c to initialize a particular audio driver.
extern int SDL_InitAudio(const char *driver_name);
// Used by src/SDL.c to shut down previously-initialized audio.
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).
extern void SDL_ChooseAudioConverters(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 a USB headset being plugged in), and should also be called for
for every device found during DetectDevices(). */ for every device found during DetectDevices(). */
extern void SDL_AddAudioDevice(const SDL_bool iscapture, const char *name, SDL_AudioSpec *spec, void *handle); extern SDL_AudioDevice *SDL_AddAudioDevice(const SDL_bool iscapture, const char *name, const SDL_AudioSpec *spec, void *handle);
/* Audio targets should call this as devices are removed, so SDL can update /* Backends should call this if an opened audio device is lost.
its list of available devices. */ This can happen due to i/o errors, or a device being unplugged, etc. */
extern void SDL_RemoveAudioDevice(const SDL_bool iscapture, void *handle); extern void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device);
/* Audio targets should call this if an opened audio device is lost while // Backends should call this if the system default device changes.
being used. This can happen due to i/o errors, or a device being unplugged, extern void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device);
etc. If the device is totally gone, please also call SDL_RemoveAudioDevice()
as appropriate so SDL's list of devices is accurate. */
extern void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device);
/* This is the size of a packet when using SDL_QueueAudio(). We allocate // Backends should call this if a device's format is changing (opened or not); SDL will update state and carry on with the new format.
these as necessary and pool them, under the assumption that we'll extern int SDL_AudioDeviceFormatChanged(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames);
eventually end up with a handful that keep recycling, meeting whatever
the app needs. We keep packing data tightly as more arrives to avoid // Same as above, but assume the device is already locked.
wasting space, and if we get a giant block of data, we'll split them extern int SDL_AudioDeviceFormatChangedAlreadyLocked(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames);
into multiple packets behind the scenes. My expectation is that most
apps will have 2-3 of these in the pool. 8k should cover most needs, but // Find the SDL_AudioDevice associated with the handle supplied to SDL_AddAudioDevice. NULL if not found. DOES NOT LOCK THE DEVICE.
if this is crippling for some embedded system, we can #ifdef this. extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByHandle(void *handle);
The system preallocates enough packets for 2 callbacks' worth of data. */
#define SDL_AUDIOBUFFERQUEUE_PACKETLEN (8 * 1024) // Find an SDL_AudioDevice, selected by a callback. NULL if not found. DOES NOT LOCK THE DEVICE.
extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByCallback(SDL_bool (*callback)(SDL_AudioDevice *device, void *userdata), void *userdata);
// Backends should call this if they change the device format, channels, freq, or sample_frames to keep other state correct.
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);
// These functions are the heart of the audio threads. Backends can call them directly if they aren't using the SDL-provided thread.
extern void SDL_OutputAudioThreadSetup(SDL_AudioDevice *device);
extern SDL_bool SDL_OutputAudioThreadIterate(SDL_AudioDevice *device);
extern void SDL_OutputAudioThreadShutdown(SDL_AudioDevice *device);
extern void SDL_CaptureAudioThreadSetup(SDL_AudioDevice *device);
extern SDL_bool SDL_CaptureAudioThreadIterate(SDL_AudioDevice *device);
extern void SDL_CaptureAudioThreadShutdown(SDL_AudioDevice *device);
extern void SDL_AudioThreadFinalize(SDL_AudioDevice *device);
typedef struct SDL_AudioDriverImpl typedef struct SDL_AudioDriverImpl
{ {
void (*DetectDevices)(void); void (*DetectDevices)(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
int (*OpenDevice)(SDL_AudioDevice *_this, const char *devname); int (*OpenDevice)(SDL_AudioDevice *device);
void (*ThreadInit)(SDL_AudioDevice *_this); /* Called by audio thread at start */ void (*ThreadInit)(SDL_AudioDevice *device); // Called by audio thread at start
void (*ThreadDeinit)(SDL_AudioDevice *_this); /* Called by audio thread at end */ void (*ThreadDeinit)(SDL_AudioDevice *device); // Called by audio thread at end
void (*WaitDevice)(SDL_AudioDevice *_this); void (*WaitDevice)(SDL_AudioDevice *device);
void (*PlayDevice)(SDL_AudioDevice *_this); void (*PlayDevice)(SDL_AudioDevice *device, const Uint8 *buffer, int buflen); // buffer and buflen are always from GetDeviceBuf, passed here for convenience.
Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *_this); Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *device, int *buffer_size);
int (*CaptureFromDevice)(SDL_AudioDevice *_this, void *buffer, int buflen); void (*WaitCaptureDevice)(SDL_AudioDevice *device);
void (*FlushCapture)(SDL_AudioDevice *_this); int (*CaptureFromDevice)(SDL_AudioDevice *device, void *buffer, int buflen);
void (*CloseDevice)(SDL_AudioDevice *_this); void (*FlushCapture)(SDL_AudioDevice *device);
void (*LockDevice)(SDL_AudioDevice *_this); void (*CloseDevice)(SDL_AudioDevice *device);
void (*UnlockDevice)(SDL_AudioDevice *_this); void (*FreeDeviceHandle)(SDL_AudioDevice *device); // SDL is done with this device; free the handle from SDL_AddAudioDevice()
void (*FreeDeviceHandle)(void *handle); /**< SDL is done with handle from SDL_AddAudioDevice() */
void (*Deinitialize)(void); void (*Deinitialize)(void);
int (*GetDefaultAudioInfo)(char **name, SDL_AudioSpec *spec, int iscapture);
/* !!! FIXME: add pause(), so we can optimize instead of mixing silence. */ // Some flags to push duplicate code into the core and reduce #ifdefs.
SDL_bool ProvidesOwnCallbackThread; // !!! FIXME: rename this, it's not a callback thread anymore.
/* Some flags to push duplicate code into the core and reduce #ifdefs. */
SDL_bool ProvidesOwnCallbackThread;
SDL_bool HasCaptureSupport; SDL_bool HasCaptureSupport;
SDL_bool OnlyHasDefaultOutputDevice; SDL_bool OnlyHasDefaultOutputDevice;
SDL_bool OnlyHasDefaultCaptureDevice; SDL_bool OnlyHasDefaultCaptureDevice;
SDL_bool AllowsArbitraryDeviceNames; SDL_bool AllowsArbitraryDeviceNames;
SDL_bool SupportsNonPow2Samples;
} SDL_AudioDriverImpl; } SDL_AudioDriverImpl;
typedef struct SDL_AudioDeviceItem
{
void *handle;
char *name;
char *original_name;
SDL_AudioSpec spec;
int dupenum;
struct SDL_AudioDeviceItem *next;
} SDL_AudioDeviceItem;
typedef struct SDL_AudioDriver typedef struct SDL_AudioDriver
{ {
/* * * */ const char *name; // The name of this audio driver
/* The name of this audio driver */ const char *desc; // The description of this audio driver
const char *name; SDL_AudioDriverImpl impl; // the backend's interface
SDL_RWLock *device_list_lock; // A mutex for device detection
/* * * */ SDL_AudioDevice *output_devices; // the list of currently-available audio output devices.
/* The description of this audio driver */ SDL_AudioDevice *capture_devices; // the list of currently-available audio capture devices.
const char *desc; SDL_AudioDeviceID default_output_device_id;
SDL_AudioDeviceID default_capture_device_id;
SDL_AudioDriverImpl impl; SDL_AtomicInt output_device_count;
SDL_AtomicInt capture_device_count;
/* A mutex for device detection */ SDL_AtomicInt last_device_instance_id; // increments on each device add to provide unique instance IDs
SDL_Mutex *detectionLock; SDL_AtomicInt shutting_down; // non-zero during SDL_Quit, so we known not to accept any last-minute device hotplugs.
SDL_bool captureDevicesRemoved;
SDL_bool outputDevicesRemoved;
int outputDeviceCount;
int inputDeviceCount;
SDL_AudioDeviceItem *outputDevices;
SDL_AudioDeviceItem *inputDevices;
} SDL_AudioDriver; } SDL_AudioDriver;
/* Define the SDL audio driver structure */ struct SDL_AudioStream
{
SDL_DataQueue *queue;
SDL_Mutex *lock; // this is just a copy of `queue`'s mutex. We share a lock.
SDL_AudioStreamRequestCallback get_callback;
void *get_callback_userdata;
SDL_AudioStreamRequestCallback 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;
int src_sample_frame_size;
int dst_sample_frame_size;
int max_sample_frame_size;
int pre_resample_channels;
int packetlen;
SDL_LogicalAudioDevice *bound_device;
SDL_AudioStream *next_binding;
SDL_AudioStream *prev_binding;
};
/* Logical devices are an abstraction in SDL3; you can open the same physical
device multiple times, and each will result in an object with its own set
of bound audio streams, etc, even though internally these are all processed
as a group when mixing the final output for the physical device. */
struct SDL_LogicalAudioDevice
{
// the unique instance ID of this device.
SDL_AudioDeviceID instance_id;
// The physical device associated with this opened device.
SDL_AudioDevice *physical_device;
// If whole logical device is paused (process no streams bound to this device).
SDL_AtomicInt paused;
// double-linked list of all audio streams currently bound to this opened device.
SDL_AudioStream *bound_streams;
// SDL_TRUE if this was opened as a default device.
SDL_bool is_default;
// double-linked list of opened devices on the same physical device.
SDL_LogicalAudioDevice *next;
SDL_LogicalAudioDevice *prev;
};
struct SDL_AudioDevice struct SDL_AudioDevice
{ {
/* * * */ // A mutex for locking access to this struct
/* Data common to all devices */ SDL_Mutex *lock;
SDL_AudioDeviceID id;
/* The device's current audio specification */ // human-readable name of the device. ("SoundBlaster Pro 16")
char *name;
// the unique instance ID of this device.
SDL_AudioDeviceID instance_id;
// a way for the backend to identify this device _when not opened_
void *handle;
// The device's current audio specification
SDL_AudioSpec spec; SDL_AudioSpec spec;
int buffer_size;
/* The callback's expected audio specification (converted vs device's spec). */ // The device's default audio specification
SDL_AudioSpec callbackspec; SDL_AudioSpec default_spec;
/* Stream that converts and resamples. NULL if not needed. */ // Number of sample frames the devices wants per-buffer.
SDL_AudioStream *stream; int sample_frames;
/* Current state flags */ // Value to use for SDL_memset to silence a buffer in this device's format
SDL_AtomicInt shutdown; /* true if we are signaling the play thread to end. */ int silence_value;
SDL_AtomicInt enabled; /* true if device is functioning and connected. */
SDL_AtomicInt paused; // non-zero if we are signaling the audio thread to end.
SDL_AtomicInt shutdown;
// non-zero if we want the device to be destroyed (so audio thread knows to do it on termination).
SDL_AtomicInt condemned;
// non-zero if this was a disconnected default device and we're waiting for its replacement.
SDL_AtomicInt zombie;
// non-zero if this has a thread running (which might be `thread` or something provided by the backend!)
SDL_AtomicInt thread_alive;
// SDL_TRUE if this is a capture device instead of an output device
SDL_bool iscapture; SDL_bool iscapture;
/* Scratch buffer used in the bridge between SDL and the user callback. */ // Scratch buffer used for mixing.
Uint8 *work_buffer; Uint8 *work_buffer;
/* Size, in bytes, of work_buffer. */ // A thread to feed the audio device
Uint32 work_buffer_len;
/* A mutex for locking the mixing buffers */
SDL_Mutex *mixer_lock;
/* A thread to feed the audio device */
SDL_Thread *thread; SDL_Thread *thread;
SDL_threadID threadid;
/* Queued buffers (if app not using callback). */ // SDL_TRUE if this physical device is currently opened by the backend.
SDL_DataQueue *buffer_queue; SDL_bool is_opened;
/* * * */ // Data private to this driver
/* Data private to this driver */
struct SDL_PrivateAudioData *hidden; struct SDL_PrivateAudioData *hidden;
void *handle; // All logical devices associated with this physical device.
SDL_LogicalAudioDevice *logical_devices;
// double-linked list of all physical devices.
struct SDL_AudioDevice *prev;
struct SDL_AudioDevice *next;
}; };
typedef struct AudioBootStrap typedef struct AudioBootStrap
@ -170,10 +288,10 @@ typedef struct AudioBootStrap
const char *name; const char *name;
const char *desc; const char *desc;
SDL_bool (*init)(SDL_AudioDriverImpl *impl); SDL_bool (*init)(SDL_AudioDriverImpl *impl);
SDL_bool demand_only; /* 1==request explicitly, or it won't be available. */ SDL_bool demand_only; // if SDL_TRUE: request explicitly, or it won't be available.
} AudioBootStrap; } AudioBootStrap;
/* Not all of these are available in a given build. Use #ifdefs, etc. */ // Not all of these are available in a given build. Use #ifdefs, etc.
extern AudioBootStrap PIPEWIRE_bootstrap; extern AudioBootStrap PIPEWIRE_bootstrap;
extern AudioBootStrap PULSEAUDIO_bootstrap; extern AudioBootStrap PULSEAUDIO_bootstrap;
extern AudioBootStrap ALSA_bootstrap; extern AudioBootStrap ALSA_bootstrap;
@ -188,8 +306,8 @@ extern AudioBootStrap HAIKUAUDIO_bootstrap;
extern AudioBootStrap COREAUDIO_bootstrap; extern AudioBootStrap COREAUDIO_bootstrap;
extern AudioBootStrap DISKAUDIO_bootstrap; extern AudioBootStrap DISKAUDIO_bootstrap;
extern AudioBootStrap DUMMYAUDIO_bootstrap; extern AudioBootStrap DUMMYAUDIO_bootstrap;
extern AudioBootStrap aaudio_bootstrap; extern AudioBootStrap AAUDIO_bootstrap;
extern AudioBootStrap openslES_bootstrap; extern AudioBootStrap openslES_bootstrap; // !!! FIXME: capitalize this to match the others
extern AudioBootStrap ANDROIDAUDIO_bootstrap; extern AudioBootStrap ANDROIDAUDIO_bootstrap;
extern AudioBootStrap PS2AUDIO_bootstrap; extern AudioBootStrap PS2AUDIO_bootstrap;
extern AudioBootStrap PSPAUDIO_bootstrap; extern AudioBootStrap PSPAUDIO_bootstrap;
@ -201,4 +319,4 @@ extern AudioBootStrap QSAAUDIO_bootstrap;
extern SDL_AudioDevice *get_audio_dev(SDL_AudioDeviceID id); extern SDL_AudioDevice *get_audio_dev(SDL_AudioDeviceID id);
extern int get_max_num_audio_dev(void); extern int get_max_num_audio_dev(void);
#endif /* SDL_sysaudio_h_ */ #endif // SDL_sysaudio_h_

View File

@ -1241,7 +1241,7 @@ static int LAW_Decode(WaveFile *file, Uint8 **audio_buf, Uint32 *audio_len)
dst = (Sint16 *)src; dst = (Sint16 *)src;
/* Work backwards, since we're expanding in-place. SDL_AudioSpec.format will /* Work backwards, since we're expanding in-place. `format` will
* inform the caller about the byte order. * inform the caller about the byte order.
*/ */
i = sample_count; i = sample_count;
@ -1553,7 +1553,7 @@ static int WaveReadPartialChunkData(SDL_RWops *src, WaveChunk *chunk, size_t len
return -2; return -2;
} }
chunk->size = (size_t) SDL_RWread(src, chunk->data, length); chunk->size = SDL_RWread(src, chunk->data, length);
if (chunk->size != length) { if (chunk->size != length) {
/* Expected to be handled by the caller. */ /* Expected to be handled by the caller. */
} }
@ -1614,16 +1614,20 @@ static int WaveReadFormat(WaveFile *file)
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
format->formattag = SDL_ReadLE16(fmtsrc); if (!SDL_ReadU16LE(fmtsrc, &format->formattag) ||
!SDL_ReadU16LE(fmtsrc, &format->channels) ||
!SDL_ReadU32LE(fmtsrc, &format->frequency) ||
!SDL_ReadU32LE(fmtsrc, &format->byterate) ||
!SDL_ReadU16LE(fmtsrc, &format->blockalign)) {
return -1;
}
format->encoding = format->formattag; format->encoding = format->formattag;
format->channels = SDL_ReadLE16(fmtsrc);
format->frequency = SDL_ReadLE32(fmtsrc);
format->byterate = SDL_ReadLE32(fmtsrc);
format->blockalign = SDL_ReadLE16(fmtsrc);
/* This is PCM specific in the first version of the specification. */ /* This is PCM specific in the first version of the specification. */
if (fmtlen >= 16) { if (fmtlen >= 16) {
format->bitspersample = SDL_ReadLE16(fmtsrc); if (!SDL_ReadU16LE(fmtsrc, &format->bitspersample)) {
return -1;
}
} else if (format->encoding == PCM_CODE) { } else if (format->encoding == PCM_CODE) {
SDL_RWclose(fmtsrc); SDL_RWclose(fmtsrc);
return SDL_SetError("Missing wBitsPerSample field in WAVE fmt chunk"); return SDL_SetError("Missing wBitsPerSample field in WAVE fmt chunk");
@ -1631,7 +1635,9 @@ static int WaveReadFormat(WaveFile *file)
/* The earlier versions also don't have this field. */ /* The earlier versions also don't have this field. */
if (fmtlen >= 18) { if (fmtlen >= 18) {
format->extsize = SDL_ReadLE16(fmtsrc); if (!SDL_ReadU16LE(fmtsrc, &format->extsize)) {
return -1;
}
} }
if (format->formattag == EXTENSIBLE_CODE) { if (format->formattag == EXTENSIBLE_CODE) {
@ -1647,10 +1653,11 @@ static int WaveReadFormat(WaveFile *file)
return SDL_SetError("Extensible WAVE header too small"); return SDL_SetError("Extensible WAVE header too small");
} }
format->validsamplebits = SDL_ReadLE16(fmtsrc); if (!SDL_ReadU16LE(fmtsrc, &format->validsamplebits) ||
!SDL_ReadU32LE(fmtsrc, &format->channelmask) ||
SDL_RWread(fmtsrc, format->subformat, 16) != 16) {
}
format->samplesperblock = format->validsamplebits; format->samplesperblock = format->validsamplebits;
format->channelmask = SDL_ReadLE32(fmtsrc);
SDL_RWread(fmtsrc, format->subformat, 16);
format->encoding = WaveGetFormatGUIDEncoding(format); format->encoding = WaveGetFormatGUIDEncoding(format);
} }
@ -1667,15 +1674,11 @@ static int WaveCheckFormat(WaveFile *file, size_t datalength)
if (format->channels == 0) { if (format->channels == 0) {
return SDL_SetError("Invalid number of channels"); return SDL_SetError("Invalid number of channels");
} else if (format->channels > 255) {
/* Limit given by SDL_AudioSpec.channels. */
return SDL_SetError("Number of channels exceeds limit of 255");
} }
if (format->frequency == 0) { if (format->frequency == 0) {
return SDL_SetError("Invalid sample rate"); return SDL_SetError("Invalid sample rate");
} else if (format->frequency > INT_MAX) { } else if (format->frequency > INT_MAX) {
/* Limit given by SDL_AudioSpec.freq. */
return SDL_SetError("Sample rate exceeds limit of %d", INT_MAX); return SDL_SetError("Sample rate exceeds limit of %d", INT_MAX);
} }
@ -1806,9 +1809,9 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 *
if (RIFFchunk.fourcc == RIFF) { if (RIFFchunk.fourcc == RIFF) {
Uint32 formtype; Uint32 formtype;
/* Read the form type. "WAVE" expected. */ /* Read the form type. "WAVE" expected. */
if (SDL_RWread(src, &formtype, sizeof(Uint32)) != sizeof(Uint32)) { if (!SDL_ReadU32LE(src, &formtype)) {
return SDL_SetError("Could not read RIFF form type"); return SDL_SetError("Could not read RIFF form type");
} else if (SDL_SwapLE32(formtype) != WAVE) { } else if (formtype != WAVE) {
return SDL_SetError("RIFF form type is not WAVE (not a Waveform file)"); return SDL_SetError("RIFF form type is not WAVE (not a Waveform file)");
} }
} else if (RIFFchunk.fourcc == WAVE) { } else if (RIFFchunk.fourcc == WAVE) {
@ -1895,10 +1898,8 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 *
} else { } else {
/* Let's use src directly, it's just too convenient. */ /* Let's use src directly, it's just too convenient. */
Sint64 position = SDL_RWseek(src, chunk->position, SDL_RW_SEEK_SET); Sint64 position = SDL_RWseek(src, chunk->position, SDL_RW_SEEK_SET);
Uint32 samplelength; if (position == chunk->position && SDL_ReadU32LE(src, &file->fact.samplelength)) {
if (position == chunk->position && SDL_RWread(src, &samplelength, sizeof(Uint32)) == sizeof(Uint32)) {
file->fact.status = 1; file->fact.status = 1;
file->fact.samplelength = SDL_SwapLE32(samplelength);
} else { } else {
file->fact.status = -1; file->fact.status = -1;
} }
@ -1941,7 +1942,7 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 *
Uint64 position = (Uint64)chunk->position + chunk->length - 1; Uint64 position = (Uint64)chunk->position + chunk->length - 1;
if (position > SDL_MAX_SINT64 || SDL_RWseek(src, (Sint64)position, SDL_RW_SEEK_SET) != (Sint64)position) { if (position > SDL_MAX_SINT64 || SDL_RWseek(src, (Sint64)position, SDL_RW_SEEK_SET) != (Sint64)position) {
return SDL_SetError("Could not seek to WAVE chunk data"); return SDL_SetError("Could not seek to WAVE chunk data");
} else if (SDL_RWread(src, &tmp, 1) != 1) { } else if (!SDL_ReadU8(src, &tmp)) {
return SDL_SetError("RIFF size truncates chunk"); return SDL_SetError("RIFF size truncates chunk");
} }
} }
@ -2025,13 +2026,12 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 *
break; break;
} }
/* Setting up the SDL_AudioSpec. All unsupported formats were filtered out /* Setting up the specs. All unsupported formats were filtered out
* by checks earlier in this function. * by checks earlier in this function.
*/ */
SDL_zerop(spec);
spec->freq = format->frequency; spec->freq = format->frequency;
spec->channels = (Uint8)format->channels; spec->channels = (Uint8)format->channels;
spec->samples = 4096; /* Good default buffer size */ spec->format = 0;
switch (format->encoding) { switch (format->encoding) {
case MS_ADPCM_CODE: case MS_ADPCM_CODE:
@ -2061,10 +2061,10 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 *
return SDL_SetError("Unexpected %u-bit PCM data format", (unsigned int)format->bitspersample); return SDL_SetError("Unexpected %u-bit PCM data format", (unsigned int)format->bitspersample);
} }
break; break;
default:
return SDL_SetError("Unexpected data format");
} }
spec->silence = SDL_GetSilenceValueForFormat(spec->format);
/* Report the end position back to the cleanup code. */ /* Report the end position back to the cleanup code. */
if (RIFFlengthknown) { if (RIFFlengthknown) {
chunk->position = RIFFend; chunk->position = RIFFend;
@ -2075,17 +2075,14 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 *
return 0; return 0;
} }
SDL_AudioSpec *SDL_LoadWAV_RW(SDL_RWops *src, SDL_bool freesrc, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) int SDL_LoadWAV_RW(SDL_RWops *src, SDL_bool freesrc, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
{ {
int result = -1; int result = -1;
WaveFile file; WaveFile file;
SDL_zero(file);
/* Make sure we are passed a valid data source */ /* Make sure we are passed a valid data source */
if (src == NULL) { if (src == NULL) {
/* Error may come from RWops. */ goto done; /* Error may come from RWops. */
goto done;
} else if (spec == NULL) { } else if (spec == NULL) {
SDL_InvalidParamError("spec"); SDL_InvalidParamError("spec");
goto done; goto done;
@ -2100,6 +2097,7 @@ SDL_AudioSpec *SDL_LoadWAV_RW(SDL_RWops *src, SDL_bool freesrc, SDL_AudioSpec *s
*audio_buf = NULL; *audio_buf = NULL;
*audio_len = 0; *audio_len = 0;
SDL_zero(file);
file.riffhint = WaveGetRiffSizeHint(); file.riffhint = WaveGetRiffSizeHint();
file.trunchint = WaveGetTruncationHint(); file.trunchint = WaveGetTruncationHint();
file.facthint = WaveGetFactChunkHint(); file.facthint = WaveGetFactChunkHint();
@ -2107,7 +2105,6 @@ SDL_AudioSpec *SDL_LoadWAV_RW(SDL_RWops *src, SDL_bool freesrc, SDL_AudioSpec *s
result = WaveLoad(src, &file, spec, audio_buf, audio_len); result = WaveLoad(src, &file, spec, audio_buf, audio_len);
if (result < 0) { if (result < 0) {
SDL_free(*audio_buf); SDL_free(*audio_buf);
spec = NULL;
audio_buf = NULL; audio_buf = NULL;
audio_len = 0; audio_len = 0;
} }
@ -2118,14 +2115,15 @@ SDL_AudioSpec *SDL_LoadWAV_RW(SDL_RWops *src, SDL_bool freesrc, SDL_AudioSpec *s
} }
WaveFreeChunkData(&file.chunk); WaveFreeChunkData(&file.chunk);
SDL_free(file.decoderdata); SDL_free(file.decoderdata);
done: done:
if (freesrc && src) { if (freesrc && src) {
SDL_RWclose(src); SDL_RWclose(src);
} }
if (result == 0) { return result;
return spec;
} else {
return NULL;
} }
int SDL_LoadWAV(const char *path, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
{
return SDL_LoadWAV_RW(SDL_RWFromFile(path, "rb"), 1, spec, audio_buf, audio_len);
} }

View File

@ -30,37 +30,37 @@
#include <stdbool.h> #include <stdbool.h>
#include <aaudio/AAudio.h> #include <aaudio/AAudio.h>
#if __ANDROID_API__ < 31
#define AAUDIO_FORMAT_PCM_I32 4
#endif
struct SDL_PrivateAudioData struct SDL_PrivateAudioData
{ {
AAudioStream *stream; AAudioStream *stream;
Uint8 *mixbuf; // Raw mixing buffer
/* Raw mixing buffer */ SDL_Semaphore *semaphore;
Uint8 *mixbuf; SDL_AtomicInt error_callback_triggered;
int mixlen; int resume; // Resume device if it was paused automatically
int frame_size;
/* Resume device if it was paused automatically */
int resume;
}; };
/* Debug */ // Debug
#if 0 #if 0
#define LOGI(...) SDL_Log(__VA_ARGS__); #define LOGI(...) SDL_Log(__VA_ARGS__);
#else #else
#define LOGI(...) #define LOGI(...)
#endif #endif
#define LIB_AAUDIO_SO "libaaudio.so"
typedef struct AAUDIO_Data typedef struct AAUDIO_Data
{ {
AAudioStreamBuilder *builder;
void *handle; void *handle;
#define SDL_PROC(ret, func, params) ret (*func) params; #define SDL_PROC(ret, func, params) ret (*func) params;
#include "SDL_aaudiofuncs.h" #include "SDL_aaudiofuncs.h"
#undef SDL_PROC
} AAUDIO_Data; } AAUDIO_Data;
static AAUDIO_Data ctx; static AAUDIO_Data ctx;
static int aaudio_LoadFunctions(AAUDIO_Data *data) static int AAUDIO_LoadFunctions(AAUDIO_Data *data)
{ {
#define SDL_PROC(ret, func, params) \ #define SDL_PROC(ret, func, params) \
do { \ do { \
@ -70,23 +70,95 @@ static int aaudio_LoadFunctions(AAUDIO_Data *data)
} \ } \
} while (0); } while (0);
#include "SDL_aaudiofuncs.h" #include "SDL_aaudiofuncs.h"
#undef SDL_PROC
return 0; return 0;
} }
void aaudio_errorCallback(AAudioStream *stream, void *userData, aaudio_result_t error);
void aaudio_errorCallback(AAudioStream *stream, void *userData, aaudio_result_t error) static void AAUDIO_errorCallback(AAudioStream *stream, void *userData, aaudio_result_t error)
{ {
LOGI("SDL aaudio_errorCallback: %d - %s", error, ctx.AAudio_convertResultToText(error)); LOGI("SDL AAUDIO_errorCallback: %d - %s", error, ctx.AAudio_convertResultToText(error));
// You MUST NOT close the audio stream from this callback, so we cannot call SDL_AudioDeviceDisconnected here.
// Just flag the device so we can kill it in WaitDevice/PlayDevice instead.
SDL_AudioDevice *device = (SDL_AudioDevice *) userData;
SDL_AtomicSet(&device->hidden->error_callback_triggered, 1);
SDL_PostSemaphore(device->hidden->semaphore); // in case we're blocking in WaitDevice.
} }
#define LIB_AAUDIO_SO "libaaudio.so" // due to the way the aaudio data callback works, PlayDevice is a no-op. The callback collects audio while SDL camps in WaitDevice and
// fires a semaphore that will unblock WaitDevice and start a new iteration, so when the callback runs again, WaitDevice is ready
static int aaudio_OpenDevice(SDL_AudioDevice *_this, const char *devname) // to hand it more data.
static aaudio_data_callback_result_t AAUDIO_dataCallback(AAudioStream *stream, void *userData, void *audioData, int32_t numFrames)
{ {
struct SDL_PrivateAudioData *private; SDL_AudioDevice *device = (SDL_AudioDevice *) userData;
SDL_bool iscapture = _this->iscapture; SDL_assert(numFrames == device->sample_frames);
if (device->iscapture) {
SDL_memcpy(device->hidden->mixbuf, audioData, device->buffer_size);
} else {
SDL_memcpy(audioData, device->hidden->mixbuf, device->buffer_size);
}
SDL_PostSemaphore(device->hidden->semaphore);
return AAUDIO_CALLBACK_RESULT_CONTINUE;
}
static Uint8 *AAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize)
{
return device->hidden->mixbuf;
}
static void AAUDIO_WaitDevice(SDL_AudioDevice *device)
{
SDL_WaitSemaphore(device->hidden->semaphore);
}
static void 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);
}
}
// no need for a FlushCapture implementation, just don't read mixbuf until the next iteration.
static int AAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{
const int cpy = SDL_min(buflen, device->buffer_size);
SDL_memcpy(buffer, device->hidden->mixbuf, cpy);
return cpy;
}
static void AAUDIO_CloseDevice(SDL_AudioDevice *device)
{
struct SDL_PrivateAudioData *hidden = device->hidden;
LOGI(__func__);
if (hidden) {
if (hidden->stream) {
ctx.AAudioStream_requestStop(hidden->stream);
// !!! FIXME: do we have to wait for the state to change to make sure all buffered audio has played, or will close do this (or will the system do this after the close)?
// !!! FIXME: also, will this definitely wait for a running data callback to finish, and then stop the callback from firing again?
ctx.AAudioStream_close(hidden->stream);
}
if (hidden->semaphore) {
SDL_DestroySemaphore(hidden->semaphore);
}
SDL_free(hidden->mixbuf);
SDL_free(hidden);
device->hidden = NULL;
}
}
static int AAUDIO_OpenDevice(SDL_AudioDevice *device)
{
struct SDL_PrivateAudioData *hidden;
const SDL_bool iscapture = device->iscapture;
aaudio_result_t res; aaudio_result_t res;
SDL_assert(device->handle != NULL); // AAUDIO_UNSPECIFIED is zero, so legit devices should all be non-zero.
LOGI(__func__); LOGI(__func__);
if (iscapture) { if (iscapture) {
@ -96,75 +168,111 @@ static int aaudio_OpenDevice(SDL_AudioDevice *_this, const char *devname)
} }
} }
_this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*_this->hidden)); hidden = device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
if (_this->hidden == NULL) { if (hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
private = _this->hidden;
ctx.AAudioStreamBuilder_setSampleRate(ctx.builder, _this->spec.freq); SDL_AtomicSet(&hidden->error_callback_triggered, 0);
ctx.AAudioStreamBuilder_setChannelCount(ctx.builder, _this->spec.channels);
if(devname != NULL) { AAudioStreamBuilder *builder = NULL;
int aaudio_device_id = SDL_atoi(devname); res = ctx.AAudio_createStreamBuilder(&builder);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudio_createStreamBuilder %d", res);
return SDL_SetError("SDL Failed AAudio_createStreamBuilder %d", res);
} else if (builder == NULL) {
LOGI("SDL Failed AAudio_createStreamBuilder - builder NULL");
return SDL_SetError("SDL Failed AAudio_createStreamBuilder - builder NULL");
}
// !!! FIXME: call AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); ?
ctx.AAudioStreamBuilder_setSampleRate(builder, device->spec.freq);
ctx.AAudioStreamBuilder_setChannelCount(builder, device->spec.channels);
const int aaudio_device_id = (int) ((size_t) device->handle);
LOGI("Opening device id %d", aaudio_device_id); LOGI("Opening device id %d", aaudio_device_id);
ctx.AAudioStreamBuilder_setDeviceId(ctx.builder, aaudio_device_id); ctx.AAudioStreamBuilder_setDeviceId(builder, aaudio_device_id);
}
{ const aaudio_direction_t direction = (iscapture ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT);
aaudio_direction_t direction = (iscapture ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT); ctx.AAudioStreamBuilder_setDirection(builder, direction);
ctx.AAudioStreamBuilder_setDirection(ctx.builder, direction); aaudio_format_t format;
} if ((device->spec.format == SDL_AUDIO_S32SYS) && (SDL_GetAndroidSDKVersion() >= 31)) {
{ format = AAUDIO_FORMAT_PCM_I32;
aaudio_format_t format = AAUDIO_FORMAT_PCM_FLOAT; } else if (device->spec.format == SDL_AUDIO_F32SYS) {
if (_this->spec.format == SDL_AUDIO_S16SYS) {
format = AAUDIO_FORMAT_PCM_I16;
} else if (_this->spec.format == SDL_AUDIO_S16SYS) {
format = AAUDIO_FORMAT_PCM_FLOAT; format = AAUDIO_FORMAT_PCM_FLOAT;
} } else {
ctx.AAudioStreamBuilder_setFormat(ctx.builder, format); format = AAUDIO_FORMAT_PCM_I16; // sint16 is a safe bet for everything else.
} }
ctx.AAudioStreamBuilder_setErrorCallback(ctx.builder, aaudio_errorCallback, private); ctx.AAudioStreamBuilder_setFormat(builder, format);
ctx.AAudioStreamBuilder_setErrorCallback(builder, AAUDIO_errorCallback, device);
ctx.AAudioStreamBuilder_setDataCallback(builder, AAUDIO_dataCallback, device);
ctx.AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
LOGI("AAudio Try to open %u hz %u bit chan %u %s samples %u", LOGI("AAudio Try to open %u hz %u bit chan %u %s samples %u",
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format), device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples); device->spec.channels, SDL_AUDIO_ISBIGENDIAN(device->spec.format) ? "BE" : "LE", device->sample_frames);
res = ctx.AAudioStreamBuilder_openStream(builder, &hidden->stream);
res = ctx.AAudioStreamBuilder_openStream(ctx.builder, &private->stream);
if (res != AAUDIO_OK) { if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res); LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res);
ctx.AAudioStreamBuilder_delete(builder);
return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
} }
_this->spec.freq = ctx.AAudioStream_getSampleRate(private->stream); device->sample_frames = (int) ctx.AAudioStream_getFramesPerDataCallback(hidden->stream);
_this->spec.channels = ctx.AAudioStream_getChannelCount(private->stream); if (device->sample_frames == AAUDIO_UNSPECIFIED) {
{ // if this happens, figure out a reasonable sample frame count, tear down this stream and force it in a new stream.
aaudio_format_t fmt = ctx.AAudioStream_getFormat(private->stream); device->sample_frames = (int) (ctx.AAudioStream_getBufferCapacityInFrames(hidden->stream) / 4);
if (fmt == AAUDIO_FORMAT_PCM_I16) { LOGI("AAUDIO: Got a stream with unspecified sample frames per data callback! Retrying with %d frames...", device->sample_frames);
_this->spec.format = SDL_AUDIO_S16SYS; ctx.AAudioStream_close(hidden->stream);
} else if (fmt == AAUDIO_FORMAT_PCM_FLOAT) { ctx.AAudioStreamBuilder_setFramesPerDataCallback(builder, device->sample_frames);
_this->spec.format = SDL_AUDIO_F32SYS; res = ctx.AAudioStreamBuilder_openStream(builder, &hidden->stream);
if (res != AAUDIO_OK) { // oh well, we tried.
LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res);
ctx.AAudioStreamBuilder_delete(builder);
return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
} }
} }
LOGI("AAudio Try to open %u hz %u bit chan %u %s samples %u", ctx.AAudioStreamBuilder_delete(builder);
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format),
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples);
SDL_CalculateAudioSpec(&_this->spec); device->spec.freq = ctx.AAudioStream_getSampleRate(hidden->stream);
device->spec.channels = ctx.AAudioStream_getChannelCount(hidden->stream);
/* Allocate mixing buffer */ format = ctx.AAudioStream_getFormat(hidden->stream);
if (!iscapture) { if (format == AAUDIO_FORMAT_PCM_I16) {
private->mixlen = _this->spec.size; device->spec.format = SDL_AUDIO_S16SYS;
private->mixbuf = (Uint8 *)SDL_malloc(private->mixlen); } else if (format == AAUDIO_FORMAT_PCM_I32) {
if (private->mixbuf == NULL) { device->spec.format = SDL_AUDIO_S32SYS;
} else if (format == AAUDIO_FORMAT_PCM_FLOAT) {
device->spec.format = SDL_AUDIO_F32SYS;
} else {
return SDL_SetError("Got unexpected audio format %d from AAudioStream_getFormat", (int) format);
}
LOGI("AAudio Actually opened %u hz %u bit chan %u %s samples %u",
device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
device->spec.channels, SDL_AUDIO_ISBIGENDIAN(device->spec.format) ? "BE" : "LE", device->sample_frames);
SDL_UpdatedAudioDeviceFormat(device);
// Allocate mixing buffer
hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
if (hidden->mixbuf == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_memset(private->mixbuf, _this->spec.silence, _this->spec.size); SDL_memset(hidden->mixbuf, device->silence_value, device->buffer_size);
hidden->semaphore = SDL_CreateSemaphore(0);
if (!hidden->semaphore) {
LOGI("SDL Failed SDL_CreateSemaphore %s iscapture:%d", SDL_GetError(), iscapture);
return -1;
} }
private->frame_size = _this->spec.channels * (SDL_AUDIO_BITSIZE(_this->spec.format) / 8); res = ctx.AAudioStream_requestStart(hidden->stream);
res = ctx.AAudioStream_requestStart(private->stream);
if (res != AAUDIO_OK) { if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestStart %d iscapture:%d", res, iscapture); LOGI("SDL Failed AAudioStream_requestStart %d iscapture:%d", res, iscapture);
return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
@ -174,98 +282,114 @@ static int aaudio_OpenDevice(SDL_AudioDevice *_this, const char *devname)
return 0; return 0;
} }
static void aaudio_CloseDevice(SDL_AudioDevice *_this) static SDL_bool PauseOneDevice(SDL_AudioDevice *device, void *userdata)
{ {
struct SDL_PrivateAudioData *private = _this->hidden; struct SDL_PrivateAudioData *hidden = (struct SDL_PrivateAudioData *)device->hidden;
if (hidden != NULL) {
if (hidden->stream) {
aaudio_result_t res; aaudio_result_t res;
LOGI(__func__);
if (private->stream) { if (device->iscapture) {
res = ctx.AAudioStream_requestStop(private->stream); // Pause() isn't implemented for 'capture', use Stop()
if (res != AAUDIO_OK) { res = ctx.AAudioStream_requestStop(hidden->stream);
LOGI("SDL Failed AAudioStream_requestStop %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
return;
}
res = ctx.AAudioStream_close(private->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStreamBuilder_delete %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
return;
}
}
SDL_free(_this->hidden->mixbuf);
SDL_free(_this->hidden);
}
static Uint8 *aaudio_GetDeviceBuf(SDL_AudioDevice *_this)
{
struct SDL_PrivateAudioData *private = _this->hidden;
return private->mixbuf;
}
static void aaudio_PlayDevice(SDL_AudioDevice *_this)
{
struct SDL_PrivateAudioData *private = _this->hidden;
aaudio_result_t res;
int64_t timeoutNanoseconds = 1 * 1000 * 1000; /* 8 ms */
res = ctx.AAudioStream_write(private->stream, private->mixbuf, private->mixlen / private->frame_size, timeoutNanoseconds);
if (res < 0) {
LOGI("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
} else { } else {
LOGI("SDL AAudio play: %d frames, wanted:%d frames", (int)res, private->mixlen / private->frame_size); res = ctx.AAudioStream_requestPause(hidden->stream);
} }
#if 0 if (res != AAUDIO_OK) {
/* Log under-run count */ LOGI("SDL Failed AAudioStream_requestPause %d", res);
{ SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
static int prev = 0;
int32_t cnt = ctx.AAudioStream_getXRunCount(private->stream);
if (cnt != prev) {
SDL_Log("AAudio underrun: %d - total: %d", cnt - prev, cnt);
prev = cnt;
}
}
#endif
} }
static int aaudio_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) SDL_LockMutex(device->lock);
{ hidden->resume = SDL_TRUE;
struct SDL_PrivateAudioData *private = _this->hidden;
aaudio_result_t res;
int64_t timeoutNanoseconds = 8 * 1000 * 1000; /* 8 ms */
res = ctx.AAudioStream_read(private->stream, buffer, buflen / private->frame_size, timeoutNanoseconds);
if (res < 0) {
LOGI("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
return -1;
} }
LOGI("SDL AAudio capture:%d frames, wanted:%d frames", (int)res, buflen / private->frame_size); }
return res * private->frame_size; return SDL_FALSE; // keep enumerating.
} }
static void aaudio_Deinitialize(void) // Pause (block) all non already paused audio devices by taking their mixer lock
void AAUDIO_PauseDevices(void)
{ {
if (ctx.handle != NULL) { // AAUDIO driver is used?
(void) SDL_FindPhysicalAudioDeviceByCallback(PauseOneDevice, NULL);
}
}
// Resume (unblock) all non already paused audio devices by releasing their mixer lock
static SDL_bool ResumeOneDevice(SDL_AudioDevice *device, void *userdata)
{
struct SDL_PrivateAudioData *hidden = device->hidden;
if (hidden != NULL) {
if (hidden->resume) {
hidden->resume = SDL_FALSE;
SDL_UnlockMutex(device->lock);
}
if (hidden->stream) {
aaudio_result_t res = ctx.AAudioStream_requestStart(hidden->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestStart %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
}
}
return SDL_FALSE; // keep enumerating.
}
void AAUDIO_ResumeDevices(void)
{
if (ctx.handle != NULL) { // AAUDIO driver is used?
(void) SDL_FindPhysicalAudioDeviceByCallback(ResumeOneDevice, NULL);
}
}
// !!! FIXME: do we need this now that we use the callback?
/*
We can sometimes get into a state where AAudioStream_write() will just block forever until we pause and unpause.
None of the standard state queries indicate any problem in my testing. And the error callback doesn't actually get called.
But, AAudioStream_getTimestamp() does return AAUDIO_ERROR_INVALID_STATE
*/
static SDL_bool DetectBrokenPlayStatePerDevice(SDL_AudioDevice *device, void *userdata)
{
SDL_assert(device != NULL);
if (!device->iscapture && device->hidden != NULL) {
struct SDL_PrivateAudioData *hidden = device->hidden;
int64_t framePosition, timeNanoseconds;
aaudio_result_t res = ctx.AAudioStream_getTimestamp(hidden->stream, CLOCK_MONOTONIC, &framePosition, &timeNanoseconds);
if (res == AAUDIO_ERROR_INVALID_STATE) {
aaudio_stream_state_t currentState = ctx.AAudioStream_getState(hidden->stream);
// AAudioStream_getTimestamp() will also return AAUDIO_ERROR_INVALID_STATE while the stream is still initially starting. But we only care if it silently went invalid while playing.
if (currentState == AAUDIO_STREAM_STATE_STARTED) {
LOGI("SDL AAUDIO_DetectBrokenPlayState: detected invalid audio device state: AAudioStream_getTimestamp result=%d, framePosition=%lld, timeNanoseconds=%lld, getState=%d", (int)res, (long long)framePosition, (long long)timeNanoseconds, (int)currentState);
return SDL_TRUE; // this guy.
}
}
}
return SDL_FALSE; // enumerate more devices.
}
SDL_bool AAUDIO_DetectBrokenPlayState(void)
{
return (ctx.handle && SDL_FindPhysicalAudioDeviceByCallback(DetectBrokenPlayStatePerDevice, NULL) != NULL) ? SDL_TRUE : SDL_FALSE;
}
static void AAUDIO_Deinitialize(void)
{
Android_StopAudioHotplug();
LOGI(__func__); LOGI(__func__);
if (ctx.handle) { if (ctx.handle) {
if (ctx.builder) {
aaudio_result_t res;
res = ctx.AAudioStreamBuilder_delete(ctx.builder);
if (res != AAUDIO_OK) {
SDL_SetError("Failed AAudioStreamBuilder_delete %s", ctx.AAudio_convertResultToText(res));
}
}
SDL_UnloadObject(ctx.handle); SDL_UnloadObject(ctx.handle);
} }
ctx.handle = NULL; SDL_zero(ctx);
ctx.builder = NULL;
LOGI("End AAUDIO %s", SDL_GetError()); LOGI("End AAUDIO %s", SDL_GetError());
} }
static SDL_bool aaudio_Init(SDL_AudioDriverImpl *impl)
static SDL_bool AAUDIO_Init(SDL_AudioDriverImpl *impl)
{ {
aaudio_result_t res;
LOGI(__func__); LOGI(__func__);
/* AAudio was introduced in Android 8.0, but has reference counting crash issues in that release, /* AAudio was introduced in Android 8.0, but has reference counting crash issues in that release,
@ -282,241 +406,34 @@ static SDL_bool aaudio_Init(SDL_AudioDriverImpl *impl)
ctx.handle = SDL_LoadObject(LIB_AAUDIO_SO); ctx.handle = SDL_LoadObject(LIB_AAUDIO_SO);
if (ctx.handle == NULL) { if (ctx.handle == NULL) {
LOGI("SDL couldn't find " LIB_AAUDIO_SO); LOGI("SDL couldn't find " LIB_AAUDIO_SO);
goto failure;
}
if (aaudio_LoadFunctions(&ctx) < 0) {
goto failure;
}
res = ctx.AAudio_createStreamBuilder(&ctx.builder);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudio_createStreamBuilder %d", res);
goto failure;
}
if (ctx.builder == NULL) {
LOGI("SDL Failed AAudio_createStreamBuilder - builder NULL");
goto failure;
}
impl->DetectDevices = Android_DetectDevices;
impl->Deinitialize = aaudio_Deinitialize;
impl->OpenDevice = aaudio_OpenDevice;
impl->CloseDevice = aaudio_CloseDevice;
impl->PlayDevice = aaudio_PlayDevice;
impl->GetDeviceBuf = aaudio_GetDeviceBuf;
impl->CaptureFromDevice = aaudio_CaptureFromDevice;
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
/* and the capabilities */
impl->HasCaptureSupport = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_FALSE;
impl->OnlyHasDefaultCaptureDevice = SDL_FALSE;
/* this audio target is available. */
LOGI("SDL aaudio_Init OK");
return SDL_TRUE;
failure:
if (ctx.handle) {
if (ctx.builder) {
ctx.AAudioStreamBuilder_delete(ctx.builder);
}
SDL_UnloadObject(ctx.handle);
}
ctx.handle = NULL;
ctx.builder = NULL;
return SDL_FALSE; return SDL_FALSE;
} }
AudioBootStrap aaudio_bootstrap = { if (AAUDIO_LoadFunctions(&ctx) < 0) {
"AAudio", "AAudio audio driver", aaudio_Init, SDL_FALSE SDL_UnloadObject(ctx.handle);
SDL_zero(ctx);
return SDL_FALSE;
}
impl->ThreadInit = Android_AudioThreadInit;
impl->DetectDevices = Android_StartAudioHotplug;
impl->Deinitialize = AAUDIO_Deinitialize;
impl->OpenDevice = AAUDIO_OpenDevice;
impl->CloseDevice = AAUDIO_CloseDevice;
impl->WaitDevice = AAUDIO_WaitDevice;
impl->PlayDevice = AAUDIO_PlayDevice;
impl->GetDeviceBuf = AAUDIO_GetDeviceBuf;
impl->WaitCaptureDevice = AAUDIO_WaitDevice;
impl->CaptureFromDevice = AAUDIO_CaptureFromDevice;
impl->HasCaptureSupport = SDL_TRUE;
LOGI("SDL AAUDIO_Init OK");
return SDL_TRUE;
}
AudioBootStrap AAUDIO_bootstrap = {
"AAudio", "AAudio audio driver", AAUDIO_Init, SDL_FALSE
}; };
/* Pause (block) all non already paused audio devices by taking their mixer lock */ #endif // SDL_AUDIO_DRIVER_AAUDIO
void aaudio_PauseDevices(void)
{
int i;
/* AAUDIO driver is not used */
if (ctx.handle == NULL) {
return;
}
for (i = 0; i < get_max_num_audio_dev(); i++) {
SDL_AudioDevice *_this = get_audio_dev(i);
SDL_AudioDevice *audioDevice = NULL;
SDL_AudioDevice *captureDevice = NULL;
if (_this == NULL) {
continue;
}
if (_this->iscapture) {
captureDevice = _this;
} else {
audioDevice = _this;
}
if (audioDevice != NULL && audioDevice->hidden != NULL) {
struct SDL_PrivateAudioData *private = (struct SDL_PrivateAudioData *)audioDevice->hidden;
if (private->stream) {
aaudio_result_t res = ctx.AAudioStream_requestPause(private->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestPause %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
}
if (SDL_AtomicGet(&audioDevice->paused)) {
/* The device is already paused, leave it alone */
private->resume = SDL_FALSE;
} else {
SDL_LockMutex(audioDevice->mixer_lock);
SDL_AtomicSet(&audioDevice->paused, 1);
private->resume = SDL_TRUE;
}
}
if (captureDevice != NULL && captureDevice->hidden != NULL) {
struct SDL_PrivateAudioData *private = (struct SDL_PrivateAudioData *)audioDevice->hidden;
if (private->stream) {
/* Pause() isn't implemented for 'capture', use Stop() */
aaudio_result_t res = ctx.AAudioStream_requestStop(private->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestStop %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
}
if (SDL_AtomicGet(&captureDevice->paused)) {
/* The device is already paused, leave it alone */
private->resume = SDL_FALSE;
} else {
SDL_LockMutex(captureDevice->mixer_lock);
SDL_AtomicSet(&captureDevice->paused, 1);
private->resume = SDL_TRUE;
}
}
}
}
/* Resume (unblock) all non already paused audio devices by releasing their mixer lock */
void aaudio_ResumeDevices(void)
{
int i;
/* AAUDIO driver is not used */
if (ctx.handle == NULL) {
return;
}
for (i = 0; i < get_max_num_audio_dev(); i++) {
SDL_AudioDevice *_this = get_audio_dev(i);
SDL_AudioDevice *audioDevice = NULL;
SDL_AudioDevice *captureDevice = NULL;
if (_this == NULL) {
continue;
}
if (_this->iscapture) {
captureDevice = _this;
} else {
audioDevice = _this;
}
if (audioDevice != NULL && audioDevice->hidden != NULL) {
struct SDL_PrivateAudioData *private = audioDevice->hidden;
if (private->resume) {
SDL_AtomicSet(&audioDevice->paused, 0);
private->resume = SDL_FALSE;
SDL_UnlockMutex(audioDevice->mixer_lock);
}
if (private->stream) {
aaudio_result_t res = ctx.AAudioStream_requestStart(private->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestStart %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
}
}
if (captureDevice != NULL && captureDevice->hidden != NULL) {
struct SDL_PrivateAudioData *private = audioDevice->hidden;
if (private->resume) {
SDL_AtomicSet(&captureDevice->paused, 0);
private->resume = SDL_FALSE;
SDL_UnlockMutex(captureDevice->mixer_lock);
}
if (private->stream) {
aaudio_result_t res = ctx.AAudioStream_requestStart(private->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestStart %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
}
}
}
}
/*
We can sometimes get into a state where AAudioStream_write() will just block forever until we pause and unpause.
None of the standard state queries indicate any problem in my testing. And the error callback doesn't actually get called.
But, AAudioStream_getTimestamp() does return AAUDIO_ERROR_INVALID_STATE
*/
SDL_bool aaudio_DetectBrokenPlayState(void)
{
int i;
/* AAUDIO driver is not used */
if (ctx.handle == NULL) {
return SDL_FALSE;
}
for (i = 0; i < get_max_num_audio_dev(); i++) {
SDL_AudioDevice *_this = get_audio_dev(i);
SDL_AudioDevice *audioDevice = NULL;
SDL_AudioDevice *captureDevice = NULL;
if (_this == NULL) {
continue;
}
if (_this->iscapture) {
captureDevice = _this;
} else {
audioDevice = _this;
}
if (audioDevice != NULL && audioDevice->hidden != NULL) {
struct SDL_PrivateAudioData *private = audioDevice->hidden;
int64_t framePosition, timeNanoseconds;
aaudio_result_t res = ctx.AAudioStream_getTimestamp(private->stream, CLOCK_MONOTONIC, &framePosition, &timeNanoseconds);
if (res == AAUDIO_ERROR_INVALID_STATE) {
aaudio_stream_state_t currentState = ctx.AAudioStream_getState(private->stream);
/* AAudioStream_getTimestamp() will also return AAUDIO_ERROR_INVALID_STATE while the stream is still initially starting. But we only care if it silently went invalid while playing. */
if (currentState == AAUDIO_STREAM_STATE_STARTED) {
LOGI("SDL aaudio_DetectBrokenPlayState: detected invalid audio device state: AAudioStream_getTimestamp result=%d, framePosition=%lld, timeNanoseconds=%lld, getState=%d", (int)res, (long long)framePosition, (long long)timeNanoseconds, (int)currentState);
return SDL_TRUE;
}
}
}
(void) captureDevice;
}
return SDL_FALSE;
}
#endif /* SDL_AUDIO_DRIVER_AAUDIO */

View File

@ -25,15 +25,15 @@
#ifdef SDL_AUDIO_DRIVER_AAUDIO #ifdef SDL_AUDIO_DRIVER_AAUDIO
void aaudio_ResumeDevices(void); void AAUDIO_ResumeDevices(void);
void aaudio_PauseDevices(void); void AAUDIO_PauseDevices(void);
SDL_bool aaudio_DetectBrokenPlayState(void); SDL_bool AAUDIO_DetectBrokenPlayState(void);
#else #else
static void aaudio_ResumeDevices(void) {} #define AAUDIO_ResumeDevices()
static void aaudio_PauseDevices(void) {} #define AAUDIO_PauseDevices()
static SDL_bool aaudio_DetectBrokenPlayState(void) { return SDL_FALSE; } #define AAUDIO_DetectBrokenPlayState() (SDL_FALSE)
#endif #endif

View File

@ -32,15 +32,15 @@ SDL_PROC(void, AAudioStreamBuilder_setFormat, (AAudioStreamBuilder * builder, aa
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSharingMode, (AAudioStreamBuilder * builder, aaudio_sharing_mode_t sharingMode)) SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSharingMode, (AAudioStreamBuilder * builder, aaudio_sharing_mode_t sharingMode))
SDL_PROC(void, AAudioStreamBuilder_setDirection, (AAudioStreamBuilder * builder, aaudio_direction_t direction)) SDL_PROC(void, AAudioStreamBuilder_setDirection, (AAudioStreamBuilder * builder, aaudio_direction_t direction))
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setBufferCapacityInFrames, (AAudioStreamBuilder * builder, int32_t numFrames)) SDL_PROC_UNUSED(void, AAudioStreamBuilder_setBufferCapacityInFrames, (AAudioStreamBuilder * builder, int32_t numFrames))
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setPerformanceMode, (AAudioStreamBuilder * builder, aaudio_performance_mode_t mode)) SDL_PROC(void, AAudioStreamBuilder_setPerformanceMode, (AAudioStreamBuilder * builder, aaudio_performance_mode_t mode))
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setUsage, (AAudioStreamBuilder * builder, aaudio_usage_t usage)) /* API 28 */ SDL_PROC_UNUSED(void, AAudioStreamBuilder_setUsage, (AAudioStreamBuilder * builder, aaudio_usage_t usage)) /* API 28 */
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setContentType, (AAudioStreamBuilder * builder, aaudio_content_type_t contentType)) /* API 28 */ SDL_PROC_UNUSED(void, AAudioStreamBuilder_setContentType, (AAudioStreamBuilder * builder, aaudio_content_type_t contentType)) /* API 28 */
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setInputPreset, (AAudioStreamBuilder * builder, aaudio_input_preset_t inputPreset)) /* API 28 */ SDL_PROC_UNUSED(void, AAudioStreamBuilder_setInputPreset, (AAudioStreamBuilder * builder, aaudio_input_preset_t inputPreset)) /* API 28 */
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setAllowedCapturePolicy, (AAudioStreamBuilder * builder, aaudio_allowed_capture_policy_t capturePolicy)) /* API 29 */ SDL_PROC_UNUSED(void, AAudioStreamBuilder_setAllowedCapturePolicy, (AAudioStreamBuilder * builder, aaudio_allowed_capture_policy_t capturePolicy)) /* API 29 */
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSessionId, (AAudioStreamBuilder * builder, aaudio_session_id_t sessionId)) /* API 28 */ SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSessionId, (AAudioStreamBuilder * builder, aaudio_session_id_t sessionId)) /* API 28 */
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setPrivacySensitive, (AAudioStreamBuilder * builder, bool privacySensitive)) /* API 30 */ SDL_PROC_UNUSED(void, AAudioStreamBuilder_setPrivacySensitive, (AAudioStreamBuilder * builder, bool privacySensitive)) /* API 30 */
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setDataCallback, (AAudioStreamBuilder * builder, AAudioStream_dataCallback callback, void *userData)) SDL_PROC(void, AAudioStreamBuilder_setDataCallback, (AAudioStreamBuilder * builder, AAudioStream_dataCallback callback, void *userData))
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setFramesPerDataCallback, (AAudioStreamBuilder * builder, int32_t numFrames)) SDL_PROC(void, AAudioStreamBuilder_setFramesPerDataCallback, (AAudioStreamBuilder * builder, int32_t numFrames))
SDL_PROC(void, AAudioStreamBuilder_setErrorCallback, (AAudioStreamBuilder * builder, AAudioStream_errorCallback callback, void *userData)) SDL_PROC(void, AAudioStreamBuilder_setErrorCallback, (AAudioStreamBuilder * builder, AAudioStream_errorCallback callback, void *userData))
SDL_PROC(aaudio_result_t, AAudioStreamBuilder_openStream, (AAudioStreamBuilder * builder, AAudioStream **stream)) SDL_PROC(aaudio_result_t, AAudioStreamBuilder_openStream, (AAudioStreamBuilder * builder, AAudioStream **stream))
SDL_PROC(aaudio_result_t, AAudioStreamBuilder_delete, (AAudioStreamBuilder * builder)) SDL_PROC(aaudio_result_t, AAudioStreamBuilder_delete, (AAudioStreamBuilder * builder))
@ -52,14 +52,14 @@ SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_requestFlush, (AAudioStream * stre
SDL_PROC(aaudio_result_t, AAudioStream_requestStop, (AAudioStream * stream)) SDL_PROC(aaudio_result_t, AAudioStream_requestStop, (AAudioStream * stream))
SDL_PROC(aaudio_stream_state_t, AAudioStream_getState, (AAudioStream * stream)) SDL_PROC(aaudio_stream_state_t, AAudioStream_getState, (AAudioStream * stream))
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_waitForStateChange, (AAudioStream * stream, aaudio_stream_state_t inputState, aaudio_stream_state_t *nextState, int64_t timeoutNanoseconds)) SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_waitForStateChange, (AAudioStream * stream, aaudio_stream_state_t inputState, aaudio_stream_state_t *nextState, int64_t timeoutNanoseconds))
SDL_PROC(aaudio_result_t, AAudioStream_read, (AAudioStream * stream, void *buffer, int32_t numFrames, int64_t timeoutNanoseconds)) SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_read, (AAudioStream * stream, void *buffer, int32_t numFrames, int64_t timeoutNanoseconds))
SDL_PROC(aaudio_result_t, AAudioStream_write, (AAudioStream * stream, const void *buffer, int32_t numFrames, int64_t timeoutNanoseconds)) SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_write, (AAudioStream * stream, const void *buffer, int32_t numFrames, int64_t timeoutNanoseconds))
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_setBufferSizeInFrames, (AAudioStream * stream, int32_t numFrames)) SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_setBufferSizeInFrames, (AAudioStream * stream, int32_t numFrames))
SDL_PROC_UNUSED(int32_t, AAudioStream_getBufferSizeInFrames, (AAudioStream * stream)) SDL_PROC_UNUSED(int32_t, AAudioStream_getBufferSizeInFrames, (AAudioStream * stream))
SDL_PROC_UNUSED(int32_t, AAudioStream_getFramesPerBurst, (AAudioStream * stream)) SDL_PROC_UNUSED(int32_t, AAudioStream_getFramesPerBurst, (AAudioStream * stream))
SDL_PROC_UNUSED(int32_t, AAudioStream_getBufferCapacityInFrames, (AAudioStream * stream)) SDL_PROC(int32_t, AAudioStream_getBufferCapacityInFrames, (AAudioStream * stream))
SDL_PROC_UNUSED(int32_t, AAudioStream_getFramesPerDataCallback, (AAudioStream * stream)) SDL_PROC(int32_t, AAudioStream_getFramesPerDataCallback, (AAudioStream * stream))
SDL_PROC(int32_t, AAudioStream_getXRunCount, (AAudioStream * stream)) SDL_PROC_UNUSED(int32_t, AAudioStream_getXRunCount, (AAudioStream * stream))
SDL_PROC(int32_t, AAudioStream_getSampleRate, (AAudioStream * stream)) SDL_PROC(int32_t, AAudioStream_getSampleRate, (AAudioStream * stream))
SDL_PROC(int32_t, AAudioStream_getChannelCount, (AAudioStream * stream)) SDL_PROC(int32_t, AAudioStream_getChannelCount, (AAudioStream * stream))
SDL_PROC_UNUSED(int32_t, AAudioStream_getSamplesPerFrame, (AAudioStream * stream)) SDL_PROC_UNUSED(int32_t, AAudioStream_getSamplesPerFrame, (AAudioStream * stream))
@ -77,3 +77,6 @@ SDL_PROC_UNUSED(aaudio_content_type_t, AAudioStream_getContentType, (AAudioStrea
SDL_PROC_UNUSED(aaudio_input_preset_t, AAudioStream_getInputPreset, (AAudioStream * stream)) /* API 28 */ SDL_PROC_UNUSED(aaudio_input_preset_t, AAudioStream_getInputPreset, (AAudioStream * stream)) /* API 28 */
SDL_PROC_UNUSED(aaudio_allowed_capture_policy_t, AAudioStream_getAllowedCapturePolicy, (AAudioStream * stream)) /* API 29 */ SDL_PROC_UNUSED(aaudio_allowed_capture_policy_t, AAudioStream_getAllowedCapturePolicy, (AAudioStream * stream)) /* API 29 */
SDL_PROC_UNUSED(bool, AAudioStream_isPrivacySensitive, (AAudioStream * stream)) /* API 30 */ SDL_PROC_UNUSED(bool, AAudioStream_isPrivacySensitive, (AAudioStream * stream)) /* API 30 */
#undef SDL_PROC
#undef SDL_PROC_UNUSED

View File

@ -20,6 +20,8 @@
*/ */
#include "SDL_internal.h" #include "SDL_internal.h"
// !!! FIXME: Clean out the fprintf and printf calls, replace with SDL_Log
#ifdef SDL_AUDIO_DRIVER_ALSA #ifdef SDL_AUDIO_DRIVER_ALSA
#ifndef SDL_ALSA_NON_BLOCKING #ifndef SDL_ALSA_NON_BLOCKING
@ -45,6 +47,7 @@
static int (*ALSA_snd_pcm_open)(snd_pcm_t **, const char *, snd_pcm_stream_t, int); static int (*ALSA_snd_pcm_open)(snd_pcm_t **, const char *, snd_pcm_stream_t, int);
static int (*ALSA_snd_pcm_close)(snd_pcm_t *pcm); static int (*ALSA_snd_pcm_close)(snd_pcm_t *pcm);
static int (*ALSA_snd_pcm_start)(snd_pcm_t *pcm);
static snd_pcm_sframes_t (*ALSA_snd_pcm_writei)(snd_pcm_t *, const void *, snd_pcm_uframes_t); static snd_pcm_sframes_t (*ALSA_snd_pcm_writei)(snd_pcm_t *, const void *, snd_pcm_uframes_t);
static snd_pcm_sframes_t (*ALSA_snd_pcm_readi)(snd_pcm_t *, void *, snd_pcm_uframes_t); static snd_pcm_sframes_t (*ALSA_snd_pcm_readi)(snd_pcm_t *, void *, snd_pcm_uframes_t);
static int (*ALSA_snd_pcm_recover)(snd_pcm_t *, int, int); static int (*ALSA_snd_pcm_recover)(snd_pcm_t *, int, int);
@ -115,6 +118,7 @@ static int load_alsa_syms(void)
{ {
SDL_ALSA_SYM(snd_pcm_open); SDL_ALSA_SYM(snd_pcm_open);
SDL_ALSA_SYM(snd_pcm_close); SDL_ALSA_SYM(snd_pcm_close);
SDL_ALSA_SYM(snd_pcm_start);
SDL_ALSA_SYM(snd_pcm_writei); SDL_ALSA_SYM(snd_pcm_writei);
SDL_ALSA_SYM(snd_pcm_readi); SDL_ALSA_SYM(snd_pcm_readi);
SDL_ALSA_SYM(snd_pcm_recover); SDL_ALSA_SYM(snd_pcm_recover);
@ -203,48 +207,21 @@ static int LoadALSALibrary(void)
static const char *get_audio_device(void *handle, const int channels) static const char *get_audio_device(void *handle, const int channels)
{ {
const char *device; SDL_assert(handle != NULL); // SDL2 used NULL to mean "default" but that's not true in SDL3.
if (handle != NULL) { if (SDL_strcmp((const char *) handle, "default") == 0) {
return (const char *)handle; const char *device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */
}
/* !!! FIXME: we also check "SDL_AUDIO_DEVICE_NAME" at the higher level. */
device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */
if (device != NULL) { if (device != NULL) {
return device; return device;
} } else if (channels == 6) {
if (channels == 6) {
return "plug:surround51"; return "plug:surround51";
} else if (channels == 4) { } else if (channels == 4) {
return "plug:surround40"; return "plug:surround40";
} }
return "default"; return "default";
} }
/* This function waits until it is possible to write a full sound buffer */ return (const char *)handle;
static void ALSA_WaitDevice(SDL_AudioDevice *_this)
{
#if SDL_ALSA_NON_BLOCKING
const snd_pcm_sframes_t needed = (snd_pcm_sframes_t)_this->spec.samples;
while (SDL_AtomicGet(&_this->enabled)) {
const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(_this->hidden->pcm_handle);
if ((rc < 0) && (rc != -EAGAIN)) {
/* Hmm, not much we can do - abort */
fprintf(stderr, "ALSA snd_pcm_avail failed (unrecoverable): %s\n",
ALSA_snd_strerror(rc));
SDL_OpenedAudioDeviceDisconnected(_this);
return;
} else if (rc < needed) {
const Uint32 delay = ((needed - (SDL_max(rc, 0))) * 1000) / _this->spec.freq;
SDL_Delay(SDL_max(delay, 10));
} else {
break; /* ready to go! */
}
}
#endif
} }
/* !!! FIXME: is there a channel swizzler in alsalib instead? */ /* !!! FIXME: is there a channel swizzler in alsalib instead? */
@ -311,15 +288,15 @@ CHANNEL_SWIZZLE(SWIZ8)
#undef SWIZ8 #undef SWIZ8
/* /*
* Called right before feeding _this->hidden->mixbuf to the hardware. Swizzle * Called right before feeding device->hidden->mixbuf to the hardware. Swizzle
* channels from Windows/Mac order to the format alsalib will want. * channels from Windows/Mac order to the format alsalib will want.
*/ */
static void swizzle_alsa_channels(SDL_AudioDevice *_this, void *buffer, Uint32 bufferlen) static void swizzle_alsa_channels(SDL_AudioDevice *device, void *buffer, Uint32 bufferlen)
{ {
switch (_this->spec.channels) { switch (device->spec.channels) {
#define CHANSWIZ(chans) \ #define CHANSWIZ(chans) \
case chans: \ case chans: \
switch ((_this->spec.format & (0xFF))) { \ switch ((device->spec.format & (0xFF))) { \
case 8: \ case 8: \
swizzle_alsa_channels_##chans##_Uint8(buffer, bufferlen); \ swizzle_alsa_channels_##chans##_Uint8(buffer, bufferlen); \
break; \ break; \
@ -348,22 +325,44 @@ static void swizzle_alsa_channels(SDL_AudioDevice *_this, void *buffer, Uint32 b
#ifdef SND_CHMAP_API_VERSION #ifdef SND_CHMAP_API_VERSION
/* Some devices have the right channel map, no swizzling necessary */ /* Some devices have the right channel map, no swizzling necessary */
static void no_swizzle(SDL_AudioDevice *_this, void *buffer, Uint32 bufferlen) static void no_swizzle(SDL_AudioDevice *device, void *buffer, Uint32 bufferlen)
{ {
} }
#endif /* SND_CHMAP_API_VERSION */ #endif /* SND_CHMAP_API_VERSION */
static void ALSA_PlayDevice(SDL_AudioDevice *_this) /* This function waits until it is possible to write a full sound buffer */
static void ALSA_WaitDevice(SDL_AudioDevice *device)
{ {
const Uint8 *sample_buf = (const Uint8 *)_this->hidden->mixbuf; const snd_pcm_sframes_t needed = (snd_pcm_sframes_t)device->sample_frames;
const int frame_size = ((SDL_AUDIO_BITSIZE(_this->spec.format)) / 8) * while (!SDL_AtomicGet(&device->shutdown)) {
_this->spec.channels; const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(device->hidden->pcm_handle);
snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t)_this->spec.samples); if ((rc < 0) && (rc != -EAGAIN)) {
/* Hmm, not much we can do - abort */
fprintf(stderr, "ALSA snd_pcm_avail failed (unrecoverable): %s\n",
ALSA_snd_strerror(rc));
SDL_AudioDeviceDisconnected(device);
return;
} else if (rc < needed) {
const Uint32 delay = ((needed - (SDL_max(rc, 0))) * 1000) / device->spec.freq;
SDL_Delay(SDL_max(delay, 10));
} else {
break; /* ready to go! */
}
}
}
_this->hidden->swizzle_func(_this, _this->hidden->mixbuf, frames_left); static void 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;
snd_pcm_uframes_t frames_left = (snd_pcm_uframes_t) (buflen / frame_size);
while (frames_left > 0 && SDL_AtomicGet(&_this->enabled)) { device->hidden->swizzle_func(device, sample_buf, frames_left);
int status = ALSA_snd_pcm_writei(_this->hidden->pcm_handle,
while ((frames_left > 0) && !SDL_AtomicGet(&device->shutdown)) {
int status = ALSA_snd_pcm_writei(device->hidden->pcm_handle,
sample_buf, frames_left); sample_buf, frames_left);
if (status < 0) { if (status < 0) {
@ -373,21 +372,20 @@ static void ALSA_PlayDevice(SDL_AudioDevice *_this)
SDL_Delay(1); SDL_Delay(1);
continue; continue;
} }
status = ALSA_snd_pcm_recover(_this->hidden->pcm_handle, status, 0); status = ALSA_snd_pcm_recover(device->hidden->pcm_handle, status, 0);
if (status < 0) { if (status < 0) {
/* Hmm, not much we can do - abort */ /* Hmm, not much we can do - abort */
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
"ALSA write failed (unrecoverable): %s\n", "ALSA write failed (unrecoverable): %s",
ALSA_snd_strerror(status)); ALSA_snd_strerror(status));
SDL_OpenedAudioDeviceDisconnected(_this); SDL_AudioDeviceDisconnected(device);
return; return;
} }
continue; continue;
} else if (status == 0) { } else if (status == 0) {
/* No frames were written (no available space in pcm device). /* No frames were written (no available space in pcm device).
Allow other threads to catch up. */ Allow other threads to catch up. */
Uint32 delay = (frames_left / 2 * 1000) / _this->spec.freq; SDL_Delay((frames_left / 2 * 1000) / device->spec.freq);
SDL_Delay(delay);
} }
sample_buf += status * frame_size; sample_buf += status * frame_size;
@ -395,34 +393,30 @@ static void ALSA_PlayDevice(SDL_AudioDevice *_this)
} }
} }
static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
return _this->hidden->mixbuf; return device->hidden->mixbuf;
} }
static int ALSA_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) static int ALSA_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{ {
Uint8 *sample_buf = (Uint8 *)buffer; Uint8 *sample_buf = (Uint8 *)buffer;
const int frame_size = ((SDL_AUDIO_BITSIZE(_this->spec.format)) / 8) * const int frame_size = ((SDL_AUDIO_BITSIZE(device->spec.format)) / 8) *
_this->spec.channels; device->spec.channels;
const int total_frames = buflen / frame_size; const int total_frames = buflen / frame_size;
snd_pcm_uframes_t frames_left = total_frames; snd_pcm_uframes_t frames_left = total_frames;
snd_pcm_uframes_t wait_time = frame_size / 2;
SDL_assert((buflen % frame_size) == 0); SDL_assert((buflen % frame_size) == 0);
while (frames_left > 0 && SDL_AtomicGet(&_this->enabled)) { while ((frames_left > 0) && !SDL_AtomicGet(&device->shutdown)) {
int status; int status = ALSA_snd_pcm_readi(device->hidden->pcm_handle,
status = ALSA_snd_pcm_readi(_this->hidden->pcm_handle,
sample_buf, frames_left); sample_buf, frames_left);
if (status == -EAGAIN) { if (status == -EAGAIN) {
ALSA_snd_pcm_wait(_this->hidden->pcm_handle, wait_time); break; // Can this even happen? Go back to WaitCaptureDevice, where the device lock isn't held.
status = 0;
} else if (status < 0) { } else if (status < 0) {
/*printf("ALSA: capture error %d\n", status);*/ /*printf("ALSA: capture error %d\n", status);*/
status = ALSA_snd_pcm_recover(_this->hidden->pcm_handle, status, 0); status = ALSA_snd_pcm_recover(device->hidden->pcm_handle, status, 0);
if (status < 0) { if (status < 0) {
/* Hmm, not much we can do - abort */ /* Hmm, not much we can do - abort */
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
@ -430,7 +424,7 @@ static int ALSA_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int bufl
ALSA_snd_strerror(status)); ALSA_snd_strerror(status));
return -1; return -1;
} }
continue; break; // Go back to WaitCaptureDevice, where the device lock isn't held.
} }
/*printf("ALSA: captured %d bytes\n", status * frame_size);*/ /*printf("ALSA: captured %d bytes\n", status * frame_size);*/
@ -438,32 +432,32 @@ static int ALSA_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int bufl
frames_left -= status; frames_left -= status;
} }
_this->hidden->swizzle_func(_this, buffer, total_frames - frames_left); device->hidden->swizzle_func(device, buffer, total_frames - frames_left);
return (total_frames - frames_left) * frame_size; return (total_frames - frames_left) * frame_size;
} }
static void ALSA_FlushCapture(SDL_AudioDevice *_this) static void ALSA_FlushCapture(SDL_AudioDevice *device)
{ {
ALSA_snd_pcm_reset(_this->hidden->pcm_handle); ALSA_snd_pcm_reset(device->hidden->pcm_handle);
} }
static void ALSA_CloseDevice(SDL_AudioDevice *_this) static void ALSA_CloseDevice(SDL_AudioDevice *device)
{ {
if (_this->hidden->pcm_handle) { if (device->hidden) {
if (device->hidden->pcm_handle) {
/* Wait for the submitted audio to drain /* Wait for the submitted audio to drain
ALSA_snd_pcm_drop() can hang, so don't use that. ALSA_snd_pcm_drop() can hang, so don't use that.
*/ */
Uint32 delay = ((_this->spec.samples * 1000) / _this->spec.freq) * 2; SDL_Delay(((device->sample_frames * 1000) / device->spec.freq) * 2);
SDL_Delay(delay); ALSA_snd_pcm_close(device->hidden->pcm_handle);
}
ALSA_snd_pcm_close(_this->hidden->pcm_handle); SDL_free(device->hidden->mixbuf);
SDL_free(device->hidden);
} }
SDL_free(_this->hidden->mixbuf);
SDL_free(_this->hidden);
} }
static int ALSA_set_buffer_size(SDL_AudioDevice *_this, snd_pcm_hw_params_t *params) static int ALSA_set_buffer_size(SDL_AudioDevice *device, snd_pcm_hw_params_t *params)
{ {
int status; int status;
snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_t *hwparams;
@ -475,9 +469,9 @@ static int ALSA_set_buffer_size(SDL_AudioDevice *_this, snd_pcm_hw_params_t *par
ALSA_snd_pcm_hw_params_copy(hwparams, params); ALSA_snd_pcm_hw_params_copy(hwparams, params);
/* Attempt to match the period size to the requested buffer size */ /* Attempt to match the period size to the requested buffer size */
persize = _this->spec.samples; persize = device->sample_frames;
status = ALSA_snd_pcm_hw_params_set_period_size_near( status = ALSA_snd_pcm_hw_params_set_period_size_near(
_this->hidden->pcm_handle, hwparams, &persize, NULL); device->hidden->pcm_handle, hwparams, &persize, NULL);
if (status < 0) { if (status < 0) {
return -1; return -1;
} }
@ -485,24 +479,24 @@ static int ALSA_set_buffer_size(SDL_AudioDevice *_this, snd_pcm_hw_params_t *par
/* Need to at least double buffer */ /* Need to at least double buffer */
periods = 2; periods = 2;
status = ALSA_snd_pcm_hw_params_set_periods_min( status = ALSA_snd_pcm_hw_params_set_periods_min(
_this->hidden->pcm_handle, hwparams, &periods, NULL); device->hidden->pcm_handle, hwparams, &periods, NULL);
if (status < 0) { if (status < 0) {
return -1; return -1;
} }
status = ALSA_snd_pcm_hw_params_set_periods_first( status = ALSA_snd_pcm_hw_params_set_periods_first(
_this->hidden->pcm_handle, hwparams, &periods, NULL); device->hidden->pcm_handle, hwparams, &periods, NULL);
if (status < 0) { if (status < 0) {
return -1; return -1;
} }
/* "set" the hardware with the desired parameters */ /* "set" the hardware with the desired parameters */
status = ALSA_snd_pcm_hw_params(_this->hidden->pcm_handle, hwparams); status = ALSA_snd_pcm_hw_params(device->hidden->pcm_handle, hwparams);
if (status < 0) { if (status < 0) {
return -1; return -1;
} }
_this->spec.samples = persize; device->sample_frames = persize;
/* This is useful for debugging */ /* This is useful for debugging */
if (SDL_getenv("SDL_AUDIO_ALSA_DEBUG")) { if (SDL_getenv("SDL_AUDIO_ALSA_DEBUG")) {
@ -518,34 +512,22 @@ static int ALSA_set_buffer_size(SDL_AudioDevice *_this, snd_pcm_hw_params_t *par
return 0; return 0;
} }
static int ALSA_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int ALSA_OpenDevice(SDL_AudioDevice *device)
{ {
const SDL_bool iscapture = device->iscapture;
int status = 0; int status = 0;
SDL_bool iscapture = _this->iscapture;
snd_pcm_t *pcm_handle = NULL;
snd_pcm_hw_params_t *hwparams = NULL;
snd_pcm_sw_params_t *swparams = NULL;
snd_pcm_format_t format = 0;
SDL_AudioFormat test_format = 0;
const SDL_AudioFormat *closefmts;
unsigned int rate = 0;
unsigned int channels = 0;
#ifdef SND_CHMAP_API_VERSION
snd_pcm_chmap_t *chmap;
char chmap_str[64];
#endif
/* Initialize all variables that we clean on shutdown */ /* Initialize all variables that we clean on shutdown */
_this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*_this->hidden)); device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
if (_this->hidden == NULL) { if (device->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_zerop(_this->hidden);
/* Open the audio device */ /* Open the audio device */
/* Name of device should depend on # channels in spec */ /* Name of device should depend on # channels in spec */
snd_pcm_t *pcm_handle = NULL;
status = ALSA_snd_pcm_open(&pcm_handle, status = ALSA_snd_pcm_open(&pcm_handle,
get_audio_device(_this->handle, _this->spec.channels), get_audio_device(device->handle, device->spec.channels),
iscapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, iscapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
SND_PCM_NONBLOCK); SND_PCM_NONBLOCK);
@ -553,9 +535,10 @@ static int ALSA_OpenDevice(SDL_AudioDevice *_this, const char *devname)
return SDL_SetError("ALSA: Couldn't open audio device: %s", ALSA_snd_strerror(status)); return SDL_SetError("ALSA: Couldn't open audio device: %s", ALSA_snd_strerror(status));
} }
_this->hidden->pcm_handle = pcm_handle; device->hidden->pcm_handle = pcm_handle;
/* Figure out what the hardware is capable of */ /* Figure out what the hardware is capable of */
snd_pcm_hw_params_t *hwparams = NULL;
snd_pcm_hw_params_alloca(&hwparams); snd_pcm_hw_params_alloca(&hwparams);
status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams); status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams);
if (status < 0) { if (status < 0) {
@ -570,7 +553,9 @@ static int ALSA_OpenDevice(SDL_AudioDevice *_this, const char *devname)
} }
/* Try for a closest match on audio format */ /* Try for a closest match on audio format */
closefmts = SDL_ClosestAudioFormats(_this->spec.format); snd_pcm_format_t format = 0;
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
SDL_AudioFormat test_format;
while ((test_format = *(closefmts++)) != 0) { while ((test_format = *(closefmts++)) != 0) {
switch (test_format) { switch (test_format) {
case SDL_AUDIO_U8: case SDL_AUDIO_U8:
@ -605,21 +590,22 @@ static int ALSA_OpenDevice(SDL_AudioDevice *_this, const char *devname)
} }
} }
if (!test_format) { if (!test_format) {
return SDL_SetError("%s: Unsupported audio format", "alsa"); return SDL_SetError("ALSA: Unsupported audio format");
} }
_this->spec.format = test_format; device->spec.format = test_format;
/* Validate number of channels and determine if swizzling is necessary /* Validate number of channels and determine if swizzling is necessary
* Assume original swizzling, until proven otherwise. * Assume original swizzling, until proven otherwise.
*/ */
_this->hidden->swizzle_func = swizzle_alsa_channels; device->hidden->swizzle_func = swizzle_alsa_channels;
#ifdef SND_CHMAP_API_VERSION #ifdef SND_CHMAP_API_VERSION
chmap = ALSA_snd_pcm_get_chmap(pcm_handle); snd_pcm_chmap_t *chmap = ALSA_snd_pcm_get_chmap(pcm_handle);
if (chmap) { if (chmap) {
char chmap_str[64];
if (ALSA_snd_pcm_chmap_print(chmap, sizeof(chmap_str), chmap_str) > 0) { if (ALSA_snd_pcm_chmap_print(chmap, sizeof(chmap_str), chmap_str) > 0) {
if (SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0 || if (SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0 ||
SDL_strcmp("FL FR FC LFE SL SR", chmap_str) == 0) { SDL_strcmp("FL FR FC LFE SL SR", chmap_str) == 0) {
_this->hidden->swizzle_func = no_swizzle; device->hidden->swizzle_func = no_swizzle;
} }
} }
free(chmap); /* This should NOT be SDL_free() */ free(chmap); /* This should NOT be SDL_free() */
@ -628,38 +614,39 @@ static int ALSA_OpenDevice(SDL_AudioDevice *_this, const char *devname)
/* Set the number of channels */ /* Set the number of channels */
status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams, status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
_this->spec.channels); device->spec.channels);
channels = _this->spec.channels; unsigned int channels = device->spec.channels;
if (status < 0) { if (status < 0) {
status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels); status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
if (status < 0) { if (status < 0) {
return SDL_SetError("ALSA: Couldn't set audio channels"); return SDL_SetError("ALSA: Couldn't set audio channels");
} }
_this->spec.channels = channels; device->spec.channels = channels;
} }
/* Set the audio rate */ /* Set the audio rate */
rate = _this->spec.freq; unsigned int rate = device->spec.freq;
status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams,
&rate, NULL); &rate, NULL);
if (status < 0) { if (status < 0) {
return SDL_SetError("ALSA: Couldn't set audio frequency: %s", ALSA_snd_strerror(status)); return SDL_SetError("ALSA: Couldn't set audio frequency: %s", ALSA_snd_strerror(status));
} }
_this->spec.freq = rate; device->spec.freq = rate;
/* Set the buffer size, in samples */ /* Set the buffer size, in samples */
status = ALSA_set_buffer_size(_this, hwparams); status = ALSA_set_buffer_size(device, hwparams);
if (status < 0) { if (status < 0) {
return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status)); return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
} }
/* Set the software parameters */ /* Set the software parameters */
snd_pcm_sw_params_t *swparams = NULL;
snd_pcm_sw_params_alloca(&swparams); snd_pcm_sw_params_alloca(&swparams);
status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams); status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
if (status < 0) { if (status < 0) {
return SDL_SetError("ALSA: Couldn't get software config: %s", ALSA_snd_strerror(status)); return SDL_SetError("ALSA: Couldn't get software config: %s", ALSA_snd_strerror(status));
} }
status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, _this->spec.samples); status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, device->sample_frames);
if (status < 0) { if (status < 0) {
return SDL_SetError("Couldn't set minimum available samples: %s", ALSA_snd_strerror(status)); return SDL_SetError("Couldn't set minimum available samples: %s", ALSA_snd_strerror(status));
} }
@ -673,17 +660,16 @@ static int ALSA_OpenDevice(SDL_AudioDevice *_this, const char *devname)
return SDL_SetError("Couldn't set software audio parameters: %s", ALSA_snd_strerror(status)); return SDL_SetError("Couldn't set software audio parameters: %s", ALSA_snd_strerror(status));
} }
/* Calculate the final parameters for this audio specification */ // Calculate the final parameters for this audio specification
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
/* Allocate mixing buffer */ // Allocate mixing buffer
if (!iscapture) { if (!iscapture) {
_this->hidden->mixlen = _this->spec.size; device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->hidden->mixlen); if (device->hidden->mixbuf == NULL) {
if (_this->hidden->mixbuf == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->hidden->mixlen); SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
} }
#if !SDL_ALSA_NON_BLOCKING #if !SDL_ALSA_NON_BLOCKING
@ -692,6 +678,8 @@ static int ALSA_OpenDevice(SDL_AudioDevice *_this, const char *devname)
} }
#endif #endif
ALSA_snd_pcm_start(pcm_handle);
/* We're ready to rock and roll. :-) */ /* We're ready to rock and roll. :-) */
return 0; return 0;
} }
@ -703,7 +691,7 @@ typedef struct ALSA_Device
struct ALSA_Device *next; struct ALSA_Device *next;
} ALSA_Device; } ALSA_Device;
static void add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSeen) static void add_device(const SDL_bool iscapture, const char *name, void *hint, ALSA_Device **pSeen)
{ {
ALSA_Device *dev = SDL_malloc(sizeof(ALSA_Device)); ALSA_Device *dev = SDL_malloc(sizeof(ALSA_Device));
char *desc; char *desc;
@ -765,21 +753,17 @@ static void add_device(const int iscapture, const char *name, void *hint, ALSA_D
static ALSA_Device *hotplug_devices = NULL; static ALSA_Device *hotplug_devices = NULL;
static void ALSA_HotplugIteration(void) static void ALSA_HotplugIteration(SDL_bool *has_default_output, SDL_bool *has_default_capture)
{ {
void **hints = NULL; void **hints = NULL;
ALSA_Device *dev; ALSA_Device *unseen = NULL;
ALSA_Device *unseen; ALSA_Device *seen = NULL;
ALSA_Device *seen;
ALSA_Device *next;
ALSA_Device *prev;
if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) { if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) {
int i, j;
const char *match = NULL; const char *match = NULL;
int bestmatch = 0xFFFF; int bestmatch = 0xFFFF;
int has_default = -1;
size_t match_len = 0; size_t match_len = 0;
int defaultdev = -1;
static const char *const prefixes[] = { static const char *const prefixes[] = {
"hw:", "sysdefault:", "default:", NULL "hw:", "sysdefault:", "default:", NULL
}; };
@ -791,18 +775,18 @@ static void ALSA_HotplugIteration(void)
actual hardware. It could be prefixed with "hw:" or "default:" actual hardware. It could be prefixed with "hw:" or "default:"
or "sysdefault:" and maybe others. Go through the list and see or "sysdefault:" and maybe others. Go through the list and see
if we can find a preferred prefix for the system. */ if we can find a preferred prefix for the system. */
for (i = 0; hints[i]; i++) { for (int i = 0; hints[i]; i++) {
char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME"); char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
if (name == NULL) { if (name == NULL) {
continue; continue;
} }
/* full name, not a prefix */ if (SDL_strcmp(name, "default") == 0) {
if ((defaultdev == -1) && (SDL_strcmp(name, "default") == 0)) { if (has_default < 0) {
defaultdev = i; has_default = i;
} }
} else {
for (j = 0; prefixes[j]; j++) { for (int j = 0; prefixes[j]; j++) {
const char *prefix = prefixes[j]; const char *prefix = prefixes[j];
const size_t prefixlen = SDL_strlen(prefix); const size_t prefixlen = SDL_strlen(prefix);
if (SDL_strncmp(name, prefix, prefixlen) == 0) { if (SDL_strncmp(name, prefix, prefixlen) == 0) {
@ -816,38 +800,45 @@ static void ALSA_HotplugIteration(void)
free(name); /* This should NOT be SDL_free() */ free(name); /* This should NOT be SDL_free() */
} }
/* look through the list of device names to find matches */
for (i = 0; hints[i]; i++) {
char *name;
/* if we didn't find a device name prefix we like at all... */
if ((match == NULL) && (defaultdev != i)) {
continue; /* ...skip anything that isn't the default device. */
} }
name = ALSA_snd_device_name_get_hint(hints[i], "NAME"); /* look through the list of device names to find matches */
if (match || (has_default >= 0)) { // did we find a device name prefix we like at all...?
for (int i = 0; hints[i]; i++) {
char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
if (name == NULL) { if (name == NULL) {
continue; continue;
} }
/* only want physical hardware interfaces */ // only want physical hardware interfaces
if (match == NULL || (SDL_strncmp(name, match, match_len) == 0)) { const SDL_bool is_default = (has_default == i) ? SDL_TRUE : SDL_FALSE;
if (is_default || (match != NULL && SDL_strncmp(name, match, match_len) == 0)) {
char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID"); char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID");
const SDL_bool isoutput = (ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0); const SDL_bool isoutput = (ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0);
const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0); const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0);
SDL_bool have_output = SDL_FALSE; SDL_bool have_output = SDL_FALSE;
SDL_bool have_input = SDL_FALSE; SDL_bool have_input = SDL_FALSE;
free(ioid); /* This should NOT be SDL_free() */ free(ioid);
if (!isoutput && !isinput) { if (!isoutput && !isinput) {
free(name); /* This should NOT be SDL_free() */ free(name);
continue; continue;
} }
prev = NULL; if (is_default) {
for (dev = unseen; dev; dev = next) { if (has_default_output && isoutput) {
*has_default_output = SDL_TRUE;
} else if (has_default_capture && isinput) {
*has_default_capture = SDL_TRUE;
}
free(name);
continue;
}
ALSA_Device *prev = NULL;
ALSA_Device *next;
for (ALSA_Device *dev = unseen; dev; dev = next) {
next = dev->next; next = dev->next;
if ((SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture))) { if ((SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture))) {
if (prev) { if (prev) {
@ -878,16 +869,18 @@ static void ALSA_HotplugIteration(void)
free(name); /* This should NOT be SDL_free() */ free(name); /* This should NOT be SDL_free() */
} }
}
ALSA_snd_device_name_free_hint(hints); ALSA_snd_device_name_free_hint(hints);
hotplug_devices = seen; /* now we have a known-good list of attached devices. */ hotplug_devices = seen; /* now we have a known-good list of attached devices. */
/* report anything still in unseen as removed. */ /* report anything still in unseen as removed. */
for (dev = unseen; dev; dev = next) { ALSA_Device *next = NULL;
for (ALSA_Device *dev = unseen; dev; dev = next) {
/*printf("ALSA: removing usb %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/ /*printf("ALSA: removing usb %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
next = dev->next; next = dev->next;
SDL_RemoveAudioDevice(dev->iscapture, dev->name); SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle(dev->name));
SDL_free(dev->name); SDL_free(dev->name);
SDL_free(dev); SDL_free(dev);
} }
@ -909,16 +902,25 @@ static int SDLCALL ALSA_HotplugThread(void *arg)
SDL_Delay(100); SDL_Delay(100);
} }
ALSA_HotplugIteration(); /* run the check. */ ALSA_HotplugIteration(NULL, NULL); /* run the check. */
} }
return 0; return 0;
} }
#endif #endif
static void ALSA_DetectDevices(void) static void ALSA_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{ {
ALSA_HotplugIteration(); /* run once now before a thread continues to check. */ // ALSA doesn't have a concept of a changeable default device, afaik, so we expose a generic default
// device here. It's the best we can do at this level.
SDL_bool has_default_output = SDL_FALSE, has_default_capture = SDL_FALSE;
ALSA_HotplugIteration(&has_default_output, &has_default_capture); // run once now before a thread continues to check. */
if (has_default_output) {
*default_output = SDL_AddAudioDevice(/*iscapture=*/SDL_FALSE, "ALSA default output device", NULL, SDL_strdup("default"));
}
if (has_default_capture) {
*default_capture = SDL_AddAudioDevice(/*iscapture=*/SDL_TRUE, "ALSA default capture device", NULL, SDL_strdup("default"));
}
#if SDL_ALSA_HOTPLUG_THREAD #if SDL_ALSA_HOTPLUG_THREAD
SDL_AtomicSet(&ALSA_hotplug_shutdown, 0); SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
@ -966,11 +968,11 @@ static SDL_bool ALSA_Init(SDL_AudioDriverImpl *impl)
impl->PlayDevice = ALSA_PlayDevice; impl->PlayDevice = ALSA_PlayDevice;
impl->CloseDevice = ALSA_CloseDevice; impl->CloseDevice = ALSA_CloseDevice;
impl->Deinitialize = ALSA_Deinitialize; impl->Deinitialize = ALSA_Deinitialize;
impl->WaitCaptureDevice = ALSA_WaitDevice;
impl->CaptureFromDevice = ALSA_CaptureFromDevice; impl->CaptureFromDevice = ALSA_CaptureFromDevice;
impl->FlushCapture = ALSA_FlushCapture; impl->FlushCapture = ALSA_FlushCapture;
impl->HasCaptureSupport = SDL_TRUE; impl->HasCaptureSupport = SDL_TRUE;
impl->SupportsNonPow2Samples = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */ return SDL_TRUE; /* this audio target is available. */
} }

View File

@ -34,7 +34,6 @@ struct SDL_PrivateAudioData
/* Raw mixing buffer */ /* Raw mixing buffer */
Uint8 *mixbuf; Uint8 *mixbuf;
int mixlen;
/* swizzle function */ /* swizzle function */
void (*swizzle_func)(SDL_AudioDevice *_this, void *buffer, Uint32 bufferlen); void (*swizzle_func)(SDL_AudioDevice *_this, void *buffer, Uint32 bufferlen);

View File

@ -22,7 +22,7 @@
#ifdef SDL_AUDIO_DRIVER_ANDROID #ifdef SDL_AUDIO_DRIVER_ANDROID
/* Output audio to Android */ // Output audio to Android (legacy interface)
#include "../SDL_sysaudio.h" #include "../SDL_sysaudio.h"
#include "../SDL_audio_c.h" #include "../SDL_audio_c.h"
@ -34,185 +34,158 @@
struct SDL_PrivateAudioData struct SDL_PrivateAudioData
{ {
/* Resume device if it was paused automatically */ int resume; // Resume device if it was paused automatically
int resume;
}; };
static SDL_AudioDevice *audioDevice = NULL; static SDL_AudioDevice *audioDevice = NULL;
static SDL_AudioDevice *captureDevice = NULL; static SDL_AudioDevice *captureDevice = NULL;
static int ANDROIDAUDIO_OpenDevice(SDL_AudioDevice *device)
static int ANDROIDAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
{ {
SDL_AudioFormat test_format; device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
const SDL_AudioFormat *closefmts; if (device->hidden == NULL) {
SDL_bool iscapture = _this->iscapture; return SDL_OutOfMemory();
}
const SDL_bool iscapture = device->iscapture;
if (iscapture) { if (iscapture) {
if (captureDevice) { if (captureDevice) {
return SDL_SetError("An audio capture device is already opened"); return SDL_SetError("An audio capture device is already opened");
} }
} captureDevice = device;
} else {
if (!iscapture) {
if (audioDevice) { if (audioDevice) {
return SDL_SetError("An audio playback device is already opened"); return SDL_SetError("An audio playback device is already opened");
} }
audioDevice = device;
} }
if (iscapture) { SDL_AudioFormat test_format;
captureDevice = _this; const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
} else {
audioDevice = _this;
}
_this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*_this->hidden));
if (_this->hidden == NULL) {
return SDL_OutOfMemory();
}
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
while ((test_format = *(closefmts++)) != 0) { while ((test_format = *(closefmts++)) != 0) {
if ((test_format == SDL_AUDIO_U8) || if ((test_format == SDL_AUDIO_U8) ||
(test_format == SDL_AUDIO_S16) || (test_format == SDL_AUDIO_S16) ||
(test_format == SDL_AUDIO_F32)) { (test_format == SDL_AUDIO_F32)) {
_this->spec.format = test_format; device->spec.format = test_format;
break; break;
} }
} }
if (!test_format) { if (!test_format) {
/* Didn't find a compatible format :( */ return SDL_SetError("android: Unsupported audio format");
return SDL_SetError("%s: Unsupported audio format", "android");
} }
{ if (Android_JNI_OpenAudioDevice(device) < 0) {
int audio_device_id = 0;
if (devname != NULL) {
audio_device_id = SDL_atoi(devname);
}
if (Android_JNI_OpenAudioDevice(iscapture, audio_device_id, &_this->spec) < 0) {
return -1; return -1;
} }
}
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
return 0; return 0;
} }
static void ANDROIDAUDIO_PlayDevice(SDL_AudioDevice *_this) // !!! FIXME: this needs a WaitDevice implementation.
static void ANDROIDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{ {
Android_JNI_WriteAudioBuffer(); Android_JNI_WriteAudioBuffer();
} }
static Uint8 *ANDROIDAUDIO_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *ANDROIDAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
return Android_JNI_GetAudioBuffer(); return Android_JNI_GetAudioBuffer();
} }
static int ANDROIDAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) static int ANDROIDAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{ {
return Android_JNI_CaptureAudioBuffer(buffer, buflen); return Android_JNI_CaptureAudioBuffer(buffer, buflen);
} }
static void ANDROIDAUDIO_FlushCapture(SDL_AudioDevice *_this) static void ANDROIDAUDIO_FlushCapture(SDL_AudioDevice *device)
{ {
Android_JNI_FlushCapturedAudio(); Android_JNI_FlushCapturedAudio();
} }
static void ANDROIDAUDIO_CloseDevice(SDL_AudioDevice *_this) static void ANDROIDAUDIO_CloseDevice(SDL_AudioDevice *device)
{ {
/* At this point SDL_CloseAudioDevice via close_audio_device took care of terminating the audio thread /* At this point SDL_CloseAudioDevice via close_audio_device took care of terminating the audio thread
so it's safe to terminate the Java side buffer and AudioTrack so it's safe to terminate the Java side buffer and AudioTrack
*/ */
Android_JNI_CloseAudioDevice(_this->iscapture); if (device->hidden) {
if (_this->iscapture) { Android_JNI_CloseAudioDevice(device->iscapture);
SDL_assert(captureDevice == _this); if (device->iscapture) {
SDL_assert(captureDevice == device);
captureDevice = NULL; captureDevice = NULL;
} else { } else {
SDL_assert(audioDevice == _this); SDL_assert(audioDevice == device);
audioDevice = NULL; audioDevice = NULL;
} }
SDL_free(_this->hidden); SDL_free(device->hidden);
device->hidden = NULL;
}
}
// Pause (block) all non already paused audio devices by taking their mixer lock
void ANDROIDAUDIO_PauseDevices(void)
{
// TODO: Handle multiple devices?
struct SDL_PrivateAudioData *hidden;
if (audioDevice != NULL && audioDevice->hidden != NULL) {
hidden = (struct SDL_PrivateAudioData *)audioDevice->hidden;
SDL_LockMutex(audioDevice->lock);
hidden->resume = SDL_TRUE;
}
if (captureDevice != NULL && captureDevice->hidden != NULL) {
hidden = (struct SDL_PrivateAudioData *)captureDevice->hidden;
SDL_LockMutex(captureDevice->lock);
hidden->resume = SDL_TRUE;
}
}
// Resume (unblock) all non already paused audio devices by releasing their mixer lock
void ANDROIDAUDIO_ResumeDevices(void)
{
// TODO: Handle multiple devices?
struct SDL_PrivateAudioData *hidden;
if (audioDevice != NULL && audioDevice->hidden != NULL) {
hidden = (struct SDL_PrivateAudioData *)audioDevice->hidden;
if (hidden->resume) {
hidden->resume = SDL_FALSE;
SDL_UnlockMutex(audioDevice->lock);
}
}
if (captureDevice != NULL && captureDevice->hidden != NULL) {
hidden = (struct SDL_PrivateAudioData *)captureDevice->hidden;
if (hidden->resume) {
hidden->resume = SDL_FALSE;
SDL_UnlockMutex(captureDevice->lock);
}
}
} }
static SDL_bool ANDROIDAUDIO_Init(SDL_AudioDriverImpl *impl) static SDL_bool ANDROIDAUDIO_Init(SDL_AudioDriverImpl *impl)
{ {
/* Set the function pointers */ // !!! FIXME: if on Android API < 24, DetectDevices and Deinitialize should be NULL and OnlyHasDefaultOutputDevice and OnlyHasDefaultCaptureDevice should be SDL_TRUE, since audio device enum and hotplug appears to require Android 7.0+.
impl->DetectDevices = Android_DetectDevices; impl->ThreadInit = Android_AudioThreadInit;
impl->DetectDevices = Android_StartAudioHotplug;
impl->Deinitialize = Android_StopAudioHotplug;
impl->OpenDevice = ANDROIDAUDIO_OpenDevice; impl->OpenDevice = ANDROIDAUDIO_OpenDevice;
impl->PlayDevice = ANDROIDAUDIO_PlayDevice; impl->PlayDevice = ANDROIDAUDIO_PlayDevice;
impl->GetDeviceBuf = ANDROIDAUDIO_GetDeviceBuf; impl->GetDeviceBuf = ANDROIDAUDIO_GetDeviceBuf;
impl->CloseDevice = ANDROIDAUDIO_CloseDevice; impl->CloseDevice = ANDROIDAUDIO_CloseDevice;
impl->CaptureFromDevice = ANDROIDAUDIO_CaptureFromDevice; impl->CaptureFromDevice = ANDROIDAUDIO_CaptureFromDevice;
impl->FlushCapture = ANDROIDAUDIO_FlushCapture; impl->FlushCapture = ANDROIDAUDIO_FlushCapture;
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
/* and the capabilities */
impl->HasCaptureSupport = SDL_TRUE; impl->HasCaptureSupport = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_FALSE;
impl->OnlyHasDefaultCaptureDevice = SDL_FALSE;
return SDL_TRUE; /* this audio target is available. */ return SDL_TRUE;
} }
AudioBootStrap ANDROIDAUDIO_bootstrap = { AudioBootStrap ANDROIDAUDIO_bootstrap = {
"android", "SDL Android audio driver", ANDROIDAUDIO_Init, SDL_FALSE "android", "SDL Android audio driver", ANDROIDAUDIO_Init, SDL_FALSE
}; };
/* Pause (block) all non already paused audio devices by taking their mixer lock */ #endif // SDL_AUDIO_DRIVER_ANDROID
void ANDROIDAUDIO_PauseDevices(void)
{
/* TODO: Handle multiple devices? */
struct SDL_PrivateAudioData *private;
if (audioDevice != NULL && audioDevice->hidden != NULL) {
private = (struct SDL_PrivateAudioData *)audioDevice->hidden;
if (SDL_AtomicGet(&audioDevice->paused)) {
/* The device is already paused, leave it alone */
private->resume = SDL_FALSE;
} else {
SDL_LockMutex(audioDevice->mixer_lock);
SDL_AtomicSet(&audioDevice->paused, 1);
private->resume = SDL_TRUE;
}
}
if (captureDevice != NULL && captureDevice->hidden != NULL) {
private = (struct SDL_PrivateAudioData *)captureDevice->hidden;
if (SDL_AtomicGet(&captureDevice->paused)) {
/* The device is already paused, leave it alone */
private->resume = SDL_FALSE;
} else {
SDL_LockMutex(captureDevice->mixer_lock);
SDL_AtomicSet(&captureDevice->paused, 1);
private->resume = SDL_TRUE;
}
}
}
/* Resume (unblock) all non already paused audio devices by releasing their mixer lock */
void ANDROIDAUDIO_ResumeDevices(void)
{
/* TODO: Handle multiple devices? */
struct SDL_PrivateAudioData *private;
if (audioDevice != NULL && audioDevice->hidden != NULL) {
private = (struct SDL_PrivateAudioData *)audioDevice->hidden;
if (private->resume) {
SDL_AtomicSet(&audioDevice->paused, 0);
private->resume = SDL_FALSE;
SDL_UnlockMutex(audioDevice->mixer_lock);
}
}
if (captureDevice != NULL && captureDevice->hidden != NULL) {
private = (struct SDL_PrivateAudioData *)captureDevice->hidden;
if (private->resume) {
SDL_AtomicSet(&captureDevice->paused, 0);
private->resume = SDL_FALSE;
SDL_UnlockMutex(captureDevice->mixer_lock);
}
}
}
#endif /* SDL_AUDIO_DRIVER_ANDROID */

View File

@ -53,15 +53,12 @@ struct SDL_PrivateAudioData
AudioQueueRef audioQueue; AudioQueueRef audioQueue;
int numAudioBuffers; int numAudioBuffers;
AudioQueueBufferRef *audioBuffer; AudioQueueBufferRef *audioBuffer;
void *buffer; AudioQueueBufferRef current_buffer;
UInt32 bufferOffset;
UInt32 bufferSize;
AudioStreamBasicDescription strdesc; AudioStreamBasicDescription strdesc;
SDL_Semaphore *ready_semaphore; SDL_Semaphore *ready_semaphore;
char *thread_error; char *thread_error;
#ifdef MACOSX_COREAUDIO #ifdef MACOSX_COREAUDIO
AudioDeviceID deviceID; AudioDeviceID deviceID;
SDL_AtomicInt device_change_flag;
#else #else
SDL_bool interrupted; SDL_bool interrupted;
CFTypeRef interruption_listener; CFTypeRef interruption_listener;

File diff suppressed because it is too large Load Diff

View File

@ -22,34 +22,34 @@
#ifdef SDL_AUDIO_DRIVER_DSOUND #ifdef SDL_AUDIO_DRIVER_DSOUND
/* Allow access to a raw mixing buffer */
#include "../SDL_audio_c.h" #include "../SDL_audio_c.h"
#include "SDL_directsound.h" #include "SDL_directsound.h"
#include <mmreg.h> #include <mmreg.h>
#ifdef HAVE_MMDEVICEAPI_H #ifdef HAVE_MMDEVICEAPI_H
#include "../../core/windows/SDL_immdevice.h" #include "../../core/windows/SDL_immdevice.h"
#endif /* HAVE_MMDEVICEAPI_H */ #endif
#ifndef WAVE_FORMAT_IEEE_FLOAT #ifndef WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_IEEE_FLOAT 0x0003 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif #endif
/* For Vista+, we can enumerate DSound devices with IMMDevice */ // For Vista+, we can enumerate DSound devices with IMMDevice
#ifdef HAVE_MMDEVICEAPI_H #ifdef HAVE_MMDEVICEAPI_H
static SDL_bool SupportsIMMDevice = SDL_FALSE; static SDL_bool SupportsIMMDevice = SDL_FALSE;
#endif /* HAVE_MMDEVICEAPI_H */ #endif
/* DirectX function pointers for audio */ // DirectX function pointers for audio
static void *DSoundDLL = NULL; static void *DSoundDLL = NULL;
typedef HRESULT(WINAPI *fnDirectSoundCreate8)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN); typedef HRESULT(WINAPI *fnDirectSoundCreate8)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
typedef HRESULT(WINAPI *fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID); typedef HRESULT(WINAPI *fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
typedef HRESULT(WINAPI *fnDirectSoundCaptureCreate8)(LPCGUID, LPDIRECTSOUNDCAPTURE8 *, LPUNKNOWN); typedef HRESULT(WINAPI *fnDirectSoundCaptureCreate8)(LPCGUID, LPDIRECTSOUNDCAPTURE8 *, LPUNKNOWN);
typedef HRESULT(WINAPI *fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID); typedef HRESULT(WINAPI *fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
typedef HRESULT(WINAPI *fnGetDeviceID)(LPCGUID, LPGUID);
static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL; static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL;
static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL; static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL;
static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = NULL; static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = NULL;
static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = NULL; static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = NULL;
static fnGetDeviceID pGetDeviceID = NULL;
static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
@ -60,6 +60,7 @@ static void DSOUND_Unload(void)
pDirectSoundEnumerateW = NULL; pDirectSoundEnumerateW = NULL;
pDirectSoundCaptureCreate8 = NULL; pDirectSoundCaptureCreate8 = NULL;
pDirectSoundCaptureEnumerateW = NULL; pDirectSoundCaptureEnumerateW = NULL;
pGetDeviceID = NULL;
if (DSoundDLL != NULL) { if (DSoundDLL != NULL) {
SDL_UnloadObject(DSoundDLL); SDL_UnloadObject(DSoundDLL);
@ -77,18 +78,19 @@ static int DSOUND_Load(void)
if (DSoundDLL == NULL) { if (DSoundDLL == NULL) {
SDL_SetError("DirectSound: failed to load DSOUND.DLL"); SDL_SetError("DirectSound: failed to load DSOUND.DLL");
} else { } else {
/* Now make sure we have DirectX 8 or better... */ // Now make sure we have DirectX 8 or better...
#define DSOUNDLOAD(f) \ #define DSOUNDLOAD(f) \
{ \ { \
p##f = (fn##f)SDL_LoadFunction(DSoundDLL, #f); \ p##f = (fn##f)SDL_LoadFunction(DSoundDLL, #f); \
if (!p##f) \ if (!p##f) \
loaded = 0; \ loaded = 0; \
} }
loaded = 1; /* will reset if necessary. */ loaded = 1; // will reset if necessary.
DSOUNDLOAD(DirectSoundCreate8); DSOUNDLOAD(DirectSoundCreate8);
DSOUNDLOAD(DirectSoundEnumerateW); DSOUNDLOAD(DirectSoundEnumerateW);
DSOUNDLOAD(DirectSoundCaptureCreate8); DSOUNDLOAD(DirectSoundCaptureCreate8);
DSOUNDLOAD(DirectSoundCaptureEnumerateW); DSOUNDLOAD(DirectSoundCaptureEnumerateW);
DSOUNDLOAD(GetDeviceID);
#undef DSOUNDLOAD #undef DSOUNDLOAD
if (!loaded) { if (!loaded) {
@ -149,56 +151,81 @@ static int SetDSerror(const char *function, int code)
return SDL_SetError("%s: %s (0x%x)", function, error, code); return SDL_SetError("%s: %s (0x%x)", function, error, code);
} }
static void DSOUND_FreeDeviceHandle(void *handle) static void DSOUND_FreeDeviceHandle(SDL_AudioDevice *device)
{
SDL_free(handle);
}
static int DSOUND_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
{ {
#ifdef HAVE_MMDEVICEAPI_H #ifdef HAVE_MMDEVICEAPI_H
if (SupportsIMMDevice) { if (SupportsIMMDevice) {
return SDL_IMMDevice_GetDefaultAudioInfo(name, spec, iscapture); SDL_IMMDevice_FreeDeviceHandle(device);
} else
#endif
{
SDL_free(device->handle);
} }
#endif /* HAVE_MMDEVICEAPI_H */
return SDL_Unsupported();
} }
static BOOL CALLBACK FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID data) // FindAllDevs is presumably only used on WinXP; Vista and later can use IMMDevice for better results.
typedef struct FindAllDevsData
{ {
const int iscapture = (int)((size_t)data); SDL_bool iscapture;
if (guid != NULL) { /* skip default device */ SDL_AudioDevice **default_device;
LPCGUID default_device_guid;
} FindAllDevsData;
static BOOL CALLBACK FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID userdata)
{
FindAllDevsData *data = (FindAllDevsData *) userdata;
if (guid != NULL) { // skip default device
char *str = WIN_LookupAudioDeviceName(desc, guid); char *str = WIN_LookupAudioDeviceName(desc, guid);
if (str != NULL) { if (str != NULL) {
LPGUID cpyguid = (LPGUID)SDL_malloc(sizeof(GUID)); LPGUID cpyguid = (LPGUID)SDL_malloc(sizeof(GUID));
if (cpyguid) {
SDL_memcpy(cpyguid, guid, sizeof(GUID)); SDL_memcpy(cpyguid, guid, sizeof(GUID));
/* Note that spec is NULL, because we are required to connect to the /* Note that spec is NULL, because we are required to connect to the
* device before getting the channel mask and output format, making * device before getting the channel mask and output format, making
* this information inaccessible at enumeration time * this information inaccessible at enumeration time
*/ */
SDL_AddAudioDevice(iscapture, str, NULL, cpyguid); SDL_AudioDevice *device = SDL_AddAudioDevice(data->iscapture, str, NULL, cpyguid);
SDL_free(str); /* addfn() makes a copy of this string. */ if (device && data->default_device && data->default_device_guid) {
if (SDL_memcmp(cpyguid, data->default_device_guid, sizeof (GUID)) == 0) {
*data->default_device = device;
} }
} }
return TRUE; /* keep enumerating. */ }
SDL_free(str); // SDL_AddAudioDevice() makes a copy of this string.
}
}
return TRUE; // keep enumerating.
} }
static void DSOUND_DetectDevices(void) static void DSOUND_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{ {
#ifdef HAVE_MMDEVICEAPI_H #ifdef HAVE_MMDEVICEAPI_H
if (SupportsIMMDevice) { if (SupportsIMMDevice) {
SDL_IMMDevice_EnumerateEndpoints(SDL_TRUE); SDL_IMMDevice_EnumerateEndpoints(default_output, default_capture);
} else { } else
#endif /* HAVE_MMDEVICEAPI_H */ #endif
pDirectSoundCaptureEnumerateW(FindAllDevs, (void *)((size_t)1)); {
pDirectSoundEnumerateW(FindAllDevs, (void *)((size_t)0)); // Without IMMDevice, you can enumerate devices and figure out the default devices,
#ifdef HAVE_MMDEVICEAPI_H // but you won't get device hotplug or default device change notifications. But this is
} // only for WinXP; Windows Vista and later should be using IMMDevice.
#endif /* HAVE_MMDEVICEAPI_H*/ FindAllDevsData data;
GUID guid;
data.iscapture = SDL_TRUE;
data.default_device = default_capture;
data.default_device_guid = (pGetDeviceID(&DSDEVID_DefaultCapture, &guid) == DS_OK) ? &guid : NULL;
pDirectSoundCaptureEnumerateW(FindAllDevs, &data);
data.iscapture = SDL_FALSE;
data.default_device = default_output;
data.default_device_guid = (pGetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) ? &guid : NULL;
pDirectSoundEnumerateW(FindAllDevs, &data);
} }
static void DSOUND_WaitDevice(SDL_AudioDevice *_this) }
static void DSOUND_WaitDevice(SDL_AudioDevice *device)
{ {
DWORD status = 0; DWORD status = 0;
DWORD cursor = 0; DWORD cursor = 0;
@ -208,11 +235,11 @@ static void DSOUND_WaitDevice(SDL_AudioDevice *_this)
/* Semi-busy wait, since we have no way of getting play notification /* Semi-busy wait, since we have no way of getting play notification
on a primary mixing buffer located in hardware (DirectX 5.0) on a primary mixing buffer located in hardware (DirectX 5.0)
*/ */
result = IDirectSoundBuffer_GetCurrentPosition(_this->hidden->mixbuf, result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf,
&junk, &cursor); &junk, &cursor);
if (result != DS_OK) { if (result != DS_OK) {
if (result == DSERR_BUFFERLOST) { if (result == DSERR_BUFFERLOST) {
IDirectSoundBuffer_Restore(_this->hidden->mixbuf); IDirectSoundBuffer_Restore(device->hidden->mixbuf);
} }
#ifdef DEBUG_SOUND #ifdef DEBUG_SOUND
SetDSerror("DirectSound GetCurrentPosition", result); SetDSerror("DirectSound GetCurrentPosition", result);
@ -220,21 +247,24 @@ static void DSOUND_WaitDevice(SDL_AudioDevice *_this)
return; return;
} }
while ((cursor / _this->spec.size) == _this->hidden->lastchunk) { while ((cursor / device->buffer_size) == device->hidden->lastchunk) {
/* FIXME: find out how much time is left and sleep that long */ if (SDL_AtomicGet(&device->shutdown)) {
return;
}
SDL_Delay(1); SDL_Delay(1);
/* Try to restore a lost sound buffer */ // Try to restore a lost sound buffer
IDirectSoundBuffer_GetStatus(_this->hidden->mixbuf, &status); IDirectSoundBuffer_GetStatus(device->hidden->mixbuf, &status);
if (status & DSBSTATUS_BUFFERLOST) { if (status & DSBSTATUS_BUFFERLOST) {
IDirectSoundBuffer_Restore(_this->hidden->mixbuf); IDirectSoundBuffer_Restore(device->hidden->mixbuf);
IDirectSoundBuffer_GetStatus(_this->hidden->mixbuf, &status); IDirectSoundBuffer_GetStatus(device->hidden->mixbuf, &status);
if (status & DSBSTATUS_BUFFERLOST) { if (status & DSBSTATUS_BUFFERLOST) {
break; break;
} }
} }
if (!(status & DSBSTATUS_PLAYING)) { if (!(status & DSBSTATUS_PLAYING)) {
result = IDirectSoundBuffer_Play(_this->hidden->mixbuf, 0, 0, result = IDirectSoundBuffer_Play(device->hidden->mixbuf, 0, 0,
DSBPLAY_LOOPING); DSBPLAY_LOOPING);
if (result == DS_OK) { if (result == DS_OK) {
continue; continue;
@ -245,8 +275,8 @@ static void DSOUND_WaitDevice(SDL_AudioDevice *_this)
return; return;
} }
/* Find out where we are playing */ // Find out where we are playing
result = IDirectSoundBuffer_GetCurrentPosition(_this->hidden->mixbuf, result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf,
&junk, &cursor); &junk, &cursor);
if (result != DS_OK) { if (result != DS_OK) {
SetDSerror("DirectSound GetCurrentPosition", result); SetDSerror("DirectSound GetCurrentPosition", result);
@ -255,102 +285,100 @@ static void DSOUND_WaitDevice(SDL_AudioDevice *_this)
} }
} }
static void DSOUND_PlayDevice(SDL_AudioDevice *_this) static void DSOUND_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{ {
/* Unlock the buffer, allowing it to play */ // Unlock the buffer, allowing it to play
if (_this->hidden->locked_buf) { SDL_assert(buflen == device->buffer_size);
IDirectSoundBuffer_Unlock(_this->hidden->mixbuf, IDirectSoundBuffer_Unlock(device->hidden->mixbuf, (LPVOID) buffer, buflen, NULL, 0);
_this->hidden->locked_buf,
_this->spec.size, NULL, 0);
}
} }
static Uint8 *DSOUND_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *DSOUND_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
DWORD cursor = 0; DWORD cursor = 0;
DWORD junk = 0; DWORD junk = 0;
HRESULT result = DS_OK; HRESULT result = DS_OK;
DWORD rawlen = 0;
/* Figure out which blocks to fill next */ SDL_assert(*buffer_size == device->buffer_size);
_this->hidden->locked_buf = NULL;
result = IDirectSoundBuffer_GetCurrentPosition(_this->hidden->mixbuf, // Figure out which blocks to fill next
device->hidden->locked_buf = NULL;
result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf,
&junk, &cursor); &junk, &cursor);
if (result == DSERR_BUFFERLOST) { if (result == DSERR_BUFFERLOST) {
IDirectSoundBuffer_Restore(_this->hidden->mixbuf); IDirectSoundBuffer_Restore(device->hidden->mixbuf);
result = IDirectSoundBuffer_GetCurrentPosition(_this->hidden->mixbuf, result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf,
&junk, &cursor); &junk, &cursor);
} }
if (result != DS_OK) { if (result != DS_OK) {
SetDSerror("DirectSound GetCurrentPosition", result); SetDSerror("DirectSound GetCurrentPosition", result);
return NULL; return NULL;
} }
cursor /= _this->spec.size; cursor /= device->buffer_size;
#ifdef DEBUG_SOUND #ifdef DEBUG_SOUND
/* Detect audio dropouts */ // Detect audio dropouts
{ {
DWORD spot = cursor; DWORD spot = cursor;
if (spot < _this->hidden->lastchunk) { if (spot < device->hidden->lastchunk) {
spot += _this->hidden->num_buffers; spot += device->hidden->num_buffers;
} }
if (spot > _this->hidden->lastchunk + 1) { if (spot > device->hidden->lastchunk + 1) {
fprintf(stderr, "Audio dropout, missed %d fragments\n", fprintf(stderr, "Audio dropout, missed %d fragments\n",
(spot - (_this->hidden->lastchunk + 1))); (spot - (device->hidden->lastchunk + 1)));
} }
} }
#endif #endif
_this->hidden->lastchunk = cursor; device->hidden->lastchunk = cursor;
cursor = (cursor + 1) % _this->hidden->num_buffers; cursor = (cursor + 1) % device->hidden->num_buffers;
cursor *= _this->spec.size; cursor *= device->buffer_size;
/* Lock the audio buffer */ // Lock the audio buffer
result = IDirectSoundBuffer_Lock(_this->hidden->mixbuf, cursor, DWORD rawlen = 0;
_this->spec.size, result = IDirectSoundBuffer_Lock(device->hidden->mixbuf, cursor,
(LPVOID *)&_this->hidden->locked_buf, device->buffer_size,
(LPVOID *)&device->hidden->locked_buf,
&rawlen, NULL, &junk, 0); &rawlen, NULL, &junk, 0);
if (result == DSERR_BUFFERLOST) { if (result == DSERR_BUFFERLOST) {
IDirectSoundBuffer_Restore(_this->hidden->mixbuf); IDirectSoundBuffer_Restore(device->hidden->mixbuf);
result = IDirectSoundBuffer_Lock(_this->hidden->mixbuf, cursor, result = IDirectSoundBuffer_Lock(device->hidden->mixbuf, cursor,
_this->spec.size, device->buffer_size,
(LPVOID *)&_this->hidden->locked_buf, &rawlen, NULL, (LPVOID *)&device->hidden->locked_buf, &rawlen, NULL,
&junk, 0); &junk, 0);
} }
if (result != DS_OK) { if (result != DS_OK) {
SetDSerror("DirectSound Lock", result); SetDSerror("DirectSound Lock", result);
return NULL; return NULL;
} }
return _this->hidden->locked_buf; return device->hidden->locked_buf;
} }
static int DSOUND_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) static void DSOUND_WaitCaptureDevice(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *h = _this->hidden; struct SDL_PrivateAudioData *h = device->hidden;
DWORD junk, cursor, ptr1len, ptr2len; while (!SDL_AtomicGet(&device->shutdown)) {
DWORD junk, cursor;
if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) != DS_OK) {
SDL_AudioDeviceDisconnected(device);
return;
} else if ((cursor / device->buffer_size) != h->lastchunk) {
return;
}
SDL_Delay(1);
}
}
static int DSOUND_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{
struct SDL_PrivateAudioData *h = device->hidden;
DWORD ptr1len, ptr2len;
VOID *ptr1, *ptr2; VOID *ptr1, *ptr2;
SDL_assert((Uint32)buflen == _this->spec.size); SDL_assert(buflen == device->buffer_size);
while (SDL_TRUE) { if (IDirectSoundCaptureBuffer_Lock(h->capturebuf, h->lastchunk * buflen, buflen, &ptr1, &ptr1len, &ptr2, &ptr2len, 0) != DS_OK) {
if (SDL_AtomicGet(&_this->shutdown)) { /* in case the buffer froze... */
SDL_memset(buffer, _this->spec.silence, buflen);
return buflen;
}
if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) != DS_OK) {
return -1;
}
if ((cursor / _this->spec.size) == h->lastchunk) {
SDL_Delay(1); /* FIXME: find out how much time is left and sleep that long */
} else {
break;
}
}
if (IDirectSoundCaptureBuffer_Lock(h->capturebuf, h->lastchunk * _this->spec.size, _this->spec.size, &ptr1, &ptr1len, &ptr2, &ptr2len, 0) != DS_OK) {
return -1; return -1;
} }
SDL_assert(ptr1len == _this->spec.size); SDL_assert(ptr1len == buflen);
SDL_assert(ptr2 == NULL); SDL_assert(ptr2 == NULL);
SDL_assert(ptr2len == 0); SDL_assert(ptr2len == 0);
@ -362,51 +390,54 @@ static int DSOUND_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int bu
h->lastchunk = (h->lastchunk + 1) % h->num_buffers; h->lastchunk = (h->lastchunk + 1) % h->num_buffers;
return ptr1len; return (int) ptr1len;
} }
static void DSOUND_FlushCapture(SDL_AudioDevice *_this) static void DSOUND_FlushCapture(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *h = _this->hidden; struct SDL_PrivateAudioData *h = device->hidden;
DWORD junk, cursor; DWORD junk, cursor;
if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) == DS_OK) { if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) == DS_OK) {
h->lastchunk = cursor / _this->spec.size; h->lastchunk = cursor / device->buffer_size;
} }
} }
static void DSOUND_CloseDevice(SDL_AudioDevice *_this) static void DSOUND_CloseDevice(SDL_AudioDevice *device)
{ {
if (_this->hidden->mixbuf != NULL) { if (device->hidden) {
IDirectSoundBuffer_Stop(_this->hidden->mixbuf); if (device->hidden->mixbuf != NULL) {
IDirectSoundBuffer_Release(_this->hidden->mixbuf); IDirectSoundBuffer_Stop(device->hidden->mixbuf);
IDirectSoundBuffer_Release(device->hidden->mixbuf);
} }
if (_this->hidden->sound != NULL) { if (device->hidden->sound != NULL) {
IDirectSound_Release(_this->hidden->sound); IDirectSound_Release(device->hidden->sound);
} }
if (_this->hidden->capturebuf != NULL) { if (device->hidden->capturebuf != NULL) {
IDirectSoundCaptureBuffer_Stop(_this->hidden->capturebuf); IDirectSoundCaptureBuffer_Stop(device->hidden->capturebuf);
IDirectSoundCaptureBuffer_Release(_this->hidden->capturebuf); IDirectSoundCaptureBuffer_Release(device->hidden->capturebuf);
} }
if (_this->hidden->capture != NULL) { if (device->hidden->capture != NULL) {
IDirectSoundCapture_Release(_this->hidden->capture); IDirectSoundCapture_Release(device->hidden->capture);
}
SDL_free(device->hidden);
device->hidden = NULL;
} }
SDL_free(_this->hidden);
} }
/* This function tries to create a secondary audio buffer, and returns the /* This function tries to create a secondary audio buffer, and returns the
number of audio chunks available in the created buffer. This is for number of audio chunks available in the created buffer. This is for
playback devices, not capture. playback devices, not capture.
*/ */
static int CreateSecondary(SDL_AudioDevice *_this, const DWORD bufsize, WAVEFORMATEX *wfmt) static int CreateSecondary(SDL_AudioDevice *device, const DWORD bufsize, WAVEFORMATEX *wfmt)
{ {
LPDIRECTSOUND sndObj = _this->hidden->sound; LPDIRECTSOUND sndObj = device->hidden->sound;
LPDIRECTSOUNDBUFFER *sndbuf = &_this->hidden->mixbuf; LPDIRECTSOUNDBUFFER *sndbuf = &device->hidden->mixbuf;
HRESULT result = DS_OK; HRESULT result = DS_OK;
DSBUFFERDESC format; DSBUFFERDESC format;
LPVOID pvAudioPtr1, pvAudioPtr2; LPVOID pvAudioPtr1, pvAudioPtr2;
DWORD dwAudioBytes1, dwAudioBytes2; DWORD dwAudioBytes1, dwAudioBytes2;
/* Try to create the secondary buffer */ // Try to create the secondary buffer
SDL_zero(format); SDL_zero(format);
format.dwSize = sizeof(format); format.dwSize = sizeof(format);
format.dwFlags = DSBCAPS_GETCURRENTPOSITION2; format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
@ -419,30 +450,29 @@ static int CreateSecondary(SDL_AudioDevice *_this, const DWORD bufsize, WAVEFORM
} }
IDirectSoundBuffer_SetFormat(*sndbuf, wfmt); IDirectSoundBuffer_SetFormat(*sndbuf, wfmt);
/* Silence the initial audio buffer */ // Silence the initial audio buffer
result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes, result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes,
(LPVOID *)&pvAudioPtr1, &dwAudioBytes1, (LPVOID *)&pvAudioPtr1, &dwAudioBytes1,
(LPVOID *)&pvAudioPtr2, &dwAudioBytes2, (LPVOID *)&pvAudioPtr2, &dwAudioBytes2,
DSBLOCK_ENTIREBUFFER); DSBLOCK_ENTIREBUFFER);
if (result == DS_OK) { if (result == DS_OK) {
SDL_memset(pvAudioPtr1, _this->spec.silence, dwAudioBytes1); SDL_memset(pvAudioPtr1, device->silence_value, dwAudioBytes1);
IDirectSoundBuffer_Unlock(*sndbuf, IDirectSoundBuffer_Unlock(*sndbuf,
(LPVOID)pvAudioPtr1, dwAudioBytes1, (LPVOID)pvAudioPtr1, dwAudioBytes1,
(LPVOID)pvAudioPtr2, dwAudioBytes2); (LPVOID)pvAudioPtr2, dwAudioBytes2);
} }
/* We're ready to go */ return 0; // We're ready to go
return 0;
} }
/* This function tries to create a capture buffer, and returns the /* This function tries to create a capture buffer, and returns the
number of audio chunks available in the created buffer. This is for number of audio chunks available in the created buffer. This is for
capture devices, not playback. capture devices, not playback.
*/ */
static int CreateCaptureBuffer(SDL_AudioDevice *_this, const DWORD bufsize, WAVEFORMATEX *wfmt) static int CreateCaptureBuffer(SDL_AudioDevice *device, const DWORD bufsize, WAVEFORMATEX *wfmt)
{ {
LPDIRECTSOUNDCAPTURE capture = _this->hidden->capture; LPDIRECTSOUNDCAPTURE capture = device->hidden->capture;
LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &_this->hidden->capturebuf; LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &device->hidden->capturebuf;
DSCBUFFERDESC format; DSCBUFFERDESC format;
HRESULT result; HRESULT result;
@ -464,7 +494,7 @@ static int CreateCaptureBuffer(SDL_AudioDevice *_this, const DWORD bufsize, WAVE
} }
#if 0 #if 0
/* presumably this starts at zero, but just in case... */ // presumably this starts at zero, but just in case...
result = IDirectSoundCaptureBuffer_GetCurrentPosition(*capturebuf, &junk, &cursor); result = IDirectSoundCaptureBuffer_GetCurrentPosition(*capturebuf, &junk, &cursor);
if (result != DS_OK) { if (result != DS_OK) {
IDirectSoundCaptureBuffer_Stop(*capturebuf); IDirectSoundCaptureBuffer_Stop(*capturebuf);
@ -472,42 +502,45 @@ static int CreateCaptureBuffer(SDL_AudioDevice *_this, const DWORD bufsize, WAVE
return SetDSerror("DirectSound GetCurrentPosition", result); return SetDSerror("DirectSound GetCurrentPosition", result);
} }
_this->hidden->lastchunk = cursor / _this->spec.size; device->hidden->lastchunk = cursor / device->buffer_size;
#endif #endif
return 0; return 0;
} }
static int DSOUND_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int DSOUND_OpenDevice(SDL_AudioDevice *device)
{ {
const DWORD numchunks = 8; // Initialize all variables that we clean on shutdown
HRESULT result; device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
SDL_bool tried_format = SDL_FALSE; if (device->hidden == NULL) {
SDL_bool iscapture = _this->iscapture;
SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts;
LPGUID guid = (LPGUID)_this->handle;
DWORD bufsize;
/* Initialize all variables that we clean on shutdown */
_this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*_this->hidden));
if (_this->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_zerop(_this->hidden);
/* Open the audio device */ // Open the audio device
if (iscapture) { LPGUID guid;
result = pDirectSoundCaptureCreate8(guid, &_this->hidden->capture, NULL); #ifdef HAVE_MMDEVICEAPI_H
if (SupportsIMMDevice) {
guid = SDL_IMMDevice_GetDirectSoundGUID(device);
} else
#endif
{
guid = (LPGUID) device->handle;
}
SDL_assert(guid != NULL);
HRESULT result;
if (device->iscapture) {
result = pDirectSoundCaptureCreate8(guid, &device->hidden->capture, NULL);
if (result != DS_OK) { if (result != DS_OK) {
return SetDSerror("DirectSoundCaptureCreate8", result); return SetDSerror("DirectSoundCaptureCreate8", result);
} }
} else { } else {
result = pDirectSoundCreate8(guid, &_this->hidden->sound, NULL); result = pDirectSoundCreate8(guid, &device->hidden->sound, NULL);
if (result != DS_OK) { if (result != DS_OK) {
return SetDSerror("DirectSoundCreate8", result); return SetDSerror("DirectSoundCreate8", result);
} }
result = IDirectSound_SetCooperativeLevel(_this->hidden->sound, result = IDirectSound_SetCooperativeLevel(device->hidden->sound,
GetDesktopWindow(), GetDesktopWindow(),
DSSCL_NORMAL); DSSCL_NORMAL);
if (result != DS_OK) { if (result != DS_OK) {
@ -515,7 +548,10 @@ static int DSOUND_OpenDevice(SDL_AudioDevice *_this, const char *devname)
} }
} }
closefmts = SDL_ClosestAudioFormats(_this->spec.format); const DWORD numchunks = 8;
SDL_bool tried_format = SDL_FALSE;
SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
while ((test_format = *(closefmts++)) != 0) { while ((test_format = *(closefmts++)) != 0) {
switch (test_format) { switch (test_format) {
case SDL_AUDIO_U8: case SDL_AUDIO_U8:
@ -524,69 +560,68 @@ static int DSOUND_OpenDevice(SDL_AudioDevice *_this, const char *devname)
case SDL_AUDIO_F32: case SDL_AUDIO_F32:
tried_format = SDL_TRUE; tried_format = SDL_TRUE;
_this->spec.format = test_format; device->spec.format = test_format;
/* Update the fragment size as size in bytes */ // Update the fragment size as size in bytes
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
bufsize = numchunks * _this->spec.size; const DWORD bufsize = numchunks * device->buffer_size;
if ((bufsize < DSBSIZE_MIN) || (bufsize > DSBSIZE_MAX)) { if ((bufsize < DSBSIZE_MIN) || (bufsize > DSBSIZE_MAX)) {
SDL_SetError("Sound buffer size must be between %d and %d", SDL_SetError("Sound buffer size must be between %d and %d",
(int)((DSBSIZE_MIN < numchunks) ? 1 : DSBSIZE_MIN / numchunks), (int)((DSBSIZE_MIN < numchunks) ? 1 : DSBSIZE_MIN / numchunks),
(int)(DSBSIZE_MAX / numchunks)); (int)(DSBSIZE_MAX / numchunks));
} else { } else {
int rc;
WAVEFORMATEXTENSIBLE wfmt; WAVEFORMATEXTENSIBLE wfmt;
SDL_zero(wfmt); SDL_zero(wfmt);
if (_this->spec.channels > 2) { if (device->spec.channels > 2) {
wfmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wfmt.Format.cbSize = sizeof(wfmt) - sizeof(WAVEFORMATEX); wfmt.Format.cbSize = sizeof(wfmt) - sizeof(WAVEFORMATEX);
if (SDL_AUDIO_ISFLOAT(_this->spec.format)) { if (SDL_AUDIO_ISFLOAT(device->spec.format)) {
SDL_memcpy(&wfmt.SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)); SDL_memcpy(&wfmt.SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID));
} else { } else {
SDL_memcpy(&wfmt.SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)); SDL_memcpy(&wfmt.SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID));
} }
wfmt.Samples.wValidBitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format); wfmt.Samples.wValidBitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
switch (_this->spec.channels) { switch (device->spec.channels) {
case 3: /* 3.0 (or 2.1) */ case 3: // 3.0 (or 2.1)
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER; wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER;
break; break;
case 4: /* 4.0 */ case 4: // 4.0
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
break; break;
case 5: /* 5.0 (or 4.1) */ case 5: // 5.0 (or 4.1)
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
break; break;
case 6: /* 5.1 */ case 6: // 5.1
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
break; break;
case 7: /* 6.1 */ case 7: // 6.1
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_BACK_CENTER; wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_BACK_CENTER;
break; break;
case 8: /* 7.1 */ case 8: // 7.1
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT;
break; break;
default: default:
SDL_assert(0 && "Unsupported channel count!"); SDL_assert(0 && "Unsupported channel count!");
break; break;
} }
} else if (SDL_AUDIO_ISFLOAT(_this->spec.format)) { } else if (SDL_AUDIO_ISFLOAT(device->spec.format)) {
wfmt.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; wfmt.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
} else { } else {
wfmt.Format.wFormatTag = WAVE_FORMAT_PCM; wfmt.Format.wFormatTag = WAVE_FORMAT_PCM;
} }
wfmt.Format.wBitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format); wfmt.Format.wBitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
wfmt.Format.nChannels = _this->spec.channels; wfmt.Format.nChannels = device->spec.channels;
wfmt.Format.nSamplesPerSec = _this->spec.freq; wfmt.Format.nSamplesPerSec = device->spec.freq;
wfmt.Format.nBlockAlign = wfmt.Format.nChannels * (wfmt.Format.wBitsPerSample / 8); wfmt.Format.nBlockAlign = wfmt.Format.nChannels * (wfmt.Format.wBitsPerSample / 8);
wfmt.Format.nAvgBytesPerSec = wfmt.Format.nSamplesPerSec * wfmt.Format.nBlockAlign; wfmt.Format.nAvgBytesPerSec = wfmt.Format.nSamplesPerSec * wfmt.Format.nBlockAlign;
rc = iscapture ? CreateCaptureBuffer(_this, bufsize, (WAVEFORMATEX *)&wfmt) : CreateSecondary(_this, bufsize, (WAVEFORMATEX *)&wfmt); const int rc = device->iscapture ? CreateCaptureBuffer(device, bufsize, (WAVEFORMATEX *)&wfmt) : CreateSecondary(device, bufsize, (WAVEFORMATEX *)&wfmt);
if (rc == 0) { if (rc == 0) {
_this->hidden->num_buffers = numchunks; device->hidden->num_buffers = numchunks;
break; break;
} }
} }
@ -599,14 +634,14 @@ static int DSOUND_OpenDevice(SDL_AudioDevice *_this, const char *devname)
if (!test_format) { if (!test_format) {
if (tried_format) { if (tried_format) {
return -1; /* CreateSecondary() should have called SDL_SetError(). */ return -1; // CreateSecondary() should have called SDL_SetError().
} }
return SDL_SetError("%s: Unsupported audio format", "directsound"); return SDL_SetError("%s: Unsupported audio format", "directsound");
} }
/* Playback buffers will auto-start playing in DSOUND_WaitDevice() */ // Playback buffers will auto-start playing in DSOUND_WaitDevice()
return 0; /* good to go. */ return 0; // good to go.
} }
static void DSOUND_Deinitialize(void) static void DSOUND_Deinitialize(void)
@ -616,7 +651,7 @@ static void DSOUND_Deinitialize(void)
SDL_IMMDevice_Quit(); SDL_IMMDevice_Quit();
SupportsIMMDevice = SDL_FALSE; SupportsIMMDevice = SDL_FALSE;
} }
#endif /* HAVE_MMDEVICEAPI_H */ #endif
DSOUND_Unload(); DSOUND_Unload();
} }
@ -628,29 +663,27 @@ static SDL_bool DSOUND_Init(SDL_AudioDriverImpl *impl)
#ifdef HAVE_MMDEVICEAPI_H #ifdef HAVE_MMDEVICEAPI_H
SupportsIMMDevice = !(SDL_IMMDevice_Init() < 0); SupportsIMMDevice = !(SDL_IMMDevice_Init() < 0);
#endif /* HAVE_MMDEVICEAPI_H */ #endif
/* Set the function pointers */
impl->DetectDevices = DSOUND_DetectDevices; impl->DetectDevices = DSOUND_DetectDevices;
impl->OpenDevice = DSOUND_OpenDevice; impl->OpenDevice = DSOUND_OpenDevice;
impl->PlayDevice = DSOUND_PlayDevice; impl->PlayDevice = DSOUND_PlayDevice;
impl->WaitDevice = DSOUND_WaitDevice; impl->WaitDevice = DSOUND_WaitDevice;
impl->GetDeviceBuf = DSOUND_GetDeviceBuf; impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
impl->WaitCaptureDevice = DSOUND_WaitCaptureDevice;
impl->CaptureFromDevice = DSOUND_CaptureFromDevice; impl->CaptureFromDevice = DSOUND_CaptureFromDevice;
impl->FlushCapture = DSOUND_FlushCapture; impl->FlushCapture = DSOUND_FlushCapture;
impl->CloseDevice = DSOUND_CloseDevice; impl->CloseDevice = DSOUND_CloseDevice;
impl->FreeDeviceHandle = DSOUND_FreeDeviceHandle; impl->FreeDeviceHandle = DSOUND_FreeDeviceHandle;
impl->Deinitialize = DSOUND_Deinitialize; impl->Deinitialize = DSOUND_Deinitialize;
impl->GetDefaultAudioInfo = DSOUND_GetDefaultAudioInfo;
impl->HasCaptureSupport = SDL_TRUE; impl->HasCaptureSupport = SDL_TRUE;
impl->SupportsNonPow2Samples = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */ return SDL_TRUE;
} }
AudioBootStrap DSOUND_bootstrap = { AudioBootStrap DSOUND_bootstrap = {
"directsound", "DirectSound", DSOUND_Init, SDL_FALSE "directsound", "DirectSound", DSOUND_Init, SDL_FALSE
}; };
#endif /* SDL_AUDIO_DRIVER_DSOUND */ #endif // SDL_AUDIO_DRIVER_DSOUND

View File

@ -27,9 +27,10 @@
#include "../SDL_sysaudio.h" #include "../SDL_sysaudio.h"
/* The DirectSound objects */ // The DirectSound objects
struct SDL_PrivateAudioData struct SDL_PrivateAudioData
{ {
// !!! FIXME: make this a union with capture/playback sections?
LPDIRECTSOUND sound; LPDIRECTSOUND sound;
LPDIRECTSOUNDBUFFER mixbuf; LPDIRECTSOUNDBUFFER mixbuf;
LPDIRECTSOUNDCAPTURE capture; LPDIRECTSOUNDCAPTURE capture;
@ -39,4 +40,4 @@ struct SDL_PrivateAudioData
Uint8 *locked_buf; Uint8 *locked_buf;
}; };
#endif /* SDL_directsound_h_ */ #endif // SDL_directsound_h_

View File

@ -22,171 +22,151 @@
#ifdef SDL_AUDIO_DRIVER_DISK #ifdef SDL_AUDIO_DRIVER_DISK
/* Output raw audio data to a file. */ // Output raw audio data to a file.
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#include "../SDL_audio_c.h" #include "../SDL_audio_c.h"
#include "SDL_diskaudio.h" #include "SDL_diskaudio.h"
/* !!! FIXME: these should be SDL hints, not environment variables. */ // !!! FIXME: these should be SDL hints, not environment variables.
/* environment variables and defaults. */ // environment variables and defaults.
#define DISKENVR_OUTFILE "SDL_DISKAUDIOFILE" #define DISKENVR_OUTFILE "SDL_DISKAUDIOFILE"
#define DISKDEFAULT_OUTFILE "sdlaudio.raw" #define DISKDEFAULT_OUTFILE "sdlaudio.raw"
#define DISKENVR_INFILE "SDL_DISKAUDIOFILEIN" #define DISKENVR_INFILE "SDL_DISKAUDIOFILEIN"
#define DISKDEFAULT_INFILE "sdlaudio-in.raw" #define DISKDEFAULT_INFILE "sdlaudio-in.raw"
#define DISKENVR_IODELAY "SDL_DISKAUDIODELAY" #define DISKENVR_IODELAY "SDL_DISKAUDIODELAY"
/* This function waits until it is possible to write a full sound buffer */ static void DISKAUDIO_WaitDevice(SDL_AudioDevice *device)
static void DISKAUDIO_WaitDevice(SDL_AudioDevice *_this)
{ {
SDL_Delay(_this->hidden->io_delay); SDL_Delay(device->hidden->io_delay);
} }
static void DISKAUDIO_PlayDevice(SDL_AudioDevice *_this) static void DISKAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
{ {
const Sint64 written = SDL_RWwrite(_this->hidden->io, const int written = (int)SDL_RWwrite(device->hidden->io, buffer, (size_t)buffer_size);
_this->hidden->mixbuf, if (written != buffer_size) { // If we couldn't write, assume fatal error for now
_this->spec.size); SDL_AudioDeviceDisconnected(device);
/* If we couldn't write, assume fatal error for now */
if (written != _this->spec.size) {
SDL_OpenedAudioDeviceDisconnected(_this);
} }
#ifdef DEBUG_AUDIO #ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", (int) written); SDL_Log("DISKAUDIO: Wrote %d bytes of audio data", (int) written);
#endif #endif
} }
static Uint8 *DISKAUDIO_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *DISKAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
return _this->hidden->mixbuf; return device->hidden->mixbuf;
} }
static int DISKAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) static int DISKAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{ {
struct SDL_PrivateAudioData *h = _this->hidden; struct SDL_PrivateAudioData *h = device->hidden;
const int origbuflen = buflen; const int origbuflen = buflen;
SDL_Delay(h->io_delay);
if (h->io) { if (h->io) {
const int br = (int) SDL_RWread(h->io, buffer, (Sint64) buflen); const int br = (int)SDL_RWread(h->io, buffer, (size_t)buflen);
buflen -= br; buflen -= br;
buffer = ((Uint8 *)buffer) + br; buffer = ((Uint8 *)buffer) + br;
if (buflen > 0) { /* EOF (or error, but whatever). */ if (buflen > 0) { // EOF (or error, but whatever).
SDL_RWclose(h->io); SDL_RWclose(h->io);
h->io = NULL; h->io = NULL;
} }
} }
/* if we ran out of file, just write silence. */ // if we ran out of file, just write silence.
SDL_memset(buffer, _this->spec.silence, buflen); SDL_memset(buffer, device->silence_value, buflen);
return origbuflen; return origbuflen;
} }
static void DISKAUDIO_FlushCapture(SDL_AudioDevice *_this) static void DISKAUDIO_FlushCapture(SDL_AudioDevice *device)
{ {
/* no op...we don't advance the file pointer or anything. */ // no op...we don't advance the file pointer or anything.
} }
static void DISKAUDIO_CloseDevice(SDL_AudioDevice *_this) static void DISKAUDIO_CloseDevice(SDL_AudioDevice *device)
{ {
if (_this->hidden->io != NULL) { if (device->hidden) {
SDL_RWclose(_this->hidden->io); if (device->hidden->io != NULL) {
SDL_RWclose(device->hidden->io);
}
SDL_free(device->hidden->mixbuf);
SDL_free(device->hidden);
device->hidden = NULL;
} }
SDL_free(_this->hidden->mixbuf);
SDL_free(_this->hidden);
} }
static const char *get_filename(const SDL_bool iscapture, const char *devname) static const char *get_filename(const SDL_bool iscapture)
{ {
if (devname == NULL) { const char *devname = SDL_getenv(iscapture ? DISKENVR_INFILE : DISKENVR_OUTFILE);
devname = SDL_getenv(iscapture ? DISKENVR_INFILE : DISKENVR_OUTFILE);
if (devname == NULL) { if (devname == NULL) {
devname = iscapture ? DISKDEFAULT_INFILE : DISKDEFAULT_OUTFILE; devname = iscapture ? DISKDEFAULT_INFILE : DISKDEFAULT_OUTFILE;
} }
}
return devname; return devname;
} }
static int DISKAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int DISKAUDIO_OpenDevice(SDL_AudioDevice *device)
{ {
void *handle = _this->handle; SDL_bool iscapture = device->iscapture;
/* handle != NULL means "user specified the placeholder name on the fake detected device list" */ const char *fname = get_filename(iscapture);
SDL_bool iscapture = _this->iscapture;
const char *fname = get_filename(iscapture, handle ? NULL : devname);
const char *envr = SDL_getenv(DISKENVR_IODELAY); const char *envr = SDL_getenv(DISKENVR_IODELAY);
_this->hidden = (struct SDL_PrivateAudioData *) device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
SDL_malloc(sizeof(*_this->hidden)); if (device->hidden == NULL) {
if (_this->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_zerop(_this->hidden);
if (envr != NULL) { if (envr != NULL) {
_this->hidden->io_delay = SDL_atoi(envr); device->hidden->io_delay = SDL_atoi(envr);
} else { } else {
_this->hidden->io_delay = ((_this->spec.samples * 1000) / _this->spec.freq); device->hidden->io_delay = ((device->sample_frames * 1000) / device->spec.freq);
} }
/* Open the audio device */ // Open the "audio device"
_this->hidden->io = SDL_RWFromFile(fname, iscapture ? "rb" : "wb"); device->hidden->io = SDL_RWFromFile(fname, iscapture ? "rb" : "wb");
if (_this->hidden->io == NULL) { if (device->hidden->io == NULL) {
return -1; return -1;
} }
/* Allocate mixing buffer */ // Allocate mixing buffer
if (!iscapture) { if (!iscapture) {
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->spec.size); device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
if (_this->hidden->mixbuf == NULL) { if (device->hidden->mixbuf == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->spec.size); SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
} }
SDL_LogCritical(SDL_LOG_CATEGORY_AUDIO, SDL_LogCritical(SDL_LOG_CATEGORY_AUDIO, "You are using the SDL disk i/o audio driver!");
"You are using the SDL disk i/o audio driver!\n"); SDL_LogCritical(SDL_LOG_CATEGORY_AUDIO, " %s file [%s].\n", iscapture ? "Reading from" : "Writing to", fname);
SDL_LogCritical(SDL_LOG_CATEGORY_AUDIO,
" %s file [%s].\n", iscapture ? "Reading from" : "Writing to",
fname);
/* We're ready to rock and roll. :-) */ return 0; // We're ready to rock and roll. :-)
return 0;
} }
static void DISKAUDIO_DetectDevices(void) static void DISKAUDIO_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{ {
SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *)0x1); *default_output = SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *)0x1);
SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *)0x2); *default_capture = SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *)0x2);
} }
static SDL_bool DISKAUDIO_Init(SDL_AudioDriverImpl *impl) static SDL_bool DISKAUDIO_Init(SDL_AudioDriverImpl *impl)
{ {
/* Set the function pointers */
impl->OpenDevice = DISKAUDIO_OpenDevice; impl->OpenDevice = DISKAUDIO_OpenDevice;
impl->WaitDevice = DISKAUDIO_WaitDevice; impl->WaitDevice = DISKAUDIO_WaitDevice;
impl->WaitCaptureDevice = DISKAUDIO_WaitDevice;
impl->PlayDevice = DISKAUDIO_PlayDevice; impl->PlayDevice = DISKAUDIO_PlayDevice;
impl->GetDeviceBuf = DISKAUDIO_GetDeviceBuf; impl->GetDeviceBuf = DISKAUDIO_GetDeviceBuf;
impl->CaptureFromDevice = DISKAUDIO_CaptureFromDevice; impl->CaptureFromDevice = DISKAUDIO_CaptureFromDevice;
impl->FlushCapture = DISKAUDIO_FlushCapture; impl->FlushCapture = DISKAUDIO_FlushCapture;
impl->CloseDevice = DISKAUDIO_CloseDevice; impl->CloseDevice = DISKAUDIO_CloseDevice;
impl->DetectDevices = DISKAUDIO_DetectDevices; impl->DetectDevices = DISKAUDIO_DetectDevices;
impl->AllowsArbitraryDeviceNames = SDL_TRUE; impl->AllowsArbitraryDeviceNames = SDL_TRUE;
impl->HasCaptureSupport = SDL_TRUE; impl->HasCaptureSupport = SDL_TRUE;
impl->SupportsNonPow2Samples = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */ return SDL_TRUE;
} }
AudioBootStrap DISKAUDIO_bootstrap = { AudioBootStrap DISKAUDIO_bootstrap = {
"disk", "direct-to-disk audio", DISKAUDIO_Init, SDL_TRUE "disk", "direct-to-disk audio", DISKAUDIO_Init, SDL_TRUE
}; };
#endif /* SDL_AUDIO_DRIVER_DISK */ #endif // SDL_AUDIO_DRIVER_DISK

View File

@ -20,9 +20,9 @@
*/ */
#include "SDL_internal.h" #include "SDL_internal.h"
#ifdef SDL_AUDIO_DRIVER_OSS // !!! FIXME: clean out perror and fprintf calls in here.
/* Allow access to a raw mixing buffer */ #ifdef SDL_AUDIO_DRIVER_OSS
#include <stdio.h> /* For perror() */ #include <stdio.h> /* For perror() */
#include <string.h> /* For strerror() */ #include <string.h> /* For strerror() */
@ -38,82 +38,69 @@
#include "../SDL_audio_c.h" #include "../SDL_audio_c.h"
#include "../SDL_audiodev_c.h" #include "../SDL_audiodev_c.h"
#include "../../SDL_utils_c.h"
#include "SDL_dspaudio.h" #include "SDL_dspaudio.h"
static void DSP_DetectDevices(void) static void DSP_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{ {
SDL_EnumUnixAudioDevices(0, NULL); SDL_EnumUnixAudioDevices(SDL_FALSE, NULL);
} }
static void DSP_CloseDevice(SDL_AudioDevice *_this) static void DSP_CloseDevice(SDL_AudioDevice *device)
{ {
if (_this->hidden->audio_fd >= 0) { if (device->hidden) {
close(_this->hidden->audio_fd); if (device->hidden->audio_fd >= 0) {
close(device->hidden->audio_fd);
}
SDL_free(device->hidden->mixbuf);
SDL_free(device->hidden);
} }
SDL_free(_this->hidden->mixbuf);
SDL_free(_this->hidden);
} }
static int DSP_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int DSP_OpenDevice(SDL_AudioDevice *device)
{ {
SDL_bool iscapture = _this->iscapture; // Make sure fragment size stays a power of 2, or OSS fails.
const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT); // (I don't know which of these are actually legal values, though...)
int format = 0; if (device->spec.channels > 8) {
int value; device->spec.channels = 8;
int frag_spec; } else if (device->spec.channels > 4) {
SDL_AudioFormat test_format; device->spec.channels = 4;
const SDL_AudioFormat *closefmts; } else if (device->spec.channels > 2) {
device->spec.channels = 2;
/* We don't care what the devname is...we'll try to open anything. */
/* ...but default to first name in the list... */
if (devname == NULL) {
devname = SDL_GetAudioDeviceName(0, iscapture);
if (devname == NULL) {
return SDL_SetError("No such audio device");
}
} }
/* Make sure fragment size stays a power of 2, or OSS fails. */ // Initialize all variables that we clean on shutdown
/* I don't know which of these are actually legal values, though... */ device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
if (_this->spec.channels > 8) { if (device->hidden == NULL) {
_this->spec.channels = 8;
} else if (_this->spec.channels > 4) {
_this->spec.channels = 4;
} else if (_this->spec.channels > 2) {
_this->spec.channels = 2;
}
/* Initialize all variables that we clean on shutdown */
_this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc(sizeof(*_this->hidden));
if (_this->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_zerop(_this->hidden);
/* Open the audio device */ // Open the audio device; we hardcode the device path in `device->name` for lack of better info, so use that.
_this->hidden->audio_fd = open(devname, flags | O_CLOEXEC, 0); const int flags = ((device->iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
if (_this->hidden->audio_fd < 0) { device->hidden->audio_fd = open(device->name, flags | O_CLOEXEC, 0);
return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno)); if (device->hidden->audio_fd < 0) {
return SDL_SetError("Couldn't open %s: %s", device->name, strerror(errno));
} }
/* Make the file descriptor use blocking i/o with fcntl() */ // Make the file descriptor use blocking i/o with fcntl()
{ {
long ctlflags; const long ctlflags = fcntl(device->hidden->audio_fd, F_GETFL) & ~O_NONBLOCK;
ctlflags = fcntl(_this->hidden->audio_fd, F_GETFL); if (fcntl(device->hidden->audio_fd, F_SETFL, ctlflags) < 0) {
ctlflags &= ~O_NONBLOCK;
if (fcntl(_this->hidden->audio_fd, F_SETFL, ctlflags) < 0) {
return SDL_SetError("Couldn't set audio blocking mode"); return SDL_SetError("Couldn't set audio blocking mode");
} }
} }
/* Get a list of supported hardware formats */ // Get a list of supported hardware formats
if (ioctl(_this->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) { int value;
if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
perror("SNDCTL_DSP_GETFMTS"); perror("SNDCTL_DSP_GETFMTS");
return SDL_SetError("Couldn't get audio format list"); return SDL_SetError("Couldn't get audio format list");
} }
/* Try for a closest match on audio format */ /* Try for a closest match on audio format */
closefmts = SDL_ClosestAudioFormats(_this->spec.format); int format = 0;
SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
while ((test_format = *(closefmts++)) != 0) { while ((test_format = *(closefmts++)) != 0) {
#ifdef DEBUG_AUDIO #ifdef DEBUG_AUDIO
fprintf(stderr, "Trying format 0x%4.4x\n", test_format); fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
@ -134,17 +121,7 @@ static int DSP_OpenDevice(SDL_AudioDevice *_this, const char *devname)
format = AFMT_S16_BE; format = AFMT_S16_BE;
} }
break; break;
#if 0
/*
* These formats are not used by any real life systems so they are not
* needed here.
*/
case SDL_AUDIO_S8:
if (value & AFMT_S8) {
format = AFMT_S8;
}
break;
#endif
default: default:
continue; continue;
} }
@ -153,40 +130,43 @@ static int DSP_OpenDevice(SDL_AudioDevice *_this, const char *devname)
if (format == 0) { if (format == 0) {
return SDL_SetError("Couldn't find any hardware audio formats"); return SDL_SetError("Couldn't find any hardware audio formats");
} }
_this->spec.format = test_format; device->spec.format = test_format;
/* Set the audio format */ // Set the audio format
value = format; value = format;
if ((ioctl(_this->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || if ((ioctl(device->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
(value != format)) { (value != format)) {
perror("SNDCTL_DSP_SETFMT"); perror("SNDCTL_DSP_SETFMT");
return SDL_SetError("Couldn't set audio format"); return SDL_SetError("Couldn't set audio format");
} }
/* Set the number of channels of output */ // Set the number of channels of output
value = _this->spec.channels; value = device->spec.channels;
if (ioctl(_this->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) { if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
perror("SNDCTL_DSP_CHANNELS"); perror("SNDCTL_DSP_CHANNELS");
return SDL_SetError("Cannot set the number of channels"); return SDL_SetError("Cannot set the number of channels");
} }
_this->spec.channels = value; device->spec.channels = value;
/* Set the DSP frequency */ // Set the DSP frequency
value = _this->spec.freq; value = device->spec.freq;
if (ioctl(_this->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) { if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
perror("SNDCTL_DSP_SPEED"); perror("SNDCTL_DSP_SPEED");
return SDL_SetError("Couldn't set audio frequency"); return SDL_SetError("Couldn't set audio frequency");
} }
_this->spec.freq = value; device->spec.freq = value;
/* Calculate the final parameters for this audio specification */ /* Calculate the final parameters for this audio specification */
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
/* Determine the power of two of the fragment size */ /* Determine the power of two of the fragment size
for (frag_spec = 0; (0x01U << frag_spec) < _this->spec.size; ++frag_spec) { Since apps don't control this in SDL3, and this driver only accepts 8, 16
} bit formats and 1, 2, 4, 8 channels, this should always be a power of 2 already. */
if ((0x01U << frag_spec) != _this->spec.size) { SDL_assert(SDL_powerof2(device->buffer_size) == device->buffer_size);
return SDL_SetError("Fragment size must be a power of two");
int frag_spec = 0;
while ((0x01U << frag_spec) < device->buffer_size) {
frag_spec++;
} }
frag_spec |= 0x00020000; /* two fragments, for low latency */ frag_spec |= 0x00020000; /* two fragments, for low latency */
@ -195,13 +175,13 @@ static int DSP_OpenDevice(SDL_AudioDevice *_this, const char *devname)
fprintf(stderr, "Requesting %d fragments of size %d\n", fprintf(stderr, "Requesting %d fragments of size %d\n",
(frag_spec >> 16), 1 << (frag_spec & 0xFFFF)); (frag_spec >> 16), 1 << (frag_spec & 0xFFFF));
#endif #endif
if (ioctl(_this->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) { if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) {
perror("SNDCTL_DSP_SETFRAGMENT"); perror("SNDCTL_DSP_SETFRAGMENT");
} }
#ifdef DEBUG_AUDIO #ifdef DEBUG_AUDIO
{ {
audio_buf_info info; audio_buf_info info;
ioctl(_this->hidden->audio_fd, SNDCTL_DSP_GETOSPACE, &info); ioctl(device->hidden->audio_fd, SNDCTL_DSP_GETOSPACE, &info);
fprintf(stderr, "fragments = %d\n", info.fragments); fprintf(stderr, "fragments = %d\n", info.fragments);
fprintf(stderr, "fragstotal = %d\n", info.fragstotal); fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
fprintf(stderr, "fragsize = %d\n", info.fragsize); fprintf(stderr, "fragsize = %d\n", info.fragsize);
@ -210,44 +190,67 @@ static int DSP_OpenDevice(SDL_AudioDevice *_this, const char *devname)
#endif #endif
/* Allocate mixing buffer */ /* Allocate mixing buffer */
if (!iscapture) { if (!device->iscapture) {
_this->hidden->mixlen = _this->spec.size; device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->hidden->mixlen); if (device->hidden->mixbuf == NULL) {
if (_this->hidden->mixbuf == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->spec.size); SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
} }
/* We're ready to rock and roll. :-) */ return 0; // We're ready to rock and roll. :-)
return 0;
} }
static void DSP_PlayDevice(SDL_AudioDevice *_this) static void DSP_WaitDevice(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *h = _this->hidden; const unsigned long ioctlreq = device->iscapture ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE;
if (write(h->audio_fd, h->mixbuf, h->mixlen) == -1) { struct SDL_PrivateAudioData *h = device->hidden;
while (!SDL_AtomicGet(&device->shutdown)) {
audio_buf_info info;
const int rc = ioctl(h->audio_fd, ioctlreq, &info);
if (rc < 0) {
if (errno == EAGAIN) {
continue;
}
// Hmm, not much we can do - abort
fprintf(stderr, "dsp WaitDevice ioctl failed (unrecoverable): %s\n", strerror(errno));
SDL_AudioDeviceDisconnected(device);
return;
} else if (info.bytes < device->buffer_size) {
SDL_Delay(10);
} else {
break; // ready to go!
}
}
}
static void 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"); perror("Audio write");
SDL_OpenedAudioDeviceDisconnected(_this); SDL_AudioDeviceDisconnected(device);
return;
} }
#ifdef DEBUG_AUDIO #ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", h->mixlen); fprintf(stderr, "Wrote %d bytes of audio data\n", h->mixlen);
#endif #endif
} }
static Uint8 *DSP_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *DSP_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
return _this->hidden->mixbuf; return device->hidden->mixbuf;
} }
static int DSP_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) static int DSP_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{ {
return (int)read(_this->hidden->audio_fd, buffer, buflen); return (int)read(device->hidden->audio_fd, buffer, buflen);
} }
static void DSP_FlushCapture(SDL_AudioDevice *_this) static void DSP_FlushCapture(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *h = _this->hidden; struct SDL_PrivateAudioData *h = device->hidden;
audio_buf_info info; audio_buf_info info;
if (ioctl(h->audio_fd, SNDCTL_DSP_GETISPACE, &info) == 0) { if (ioctl(h->audio_fd, SNDCTL_DSP_GETISPACE, &info) == 0) {
while (info.bytes > 0) { while (info.bytes > 0) {
@ -263,17 +266,17 @@ static void DSP_FlushCapture(SDL_AudioDevice *_this)
} }
static SDL_bool InitTimeDevicesExist = SDL_FALSE; static SDL_bool InitTimeDevicesExist = SDL_FALSE;
static int look_for_devices_test(int fd) static SDL_bool look_for_devices_test(int fd)
{ {
InitTimeDevicesExist = SDL_TRUE; /* note that _something_ exists. */ InitTimeDevicesExist = SDL_TRUE; /* note that _something_ exists. */
/* Don't add to the device list, we're just seeing if any devices exist. */ /* Don't add to the device list, we're just seeing if any devices exist. */
return 0; return SDL_FALSE;
} }
static SDL_bool DSP_Init(SDL_AudioDriverImpl *impl) static SDL_bool DSP_Init(SDL_AudioDriverImpl *impl)
{ {
InitTimeDevicesExist = SDL_FALSE; InitTimeDevicesExist = SDL_FALSE;
SDL_EnumUnixAudioDevices(0, look_for_devices_test); SDL_EnumUnixAudioDevices(SDL_FALSE, look_for_devices_test);
if (!InitTimeDevicesExist) { if (!InitTimeDevicesExist) {
SDL_SetError("dsp: No such audio device"); SDL_SetError("dsp: No such audio device");
return SDL_FALSE; /* maybe try a different backend. */ return SDL_FALSE; /* maybe try a different backend. */
@ -282,9 +285,11 @@ static SDL_bool DSP_Init(SDL_AudioDriverImpl *impl)
/* Set the function pointers */ /* Set the function pointers */
impl->DetectDevices = DSP_DetectDevices; impl->DetectDevices = DSP_DetectDevices;
impl->OpenDevice = DSP_OpenDevice; impl->OpenDevice = DSP_OpenDevice;
impl->WaitDevice = DSP_WaitDevice;
impl->PlayDevice = DSP_PlayDevice; impl->PlayDevice = DSP_PlayDevice;
impl->GetDeviceBuf = DSP_GetDeviceBuf; impl->GetDeviceBuf = DSP_GetDeviceBuf;
impl->CloseDevice = DSP_CloseDevice; impl->CloseDevice = DSP_CloseDevice;
impl->WaitCaptureDevice = DSP_WaitDevice;
impl->CaptureFromDevice = DSP_CaptureFromDevice; impl->CaptureFromDevice = DSP_CaptureFromDevice;
impl->FlushCapture = DSP_FlushCapture; impl->FlushCapture = DSP_FlushCapture;
@ -295,7 +300,7 @@ static SDL_bool DSP_Init(SDL_AudioDriverImpl *impl)
} }
AudioBootStrap DSP_bootstrap = { AudioBootStrap DSP_bootstrap = {
"dsp", "OSS /dev/dsp standard audio", DSP_Init, SDL_FALSE "dsp", "Open Sound System (/dev/dsp)", DSP_Init, SDL_FALSE
}; };
#endif /* SDL_AUDIO_DRIVER_OSS */ #endif /* SDL_AUDIO_DRIVER_OSS */

View File

@ -32,8 +32,6 @@ struct SDL_PrivateAudioData
/* Raw mixing buffer */ /* Raw mixing buffer */
Uint8 *mixbuf; Uint8 *mixbuf;
int mixlen;
}; };
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
#endif /* SDL_dspaudio_h_ */ #endif /* SDL_dspaudio_h_ */

View File

@ -20,39 +20,75 @@
*/ */
#include "SDL_internal.h" #include "SDL_internal.h"
/* Output audio to nowhere... */ // Output audio to nowhere...
#include "../SDL_audio_c.h" #include "../SDL_audio_c.h"
#include "SDL_dummyaudio.h" #include "SDL_dummyaudio.h"
static int DUMMYAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) // !!! FIXME: this should be an SDL hint, not an environment variable.
{ #define DUMMYENVR_IODELAY "SDL_DUMMYAUDIODELAY"
_this->hidden = (void *)0x1; /* just something non-NULL */
return 0; /* always succeeds. */ static void DUMMYAUDIO_WaitDevice(SDL_AudioDevice *device)
{
SDL_Delay(device->hidden->io_delay);
} }
static int DUMMYAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) static int DUMMYAUDIO_OpenDevice(SDL_AudioDevice *device)
{ {
/* Delay to make this sort of simulate real audio input. */ const char *envr = SDL_getenv(DUMMYENVR_IODELAY);
SDL_Delay((_this->spec.samples * 1000) / _this->spec.freq);
/* always return a full buffer of silence. */ device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
SDL_memset(buffer, _this->spec.silence, buflen); if (!device->hidden) {
return SDL_OutOfMemory();
}
if (!device->iscapture) {
device->hidden->mixbuf = (Uint8 *) SDL_malloc(device->buffer_size);
if (!device->hidden->mixbuf) {
return SDL_OutOfMemory();
}
}
device->hidden->io_delay = (Uint32) (envr ? SDL_atoi(envr) : ((device->sample_frames * 1000) / device->spec.freq));
return 0; // we're good; don't change reported device format.
}
static void DUMMYAUDIO_CloseDevice(SDL_AudioDevice *device)
{
if (device->hidden) {
SDL_free(device->hidden->mixbuf);
SDL_free(device->hidden);
device->hidden = NULL;
}
}
static Uint8 *DUMMYAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{
return device->hidden->mixbuf;
}
static int DUMMYAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{
// always return a full buffer of silence.
SDL_memset(buffer, device->silence_value, buflen);
return buflen; return buflen;
} }
static SDL_bool DUMMYAUDIO_Init(SDL_AudioDriverImpl *impl) static SDL_bool DUMMYAUDIO_Init(SDL_AudioDriverImpl *impl)
{ {
/* Set the function pointers */
impl->OpenDevice = DUMMYAUDIO_OpenDevice; impl->OpenDevice = DUMMYAUDIO_OpenDevice;
impl->CloseDevice = DUMMYAUDIO_CloseDevice;
impl->WaitDevice = DUMMYAUDIO_WaitDevice;
impl->GetDeviceBuf = DUMMYAUDIO_GetDeviceBuf;
impl->WaitCaptureDevice = DUMMYAUDIO_WaitDevice;
impl->CaptureFromDevice = DUMMYAUDIO_CaptureFromDevice; impl->CaptureFromDevice = DUMMYAUDIO_CaptureFromDevice;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE; impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE; impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
impl->HasCaptureSupport = SDL_TRUE; impl->HasCaptureSupport = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */ return SDL_TRUE;
} }
AudioBootStrap DUMMYAUDIO_bootstrap = { AudioBootStrap DUMMYAUDIO_bootstrap = {

View File

@ -27,11 +27,8 @@
struct SDL_PrivateAudioData struct SDL_PrivateAudioData
{ {
/* The file descriptor for the audio device */ Uint8 *mixbuf; // The file descriptor for the audio device
Uint8 *mixbuf; Uint32 io_delay; // miliseconds to sleep in WaitDevice.
Uint32 mixlen;
Uint32 write_delay;
Uint32 initial_calls;
}; };
#endif /* SDL_dummyaudio_h_ */ #endif // SDL_dummyaudio_h_

View File

@ -27,15 +27,18 @@
#include <emscripten/emscripten.h> #include <emscripten/emscripten.h>
/* !!! FIXME: this currently expects that the audio callback runs in the main thread, // just turn off clang-format for this whole file, this INDENT_OFF stuff on
!!! FIXME: in intervals when the application isn't running, but that may not be // each EM_ASM section is ugly.
!!! FIXME: true always once pthread support becomes widespread. Revisit this code
!!! FIXME: at some point and see what needs to be done for that! */
static void FeedAudioDevice(SDL_AudioDevice *_this, const void *buf, const int buflen)
{
const int framelen = (SDL_AUDIO_BITSIZE(_this->spec.format) / 8) * _this->spec.channels;
/* *INDENT-OFF* */ /* clang-format off */ /* *INDENT-OFF* */ /* clang-format off */
static Uint8 *EMSCRIPTENAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{
return device->hidden->mixbuf;
}
static void EMSCRIPTENAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
{
const int framelen = (SDL_AUDIO_BITSIZE(device->spec.format) / 8) * device->spec.channels;
MAIN_THREAD_EM_ASM({ MAIN_THREAD_EM_ASM({
var SDL3 = Module['SDL3']; var SDL3 = Module['SDL3'];
var numChannels = SDL3.audio.currentOutputBuffer['numberOfChannels']; var numChannels = SDL3.audio.currentOutputBuffer['numberOfChannels'];
@ -46,65 +49,25 @@ static void FeedAudioDevice(SDL_AudioDevice *_this, const void *buf, const int b
} }
for (var j = 0; j < $1; ++j) { for (var j = 0; j < $1; ++j) {
channelData[j] = HEAPF32[$0 + ((j*numChannels + c) << 2) >> 2]; /* !!! FIXME: why are these shifts here? */ channelData[j] = HEAPF32[$0 + ((j*numChannels + c) << 2) >> 2]; // !!! FIXME: why are these shifts here?
} }
} }
}, buf, buflen / framelen); }, buffer, buffer_size / framelen);
/* *INDENT-ON* */ /* clang-format on */
} }
static void HandleAudioProcess(SDL_AudioDevice *_this) static void HandleAudioProcess(SDL_AudioDevice *device) // this fires when the main thread is idle.
{ {
SDL_AudioCallback callback = _this->callbackspec.callback; SDL_OutputAudioThreadIterate(device);
const int stream_len = _this->callbackspec.size;
/* Only do something if audio is enabled */
if (!SDL_AtomicGet(&_this->enabled) || SDL_AtomicGet(&_this->paused)) {
if (_this->stream) {
SDL_ClearAudioStream(_this->stream);
} }
SDL_memset(_this->work_buffer, _this->spec.silence, _this->spec.size);
FeedAudioDevice(_this, _this->work_buffer, _this->spec.size);
return;
}
if (_this->stream == NULL) { /* no conversion necessary. */ static void EMSCRIPTENAUDIO_FlushCapture(SDL_AudioDevice *device)
SDL_assert(_this->spec.size == stream_len);
callback(_this->callbackspec.userdata, _this->work_buffer, stream_len);
} else { /* streaming/converting */
int got;
while (SDL_GetAudioStreamAvailable(_this->stream) < ((int)_this->spec.size)) {
callback(_this->callbackspec.userdata, _this->work_buffer, stream_len);
if (SDL_PutAudioStreamData(_this->stream, _this->work_buffer, stream_len) == -1) {
SDL_ClearAudioStream(_this->stream);
SDL_AtomicSet(&_this->enabled, 0);
break;
}
}
got = SDL_GetAudioStreamData(_this->stream, _this->work_buffer, _this->spec.size);
SDL_assert((got < 0) || (got == _this->spec.size));
if (got != _this->spec.size) {
SDL_memset(_this->work_buffer, _this->spec.silence, _this->spec.size);
}
}
FeedAudioDevice(_this, _this->work_buffer, _this->spec.size);
}
static void HandleCaptureProcess(SDL_AudioDevice *_this)
{ {
SDL_AudioCallback callback = _this->callbackspec.callback; // Do nothing, the new data will just be dropped.
const int stream_len = _this->callbackspec.size;
/* Only do something if audio is enabled */
if (!SDL_AtomicGet(&_this->enabled) || SDL_AtomicGet(&_this->paused)) {
SDL_ClearAudioStream(_this->stream);
return;
} }
/* *INDENT-OFF* */ /* clang-format off */ static int EMSCRIPTENAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{
MAIN_THREAD_EM_ASM({ MAIN_THREAD_EM_ASM({
var SDL3 = Module['SDL3']; var SDL3 = Module['SDL3'];
var numChannels = SDL3.capture.currentCaptureBuffer.numberOfChannels; var numChannels = SDL3.capture.currentCaptureBuffer.numberOfChannels;
@ -114,7 +77,7 @@ static void HandleCaptureProcess(SDL_AudioDevice *_this)
throw 'Web Audio capture buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!'; throw 'Web Audio capture buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!';
} }
if (numChannels == 1) { /* fastpath this a little for the common (mono) case. */ if (numChannels == 1) { // fastpath this a little for the common (mono) case.
for (var j = 0; j < $1; ++j) { for (var j = 0; j < $1; ++j) {
setValue($0 + (j * 4), channelData[j], 'float'); setValue($0 + (j * 4), channelData[j], 'float');
} }
@ -124,33 +87,22 @@ static void HandleCaptureProcess(SDL_AudioDevice *_this)
} }
} }
} }
}, _this->work_buffer, (_this->spec.size / sizeof(float)) / _this->spec.channels); }, buffer, (buflen / sizeof(float)) / device->spec.channels);
/* *INDENT-ON* */ /* clang-format on */
/* okay, we've got an interleaved float32 array in C now. */ return buflen;
if (_this->stream == NULL) { /* no conversion necessary. */
SDL_assert(_this->spec.size == stream_len);
callback(_this->callbackspec.userdata, _this->work_buffer, stream_len);
} else { /* streaming/converting */
if (SDL_PutAudioStreamData(_this->stream, _this->work_buffer, _this->spec.size) == -1) {
SDL_AtomicSet(&_this->enabled, 0);
} }
while (SDL_GetAudioStreamAvailable(_this->stream) >= stream_len) { static void HandleCaptureProcess(SDL_AudioDevice *device) // this fires when the main thread is idle.
const int got = SDL_GetAudioStreamData(_this->stream, _this->work_buffer, stream_len);
SDL_assert((got < 0) || (got == stream_len));
if (got != stream_len) {
SDL_memset(_this->work_buffer, _this->callbackspec.silence, stream_len);
}
callback(_this->callbackspec.userdata, _this->work_buffer, stream_len); /* Send it to the app. */
}
}
}
static void EMSCRIPTENAUDIO_CloseDevice(SDL_AudioDevice *_this)
{ {
/* *INDENT-OFF* */ /* clang-format off */ SDL_CaptureAudioThreadIterate(device);
}
static void EMSCRIPTENAUDIO_CloseDevice(SDL_AudioDevice *device)
{
if (!device->hidden) {
return;
}
MAIN_THREAD_EM_ASM({ MAIN_THREAD_EM_ASM({
var SDL3 = Module['SDL3']; var SDL3 = Module['SDL3'];
if ($0) { if ($0) {
@ -188,29 +140,23 @@ static void EMSCRIPTENAUDIO_CloseDevice(SDL_AudioDevice *_this)
SDL3.audioContext.close(); SDL3.audioContext.close();
SDL3.audioContext = undefined; SDL3.audioContext = undefined;
} }
}, _this->iscapture); }, device->iscapture);
/* *INDENT-ON* */ /* clang-format on */
#if 0 /* !!! FIXME: currently not used. Can we move some stuff off the SDL3 namespace? --ryan. */ SDL_free(device->hidden->mixbuf);
SDL_free(_this->hidden); SDL_free(device->hidden);
#endif device->hidden = NULL;
SDL_AudioThreadFinalize(device);
} }
EM_JS_DEPS(sdlaudio, "$autoResumeAudioContext,$dynCall"); EM_JS_DEPS(sdlaudio, "$autoResumeAudioContext,$dynCall");
static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device)
{ {
SDL_AudioFormat test_format; // based on parts of library_sdl.js
const SDL_AudioFormat *closefmts;
SDL_bool iscapture = _this->iscapture;
int result;
/* based on parts of library_sdl.js */ // create context
const int result = MAIN_THREAD_EM_ASM_INT({
/* *INDENT-OFF* */ /* clang-format off */
/* create context */
result = MAIN_THREAD_EM_ASM_INT({
if (typeof(Module['SDL3']) === 'undefined') { if (typeof(Module['SDL3']) === 'undefined') {
Module['SDL3'] = {}; Module['SDL3'] = {};
} }
@ -232,57 +178,41 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devnam
} }
} }
return SDL3.audioContext === undefined ? -1 : 0; return SDL3.audioContext === undefined ? -1 : 0;
}, iscapture); }, device->iscapture);
/* *INDENT-ON* */ /* clang-format on */
if (result < 0) { if (result < 0) {
return SDL_SetError("Web Audio API is not available!"); return SDL_SetError("Web Audio API is not available!");
} }
closefmts = SDL_ClosestAudioFormats(_this->spec.format); device->spec.format = SDL_AUDIO_F32; // web audio only supports floats
while ((test_format = *(closefmts++)) != 0) {
switch (test_format) {
case SDL_AUDIO_F32: /* web audio only supports floats */
break;
default:
continue;
}
break;
}
if (!test_format) { // Initialize all variables that we clean on shutdown
/* Didn't find a compatible format :( */ device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
return SDL_SetError("%s: Unsupported audio format", "emscripten"); if (device->hidden == NULL) {
}
_this->spec.format = test_format;
/* Initialize all variables that we clean on shutdown */
#if 0 /* !!! FIXME: currently not used. Can we move some stuff off the SDL3 namespace? --ryan. */
_this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*_this->hidden));
if (_this->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_zerop(_this->hidden);
#endif
_this->hidden = (struct SDL_PrivateAudioData *)0x1;
/* limit to native freq */ // limit to native freq
_this->spec.freq = EM_ASM_INT({ device->spec.freq = EM_ASM_INT({ return Module['SDL3'].audioContext.sampleRate; });
var SDL3 = Module['SDL3'];
return SDL3.audioContext.sampleRate;
});
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
/* *INDENT-OFF* */ /* clang-format off */ if (!device->iscapture) {
if (iscapture) { device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
if (device->hidden->mixbuf == NULL) {
return SDL_OutOfMemory();
}
SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
}
if (device->iscapture) {
/* The idea is to take the capture media stream, hook it up to an /* The idea is to take the capture media stream, hook it up to an
audio graph where we can pass it through a ScriptProcessorNode audio graph where we can pass it through a ScriptProcessorNode
to access the raw PCM samples and push them to the SDL app's to access the raw PCM samples and push them to the SDL app's
callback. From there, we "process" the audio data into silence callback. From there, we "process" the audio data into silence
and forget about it. */ and forget about it.
/* This should, strictly speaking, use MediaRecorder for capture, but This should, strictly speaking, use MediaRecorder for capture, but
this API is cleaner to use and better supported, and fires a this API is cleaner to use and better supported, and fires a
callback whenever there's enough data to fire down into the app. callback whenever there's enough data to fire down into the app.
The downside is that we are spending CPU time silencing a buffer The downside is that we are spending CPU time silencing a buffer
@ -317,7 +247,7 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devnam
//console.log('SDL audio capture: we DO NOT have a microphone! (' + error.name + ')...leaving silence callback running.'); //console.log('SDL audio capture: we DO NOT have a microphone! (' + error.name + ')...leaving silence callback running.');
}; };
/* we write silence to the audio callback until the microphone is available (user approves use, etc). */ // we write silence to the audio callback until the microphone is available (user approves use, etc).
SDL3.capture.silenceBuffer = SDL3.audioContext.createBuffer($0, $1, SDL3.audioContext.sampleRate); SDL3.capture.silenceBuffer = SDL3.audioContext.createBuffer($0, $1, SDL3.audioContext.sampleRate);
SDL3.capture.silenceBuffer.getChannelData(0).fill(0.0); SDL3.capture.silenceBuffer.getChannelData(0).fill(0.0);
var silence_callback = function() { var silence_callback = function() {
@ -332,9 +262,9 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devnam
} else if (navigator.webkitGetUserMedia !== undefined) { } else if (navigator.webkitGetUserMedia !== undefined) {
navigator.webkitGetUserMedia({ audio: true, video: false }, have_microphone, no_microphone); navigator.webkitGetUserMedia({ audio: true, video: false }, have_microphone, no_microphone);
} }
}, _this->spec.channels, _this->spec.samples, HandleCaptureProcess, _this); }, device->spec.channels, device->sample_frames, HandleCaptureProcess, device);
} else { } else {
/* setup a ScriptProcessorNode */ // setup a ScriptProcessorNode
MAIN_THREAD_EM_ASM({ MAIN_THREAD_EM_ASM({
var SDL3 = Module['SDL3']; var SDL3 = Module['SDL3'];
SDL3.audio.scriptProcessorNode = SDL3.audioContext['createScriptProcessor']($1, 0, $0); SDL3.audio.scriptProcessorNode = SDL3.audioContext['createScriptProcessor']($1, 0, $0);
@ -344,33 +274,29 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devnam
dynCall('vi', $2, [$3]); dynCall('vi', $2, [$3]);
}; };
SDL3.audio.scriptProcessorNode['connect'](SDL3.audioContext['destination']); SDL3.audio.scriptProcessorNode['connect'](SDL3.audioContext['destination']);
}, _this->spec.channels, _this->spec.samples, HandleAudioProcess, _this); }, device->spec.channels, device->sample_frames, HandleAudioProcess, device);
} }
/* *INDENT-ON* */ /* clang-format on */
return 0; return 0;
} }
static void EMSCRIPTENAUDIO_LockOrUnlockDeviceWithNoMixerLock(SDL_AudioDevice *device)
{
}
static SDL_bool EMSCRIPTENAUDIO_Init(SDL_AudioDriverImpl *impl) static SDL_bool EMSCRIPTENAUDIO_Init(SDL_AudioDriverImpl *impl)
{ {
SDL_bool available, capture_available; SDL_bool available, capture_available;
/* Set the function pointers */
impl->OpenDevice = EMSCRIPTENAUDIO_OpenDevice; impl->OpenDevice = EMSCRIPTENAUDIO_OpenDevice;
impl->CloseDevice = EMSCRIPTENAUDIO_CloseDevice; impl->CloseDevice = EMSCRIPTENAUDIO_CloseDevice;
impl->GetDeviceBuf = EMSCRIPTENAUDIO_GetDeviceBuf;
impl->PlayDevice = EMSCRIPTENAUDIO_PlayDevice;
impl->FlushCapture = EMSCRIPTENAUDIO_FlushCapture;
impl->CaptureFromDevice = EMSCRIPTENAUDIO_CaptureFromDevice;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE; impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
/* no threads here */ // technically, this is just runs in idle time in the main thread, but it's close enough to a "thread" for our purposes.
impl->LockDevice = impl->UnlockDevice = EMSCRIPTENAUDIO_LockOrUnlockDeviceWithNoMixerLock;
impl->ProvidesOwnCallbackThread = SDL_TRUE; impl->ProvidesOwnCallbackThread = SDL_TRUE;
/* *INDENT-OFF* */ /* clang-format off */ // check availability
/* check availability */
available = MAIN_THREAD_EM_ASM_INT({ available = MAIN_THREAD_EM_ASM_INT({
if (typeof(AudioContext) !== 'undefined') { if (typeof(AudioContext) !== 'undefined') {
return true; return true;
@ -378,14 +304,12 @@ static SDL_bool EMSCRIPTENAUDIO_Init(SDL_AudioDriverImpl *impl)
return true; return true;
} }
return false; return false;
}); }) ? SDL_TRUE : SDL_FALSE;
/* *INDENT-ON* */ /* clang-format on */
if (!available) { if (!available) {
SDL_SetError("No audio context available"); SDL_SetError("No audio context available");
} }
/* *INDENT-OFF* */ /* clang-format off */
capture_available = available && MAIN_THREAD_EM_ASM_INT({ capture_available = available && MAIN_THREAD_EM_ASM_INT({
if ((typeof(navigator.mediaDevices) !== 'undefined') && (typeof(navigator.mediaDevices.getUserMedia) !== 'undefined')) { if ((typeof(navigator.mediaDevices) !== 'undefined') && (typeof(navigator.mediaDevices.getUserMedia) !== 'undefined')) {
return true; return true;
@ -393,8 +317,7 @@ static SDL_bool EMSCRIPTENAUDIO_Init(SDL_AudioDriverImpl *impl)
return true; return true;
} }
return false; return false;
}); }) ? SDL_TRUE : SDL_FALSE;
/* *INDENT-ON* */ /* clang-format on */
impl->HasCaptureSupport = capture_available ? SDL_TRUE : SDL_FALSE; impl->HasCaptureSupport = capture_available ? SDL_TRUE : SDL_FALSE;
impl->OnlyHasDefaultCaptureDevice = capture_available ? SDL_TRUE : SDL_FALSE; impl->OnlyHasDefaultCaptureDevice = capture_available ? SDL_TRUE : SDL_FALSE;
@ -406,4 +329,6 @@ AudioBootStrap EMSCRIPTENAUDIO_bootstrap = {
"emscripten", "SDL emscripten audio driver", EMSCRIPTENAUDIO_Init, SDL_FALSE "emscripten", "SDL emscripten audio driver", EMSCRIPTENAUDIO_Init, SDL_FALSE
}; };
#endif /* SDL_AUDIO_DRIVER_EMSCRIPTEN */ /* *INDENT-ON* */ /* clang-format on */
#endif // SDL_AUDIO_DRIVER_EMSCRIPTEN

View File

@ -27,7 +27,7 @@
struct SDL_PrivateAudioData struct SDL_PrivateAudioData
{ {
int unused; Uint8 *mixbuf;
}; };
#endif /* SDL_emscriptenaudio_h_ */ #endif /* SDL_emscriptenaudio_h_ */

View File

@ -22,7 +22,7 @@
#ifdef SDL_AUDIO_DRIVER_HAIKU #ifdef SDL_AUDIO_DRIVER_HAIKU
/* Allow access to the audio stream on Haiku */ // Allow access to the audio stream on Haiku
#include <SoundPlayer.h> #include <SoundPlayer.h>
#include <signal.h> #include <signal.h>
@ -38,58 +38,45 @@ extern "C"
} }
static Uint8 *HAIKUAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
/* !!! FIXME: have the callback call the higher level to avoid code dupe. */
/* The Haiku callback for handling the audio buffer */
static void FillSound(void *device, void *stream, size_t len,
const media_raw_audio_format & format)
{ {
SDL_AudioDevice *audio = (SDL_AudioDevice *) device; SDL_assert(device->hidden->current_buffer != NULL);
SDL_AudioCallback callback = audio->callbackspec.callback; SDL_assert(device->hidden->current_buffer_len > 0);
*buffer_size = device->hidden->current_buffer_len;
SDL_LockMutex(audio->mixer_lock); return device->hidden->current_buffer;
/* Only do something if audio is enabled */
if (!SDL_AtomicGet(&audio->enabled) || SDL_AtomicGet(&audio->paused)) {
if (audio->stream) {
SDL_ClearAudioStream(audio->stream);
}
SDL_memset(stream, audio->spec.silence, len);
} else {
SDL_assert(audio->spec.size == len);
if (audio->stream == NULL) { /* no conversion necessary. */
callback(audio->callbackspec.userdata, (Uint8 *) stream, len);
} else { /* streaming/converting */
const int stream_len = audio->callbackspec.size;
const int ilen = (int) len;
while (SDL_GetAudioStreamAvailable(audio->stream) < ilen) {
callback(audio->callbackspec.userdata, audio->work_buffer, stream_len);
if (SDL_PutAudioStreamData(audio->stream, audio->work_buffer, stream_len) == -1) {
SDL_ClearAudioStream(audio->stream);
SDL_AtomicSet(&audio->enabled, 0);
break;
}
} }
const int got = SDL_GetAudioStreamData(audio->stream, stream, ilen); static void HAIKUAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
SDL_assert((got < 0) || (got == ilen));
if (got != ilen) {
SDL_memset(stream, audio->spec.silence, len);
}
}
}
SDL_UnlockMutex(audio->mixer_lock);
}
static void HAIKUAUDIO_CloseDevice(SDL_AudioDevice *_this)
{ {
if (_this->hidden->audio_obj) { // We already wrote our output right into the BSoundPlayer's callback's stream. Just clean up our stuff.
_this->hidden->audio_obj->Stop(); SDL_assert(device->hidden->current_buffer != NULL);
delete _this->hidden->audio_obj; SDL_assert(device->hidden->current_buffer_len > 0);
device->hidden->current_buffer = NULL;
device->hidden->current_buffer_len = 0;
}
// The Haiku callback for handling the audio buffer
static void FillSound(void *data, void *stream, size_t len, const media_raw_audio_format & format)
{
SDL_AudioDevice *device = (SDL_AudioDevice *)data;
SDL_assert(device->hidden->current_buffer == NULL);
SDL_assert(device->hidden->current_buffer_len == 0);
device->hidden->current_buffer = (Uint8 *) stream;
device->hidden->current_buffer_len = (int) len;
SDL_OutputAudioThreadIterate(device);
}
static void HAIKUAUDIO_CloseDevice(SDL_AudioDevice *device)
{
if (device->hidden) {
if (device->hidden->audio_obj) {
device->hidden->audio_obj->Stop();
delete device->hidden->audio_obj;
}
delete device->hidden;
device->hidden = NULL;
SDL_AudioThreadFinalize(device);
} }
delete _this->hidden;
} }
@ -115,26 +102,24 @@ static inline void UnmaskSignals(sigset_t * omask)
} }
static int HAIKUAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int HAIKUAUDIO_OpenDevice(SDL_AudioDevice *device)
{ {
media_raw_audio_format format; // Initialize all variables that we clean on shutdown
SDL_AudioFormat test_format; device->hidden = new SDL_PrivateAudioData;
const SDL_AudioFormat *closefmts; if (device->hidden == NULL) {
/* Initialize all variables that we clean on shutdown */
_this->hidden = new SDL_PrivateAudioData;
if (_this->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_zerop(_this->hidden); SDL_zerop(device->hidden);
/* Parse the audio format and fill the Be raw audio format */ // Parse the audio format and fill the Be raw audio format
media_raw_audio_format format;
SDL_zero(format); SDL_zero(format);
format.byte_order = B_MEDIA_LITTLE_ENDIAN; format.byte_order = B_MEDIA_LITTLE_ENDIAN;
format.frame_rate = (float) _this->spec.freq; format.frame_rate = (float) device->spec.freq;
format.channel_count = _this->spec.channels; /* !!! FIXME: support > 2? */ format.channel_count = device->spec.channels; // !!! FIXME: support > 2?
closefmts = SDL_ClosestAudioFormats(_this->spec.format); SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
while ((test_format = *(closefmts++)) != 0) { while ((test_format = *(closefmts++)) != 0) {
switch (test_format) { switch (test_format) {
case SDL_AUDIO_S8: case SDL_AUDIO_S8:
@ -178,31 +163,30 @@ static int HAIKUAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
break; break;
} }
if (!test_format) { /* shouldn't happen, but just in case... */ if (!test_format) { // shouldn't happen, but just in case...
return SDL_SetError("%s: Unsupported audio format", "haiku"); return SDL_SetError("HAIKU: Unsupported audio format");
} }
_this->spec.format = test_format; device->spec.format = test_format;
/* Calculate the final parameters for this audio specification */ // Calculate the final parameters for this audio specification
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
format.buffer_size = _this->spec.size; format.buffer_size = device->buffer_size;
/* Subscribe to the audio stream (creates a new thread) */ // Subscribe to the audio stream (creates a new thread)
sigset_t omask; sigset_t omask;
MaskSignals(&omask); MaskSignals(&omask);
_this->hidden->audio_obj = new BSoundPlayer(&format, "SDL Audio", device->hidden->audio_obj = new BSoundPlayer(&format, "SDL Audio",
FillSound, NULL, _this); FillSound, NULL, device);
UnmaskSignals(&omask); UnmaskSignals(&omask);
if (_this->hidden->audio_obj->Start() == B_NO_ERROR) { if (device->hidden->audio_obj->Start() == B_NO_ERROR) {
_this->hidden->audio_obj->SetHasData(true); device->hidden->audio_obj->SetHasData(true);
} else { } else {
return SDL_SetError("Unable to start Be audio"); return SDL_SetError("Unable to start Haiku audio");
} }
/* We're running! */ return 0; // We're running!
return 0;
} }
static void HAIKUAUDIO_Deinitialize(void) static void HAIKUAUDIO_Deinitialize(void)
@ -212,27 +196,27 @@ static void HAIKUAUDIO_Deinitialize(void)
static SDL_bool HAIKUAUDIO_Init(SDL_AudioDriverImpl *impl) static SDL_bool HAIKUAUDIO_Init(SDL_AudioDriverImpl *impl)
{ {
/* Initialize the Be Application, if it's not already started */
if (SDL_InitBeApp() < 0) { if (SDL_InitBeApp() < 0) {
return SDL_FALSE; return SDL_FALSE;
} }
/* Set the function pointers */ // Set the function pointers
impl->OpenDevice = HAIKUAUDIO_OpenDevice; impl->OpenDevice = HAIKUAUDIO_OpenDevice;
impl->GetDeviceBuf = HAIKUAUDIO_GetDeviceBuf;
impl->PlayDevice = HAIKUAUDIO_PlayDevice;
impl->CloseDevice = HAIKUAUDIO_CloseDevice; impl->CloseDevice = HAIKUAUDIO_CloseDevice;
impl->Deinitialize = HAIKUAUDIO_Deinitialize; impl->Deinitialize = HAIKUAUDIO_Deinitialize;
impl->ProvidesOwnCallbackThread = SDL_TRUE; impl->ProvidesOwnCallbackThread = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE; impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */ return SDL_TRUE;
} }
extern "C"
{ extern "C" { extern AudioBootStrap HAIKUAUDIO_bootstrap; }
extern AudioBootStrap HAIKUAUDIO_bootstrap;
}
AudioBootStrap HAIKUAUDIO_bootstrap = { AudioBootStrap HAIKUAUDIO_bootstrap = {
"haiku", "Haiku BSoundPlayer", HAIKUAUDIO_Init, SDL_FALSE "haiku", "Haiku BSoundPlayer", HAIKUAUDIO_Init, SDL_FALSE
}; };
#endif /* SDL_AUDIO_DRIVER_HAIKU */ #endif // SDL_AUDIO_DRIVER_HAIKU

View File

@ -28,6 +28,8 @@
struct SDL_PrivateAudioData struct SDL_PrivateAudioData
{ {
BSoundPlayer *audio_obj; BSoundPlayer *audio_obj;
Uint8 *current_buffer;
int current_buffer_len;
}; };
#endif /* SDL_haikuaudio_h_ */ #endif /* SDL_haikuaudio_h_ */

View File

@ -135,9 +135,7 @@ static int load_jack_syms(void)
static void jackShutdownCallback(void *arg) /* JACK went away; device is lost. */ static void jackShutdownCallback(void *arg) /* JACK went away; device is lost. */
{ {
SDL_AudioDevice *_this = (SDL_AudioDevice *)arg; SDL_AudioDeviceDisconnected((SDL_AudioDevice *)arg);
SDL_OpenedAudioDeviceDisconnected(_this);
SDL_PostSemaphore(_this->hidden->iosem); /* unblock the SDL thread. */
} }
// !!! FIXME: implement and register these! // !!! FIXME: implement and register these!
@ -146,124 +144,117 @@ static void jackShutdownCallback(void *arg) /* JACK went away; device is lost. *
static int jackProcessPlaybackCallback(jack_nframes_t nframes, void *arg) static int jackProcessPlaybackCallback(jack_nframes_t nframes, void *arg)
{ {
SDL_AudioDevice *_this = (SDL_AudioDevice *)arg; SDL_assert(nframes == ((SDL_AudioDevice *)arg)->sample_frames);
jack_port_t **ports = _this->hidden->sdlports; SDL_OutputAudioThreadIterate((SDL_AudioDevice *)arg);
const int total_channels = _this->spec.channels; return 0;
const int total_frames = _this->spec.samples;
int channelsi;
if (!SDL_AtomicGet(&_this->enabled)) {
/* silence the buffer to avoid repeats and corruption. */
SDL_memset(_this->hidden->iobuffer, '\0', _this->spec.size);
} }
for (channelsi = 0; channelsi < total_channels; channelsi++) { static void JACK_PlayDevice(SDL_AudioDevice *device, const Uint8 *ui8buffer, int buflen)
{
const float *buffer = (float *) ui8buffer;
jack_port_t **ports = device->hidden->sdlports;
const int total_channels = device->spec.channels;
const int total_frames = device->sample_frames;
const jack_nframes_t nframes = (jack_nframes_t) device->sample_frames;
for (int channelsi = 0; channelsi < total_channels; channelsi++) {
float *dst = (float *)JACK_jack_port_get_buffer(ports[channelsi], nframes); float *dst = (float *)JACK_jack_port_get_buffer(ports[channelsi], nframes);
if (dst) { if (dst) {
const float *src = _this->hidden->iobuffer + channelsi; const float *src = buffer + channelsi;
int framesi; for (int framesi = 0; framesi < total_frames; framesi++) {
for (framesi = 0; framesi < total_frames; framesi++) {
*(dst++) = *src; *(dst++) = *src;
src += total_channels; src += total_channels;
} }
} }
} }
SDL_PostSemaphore(_this->hidden->iosem); /* tell SDL thread we're done; refill the buffer. */
return 0;
} }
/* This function waits until it is possible to write a full sound buffer */ static Uint8 *JACK_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
static void JACK_WaitDevice(SDL_AudioDevice *_this)
{ {
if (SDL_AtomicGet(&_this->enabled)) { return (Uint8 *)device->hidden->iobuffer;
if (SDL_WaitSemaphore(_this->hidden->iosem) == -1) {
SDL_OpenedAudioDeviceDisconnected(_this);
}
}
}
static Uint8 *JACK_GetDeviceBuf(SDL_AudioDevice *_this)
{
return (Uint8 *)_this->hidden->iobuffer;
} }
static int jackProcessCaptureCallback(jack_nframes_t nframes, void *arg) static int jackProcessCaptureCallback(jack_nframes_t nframes, void *arg)
{ {
SDL_AudioDevice *_this = (SDL_AudioDevice *)arg; SDL_assert(nframes == ((SDL_AudioDevice *)arg)->sample_frames);
if (SDL_AtomicGet(&_this->enabled)) { SDL_CaptureAudioThreadIterate((SDL_AudioDevice *)arg);
jack_port_t **ports = _this->hidden->sdlports; return 0;
const int total_channels = _this->spec.channels; }
const int total_frames = _this->spec.samples;
int channelsi;
for (channelsi = 0; channelsi < total_channels; channelsi++) { static int JACK_CaptureFromDevice(SDL_AudioDevice *device, void *vbuffer, int buflen)
{
float *buffer = (float *) vbuffer;
jack_port_t **ports = device->hidden->sdlports;
const int total_channels = device->spec.channels;
const int total_frames = device->sample_frames;
const jack_nframes_t nframes = (jack_nframes_t) device->sample_frames;
for (int channelsi = 0; channelsi < total_channels; channelsi++) {
const float *src = (const float *)JACK_jack_port_get_buffer(ports[channelsi], nframes); const float *src = (const float *)JACK_jack_port_get_buffer(ports[channelsi], nframes);
if (src) { if (src) {
float *dst = _this->hidden->iobuffer + channelsi; float *dst = buffer + channelsi;
int framesi; for (int framesi = 0; framesi < total_frames; framesi++) {
for (framesi = 0; framesi < total_frames; framesi++) {
*dst = *(src++); *dst = *(src++);
dst += total_channels; dst += total_channels;
} }
} }
} }
}
SDL_PostSemaphore(_this->hidden->iosem); /* tell SDL thread we're done; new buffer is ready! */
return 0;
}
static int JACK_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
{
SDL_assert(buflen == _this->spec.size); /* we always fill a full buffer. */
/* Wait for JACK to fill the iobuffer */
if (SDL_WaitSemaphore(_this->hidden->iosem) == -1) {
return -1;
}
SDL_memcpy(buffer, _this->hidden->iobuffer, buflen);
return buflen; return buflen;
} }
static void JACK_FlushCapture(SDL_AudioDevice *_this) static void JACK_FlushCapture(SDL_AudioDevice *device)
{ {
SDL_WaitSemaphore(_this->hidden->iosem); // do nothing, the data will just be replaced next callback.
} }
static void JACK_CloseDevice(SDL_AudioDevice *_this) static void JACK_CloseDevice(SDL_AudioDevice *device)
{ {
if (_this->hidden->client) { if (device->hidden) {
JACK_jack_deactivate(_this->hidden->client); if (device->hidden->client) {
JACK_jack_deactivate(device->hidden->client);
if (_this->hidden->sdlports) { if (device->hidden->sdlports) {
const int channels = _this->spec.channels; const int channels = device->spec.channels;
int i; int i;
for (i = 0; i < channels; i++) { for (i = 0; i < channels; i++) {
JACK_jack_port_unregister(_this->hidden->client, _this->hidden->sdlports[i]); JACK_jack_port_unregister(device->hidden->client, device->hidden->sdlports[i]);
} }
SDL_free(_this->hidden->sdlports); SDL_free(device->hidden->sdlports);
} }
JACK_jack_client_close(_this->hidden->client); JACK_jack_client_close(device->hidden->client);
} }
if (_this->hidden->iosem) { SDL_free(device->hidden->iobuffer);
SDL_DestroySemaphore(_this->hidden->iosem); SDL_free(device->hidden);
device->hidden = NULL;
SDL_AudioThreadFinalize(device);
}
} }
SDL_free(_this->hidden->iobuffer); // !!! FIXME: unify this (PulseAudio has a getAppName, Pipewire has a thing, etc
SDL_free(_this->hidden); static const char *GetJackAppName(void)
{
const char *retval = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME);
if (retval && *retval) {
return retval;
}
retval = SDL_GetHint(SDL_HINT_APP_NAME);
if (retval && *retval) {
return retval;
}
return "SDL Application";
} }
static int JACK_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int JACK_OpenDevice(SDL_AudioDevice *device)
{ {
/* Note that JACK uses "output" for capture devices (they output audio /* Note that JACK uses "output" for capture devices (they output audio
data to us) and "input" for playback (we input audio data to them). data to us) and "input" for playback (we input audio data to them).
Likewise, SDL's playback port will be "output" (we write data out) Likewise, SDL's playback port will be "output" (we write data out)
and capture will be "input" (we read data in). */ and capture will be "input" (we read data in). */
SDL_bool iscapture = _this->iscapture; SDL_bool iscapture = device->iscapture;
const unsigned long sysportflags = iscapture ? JackPortIsOutput : JackPortIsInput; const unsigned long sysportflags = iscapture ? JackPortIsOutput : JackPortIsInput;
const unsigned long sdlportflags = iscapture ? JackPortIsInput : JackPortIsOutput; const unsigned long sdlportflags = iscapture ? JackPortIsInput : JackPortIsOutput;
const JackProcessCallback callback = iscapture ? jackProcessCaptureCallback : jackProcessPlaybackCallback; const JackProcessCallback callback = iscapture ? jackProcessCaptureCallback : jackProcessPlaybackCallback;
@ -277,14 +268,13 @@ static int JACK_OpenDevice(SDL_AudioDevice *_this, const char *devname)
int i; int i;
/* Initialize all variables that we clean on shutdown */ /* Initialize all variables that we clean on shutdown */
_this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*_this->hidden)); device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
if (_this->hidden == NULL) { if (device->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
/* !!! FIXME: we _still_ need an API to specify an app name */ client = JACK_jack_client_open(GetJackAppName(), JackNoStartServer, &status, NULL);
client = JACK_jack_client_open("SDL", JackNoStartServer, &status, NULL); device->hidden->client = client;
_this->hidden->client = client;
if (client == NULL) { if (client == NULL) {
return SDL_SetError("Can't open JACK client"); return SDL_SetError("Can't open JACK client");
} }
@ -317,28 +307,24 @@ static int JACK_OpenDevice(SDL_AudioDevice *_this, const char *devname)
/* !!! 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." */ /* !!! 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. */ /* Jack pretty much demands what it wants. */
_this->spec.format = SDL_AUDIO_F32SYS; device->spec.format = SDL_AUDIO_F32SYS;
_this->spec.freq = JACK_jack_get_sample_rate(client); device->spec.freq = JACK_jack_get_sample_rate(client);
_this->spec.channels = channels; device->spec.channels = channels;
_this->spec.samples = JACK_jack_get_buffer_size(client); device->sample_frames = JACK_jack_get_buffer_size(client);
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
_this->hidden->iosem = SDL_CreateSemaphore(0); if (!device->iscapture) {
if (!_this->hidden->iosem) { device->hidden->iobuffer = (float *)SDL_calloc(1, device->buffer_size);
SDL_free(audio_ports); if (!device->hidden->iobuffer) {
return -1; /* error was set by SDL_CreateSemaphore */
}
_this->hidden->iobuffer = (float *)SDL_calloc(1, _this->spec.size);
if (!_this->hidden->iobuffer) {
SDL_free(audio_ports); SDL_free(audio_ports);
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
}
/* Build SDL's ports, which we will connect to the device ports. */ /* Build SDL's ports, which we will connect to the device ports. */
_this->hidden->sdlports = (jack_port_t **)SDL_calloc(channels, sizeof(jack_port_t *)); device->hidden->sdlports = (jack_port_t **)SDL_calloc(channels, sizeof(jack_port_t *));
if (_this->hidden->sdlports == NULL) { if (device->hidden->sdlports == NULL) {
SDL_free(audio_ports); SDL_free(audio_ports);
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
@ -346,19 +332,19 @@ static int JACK_OpenDevice(SDL_AudioDevice *_this, const char *devname)
for (i = 0; i < channels; i++) { for (i = 0; i < channels; i++) {
char portname[32]; char portname[32];
(void)SDL_snprintf(portname, sizeof(portname), "sdl_jack_%s_%d", sdlportstr, i); (void)SDL_snprintf(portname, sizeof(portname), "sdl_jack_%s_%d", sdlportstr, i);
_this->hidden->sdlports[i] = JACK_jack_port_register(client, portname, JACK_DEFAULT_AUDIO_TYPE, sdlportflags, 0); device->hidden->sdlports[i] = JACK_jack_port_register(client, portname, JACK_DEFAULT_AUDIO_TYPE, sdlportflags, 0);
if (_this->hidden->sdlports[i] == NULL) { if (device->hidden->sdlports[i] == NULL) {
SDL_free(audio_ports); SDL_free(audio_ports);
return SDL_SetError("jack_port_register failed"); return SDL_SetError("jack_port_register failed");
} }
} }
if (JACK_jack_set_process_callback(client, callback, _this) != 0) { if (JACK_jack_set_process_callback(client, callback, device) != 0) {
SDL_free(audio_ports); SDL_free(audio_ports);
return SDL_SetError("JACK: Couldn't set process callback"); return SDL_SetError("JACK: Couldn't set process callback");
} }
JACK_jack_on_shutdown(client, jackShutdownCallback, _this); JACK_jack_on_shutdown(client, jackShutdownCallback, device);
if (JACK_jack_activate(client) != 0) { if (JACK_jack_activate(client) != 0) {
SDL_free(audio_ports); SDL_free(audio_ports);
@ -367,7 +353,7 @@ static int JACK_OpenDevice(SDL_AudioDevice *_this, const char *devname)
/* once activated, we can connect all the ports. */ /* once activated, we can connect all the ports. */
for (i = 0; i < channels; i++) { for (i = 0; i < channels; i++) {
const char *sdlport = JACK_jack_port_name(_this->hidden->sdlports[i]); const char *sdlport = JACK_jack_port_name(device->hidden->sdlports[i]);
const char *srcport = iscapture ? devports[audio_ports[i]] : sdlport; const char *srcport = iscapture ? devports[audio_ports[i]] : sdlport;
const char *dstport = iscapture ? sdlport : devports[audio_ports[i]]; const char *dstport = iscapture ? sdlport : devports[audio_ports[i]];
if (JACK_jack_connect(client, srcport, dstport) != 0) { if (JACK_jack_connect(client, srcport, dstport) != 0) {
@ -406,8 +392,8 @@ static SDL_bool JACK_Init(SDL_AudioDriverImpl *impl)
/* Set the function pointers */ /* Set the function pointers */
impl->OpenDevice = JACK_OpenDevice; impl->OpenDevice = JACK_OpenDevice;
impl->WaitDevice = JACK_WaitDevice;
impl->GetDeviceBuf = JACK_GetDeviceBuf; impl->GetDeviceBuf = JACK_GetDeviceBuf;
impl->PlayDevice = JACK_PlayDevice;
impl->CloseDevice = JACK_CloseDevice; impl->CloseDevice = JACK_CloseDevice;
impl->Deinitialize = JACK_Deinitialize; impl->Deinitialize = JACK_Deinitialize;
impl->CaptureFromDevice = JACK_CaptureFromDevice; impl->CaptureFromDevice = JACK_CaptureFromDevice;
@ -415,6 +401,7 @@ static SDL_bool JACK_Init(SDL_AudioDriverImpl *impl)
impl->OnlyHasDefaultOutputDevice = SDL_TRUE; impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE; impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
impl->HasCaptureSupport = SDL_TRUE; impl->HasCaptureSupport = SDL_TRUE;
impl->ProvidesOwnCallbackThread = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */ return SDL_TRUE; /* this audio target is available. */
} }

View File

@ -28,9 +28,8 @@
struct SDL_PrivateAudioData struct SDL_PrivateAudioData
{ {
jack_client_t *client; jack_client_t *client;
SDL_Semaphore *iosem;
float *iobuffer;
jack_port_t **sdlports; jack_port_t **sdlports;
float *iobuffer;
}; };
#endif /* SDL_jackaudio_h_ */ #endif /* SDL_jackaudio_h_ */

View File

@ -22,7 +22,7 @@
#ifdef SDL_AUDIO_DRIVER_N3DS #ifdef SDL_AUDIO_DRIVER_N3DS
/* N3DS Audio driver */ // N3DS Audio driver
#include "../SDL_sysaudio.h" #include "../SDL_sysaudio.h"
#include "SDL_n3dsaudio.h" #include "SDL_n3dsaudio.h"
@ -32,27 +32,14 @@
static dspHookCookie dsp_hook; static dspHookCookie dsp_hook;
static SDL_AudioDevice *audio_device; static SDL_AudioDevice *audio_device;
static void FreePrivateData(SDL_AudioDevice *_this); static SDL_INLINE void contextLock(SDL_AudioDevice *device)
static int FindAudioFormat(SDL_AudioDevice *_this);
static SDL_INLINE void contextLock(SDL_AudioDevice *_this)
{ {
LightLock_Lock(&_this->hidden->lock); LightLock_Lock(&device->hidden->lock);
} }
static SDL_INLINE void contextUnlock(SDL_AudioDevice *_this) static SDL_INLINE void contextUnlock(SDL_AudioDevice *device)
{ {
LightLock_Unlock(&_this->hidden->lock); LightLock_Unlock(&device->hidden->lock);
}
static void N3DSAUD_LockAudio(SDL_AudioDevice *_this)
{
contextLock(_this);
}
static void N3DSAUD_UnlockAudio(SDL_AudioDevice *_this)
{
contextUnlock(_this);
} }
static void N3DSAUD_DspHook(DSP_HookType hook) static void N3DSAUD_DspHook(DSP_HookType hook)
@ -60,46 +47,46 @@ static void N3DSAUD_DspHook(DSP_HookType hook)
if (hook == DSPHOOK_ONCANCEL) { if (hook == DSPHOOK_ONCANCEL) {
contextLock(audio_device); contextLock(audio_device);
audio_device->hidden->isCancelled = SDL_TRUE; audio_device->hidden->isCancelled = SDL_TRUE;
SDL_AtomicSet(&audio_device->enabled, SDL_FALSE); SDL_AudioDeviceDisconnected(audio_device);
CondVar_Broadcast(&audio_device->hidden->cv); CondVar_Broadcast(&audio_device->hidden->cv);
contextUnlock(audio_device); contextUnlock(audio_device);
} }
} }
static void AudioFrameFinished(void *device) static void AudioFrameFinished(void *vdevice)
{ {
bool shouldBroadcast = false; bool shouldBroadcast = false;
unsigned i; unsigned i;
SDL_AudioDevice *_this = (SDL_AudioDevice *)device; SDL_AudioDevice *device = (SDL_AudioDevice *)vdevice;
contextLock(_this); contextLock(device);
for (i = 0; i < NUM_BUFFERS; i++) { for (i = 0; i < NUM_BUFFERS; i++) {
if (_this->hidden->waveBuf[i].status == NDSP_WBUF_DONE) { if (device->hidden->waveBuf[i].status == NDSP_WBUF_DONE) {
_this->hidden->waveBuf[i].status = NDSP_WBUF_FREE; device->hidden->waveBuf[i].status = NDSP_WBUF_FREE;
shouldBroadcast = SDL_TRUE; shouldBroadcast = SDL_TRUE;
} }
} }
if (shouldBroadcast) { if (shouldBroadcast) {
CondVar_Broadcast(&_this->hidden->cv); CondVar_Broadcast(&device->hidden->cv);
} }
contextUnlock(_this); contextUnlock(device);
} }
static int N3DSAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int N3DSAUDIO_OpenDevice(SDL_AudioDevice *device)
{ {
Result ndsp_init_res; Result ndsp_init_res;
Uint8 *data_vaddr; Uint8 *data_vaddr;
float mix[12]; float mix[12];
_this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*_this->hidden));
if (_this->hidden == NULL) { device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
if (device->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
/* Initialise the DSP service */ // Initialise the DSP service
ndsp_init_res = ndspInit(); ndsp_init_res = ndspInit();
if (R_FAILED(ndsp_init_res)) { if (R_FAILED(ndsp_init_res)) {
if ((R_SUMMARY(ndsp_init_res) == RS_NOTFOUND) && (R_MODULE(ndsp_init_res) == RM_DSP)) { if ((R_SUMMARY(ndsp_init_res) == RS_NOTFOUND) && (R_MODULE(ndsp_init_res) == RM_DSP)) {
@ -110,173 +97,180 @@ static int N3DSAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
return -1; return -1;
} }
/* Initialise internal state */ // Initialise internal state
LightLock_Init(&_this->hidden->lock); LightLock_Init(&device->hidden->lock);
CondVar_Init(&_this->hidden->cv); CondVar_Init(&device->hidden->cv);
if (_this->spec.channels > 2) { if (device->spec.channels > 2) {
_this->spec.channels = 2; device->spec.channels = 2;
} }
/* Should not happen but better be safe. */ Uint32 format = 0;
if (FindAudioFormat(_this) < 0) { SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
while ((test_format = *(closefmts++)) != 0) {
if (test_format == SDL_AUDIO_S8) { // Signed 8-bit audio supported
format = (device->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM8 : NDSP_FORMAT_MONO_PCM8;
break;
} else if (test_format == SDL_AUDIO_S16) { // Signed 16-bit audio supported
format = (device->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM16 : NDSP_FORMAT_MONO_PCM16;
break;
}
}
if (!test_format) { // shouldn't happen, but just in case...
return SDL_SetError("No supported audio format found."); return SDL_SetError("No supported audio format found.");
} }
/* Update the fragment size as size in bytes */ device->spec.format = test_format;
SDL_CalculateAudioSpec(&_this->spec);
/* Allocate mixing buffer */ // Update the fragment size as size in bytes
if (_this->spec.size >= SDL_MAX_UINT32 / 2) { SDL_UpdatedAudioDeviceFormat(device);
// Allocate mixing buffer
if (device->buffer_size >= SDL_MAX_UINT32 / 2) {
return SDL_SetError("Mixing buffer is too large."); return SDL_SetError("Mixing buffer is too large.");
} }
_this->hidden->mixlen = _this->spec.size; device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->spec.size); if (device->hidden->mixbuf == NULL) {
if (_this->hidden->mixbuf == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->spec.size); SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
data_vaddr = (Uint8 *)linearAlloc(_this->hidden->mixlen * NUM_BUFFERS); data_vaddr = (Uint8 *)linearAlloc(device->buffer_size * NUM_BUFFERS);
if (data_vaddr == NULL) { if (data_vaddr == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_memset(data_vaddr, 0, _this->hidden->mixlen * NUM_BUFFERS); SDL_memset(data_vaddr, 0, device->buffer_size * NUM_BUFFERS);
DSP_FlushDataCache(data_vaddr, _this->hidden->mixlen * NUM_BUFFERS); DSP_FlushDataCache(data_vaddr, device->buffer_size * NUM_BUFFERS);
_this->hidden->nextbuf = 0; device->hidden->nextbuf = 0;
_this->hidden->channels = _this->spec.channels;
_this->hidden->samplerate = _this->spec.freq;
ndspChnReset(0); ndspChnReset(0);
ndspChnSetInterp(0, NDSP_INTERP_LINEAR); ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
ndspChnSetRate(0, _this->spec.freq); ndspChnSetRate(0, device->spec.freq);
ndspChnSetFormat(0, _this->hidden->format); ndspChnSetFormat(0, format);
SDL_memset(mix, 0, sizeof(mix)); SDL_zeroa(mix);
mix[0] = 1.0; mix[0] = mix[1] = 1.0f;
mix[1] = 1.0;
ndspChnSetMix(0, mix); ndspChnSetMix(0, mix);
SDL_memset(_this->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS); 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);
for (unsigned i = 0; i < NUM_BUFFERS; i++) { for (unsigned i = 0; i < NUM_BUFFERS; i++) {
_this->hidden->waveBuf[i].data_vaddr = data_vaddr; device->hidden->waveBuf[i].data_vaddr = data_vaddr;
_this->hidden->waveBuf[i].nsamples = _this->hidden->mixlen / _this->hidden->bytePerSample; device->hidden->waveBuf[i].nsamples = device->buffer_size / sample_frame_size;
data_vaddr += _this->hidden->mixlen; data_vaddr += device->buffer_size;
} }
/* Setup callback */ // Setup callback
audio_device = _this; audio_device = device;
ndspSetCallback(AudioFrameFinished, _this); ndspSetCallback(AudioFrameFinished, device);
dspHook(&dsp_hook, N3DSAUD_DspHook); dspHook(&dsp_hook, N3DSAUD_DspHook);
return 0; return 0;
} }
static int N3DSAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) static void N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{ {
/* Delay to make this sort of simulate real audio input. */ contextLock(device);
SDL_Delay((_this->spec.samples * 1000) / _this->spec.freq);
/* always return a full buffer of silence. */ const size_t nextbuf = device->hidden->nextbuf;
SDL_memset(buffer, _this->spec.silence, buflen);
return buflen;
}
static void N3DSAUDIO_PlayDevice(SDL_AudioDevice *_this) if (device->hidden->isCancelled ||
{ device->hidden->waveBuf[nextbuf].status != NDSP_WBUF_FREE) {
size_t nextbuf; contextUnlock(device);
size_t sampleLen;
contextLock(_this);
nextbuf = _this->hidden->nextbuf;
sampleLen = _this->hidden->mixlen;
if (_this->hidden->isCancelled ||
_this->hidden->waveBuf[nextbuf].status != NDSP_WBUF_FREE) {
contextUnlock(_this);
return; return;
} }
_this->hidden->nextbuf = (nextbuf + 1) % NUM_BUFFERS; device->hidden->nextbuf = (nextbuf + 1) % NUM_BUFFERS;
contextUnlock(_this); contextUnlock(device);
SDL_memcpy((void *)_this->hidden->waveBuf[nextbuf].data_vaddr, SDL_memcpy((void *)device->hidden->waveBuf[nextbuf].data_vaddr, buffer, buflen);
_this->hidden->mixbuf, sampleLen); DSP_FlushDataCache(device->hidden->waveBuf[nextbuf].data_vaddr, buflen);
DSP_FlushDataCache(_this->hidden->waveBuf[nextbuf].data_vaddr, sampleLen);
ndspChnWaveBufAdd(0, &_this->hidden->waveBuf[nextbuf]); ndspChnWaveBufAdd(0, &device->hidden->waveBuf[nextbuf]);
} }
static void N3DSAUDIO_WaitDevice(SDL_AudioDevice *_this) static void N3DSAUDIO_WaitDevice(SDL_AudioDevice *device)
{ {
contextLock(_this); contextLock(device);
while (!_this->hidden->isCancelled && while (!device->hidden->isCancelled && !SDL_AtomicGet(&device->shutdown) &&
_this->hidden->waveBuf[_this->hidden->nextbuf].status != NDSP_WBUF_FREE) { device->hidden->waveBuf[device->hidden->nextbuf].status != NDSP_WBUF_FREE) {
CondVar_Wait(&_this->hidden->cv, &_this->hidden->lock); CondVar_Wait(&device->hidden->cv, &device->hidden->lock);
} }
contextUnlock(_this); contextUnlock(device);
} }
static Uint8 *N3DSAUDIO_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *N3DSAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
return _this->hidden->mixbuf; return device->hidden->mixbuf;
} }
static void N3DSAUDIO_CloseDevice(SDL_AudioDevice *_this) static void N3DSAUDIO_CloseDevice(SDL_AudioDevice *device)
{ {
contextLock(_this); if (!device->hidden) {
return;
}
contextLock(device);
dspUnhook(&dsp_hook); dspUnhook(&dsp_hook);
ndspSetCallback(NULL, NULL); ndspSetCallback(NULL, NULL);
if (!_this->hidden->isCancelled) { if (!device->hidden->isCancelled) {
ndspChnReset(0); ndspChnReset(0);
SDL_memset(_this->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS); SDL_memset(device->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS);
CondVar_Broadcast(&_this->hidden->cv); CondVar_Broadcast(&device->hidden->cv);
} }
contextUnlock(_this); contextUnlock(device);
ndspExit(); ndspExit();
FreePrivateData(_this); if (device->hidden->waveBuf[0].data_vaddr) {
linearFree((void *)device->hidden->waveBuf[0].data_vaddr);
} }
static void N3DSAUDIO_ThreadInit(SDL_AudioDevice *_this) if (device->hidden->mixbuf) {
SDL_free(device->hidden->mixbuf);
device->hidden->mixbuf = NULL;
}
SDL_free(device->hidden);
device->hidden = NULL;
}
static void N3DSAUDIO_ThreadInit(SDL_AudioDevice *device)
{ {
s32 current_priority; s32 current_priority;
svcGetThreadPriority(&current_priority, CUR_THREAD_HANDLE); svcGetThreadPriority(&current_priority, CUR_THREAD_HANDLE);
current_priority--; current_priority--;
/* 0x18 is reserved for video, 0x30 is the default for main thread */ // 0x18 is reserved for video, 0x30 is the default for main thread
current_priority = SDL_clamp(current_priority, 0x19, 0x2F); current_priority = SDL_clamp(current_priority, 0x19, 0x2F);
svcSetThreadPriority(CUR_THREAD_HANDLE, current_priority); svcSetThreadPriority(CUR_THREAD_HANDLE, current_priority);
} }
static SDL_bool N3DSAUDIO_Init(SDL_AudioDriverImpl *impl) static SDL_bool N3DSAUDIO_Init(SDL_AudioDriverImpl *impl)
{ {
/* Set the function pointers */
impl->OpenDevice = N3DSAUDIO_OpenDevice; impl->OpenDevice = N3DSAUDIO_OpenDevice;
impl->PlayDevice = N3DSAUDIO_PlayDevice; impl->PlayDevice = N3DSAUDIO_PlayDevice;
impl->WaitDevice = N3DSAUDIO_WaitDevice; impl->WaitDevice = N3DSAUDIO_WaitDevice;
impl->GetDeviceBuf = N3DSAUDIO_GetDeviceBuf; impl->GetDeviceBuf = N3DSAUDIO_GetDeviceBuf;
impl->CloseDevice = N3DSAUDIO_CloseDevice; impl->CloseDevice = N3DSAUDIO_CloseDevice;
impl->ThreadInit = N3DSAUDIO_ThreadInit; impl->ThreadInit = N3DSAUDIO_ThreadInit;
impl->LockDevice = N3DSAUD_LockAudio;
impl->UnlockDevice = N3DSAUD_UnlockAudio;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE; impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
/* Should be possible, but micInit would fail */ // Should be possible, but micInit would fail
impl->HasCaptureSupport = SDL_FALSE; impl->HasCaptureSupport = SDL_FALSE;
impl->CaptureFromDevice = N3DSAUDIO_CaptureFromDevice;
return SDL_TRUE; /* this audio target is available. */ return SDL_TRUE;
} }
AudioBootStrap N3DSAUDIO_bootstrap = { AudioBootStrap N3DSAUDIO_bootstrap = {
@ -286,51 +280,4 @@ AudioBootStrap N3DSAUDIO_bootstrap = {
0 0
}; };
/** #endif // SDL_AUDIO_DRIVER_N3DS
* Cleans up all allocated memory, safe to call with null pointers
*/
static void FreePrivateData(SDL_AudioDevice *_this)
{
if (!_this->hidden) {
return;
}
if (_this->hidden->waveBuf[0].data_vaddr) {
linearFree((void *)_this->hidden->waveBuf[0].data_vaddr);
}
if (_this->hidden->mixbuf) {
SDL_free(_this->hidden->mixbuf);
_this->hidden->mixbuf = NULL;
}
SDL_free(_this->hidden);
_this->hidden = NULL;
}
static int FindAudioFormat(SDL_AudioDevice *_this)
{
SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(_this->spec.format);
while ((test_format = *(closefmts++)) != 0) {
_this->spec.format = test_format;
switch (test_format) {
case SDL_AUDIO_S8:
/* Signed 8-bit audio supported */
_this->hidden->format = (_this->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM8 : NDSP_FORMAT_MONO_PCM8;
_this->hidden->isSigned = 1;
_this->hidden->bytePerSample = _this->spec.channels;
return 0;
case SDL_AUDIO_S16:
/* Signed 16-bit audio supported */
_this->hidden->format = (_this->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM16 : NDSP_FORMAT_MONO_PCM16;
_this->hidden->isSigned = 1;
_this->hidden->bytePerSample = _this->spec.channels * 2;
return 0;
}
}
return -1;
}
#endif /* SDL_AUDIO_DRIVER_N3DS */

View File

@ -30,12 +30,6 @@ struct SDL_PrivateAudioData
{ {
/* Speaker data */ /* Speaker data */
Uint8 *mixbuf; Uint8 *mixbuf;
Uint32 mixlen;
Uint32 format;
Uint32 samplerate;
Uint32 channels;
Uint8 bytePerSample;
Uint32 isSigned;
Uint32 nextbuf; Uint32 nextbuf;
ndspWaveBuf waveBuf[NUM_BUFFERS]; ndspWaveBuf waveBuf[NUM_BUFFERS];
LightLock lock; LightLock lock;

View File

@ -22,10 +22,7 @@
#ifdef SDL_AUDIO_DRIVER_NETBSD #ifdef SDL_AUDIO_DRIVER_NETBSD
/* // Driver for native NetBSD audio(4).
* Driver for native NetBSD audio(4).
* nia@NetBSD.org
*/
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
@ -41,26 +38,26 @@
#include "../SDL_audiodev_c.h" #include "../SDL_audiodev_c.h"
#include "SDL_netbsdaudio.h" #include "SDL_netbsdaudio.h"
/* #define DEBUG_AUDIO */ //#define DEBUG_AUDIO
static void NETBSDAUDIO_DetectDevices(void) static void NETBSDAUDIO_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{ {
SDL_EnumUnixAudioDevices(0, NULL); SDL_EnumUnixAudioDevices(SDL_FALSE, NULL);
} }
static void NETBSDAUDIO_Status(SDL_AudioDevice *_this) static void NETBSDAUDIO_Status(SDL_AudioDevice *device)
{ {
#ifdef DEBUG_AUDIO #ifdef DEBUG_AUDIO
/* *INDENT-OFF* */ /* clang-format off */ /* *INDENT-OFF* */ /* clang-format off */
audio_info_t info; audio_info_t info;
const struct audio_prinfo *prinfo; const struct audio_prinfo *prinfo;
if (ioctl(_this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) { if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
fprintf(stderr, "AUDIO_GETINFO failed.\n"); fprintf(stderr, "AUDIO_GETINFO failed.\n");
return; return;
} }
prinfo = _this->iscapture ? &info.record : &info.play; prinfo = device->iscapture ? &info.record : &info.play;
fprintf(stderr, "\n" fprintf(stderr, "\n"
"[%s info]\n" "[%s info]\n"
@ -77,7 +74,7 @@ static void NETBSDAUDIO_Status(SDL_AudioDevice *_this)
"waiting : %s\n" "waiting : %s\n"
"active : %s\n" "active : %s\n"
"", "",
_this->iscapture ? "record" : "play", device->iscapture ? "record" : "play",
prinfo->buffer_size, prinfo->buffer_size,
prinfo->sample_rate, prinfo->sample_rate,
prinfo->channels, prinfo->channels,
@ -103,7 +100,7 @@ static void NETBSDAUDIO_Status(SDL_AudioDevice *_this)
info.blocksize, info.blocksize,
info.hiwat, info.lowat, info.hiwat, info.lowat,
(info.mode == AUMODE_PLAY) ? "PLAY" (info.mode == AUMODE_PLAY) ? "PLAY"
: (info.mode = AUMODE_RECORD) ? "RECORD" : (info.mode == AUMODE_RECORD) ? "RECORD"
: (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?")); : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?"));
fprintf(stderr, "\n" fprintf(stderr, "\n"
@ -111,23 +108,46 @@ static void NETBSDAUDIO_Status(SDL_AudioDevice *_this)
"format : 0x%x\n" "format : 0x%x\n"
"size : %u\n" "size : %u\n"
"", "",
_this->spec.format, device->spec.format,
_this->spec.size); device->buffer_size);
/* *INDENT-ON* */ /* clang-format on */ /* *INDENT-ON* */ /* clang-format on */
#endif /* DEBUG_AUDIO */ #endif // DEBUG_AUDIO
} }
static void NETBSDAUDIO_PlayDevice(SDL_AudioDevice *_this) static void NETBSDAUDIO_WaitDevice(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *h = _this->hidden; const SDL_bool iscapture = device->iscapture;
int written; while (!SDL_AtomicGet(&device->shutdown)) {
audio_info_t info;
const int rc = ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info);
if (rc < 0) {
if (errno == EAGAIN) {
continue;
}
// Hmm, not much we can do - abort
fprintf(stderr, "netbsdaudio WaitDevice ioctl failed (unrecoverable): %s\n", strerror(errno));
SDL_AudioDeviceDisconnected(device);
return;
}
const size_t remain = (size_t)((iscapture ? info.record.seek : info.play.seek) * (SDL_AUDIO_BITSIZE(device->spec.format) / 8));
if (!iscapture && (remain >= device->buffer_size)) {
SDL_Delay(10);
} else if (iscapture && (remain < device->buffer_size)) {
SDL_Delay(10);
} else {
break; /* ready to go! */
}
}
}
/* Write the audio data */ static void NETBSDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
written = write(h->audio_fd, h->mixbuf, h->mixlen); {
struct SDL_PrivateAudioData *h = device->hidden;
const int written = write(h->audio_fd, buffer, buflen);
if (written == -1) { if (written == -1) {
/* Non recoverable error has occurred. It should be reported!!! */ // Non recoverable error has occurred. It should be reported!!!
SDL_OpenedAudioDeviceDisconnected(_this); SDL_AudioDeviceDisconnected(device);
perror("audio"); perror("audio");
return; return;
} }
@ -137,19 +157,17 @@ static void NETBSDAUDIO_PlayDevice(SDL_AudioDevice *_this)
#endif #endif
} }
static Uint8 *NETBSDAUDIO_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *NETBSDAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
return _this->hidden->mixbuf; return device->hidden->mixbuf;
} }
static int NETBSDAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *_buffer, int buflen) static int NETBSDAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *vbuffer, int buflen)
{ {
Uint8 *buffer = (Uint8 *)_buffer; Uint8 *buffer = (Uint8 *)vbuffer;
int br; const int br = read(device->hidden->audio_fd, buffer, buflen);
br = read(_this->hidden->audio_fd, buffer, buflen);
if (br == -1) { if (br == -1) {
/* Non recoverable error has occurred. It should be reported!!! */ // Non recoverable error has occurred. It should be reported!!!
perror("audio"); perror("audio");
return -1; return -1;
} }
@ -157,86 +175,73 @@ static int NETBSDAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *_buffer,
#ifdef DEBUG_AUDIO #ifdef DEBUG_AUDIO
fprintf(stderr, "Captured %d bytes of audio data\n", br); fprintf(stderr, "Captured %d bytes of audio data\n", br);
#endif #endif
return 0; return br;
} }
static void NETBSDAUDIO_FlushCapture(SDL_AudioDevice *_this) static void NETBSDAUDIO_FlushCapture(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *h = device->hidden;
audio_info_t info; audio_info_t info;
size_t remain; if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) == 0) {
Uint8 buf[512]; size_t remain = (size_t)(info.record.seek * (SDL_AUDIO_BITSIZE(device->spec.format) / 8));
if (ioctl(_this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
return; /* oh well. */
}
remain = (size_t)(info.record.samples * (SDL_AUDIO_BITSIZE(_this->spec.format) / 8));
while (remain > 0) { while (remain > 0) {
char buf[512];
const size_t len = SDL_min(sizeof(buf), remain); const size_t len = SDL_min(sizeof(buf), remain);
const int br = read(_this->hidden->audio_fd, buf, len); const ssize_t br = read(h->audio_fd, buf, len);
if (br <= 0) { if (br <= 0) {
return; /* oh well. */ break;
} }
remain -= br; remain -= br;
} }
} }
static void NETBSDAUDIO_CloseDevice(SDL_AudioDevice *_this)
{
if (_this->hidden->audio_fd >= 0) {
close(_this->hidden->audio_fd);
}
SDL_free(_this->hidden->mixbuf);
SDL_free(_this->hidden);
} }
static int NETBSDAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) static void NETBSDAUDIO_CloseDevice(SDL_AudioDevice *device)
{ {
SDL_bool iscapture = _this->iscapture; if (device->hidden) {
SDL_AudioFormat test_format; if (device->hidden->audio_fd >= 0) {
const SDL_AudioFormat *closefmts; close(device->hidden->audio_fd);
}
SDL_free(device->hidden->mixbuf);
SDL_free(device->hidden);
device->hidden = NULL;
}
}
static int NETBSDAUDIO_OpenDevice(SDL_AudioDevice *device)
{
const SDL_bool iscapture = device->iscapture;
int encoding = AUDIO_ENCODING_NONE; int encoding = AUDIO_ENCODING_NONE;
audio_info_t info, hwinfo; audio_info_t info, hwinfo;
struct audio_prinfo *prinfo = iscapture ? &info.record : &info.play; struct audio_prinfo *prinfo = iscapture ? &info.record : &info.play;
/* We don't care what the devname is...we'll try to open anything. */ // Initialize all variables that we clean on shutdown
/* ...but default to first name in the list... */ device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
if (devname == NULL) { if (device->hidden == NULL) {
devname = SDL_GetAudioDeviceName(0, iscapture);
if (devname == NULL) {
return SDL_SetError("No such audio device");
}
}
/* Initialize all variables that we clean on shutdown */
_this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc(sizeof(*_this->hidden));
if (_this->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_zerop(_this->hidden);
/* Open the audio device */ // Open the audio device; we hardcode the device path in `device->name` for lack of better info, so use that.
_this->hidden->audio_fd = open(devname, (iscapture ? O_RDONLY : O_WRONLY) | O_CLOEXEC); const int flags = ((device->iscapture) ? O_RDONLY : O_WRONLY);
if (_this->hidden->audio_fd < 0) { device->hidden->audio_fd = open(device->name, flags | O_CLOEXEC);
return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno)); if (device->hidden->audio_fd < 0) {
return SDL_SetError("Couldn't open %s: %s", device->name, strerror(errno));
} }
AUDIO_INITINFO(&info); AUDIO_INITINFO(&info);
#ifdef AUDIO_GETFORMAT /* Introduced in NetBSD 9.0 */ #ifdef AUDIO_GETFORMAT // Introduced in NetBSD 9.0
if (ioctl(_this->hidden->audio_fd, AUDIO_GETFORMAT, &hwinfo) != -1) { if (ioctl(device->hidden->audio_fd, AUDIO_GETFORMAT, &hwinfo) != -1) {
/* // Use the device's native sample rate so the kernel doesn't have to resample.
* Use the device's native sample rate so the kernel doesn't have to device->spec.freq = iscapture ? hwinfo.record.sample_rate : hwinfo.play.sample_rate;
* resample.
*/
_this->spec.freq = iscapture ? hwinfo.record.sample_rate : hwinfo.play.sample_rate;
} }
#endif #endif
prinfo->sample_rate = _this->spec.freq; prinfo->sample_rate = device->spec.freq;
prinfo->channels = _this->spec.channels; prinfo->channels = device->spec.channels;
closefmts = SDL_ClosestAudioFormats(_this->spec.format); SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
while ((test_format = *(closefmts++)) != 0) { while ((test_format = *(closefmts++)) != 0) {
switch (test_format) { switch (test_format) {
case SDL_AUDIO_U8: case SDL_AUDIO_U8:
@ -271,56 +276,56 @@ static int NETBSDAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
info.hiwat = 5; info.hiwat = 5;
info.lowat = 3; info.lowat = 3;
if (ioctl(_this->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) { if (ioctl(device->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) {
return SDL_SetError("AUDIO_SETINFO failed for %s: %s", devname, strerror(errno)); return SDL_SetError("AUDIO_SETINFO failed for %s: %s", device->name, strerror(errno));
} }
if (ioctl(_this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) { if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
return SDL_SetError("AUDIO_GETINFO failed for %s: %s", devname, strerror(errno)); return SDL_SetError("AUDIO_GETINFO failed for %s: %s", device->name, strerror(errno));
} }
/* Final spec used for the device. */ // Final spec used for the device.
_this->spec.format = test_format; device->spec.format = test_format;
_this->spec.freq = prinfo->sample_rate; device->spec.freq = prinfo->sample_rate;
_this->spec.channels = prinfo->channels; device->spec.channels = prinfo->channels;
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
if (!iscapture) { if (!iscapture) {
/* Allocate mixing buffer */ // Allocate mixing buffer
_this->hidden->mixlen = _this->spec.size; device->hidden->mixlen = device->buffer_size;
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->hidden->mixlen); device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->hidden->mixlen);
if (_this->hidden->mixbuf == NULL) { if (device->hidden->mixbuf == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->spec.size); SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
} }
NETBSDAUDIO_Status(_this); NETBSDAUDIO_Status(device);
/* We're ready to rock and roll. :-) */ return 0; // We're ready to rock and roll. :-)
return 0;
} }
static SDL_bool NETBSDAUDIO_Init(SDL_AudioDriverImpl *impl) static SDL_bool NETBSDAUDIO_Init(SDL_AudioDriverImpl *impl)
{ {
/* Set the function pointers */
impl->DetectDevices = NETBSDAUDIO_DetectDevices; impl->DetectDevices = NETBSDAUDIO_DetectDevices;
impl->OpenDevice = NETBSDAUDIO_OpenDevice; impl->OpenDevice = NETBSDAUDIO_OpenDevice;
impl->WaitDevice = NETBSDAUDIO_WaitDevice;
impl->PlayDevice = NETBSDAUDIO_PlayDevice; impl->PlayDevice = NETBSDAUDIO_PlayDevice;
impl->GetDeviceBuf = NETBSDAUDIO_GetDeviceBuf; impl->GetDeviceBuf = NETBSDAUDIO_GetDeviceBuf;
impl->CloseDevice = NETBSDAUDIO_CloseDevice; impl->CloseDevice = NETBSDAUDIO_CloseDevice;
impl->WaitCaptureDevice = NETBSDAUDIO_WaitDevice;
impl->CaptureFromDevice = NETBSDAUDIO_CaptureFromDevice; impl->CaptureFromDevice = NETBSDAUDIO_CaptureFromDevice;
impl->FlushCapture = NETBSDAUDIO_FlushCapture; impl->FlushCapture = NETBSDAUDIO_FlushCapture;
impl->HasCaptureSupport = SDL_TRUE; impl->HasCaptureSupport = SDL_TRUE;
impl->AllowsArbitraryDeviceNames = SDL_TRUE; impl->AllowsArbitraryDeviceNames = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */ return SDL_TRUE;
} }
AudioBootStrap NETBSDAUDIO_bootstrap = { AudioBootStrap NETBSDAUDIO_bootstrap = {
"netbsd", "NetBSD audio", NETBSDAUDIO_Init, SDL_FALSE "netbsd", "NetBSD audio", NETBSDAUDIO_Init, SDL_FALSE
}; };
#endif /* SDL_AUDIO_DRIVER_NETBSD */ #endif // SDL_AUDIO_DRIVER_NETBSD

View File

@ -22,9 +22,8 @@
#ifdef SDL_AUDIO_DRIVER_OPENSLES #ifdef SDL_AUDIO_DRIVER_OPENSLES
/* For more discussion of low latency audio on Android, see this: // For more discussion of low latency audio on Android, see this:
https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html // https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html
*/
#include "../SDL_sysaudio.h" #include "../SDL_sysaudio.h"
#include "../SDL_audio_c.h" #include "../SDL_audio_c.h"
@ -36,7 +35,7 @@
#include <android/log.h> #include <android/log.h>
#define NUM_BUFFERS 2 /* -- Don't lower this! */ #define NUM_BUFFERS 2 // -- Don't lower this!
struct SDL_PrivateAudioData struct SDL_PrivateAudioData
{ {
@ -83,14 +82,14 @@ struct SDL_PrivateAudioData
#define SL_ANDROID_SPEAKER_5DOT1 (SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY) #define SL_ANDROID_SPEAKER_5DOT1 (SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY)
#define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT) #define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT)
/* engine interfaces */ // engine interfaces
static SLObjectItf engineObject = NULL; static SLObjectItf engineObject = NULL;
static SLEngineItf engineEngine = NULL; static SLEngineItf engineEngine = NULL;
/* output mix interfaces */ // output mix interfaces
static SLObjectItf outputMixObject = NULL; static SLObjectItf outputMixObject = NULL;
/* buffer queue player interfaces */ // buffer queue player interfaces
static SLObjectItf bqPlayerObject = NULL; static SLObjectItf bqPlayerObject = NULL;
static SLPlayItf bqPlayerPlay = NULL; static SLPlayItf bqPlayerPlay = NULL;
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL; static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
@ -98,7 +97,7 @@ static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
static SLVolumeItf bqPlayerVolume; static SLVolumeItf bqPlayerVolume;
#endif #endif
/* recorder interfaces */ // recorder interfaces
static SLObjectItf recorderObject = NULL; static SLObjectItf recorderObject = NULL;
static SLRecordItf recorderRecord = NULL; static SLRecordItf recorderRecord = NULL;
static SLAndroidSimpleBufferQueueItf recorderBufferQueue = NULL; static SLAndroidSimpleBufferQueueItf recorderBufferQueue = NULL;
@ -123,13 +122,13 @@ static void openslES_DestroyEngine(void)
{ {
LOGI("openslES_DestroyEngine()"); LOGI("openslES_DestroyEngine()");
/* destroy output mix object, and invalidate all associated interfaces */ // destroy output mix object, and invalidate all associated interfaces
if (outputMixObject != NULL) { if (outputMixObject != NULL) {
(*outputMixObject)->Destroy(outputMixObject); (*outputMixObject)->Destroy(outputMixObject);
outputMixObject = NULL; outputMixObject = NULL;
} }
/* destroy engine object, and invalidate all associated interfaces */ // destroy engine object, and invalidate all associated interfaces
if (engineObject != NULL) { if (engineObject != NULL) {
(*engineObject)->Destroy(engineObject); (*engineObject)->Destroy(engineObject);
engineObject = NULL; engineObject = NULL;
@ -145,7 +144,7 @@ static int openslES_CreateEngine(void)
LOGI("openSLES_CreateEngine()"); LOGI("openSLES_CreateEngine()");
/* create engine */ // create engine
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("slCreateEngine failed: %d", result); LOGE("slCreateEngine failed: %d", result);
@ -153,7 +152,7 @@ static int openslES_CreateEngine(void)
} }
LOGI("slCreateEngine OK"); LOGI("slCreateEngine OK");
/* realize the engine */ // realize the engine
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeEngine failed: %d", result); LOGE("RealizeEngine failed: %d", result);
@ -161,7 +160,7 @@ static int openslES_CreateEngine(void)
} }
LOGI("RealizeEngine OK"); LOGI("RealizeEngine OK");
/* get the engine interface, which is needed in order to create other objects */ // get the engine interface, which is needed in order to create other objects
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("EngineGetInterface failed: %d", result); LOGE("EngineGetInterface failed: %d", result);
@ -169,7 +168,7 @@ static int openslES_CreateEngine(void)
} }
LOGI("EngineGetInterface OK"); LOGI("EngineGetInterface OK");
/* create output mix */ // create output mix
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req); result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("CreateOutputMix failed: %d", result); LOGE("CreateOutputMix failed: %d", result);
@ -177,7 +176,7 @@ static int openslES_CreateEngine(void)
} }
LOGI("CreateOutputMix OK"); LOGI("CreateOutputMix OK");
/* realize the output mix */ // realize the output mix
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeOutputMix failed: %d", result); LOGE("RealizeOutputMix failed: %d", result);
@ -190,7 +189,7 @@ error:
return 0; return 0;
} }
/* this callback handler is called every time a buffer finishes recording */ // this callback handler is called every time a buffer finishes recording
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context) static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{ {
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context; struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context;
@ -199,12 +198,12 @@ static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
SDL_PostSemaphore(audiodata->playsem); SDL_PostSemaphore(audiodata->playsem);
} }
static void openslES_DestroyPCMRecorder(SDL_AudioDevice *_this) static void openslES_DestroyPCMRecorder(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *audiodata = _this->hidden; struct SDL_PrivateAudioData *audiodata = device->hidden;
SLresult result; SLresult result;
/* stop recording */ // stop recording
if (recorderRecord != NULL) { if (recorderRecord != NULL) {
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED); result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
@ -212,7 +211,7 @@ static void openslES_DestroyPCMRecorder(SDL_AudioDevice *_this)
} }
} }
/* destroy audio recorder object, and invalidate all associated interfaces */ // destroy audio recorder object, and invalidate all associated interfaces
if (recorderObject != NULL) { if (recorderObject != NULL) {
(*recorderObject)->Destroy(recorderObject); (*recorderObject)->Destroy(recorderObject);
recorderObject = NULL; recorderObject = NULL;
@ -230,9 +229,9 @@ static void openslES_DestroyPCMRecorder(SDL_AudioDevice *_this)
} }
} }
static int openslES_CreatePCMRecorder(SDL_AudioDevice *_this) static int openslES_CreatePCMRecorder(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *audiodata = _this->hidden; struct SDL_PrivateAudioData *audiodata = device->hidden;
SLDataFormat_PCM format_pcm; SLDataFormat_PCM format_pcm;
SLDataLocator_AndroidSimpleBufferQueue loc_bufq; SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
SLDataSink audioSnk; SLDataSink audioSnk;
@ -248,19 +247,19 @@ static int openslES_CreatePCMRecorder(SDL_AudioDevice *_this)
return SDL_SetError("This app doesn't have RECORD_AUDIO permission"); return SDL_SetError("This app doesn't have RECORD_AUDIO permission");
} }
/* Just go with signed 16-bit audio as it's the most compatible */ // Just go with signed 16-bit audio as it's the most compatible
_this->spec.format = SDL_AUDIO_S16SYS; device->spec.format = SDL_AUDIO_S16SYS;
_this->spec.channels = 1; device->spec.channels = 1;
/*_this->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/ //device->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/
/* Update the fragment size as size in bytes */ // Update the fragment size as size in bytes
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
LOGI("Try to open %u hz %u bit chan %u %s samples %u", LOGI("Try to open %u hz %u bit chan %u %s samples %u",
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format), device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples); device->spec.channels, (device->spec.format & 0x1000) ? "BE" : "LE", device->sample_frames);
/* configure audio source */ // configure audio source
loc_dev.locatorType = SL_DATALOCATOR_IODEVICE; loc_dev.locatorType = SL_DATALOCATOR_IODEVICE;
loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT; loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT;
loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
@ -268,93 +267,93 @@ static int openslES_CreatePCMRecorder(SDL_AudioDevice *_this)
audioSrc.pLocator = &loc_dev; audioSrc.pLocator = &loc_dev;
audioSrc.pFormat = NULL; audioSrc.pFormat = NULL;
/* configure audio sink */ // configure audio sink
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
loc_bufq.numBuffers = NUM_BUFFERS; loc_bufq.numBuffers = NUM_BUFFERS;
format_pcm.formatType = SL_DATAFORMAT_PCM; format_pcm.formatType = SL_DATAFORMAT_PCM;
format_pcm.numChannels = _this->spec.channels; format_pcm.numChannels = device->spec.channels;
format_pcm.samplesPerSec = _this->spec.freq * 1000; /* / kilo Hz to milli Hz */ format_pcm.samplesPerSec = device->spec.freq * 1000; // / kilo Hz to milli Hz
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format); format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
format_pcm.containerSize = SDL_AUDIO_BITSIZE(_this->spec.format); format_pcm.containerSize = SDL_AUDIO_BITSIZE(device->spec.format);
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER; format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER;
audioSnk.pLocator = &loc_bufq; audioSnk.pLocator = &loc_bufq;
audioSnk.pFormat = &format_pcm; audioSnk.pFormat = &format_pcm;
/* create audio recorder */ // create audio recorder
/* (requires the RECORD_AUDIO permission) */ // (requires the RECORD_AUDIO permission)
result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk, 1, ids, req); result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk, 1, ids, req);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("CreateAudioRecorder failed: %d", result); LOGE("CreateAudioRecorder failed: %d", result);
goto failed; goto failed;
} }
/* realize the recorder */ // realize the recorder
result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE); result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeAudioPlayer failed: %d", result); LOGE("RealizeAudioPlayer failed: %d", result);
goto failed; goto failed;
} }
/* get the record interface */ // get the record interface
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord); result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_RECORD interface get failed: %d", result); LOGE("SL_IID_RECORD interface get failed: %d", result);
goto failed; goto failed;
} }
/* get the buffer queue interface */ // get the buffer queue interface
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue); result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result); LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
goto failed; goto failed;
} }
/* register callback on the buffer queue */ // register callback on the buffer queue
/* context is '(SDL_PrivateAudioData *)_this->hidden' */ // context is '(SDL_PrivateAudioData *)device->hidden'
result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback, _this->hidden); result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback, device->hidden);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RegisterCallback failed: %d", result); LOGE("RegisterCallback failed: %d", result);
goto failed; goto failed;
} }
/* Create the audio buffer semaphore */ // Create the audio buffer semaphore
audiodata->playsem = SDL_CreateSemaphore(0); audiodata->playsem = SDL_CreateSemaphore(0);
if (!audiodata->playsem) { if (!audiodata->playsem) {
LOGE("cannot create Semaphore!"); LOGE("cannot create Semaphore!");
goto failed; goto failed;
} }
/* Create the sound buffers */ // Create the sound buffers
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * _this->spec.size); audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * device->buffer_size);
if (audiodata->mixbuff == NULL) { if (audiodata->mixbuff == NULL) {
LOGE("mixbuffer allocate - out of memory"); LOGE("mixbuffer allocate - out of memory");
goto failed; goto failed;
} }
for (i = 0; i < NUM_BUFFERS; i++) { for (i = 0; i < NUM_BUFFERS; i++) {
audiodata->pmixbuff[i] = audiodata->mixbuff + i * _this->spec.size; audiodata->pmixbuff[i] = audiodata->mixbuff + i * device->buffer_size;
} }
/* in case already recording, stop recording and clear buffer queue */ // in case already recording, stop recording and clear buffer queue
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED); result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("Record set state failed: %d", result); LOGE("Record set state failed: %d", result);
goto failed; goto failed;
} }
/* enqueue empty buffers to be filled by the recorder */ // enqueue empty buffers to be filled by the recorder
for (i = 0; i < NUM_BUFFERS; i++) { for (i = 0; i < NUM_BUFFERS; i++) {
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[i], _this->spec.size); result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[i], device->buffer_size);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("Record enqueue buffers failed: %d", result); LOGE("Record enqueue buffers failed: %d", result);
goto failed; goto failed;
} }
} }
/* start recording */ // start recording
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING); result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("Record set state failed: %d", result); LOGE("Record set state failed: %d", result);
@ -367,7 +366,7 @@ failed:
return SDL_SetError("Open device failed!"); return SDL_SetError("Open device failed!");
} }
/* this callback handler is called every time a buffer finishes playing */ // this callback handler is called every time a buffer finishes playing
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{ {
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context; struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context;
@ -376,20 +375,19 @@ static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
SDL_PostSemaphore(audiodata->playsem); SDL_PostSemaphore(audiodata->playsem);
} }
static void openslES_DestroyPCMPlayer(SDL_AudioDevice *_this) static void openslES_DestroyPCMPlayer(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *audiodata = _this->hidden; struct SDL_PrivateAudioData *audiodata = device->hidden;
SLresult result;
/* set the player's state to 'stopped' */ // set the player's state to 'stopped'
if (bqPlayerPlay != NULL) { if (bqPlayerPlay != NULL) {
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED); const SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("SetPlayState stopped failed: %d", result); LOGE("SetPlayState stopped failed: %d", result);
} }
} }
/* destroy buffer queue audio player object, and invalidate all associated interfaces */ // destroy buffer queue audio player object, and invalidate all associated interfaces
if (bqPlayerObject != NULL) { if (bqPlayerObject != NULL) {
(*bqPlayerObject)->Destroy(bqPlayerObject); (*bqPlayerObject)->Destroy(bqPlayerObject);
@ -408,26 +406,14 @@ static void openslES_DestroyPCMPlayer(SDL_AudioDevice *_this)
} }
} }
static int openslES_CreatePCMPlayer(SDL_AudioDevice *_this) static int openslES_CreatePCMPlayer(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *audiodata = _this->hidden;
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
SLDataFormat_PCM format_pcm;
SLAndroidDataFormat_PCM_EX format_pcm_ex;
SLDataSource audioSrc;
SLDataSink audioSnk;
SLDataLocator_OutputMix loc_outmix;
const SLInterfaceID ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };
const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE };
SLresult result;
int i;
/* If we want to add floating point audio support (requires API level 21) /* If we want to add floating point audio support (requires API level 21)
it can be done as described here: it can be done as described here:
https://developer.android.com/ndk/guides/audio/opensl/android-extensions.html#floating-point https://developer.android.com/ndk/guides/audio/opensl/android-extensions.html#floating-point
*/ */
if (SDL_GetAndroidSDKVersion() >= 21) { if (SDL_GetAndroidSDKVersion() >= 21) {
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(_this->spec.format); const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
SDL_AudioFormat test_format; SDL_AudioFormat test_format;
while ((test_format = *(closefmts++)) != 0) { while ((test_format = *(closefmts++)) != 0) {
if (SDL_AUDIO_ISSIGNED(test_format)) { if (SDL_AUDIO_ISSIGNED(test_format)) {
@ -436,40 +422,42 @@ static int openslES_CreatePCMPlayer(SDL_AudioDevice *_this)
} }
if (!test_format) { if (!test_format) {
/* Didn't find a compatible format : */ // Didn't find a compatible format :
LOGI("No compatible audio format, using signed 16-bit audio"); LOGI("No compatible audio format, using signed 16-bit audio");
test_format = SDL_AUDIO_S16SYS; test_format = SDL_AUDIO_S16SYS;
} }
_this->spec.format = test_format; device->spec.format = test_format;
} else { } else {
/* Just go with signed 16-bit audio as it's the most compatible */ // Just go with signed 16-bit audio as it's the most compatible
_this->spec.format = SDL_AUDIO_S16SYS; device->spec.format = SDL_AUDIO_S16SYS;
} }
/* Update the fragment size as size in bytes */ // Update the fragment size as size in bytes
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
LOGI("Try to open %u hz %s %u bit chan %u %s samples %u", LOGI("Try to open %u hz %s %u bit chan %u %s samples %u",
_this->spec.freq, SDL_AUDIO_ISFLOAT(_this->spec.format) ? "float" : "pcm", SDL_AUDIO_BITSIZE(_this->spec.format), device->spec.freq, SDL_AUDIO_ISFLOAT(device->spec.format) ? "float" : "pcm", SDL_AUDIO_BITSIZE(device->spec.format),
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples); device->spec.channels, (device->spec.format & 0x1000) ? "BE" : "LE", device->sample_frames);
/* configure audio source */ // configure audio source
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
loc_bufq.numBuffers = NUM_BUFFERS; loc_bufq.numBuffers = NUM_BUFFERS;
SLDataFormat_PCM format_pcm;
format_pcm.formatType = SL_DATAFORMAT_PCM; format_pcm.formatType = SL_DATAFORMAT_PCM;
format_pcm.numChannels = _this->spec.channels; format_pcm.numChannels = device->spec.channels;
format_pcm.samplesPerSec = _this->spec.freq * 1000; /* / kilo Hz to milli Hz */ format_pcm.samplesPerSec = device->spec.freq * 1000; // / kilo Hz to milli Hz
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format); format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
format_pcm.containerSize = SDL_AUDIO_BITSIZE(_this->spec.format); format_pcm.containerSize = SDL_AUDIO_BITSIZE(device->spec.format);
if (SDL_AUDIO_ISBIGENDIAN(_this->spec.format)) { if (SDL_AUDIO_ISBIGENDIAN(device->spec.format)) {
format_pcm.endianness = SL_BYTEORDER_BIGENDIAN; format_pcm.endianness = SL_BYTEORDER_BIGENDIAN;
} else { } else {
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
} }
switch (_this->spec.channels) { switch (device->spec.channels) {
case 1: case 1:
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT; format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT;
break; break;
@ -495,14 +483,19 @@ static int openslES_CreatePCMPlayer(SDL_AudioDevice *_this)
format_pcm.channelMask = SL_ANDROID_SPEAKER_7DOT1; format_pcm.channelMask = SL_ANDROID_SPEAKER_7DOT1;
break; break;
default: default:
/* Unknown number of channels, fall back to stereo */ // Unknown number of channels, fall back to stereo
_this->spec.channels = 2; device->spec.channels = 2;
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
break; break;
} }
if (SDL_AUDIO_ISFLOAT(_this->spec.format)) { SLDataSink audioSnk;
/* Copy all setup into PCM EX structure */ SLDataSource audioSrc;
audioSrc.pFormat = (void *)&format_pcm;
SLAndroidDataFormat_PCM_EX format_pcm_ex;
if (SDL_AUDIO_ISFLOAT(device->spec.format)) {
// Copy all setup into PCM EX structure
format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX; format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
format_pcm_ex.endianness = format_pcm.endianness; format_pcm_ex.endianness = format_pcm.endianness;
format_pcm_ex.channelMask = format_pcm.channelMask; format_pcm_ex.channelMask = format_pcm.channelMask;
@ -511,81 +504,87 @@ static int openslES_CreatePCMPlayer(SDL_AudioDevice *_this)
format_pcm_ex.bitsPerSample = format_pcm.bitsPerSample; format_pcm_ex.bitsPerSample = format_pcm.bitsPerSample;
format_pcm_ex.containerSize = format_pcm.containerSize; format_pcm_ex.containerSize = format_pcm.containerSize;
format_pcm_ex.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; format_pcm_ex.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
audioSrc.pFormat = (void *)&format_pcm_ex;
} }
audioSrc.pLocator = &loc_bufq; audioSrc.pLocator = &loc_bufq;
audioSrc.pFormat = SDL_AUDIO_ISFLOAT(_this->spec.format) ? (void *)&format_pcm_ex : (void *)&format_pcm;
/* configure audio sink */ // configure audio sink
SLDataLocator_OutputMix loc_outmix;
loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
loc_outmix.outputMix = outputMixObject; loc_outmix.outputMix = outputMixObject;
audioSnk.pLocator = &loc_outmix; audioSnk.pLocator = &loc_outmix;
audioSnk.pFormat = NULL; audioSnk.pFormat = NULL;
/* create audio player */ // create audio player
const SLInterfaceID ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };
const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE };
SLresult result;
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req); result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("CreateAudioPlayer failed: %d", result); LOGE("CreateAudioPlayer failed: %d", result);
goto failed; goto failed;
} }
/* realize the player */ // realize the player
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE); result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeAudioPlayer failed: %d", result); LOGE("RealizeAudioPlayer failed: %d", result);
goto failed; goto failed;
} }
/* get the play interface */ // get the play interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay); result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_PLAY interface get failed: %d", result); LOGE("SL_IID_PLAY interface get failed: %d", result);
goto failed; goto failed;
} }
/* get the buffer queue interface */ // get the buffer queue interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue); result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result); LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
goto failed; goto failed;
} }
/* register callback on the buffer queue */ // register callback on the buffer queue
/* context is '(SDL_PrivateAudioData *)_this->hidden' */ // context is '(SDL_PrivateAudioData *)device->hidden'
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, _this->hidden); result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, device->hidden);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RegisterCallback failed: %d", result); LOGE("RegisterCallback failed: %d", result);
goto failed; goto failed;
} }
#if 0 #if 0
/* get the volume interface */ // get the volume interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume); result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_VOLUME interface get failed: %d", result); LOGE("SL_IID_VOLUME interface get failed: %d", result);
/* goto failed; */ // goto failed;
} }
#endif #endif
/* Create the audio buffer semaphore */ struct SDL_PrivateAudioData *audiodata = device->hidden;
// Create the audio buffer semaphore
audiodata->playsem = SDL_CreateSemaphore(NUM_BUFFERS - 1); audiodata->playsem = SDL_CreateSemaphore(NUM_BUFFERS - 1);
if (!audiodata->playsem) { if (!audiodata->playsem) {
LOGE("cannot create Semaphore!"); LOGE("cannot create Semaphore!");
goto failed; goto failed;
} }
/* Create the sound buffers */ // Create the sound buffers
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * _this->spec.size); audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * device->buffer_size);
if (audiodata->mixbuff == NULL) { if (audiodata->mixbuff == NULL) {
LOGE("mixbuffer allocate - out of memory"); LOGE("mixbuffer allocate - out of memory");
goto failed; goto failed;
} }
for (i = 0; i < NUM_BUFFERS; i++) { for (int i = 0; i < NUM_BUFFERS; i++) {
audiodata->pmixbuff[i] = audiodata->mixbuff + i * _this->spec.size; audiodata->pmixbuff[i] = audiodata->mixbuff + i * device->buffer_size;
} }
/* set the player's state to playing */ // set the player's state to playing
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("Play set state failed: %d", result); LOGE("Play set state failed: %d", result);
@ -598,103 +597,98 @@ failed:
return -1; return -1;
} }
static int openslES_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int openslES_OpenDevice(SDL_AudioDevice *device)
{ {
_this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*_this->hidden)); device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
if (_this->hidden == NULL) { if (device->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
if (_this->iscapture) { if (device->iscapture) {
LOGI("openslES_OpenDevice() %s for capture", devname); LOGI("openslES_OpenDevice() for capture");
return openslES_CreatePCMRecorder(_this); return openslES_CreatePCMRecorder(device);
} else { } else {
int ret; int ret;
LOGI("openslES_OpenDevice() %s for playing", devname); LOGI("openslES_OpenDevice() for playing");
ret = openslES_CreatePCMPlayer(_this); ret = openslES_CreatePCMPlayer(device);
if (ret < 0) { if (ret < 0) {
/* Another attempt to open the device with a lower frequency */ // Another attempt to open the device with a lower frequency
if (_this->spec.freq > 48000) { if (device->spec.freq > 48000) {
openslES_DestroyPCMPlayer(_this); openslES_DestroyPCMPlayer(device);
_this->spec.freq = 48000; device->spec.freq = 48000;
ret = openslES_CreatePCMPlayer(_this); ret = openslES_CreatePCMPlayer(device);
} }
} }
if (ret == 0) { if (ret != 0) {
return 0;
} else {
return SDL_SetError("Open device failed!"); return SDL_SetError("Open device failed!");
} }
} }
return 0;
} }
static void openslES_WaitDevice(SDL_AudioDevice *_this) static void openslES_WaitDevice(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *audiodata = _this->hidden; struct SDL_PrivateAudioData *audiodata = device->hidden;
LOGV("openslES_WaitDevice()"); LOGV("openslES_WaitDevice()");
/* Wait for an audio chunk to finish */ // Wait for an audio chunk to finish
SDL_WaitSemaphore(audiodata->playsem); SDL_WaitSemaphore(audiodata->playsem);
} }
static void openslES_PlayDevice(SDL_AudioDevice *_this) static void openslES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{ {
struct SDL_PrivateAudioData *audiodata = _this->hidden; struct SDL_PrivateAudioData *audiodata = device->hidden;
SLresult result;
LOGV("======openslES_PlayDevice()======"); LOGV("======openslES_PlayDevice()======");
/* Queue it up */ // Queue it up
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], _this->spec.size); const SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer, buflen);
audiodata->next_buffer++; audiodata->next_buffer++;
if (audiodata->next_buffer >= NUM_BUFFERS) { if (audiodata->next_buffer >= NUM_BUFFERS) {
audiodata->next_buffer = 0; audiodata->next_buffer = 0;
} }
/* If Enqueue fails, callback won't be called. // If Enqueue fails, callback won't be called.
* Post the semaphore, not to run out of buffer */ // Post the semaphore, not to run out of buffer
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
SDL_PostSemaphore(audiodata->playsem); SDL_PostSemaphore(audiodata->playsem);
} }
} }
/*/ n playn sem */ /// n playn sem
/* getbuf 0 - 1 */ // getbuf 0 - 1
/* fill buff 0 - 1 */ // fill buff 0 - 1
/* play 0 - 0 1 */ // play 0 - 0 1
/* wait 1 0 0 */ // wait 1 0 0
/* getbuf 1 0 0 */ // getbuf 1 0 0
/* fill buff 1 0 0 */ // fill buff 1 0 0
/* play 0 0 0 */ // play 0 0 0
/* wait */ // wait
/* */ //
/* okay.. */ // okay..
static Uint8 *openslES_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *openslES_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize)
{ {
struct SDL_PrivateAudioData *audiodata = _this->hidden; struct SDL_PrivateAudioData *audiodata = device->hidden;
LOGV("openslES_GetDeviceBuf()"); LOGV("openslES_GetDeviceBuf()");
return audiodata->pmixbuff[audiodata->next_buffer]; return audiodata->pmixbuff[audiodata->next_buffer];
} }
static int openslES_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) static int openslES_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{ {
struct SDL_PrivateAudioData *audiodata = _this->hidden; struct SDL_PrivateAudioData *audiodata = device->hidden;
SLresult result;
/* Wait for new recorded data */ // Copy it to the output buffer
SDL_WaitSemaphore(audiodata->playsem); SDL_assert(buflen == device->buffer_size);
SDL_memcpy(buffer, audiodata->pmixbuff[audiodata->next_buffer], device->buffer_size);
/* Copy it to the output buffer */ // Re-enqueue the buffer
SDL_assert(buflen == _this->spec.size); const SLresult result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], device->buffer_size);
SDL_memcpy(buffer, audiodata->pmixbuff[audiodata->next_buffer], _this->spec.size);
/* Re-enqueue the buffer */
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], _this->spec.size);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("Record enqueue buffers failed: %d", result); LOGE("Record enqueue buffers failed: %d", result);
return -1; return -1;
@ -705,22 +699,24 @@ static int openslES_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int
audiodata->next_buffer = 0; audiodata->next_buffer = 0;
} }
return _this->spec.size; return device->buffer_size;
} }
static void openslES_CloseDevice(SDL_AudioDevice *_this) static void openslES_CloseDevice(SDL_AudioDevice *device)
{ {
/* struct SDL_PrivateAudioData *audiodata = _this->hidden; */ // struct SDL_PrivateAudioData *audiodata = device->hidden;
if (device->hidden) {
if (_this->iscapture) { if (device->iscapture) {
LOGI("openslES_CloseDevice() for capture"); LOGI("openslES_CloseDevice() for capture");
openslES_DestroyPCMRecorder(_this); openslES_DestroyPCMRecorder(device);
} else { } else {
LOGI("openslES_CloseDevice() for playing"); LOGI("openslES_CloseDevice() for playing");
openslES_DestroyPCMPlayer(_this); openslES_DestroyPCMPlayer(device);
} }
SDL_free(_this->hidden); SDL_free(device->hidden);
device->hidden = NULL;
}
} }
static SDL_bool openslES_Init(SDL_AudioDriverImpl *impl) static SDL_bool openslES_Init(SDL_AudioDriverImpl *impl)
@ -733,24 +729,26 @@ static SDL_bool openslES_Init(SDL_AudioDriverImpl *impl)
LOGI("openslES_Init() - set pointers"); LOGI("openslES_Init() - set pointers");
/* Set the function pointers */ // Set the function pointers
/* impl->DetectDevices = openslES_DetectDevices; */ // impl->DetectDevices = openslES_DetectDevices;
impl->ThreadInit = Android_AudioThreadInit;
impl->OpenDevice = openslES_OpenDevice; impl->OpenDevice = openslES_OpenDevice;
impl->WaitDevice = openslES_WaitDevice; impl->WaitDevice = openslES_WaitDevice;
impl->PlayDevice = openslES_PlayDevice; impl->PlayDevice = openslES_PlayDevice;
impl->GetDeviceBuf = openslES_GetDeviceBuf; impl->GetDeviceBuf = openslES_GetDeviceBuf;
impl->WaitCaptureDevice = openslES_WaitDevice;
impl->CaptureFromDevice = openslES_CaptureFromDevice; impl->CaptureFromDevice = openslES_CaptureFromDevice;
impl->CloseDevice = openslES_CloseDevice; impl->CloseDevice = openslES_CloseDevice;
impl->Deinitialize = openslES_DestroyEngine; impl->Deinitialize = openslES_DestroyEngine;
/* and the capabilities */ // and the capabilities
impl->HasCaptureSupport = SDL_TRUE; impl->HasCaptureSupport = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE; impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE; impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
LOGI("openslES_Init() - success"); LOGI("openslES_Init() - success");
/* this audio target is available. */ // this audio target is available.
return SDL_TRUE; return SDL_TRUE;
} }
@ -761,7 +759,7 @@ AudioBootStrap openslES_bootstrap = {
void openslES_ResumeDevices(void) void openslES_ResumeDevices(void)
{ {
if (bqPlayerPlay != NULL) { if (bqPlayerPlay != NULL) {
/* set the player's state to 'playing' */ // set the player's state to 'playing'
SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("openslES_ResumeDevices failed: %d", result); LOGE("openslES_ResumeDevices failed: %d", result);
@ -772,7 +770,7 @@ void openslES_ResumeDevices(void)
void openslES_PauseDevices(void) void openslES_PauseDevices(void)
{ {
if (bqPlayerPlay != NULL) { if (bqPlayerPlay != NULL) {
/* set the player's state to 'paused' */ // set the player's state to 'paused'
SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED); SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("openslES_PauseDevices failed: %d", result); LOGE("openslES_PauseDevices failed: %d", result);
@ -780,4 +778,4 @@ void openslES_PauseDevices(void)
} }
} }
#endif /* SDL_AUDIO_DRIVER_OPENSLES */ #endif // SDL_AUDIO_DRIVER_OPENSLES

View File

@ -327,7 +327,7 @@ static void io_list_remove(Uint32 id)
spa_list_remove(&n->link); spa_list_remove(&n->link);
if (hotplug_events_enabled) { if (hotplug_events_enabled) {
SDL_RemoveAudioDevice(n->is_capture, PW_ID_TO_HANDLE(id)); SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle(PW_ID_TO_HANDLE(id)));
} }
SDL_free(n); SDL_free(n);
@ -337,31 +337,6 @@ static void io_list_remove(Uint32 id)
} }
} }
static void io_list_sort(void)
{
struct io_node *default_sink = NULL, *default_source = NULL;
struct io_node *n, *temp;
/* Find and move the default nodes to the beginning of the list */
spa_list_for_each_safe (n, temp, &hotplug_io_list, link) {
if (pipewire_default_sink_id != NULL && SDL_strcmp(n->path, pipewire_default_sink_id) == 0) {
default_sink = n;
spa_list_remove(&n->link);
} else if (pipewire_default_source_id != NULL && SDL_strcmp(n->path, pipewire_default_source_id) == 0) {
default_source = n;
spa_list_remove(&n->link);
}
}
if (default_source) {
spa_list_prepend(&hotplug_io_list, &default_source->link);
}
if (default_sink) {
spa_list_prepend(&hotplug_io_list, &default_sink->link);
}
}
static void io_list_clear(void) static void io_list_clear(void)
{ {
struct io_node *n, *temp; struct io_node *n, *temp;
@ -383,17 +358,6 @@ static struct io_node *io_list_get_by_id(Uint32 id)
return NULL; return NULL;
} }
static struct io_node *io_list_get_by_path(char *path)
{
struct io_node *n, *temp;
spa_list_for_each_safe (n, temp, &hotplug_io_list, link) {
if (SDL_strcmp(n->path, path) == 0) {
return n;
}
}
return NULL;
}
static void node_object_destroy(struct node_object *node) static void node_object_destroy(struct node_object *node)
{ {
SDL_assert(node); SDL_assert(node);
@ -640,6 +604,19 @@ static char *get_name_from_json(const char *json)
return SDL_strdup(value); return SDL_strdup(value);
} }
static void change_default_device(const char *path)
{
if (hotplug_events_enabled) {
struct io_node *n, *temp;
spa_list_for_each_safe (n, temp, &hotplug_io_list, link) {
if (SDL_strcmp(n->path, path) == 0) {
SDL_DefaultAudioDeviceChanged(SDL_FindPhysicalAudioDeviceByHandle(PW_ID_TO_HANDLE(n->id)));
return; // found it, we're done.
}
}
}
}
/* Metadata node callback */ /* Metadata node callback */
static int metadata_property(void *object, Uint32 subject, const char *key, const char *type, const char *value) static int metadata_property(void *object, Uint32 subject, const char *key, const char *type, const char *value)
{ {
@ -652,12 +629,14 @@ static int metadata_property(void *object, Uint32 subject, const char *key, cons
} }
pipewire_default_sink_id = get_name_from_json(value); pipewire_default_sink_id = get_name_from_json(value);
node->persist = SDL_TRUE; node->persist = SDL_TRUE;
change_default_device(pipewire_default_sink_id);
} else if (!SDL_strcmp(key, "default.audio.source")) { } else if (!SDL_strcmp(key, "default.audio.source")) {
if (pipewire_default_source_id != NULL) { if (pipewire_default_source_id != NULL) {
SDL_free(pipewire_default_source_id); SDL_free(pipewire_default_source_id);
} }
pipewire_default_source_id = get_name_from_json(value); pipewire_default_source_id = get_name_from_json(value);
node->persist = SDL_TRUE; node->persist = SDL_TRUE;
change_default_device(pipewire_default_source_id);
} }
} }
@ -833,7 +812,7 @@ static void hotplug_loop_destroy(void)
} }
} }
static void PIPEWIRE_DetectDevices(void) static void PIPEWIRE_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{ {
struct io_node *io; struct io_node *io;
@ -844,11 +823,15 @@ static void PIPEWIRE_DetectDevices(void)
PIPEWIRE_pw_thread_loop_wait(hotplug_loop); PIPEWIRE_pw_thread_loop_wait(hotplug_loop);
} }
/* Sort the I/O list so the default source/sink are listed first */
io_list_sort();
spa_list_for_each (io, &hotplug_io_list, link) { spa_list_for_each (io, &hotplug_io_list, link) {
SDL_AddAudioDevice(io->is_capture, io->name, &io->spec, PW_ID_TO_HANDLE(io->id)); SDL_AudioDevice *device = SDL_AddAudioDevice(io->is_capture, io->name, &io->spec, PW_ID_TO_HANDLE(io->id));
if (pipewire_default_sink_id != NULL && SDL_strcmp(io->path, pipewire_default_sink_id) == 0) {
SDL_assert(!io->is_capture);
*default_output = device;
} else if (pipewire_default_source_id != NULL && SDL_strcmp(io->path, pipewire_default_source_id) == 0) {
SDL_assert(io->is_capture);
*default_capture = device;
}
} }
hotplug_events_enabled = SDL_TRUE; hotplug_events_enabled = SDL_TRUE;
@ -936,167 +919,115 @@ static void initialize_spa_info(const SDL_AudioSpec *spec, struct spa_audio_info
} }
} }
static void output_callback(void *data) static Uint8 *PIPEWIRE_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
struct pw_buffer *pw_buf; // See if a buffer is available. If this returns NULL, SDL_OutputAudioThreadIterate will return SDL_FALSE, but since we own the thread, it won't kill playback.
struct spa_buffer *spa_buf; // !!! FIXME: It's not clear to me if this ever returns NULL or if this was just defensive coding.
Uint8 *dst;
SDL_AudioDevice *_this = (SDL_AudioDevice *)data; struct pw_stream *stream = device->hidden->stream;
struct pw_stream *stream = _this->hidden->stream; struct pw_buffer *pw_buf = PIPEWIRE_pw_stream_dequeue_buffer(stream);
/* Shutting down, don't do anything */
if (SDL_AtomicGet(&_this->shutdown)) {
return;
}
/* See if a buffer is available */
pw_buf = PIPEWIRE_pw_stream_dequeue_buffer(stream);
if (pw_buf == NULL) { if (pw_buf == NULL) {
return; return NULL;
} }
spa_buf = pw_buf->buffer; struct spa_buffer *spa_buf = pw_buf->buffer;
if (spa_buf->datas[0].data == NULL) { if (spa_buf->datas[0].data == NULL) {
return; PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf);
return NULL;
} }
/* device->hidden->pw_buf = pw_buf;
* If the device is disabled, write silence to the stream buffer return (Uint8 *) spa_buf->datas[0].data;
* and run the callback with the work buffer to keep the callback
* firing regularly in case the audio is being used as a timer.
*/
SDL_LockMutex(_this->mixer_lock);
if (!SDL_AtomicGet(&_this->paused)) {
if (SDL_AtomicGet(&_this->enabled)) {
dst = spa_buf->datas[0].data;
} else {
dst = _this->work_buffer;
SDL_memset(spa_buf->datas[0].data, _this->spec.silence, _this->spec.size);
} }
if (!_this->stream) { static void PIPEWIRE_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
_this->callbackspec.callback(_this->callbackspec.userdata, dst, _this->callbackspec.size); {
} else { struct pw_stream *stream = device->hidden->stream;
int got; struct pw_buffer *pw_buf = device->hidden->pw_buf;
struct spa_buffer *spa_buf = pw_buf->buffer;
/* Fire the callback until we have enough to fill a buffer */
while (SDL_GetAudioStreamAvailable(_this->stream) < _this->spec.size) {
_this->callbackspec.callback(_this->callbackspec.userdata, _this->work_buffer, _this->callbackspec.size);
SDL_PutAudioStreamData(_this->stream, _this->work_buffer, _this->callbackspec.size);
}
got = SDL_GetAudioStreamData(_this->stream, dst, _this->spec.size);
SDL_assert(got == _this->spec.size);
}
} else {
SDL_memset(spa_buf->datas[0].data, _this->spec.silence, _this->spec.size);
}
SDL_UnlockMutex(_this->mixer_lock);
spa_buf->datas[0].chunk->offset = 0; spa_buf->datas[0].chunk->offset = 0;
spa_buf->datas[0].chunk->stride = _this->hidden->stride; spa_buf->datas[0].chunk->stride = device->hidden->stride;
spa_buf->datas[0].chunk->size = _this->spec.size; spa_buf->datas[0].chunk->size = buffer_size;
PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf); PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf);
device->hidden->pw_buf = NULL;
}
static void output_callback(void *data)
{
SDL_OutputAudioThreadIterate((SDL_AudioDevice *)data);
}
static void PIPEWIRE_FlushCapture(SDL_AudioDevice *device)
{
struct pw_stream *stream = device->hidden->stream;
struct pw_buffer *pw_buf = PIPEWIRE_pw_stream_dequeue_buffer(stream);
if (pw_buf != NULL) { // just requeue it without any further thought.
PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf);
}
}
static int PIPEWIRE_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{
struct pw_stream *stream = device->hidden->stream;
struct pw_buffer *pw_buf = PIPEWIRE_pw_stream_dequeue_buffer(stream);
if (!pw_buf) {
return 0;
}
struct spa_buffer *spa_buf = pw_buf->buffer;
if (!spa_buf) {
PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf);
return 0;
}
const Uint8 *src = (const Uint8 *)spa_buf->datas[0].data;
const Uint32 offset = SPA_MIN(spa_buf->datas[0].chunk->offset, spa_buf->datas[0].maxsize);
const Uint32 size = SPA_MIN(spa_buf->datas[0].chunk->size, spa_buf->datas[0].maxsize - offset);
const int cpy = SDL_min(buflen, (int) size);
SDL_assert(size <= buflen); // We'll have to reengineer some stuff if this turns out to not be true.
SDL_memcpy(buffer, src + offset, cpy);
PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf);
return cpy;
} }
static void input_callback(void *data) static void input_callback(void *data)
{ {
struct pw_buffer *pw_buf; SDL_CaptureAudioThreadIterate((SDL_AudioDevice *)data);
struct spa_buffer *spa_buf;
Uint8 *src;
SDL_AudioDevice *_this = (SDL_AudioDevice *)data;
struct pw_stream *stream = _this->hidden->stream;
/* Shutting down, don't do anything */
if (SDL_AtomicGet(&_this->shutdown)) {
return;
}
pw_buf = PIPEWIRE_pw_stream_dequeue_buffer(stream);
if (pw_buf == NULL) {
return;
}
spa_buf = pw_buf->buffer;
(src = (Uint8 *)spa_buf->datas[0].data);
if (src == NULL) {
return;
}
if (!SDL_AtomicGet(&_this->paused)) {
/* Calculate the offset and data size */
const Uint32 offset = SPA_MIN(spa_buf->datas[0].chunk->offset, spa_buf->datas[0].maxsize);
const Uint32 size = SPA_MIN(spa_buf->datas[0].chunk->size, spa_buf->datas[0].maxsize - offset);
src += offset;
/* Fill the buffer with silence if the stream is disabled. */
if (!SDL_AtomicGet(&_this->enabled)) {
SDL_memset(src, _this->callbackspec.silence, size);
}
/* Pipewire can vary the latency, so buffer all incoming data */
SDL_WriteToDataQueue(_this->hidden->buffer, src, size);
while (SDL_GetDataQueueSize(_this->hidden->buffer) >= _this->callbackspec.size) {
SDL_ReadFromDataQueue(_this->hidden->buffer, _this->work_buffer, _this->callbackspec.size);
SDL_LockMutex(_this->mixer_lock);
_this->callbackspec.callback(_this->callbackspec.userdata, _this->work_buffer, _this->callbackspec.size);
SDL_UnlockMutex(_this->mixer_lock);
}
} else if (_this->hidden->buffer) { /* Flush the buffer when paused */
if (SDL_GetDataQueueSize(_this->hidden->buffer) != 0) {
SDL_ClearDataQueue(_this->hidden->buffer, _this->hidden->input_buffer_packet_size);
}
}
PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf);
} }
static void stream_add_buffer_callback(void *data, struct pw_buffer *buffer) static void stream_add_buffer_callback(void *data, struct pw_buffer *buffer)
{ {
SDL_AudioDevice *_this = data; SDL_AudioDevice *device = (SDL_AudioDevice *) data;
if (_this->iscapture == SDL_FALSE) { if (device->iscapture == SDL_FALSE) {
/* /* Clamp the output spec samples and size to the max size of the Pipewire buffer.
* Clamp the output spec samples and size to the max size of the Pipewire buffer. If they exceed the maximum size of the Pipewire buffer, double buffering will be used. */
* If they exceed the maximum size of the Pipewire buffer, double buffering will be used. if (device->buffer_size > buffer->buffer->datas[0].maxsize) {
*/ SDL_LockMutex(device->lock);
if (_this->spec.size > buffer->buffer->datas[0].maxsize) { device->sample_frames = buffer->buffer->datas[0].maxsize / device->hidden->stride;
_this->spec.samples = buffer->buffer->datas[0].maxsize / _this->hidden->stride; device->buffer_size = buffer->buffer->datas[0].maxsize;
_this->spec.size = buffer->buffer->datas[0].maxsize; SDL_UnlockMutex(device->lock);
} }
} else if (_this->hidden->buffer == NULL) {
/*
* The latency of source nodes can change, so buffering is always required.
*
* Ensure that the intermediate input buffer is large enough to hold the requested
* application packet size or a full buffer of data from Pipewire, whichever is larger.
*
* A packet size of 2 periods should be more than is ever needed.
*/
_this->hidden->input_buffer_packet_size = SPA_MAX(_this->spec.size, buffer->buffer->datas[0].maxsize) * 2;
_this->hidden->buffer = SDL_CreateDataQueue(_this->hidden->input_buffer_packet_size, _this->hidden->input_buffer_packet_size);
} }
_this->hidden->stream_init_status |= PW_READY_FLAG_BUFFER_ADDED; device->hidden->stream_init_status |= PW_READY_FLAG_BUFFER_ADDED;
PIPEWIRE_pw_thread_loop_signal(_this->hidden->loop, false); PIPEWIRE_pw_thread_loop_signal(device->hidden->loop, false);
} }
static void stream_state_changed_callback(void *data, enum pw_stream_state old, enum pw_stream_state state, const char *error) static void stream_state_changed_callback(void *data, enum pw_stream_state old, enum pw_stream_state state, const char *error)
{ {
SDL_AudioDevice *_this = data; SDL_AudioDevice *device = (SDL_AudioDevice *) data;
if (state == PW_STREAM_STATE_STREAMING) { if (state == PW_STREAM_STATE_STREAMING) {
_this->hidden->stream_init_status |= PW_READY_FLAG_STREAM_READY; device->hidden->stream_init_status |= PW_READY_FLAG_STREAM_READY;
} }
if (state == PW_STREAM_STATE_STREAMING || state == PW_STREAM_STATE_ERROR) { if (state == PW_STREAM_STATE_STREAMING || state == PW_STREAM_STATE_ERROR) {
PIPEWIRE_pw_thread_loop_signal(_this->hidden->loop, false); PIPEWIRE_pw_thread_loop_signal(device->hidden->loop, false);
} }
} }
@ -1109,7 +1040,7 @@ static const struct pw_stream_events stream_input_events = { PW_VERSION_STREAM_E
.add_buffer = stream_add_buffer_callback, .add_buffer = stream_add_buffer_callback,
.process = input_callback }; .process = input_callback };
static int PIPEWIRE_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int PIPEWIRE_OpenDevice(SDL_AudioDevice *device)
{ {
/* /*
* NOTE: The PW_STREAM_FLAG_RT_PROCESS flag can be set to call the stream * NOTE: The PW_STREAM_FLAG_RT_PROCESS flag can be set to call the stream
@ -1128,12 +1059,12 @@ static int PIPEWIRE_OpenDevice(SDL_AudioDevice *_this, const char *devname)
struct SDL_PrivateAudioData *priv; struct SDL_PrivateAudioData *priv;
struct pw_properties *props; struct pw_properties *props;
const char *app_name, *app_id, *stream_name, *stream_role, *error; const char *app_name, *app_id, *stream_name, *stream_role, *error;
Uint32 node_id = _this->handle == NULL ? PW_ID_ANY : PW_HANDLE_TO_ID(_this->handle); Uint32 node_id = device->handle == NULL ? PW_ID_ANY : PW_HANDLE_TO_ID(device->handle);
SDL_bool iscapture = _this->iscapture; const SDL_bool iscapture = device->iscapture;
int res; int res;
/* Clamp the period size to sane values */ /* Clamp the period size to sane values */
const int min_period = PW_MIN_SAMPLES * SPA_MAX(_this->spec.freq / PW_BASE_CLOCK_RATE, 1); const int min_period = PW_MIN_SAMPLES * SPA_MAX(device->spec.freq / PW_BASE_CLOCK_RATE, 1);
/* Get the hints for the application name, stream name and role */ /* Get the hints for the application name, stream name and role */
app_name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME); app_name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME);
@ -1162,27 +1093,28 @@ static int PIPEWIRE_OpenDevice(SDL_AudioDevice *_this, const char *devname)
} }
/* Initialize the Pipewire stream info from the SDL audio spec */ /* Initialize the Pipewire stream info from the SDL audio spec */
initialize_spa_info(&_this->spec, &spa_info); initialize_spa_info(&device->spec, &spa_info);
params = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &spa_info); params = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &spa_info);
if (params == NULL) { if (params == NULL) {
return SDL_SetError("Pipewire: Failed to set audio format parameters"); return SDL_SetError("Pipewire: Failed to set audio format parameters");
} }
priv = SDL_calloc(1, sizeof(struct SDL_PrivateAudioData)); priv = SDL_calloc(1, sizeof(struct SDL_PrivateAudioData));
_this->hidden = priv; device->hidden = priv;
if (priv == NULL) { if (priv == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
/* Size of a single audio frame in bytes */ /* Size of a single audio frame in bytes */
priv->stride = (SDL_AUDIO_BITSIZE(_this->spec.format) >> 3) * _this->spec.channels; priv->stride = (SDL_AUDIO_BITSIZE(device->spec.format) / 8) * device->spec.channels;
if (_this->spec.samples < min_period) { if (device->sample_frames < min_period) {
_this->spec.samples = min_period; device->sample_frames = min_period;
_this->spec.size = _this->spec.samples * priv->stride;
} }
(void)SDL_snprintf(thread_name, sizeof(thread_name), "SDLAudio%c%ld", (iscapture) ? 'C' : 'P', (long)_this->handle); SDL_UpdatedAudioDeviceFormat(device);
SDL_GetAudioThreadName(device, thread_name, sizeof(thread_name));
priv->loop = PIPEWIRE_pw_thread_loop_new(thread_name, NULL); priv->loop = PIPEWIRE_pw_thread_loop_new(thread_name, NULL);
if (priv->loop == NULL) { if (priv->loop == NULL) {
return SDL_SetError("Pipewire: Failed to create stream loop (%i)", errno); return SDL_SetError("Pipewire: Failed to create stream loop (%i)", errno);
@ -1213,9 +1145,10 @@ static int PIPEWIRE_OpenDevice(SDL_AudioDevice *_this, const char *devname)
} }
PIPEWIRE_pw_properties_set(props, PW_KEY_NODE_NAME, stream_name); PIPEWIRE_pw_properties_set(props, PW_KEY_NODE_NAME, stream_name);
PIPEWIRE_pw_properties_set(props, PW_KEY_NODE_DESCRIPTION, stream_name); PIPEWIRE_pw_properties_set(props, PW_KEY_NODE_DESCRIPTION, stream_name);
PIPEWIRE_pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%i", _this->spec.samples, _this->spec.freq); PIPEWIRE_pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%i", device->sample_frames, device->spec.freq);
PIPEWIRE_pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", _this->spec.freq); PIPEWIRE_pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", device->spec.freq);
PIPEWIRE_pw_properties_set(props, PW_KEY_NODE_ALWAYS_PROCESS, "true"); PIPEWIRE_pw_properties_set(props, PW_KEY_NODE_ALWAYS_PROCESS, "true");
PIPEWIRE_pw_properties_set(props, PW_KEY_NODE_DONT_RECONNECT, "true"); // Requesting a specific device, don't migrate to new default hardware.
/* /*
* Pipewire 0.3.44 introduced PW_KEY_TARGET_OBJECT that takes either a path * Pipewire 0.3.44 introduced PW_KEY_TARGET_OBJECT that takes either a path
@ -1240,7 +1173,7 @@ static int PIPEWIRE_OpenDevice(SDL_AudioDevice *_this, const char *devname)
/* Create the new stream */ /* Create the new stream */
priv->stream = PIPEWIRE_pw_stream_new_simple(PIPEWIRE_pw_thread_loop_get_loop(priv->loop), stream_name, props, priv->stream = PIPEWIRE_pw_stream_new_simple(PIPEWIRE_pw_thread_loop_get_loop(priv->loop), stream_name, props,
iscapture ? &stream_input_events : &stream_output_events, _this); iscapture ? &stream_input_events : &stream_output_events, device);
if (priv->stream == NULL) { if (priv->stream == NULL) {
return SDL_SetError("Pipewire: Failed to create stream (%i)", errno); return SDL_SetError("Pipewire: Failed to create stream (%i)", errno);
} }
@ -1268,75 +1201,35 @@ static int PIPEWIRE_OpenDevice(SDL_AudioDevice *_this, const char *devname)
return SDL_SetError("Pipewire: Stream error: %s", error); return SDL_SetError("Pipewire: Stream error: %s", error);
} }
/* If this is a capture stream, make sure the intermediate buffer was successfully allocated. */
if (iscapture && priv->buffer == NULL) {
return SDL_SetError("Pipewire: Failed to allocate source buffer");
}
return 0; return 0;
} }
static void PIPEWIRE_CloseDevice(SDL_AudioDevice *_this) static void PIPEWIRE_CloseDevice(SDL_AudioDevice *device)
{ {
if (_this->hidden->loop) { if (!device->hidden) {
PIPEWIRE_pw_thread_loop_stop(_this->hidden->loop); return;
} }
if (_this->hidden->stream) { if (device->hidden->loop) {
PIPEWIRE_pw_stream_destroy(_this->hidden->stream); PIPEWIRE_pw_thread_loop_stop(device->hidden->loop);
} }
if (_this->hidden->context) { if (device->hidden->stream) {
PIPEWIRE_pw_context_destroy(_this->hidden->context); PIPEWIRE_pw_stream_destroy(device->hidden->stream);
} }
if (_this->hidden->loop) { if (device->hidden->context) {
PIPEWIRE_pw_thread_loop_destroy(_this->hidden->loop); PIPEWIRE_pw_context_destroy(device->hidden->context);
} }
if (_this->hidden->buffer) { if (device->hidden->loop) {
SDL_DestroyDataQueue(_this->hidden->buffer); PIPEWIRE_pw_thread_loop_destroy(device->hidden->loop);
} }
SDL_free(_this->hidden); SDL_free(device->hidden);
} device->hidden = NULL;
static int PIPEWIRE_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture) SDL_AudioThreadFinalize(device);
{
struct io_node *node;
char *target;
int ret = 0;
PIPEWIRE_pw_thread_loop_lock(hotplug_loop);
if (iscapture) {
if (pipewire_default_source_id == NULL) {
ret = SDL_SetError("PipeWire could not find a default source");
goto failed;
}
target = pipewire_default_source_id;
} else {
if (pipewire_default_sink_id == NULL) {
ret = SDL_SetError("PipeWire could not find a default sink");
goto failed;
}
target = pipewire_default_sink_id;
}
node = io_list_get_by_path(target);
if (node == NULL) {
ret = SDL_SetError("PipeWire device list is out of sync with defaults");
goto failed;
}
if (name != NULL) {
*name = SDL_strdup(node->name);
}
SDL_copyp(spec, &node->spec);
failed:
PIPEWIRE_pw_thread_loop_unlock(hotplug_loop);
return ret;
} }
static void PIPEWIRE_Deinitialize(void) static void PIPEWIRE_Deinitialize(void)
@ -1366,13 +1259,15 @@ static SDL_bool PIPEWIRE_Init(SDL_AudioDriverImpl *impl)
/* Set the function pointers */ /* Set the function pointers */
impl->DetectDevices = PIPEWIRE_DetectDevices; impl->DetectDevices = PIPEWIRE_DetectDevices;
impl->OpenDevice = PIPEWIRE_OpenDevice; impl->OpenDevice = PIPEWIRE_OpenDevice;
impl->CloseDevice = PIPEWIRE_CloseDevice;
impl->Deinitialize = PIPEWIRE_Deinitialize; impl->Deinitialize = PIPEWIRE_Deinitialize;
impl->GetDefaultAudioInfo = PIPEWIRE_GetDefaultAudioInfo; impl->PlayDevice = PIPEWIRE_PlayDevice;
impl->GetDeviceBuf = PIPEWIRE_GetDeviceBuf;
impl->CaptureFromDevice = PIPEWIRE_CaptureFromDevice;
impl->FlushCapture = PIPEWIRE_FlushCapture;
impl->CloseDevice = PIPEWIRE_CloseDevice;
impl->HasCaptureSupport = SDL_TRUE; impl->HasCaptureSupport = SDL_TRUE;
impl->ProvidesOwnCallbackThread = SDL_TRUE; impl->ProvidesOwnCallbackThread = SDL_TRUE;
impl->SupportsNonPow2Samples = SDL_TRUE;
return SDL_TRUE; return SDL_TRUE;
} }

View File

@ -32,11 +32,12 @@ struct SDL_PrivateAudioData
struct pw_thread_loop *loop; struct pw_thread_loop *loop;
struct pw_stream *stream; struct pw_stream *stream;
struct pw_context *context; struct pw_context *context;
struct SDL_DataQueue *buffer;
size_t input_buffer_packet_size;
Sint32 stride; /* Bytes-per-frame */ Sint32 stride; /* Bytes-per-frame */
int stream_init_status; int stream_init_status;
// Set in GetDeviceBuf, filled in AudioThreadIterate, queued in PlayDevice
struct pw_buffer *pw_buf;
}; };
#endif /* SDL_pipewire_h_ */ #endif /* SDL_pipewire_h_ */

View File

@ -20,8 +20,6 @@
*/ */
#include "SDL_internal.h" #include "SDL_internal.h"
/* Output audio to nowhere... */
#include "../SDL_audio_c.h" #include "../SDL_audio_c.h"
#include "SDL_ps2audio.h" #include "SDL_ps2audio.h"
@ -29,23 +27,15 @@
#include <audsrv.h> #include <audsrv.h>
#include <ps2_audio_driver.h> #include <ps2_audio_driver.h>
/* The tag name used by PS2 audio */ static int PS2AUDIO_OpenDevice(SDL_AudioDevice *device)
#define PS2AUDIO_DRIVER_NAME "ps2"
static int PS2AUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
{ {
int i, mixlen; device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
struct audsrv_fmt_t format; if (device->hidden == NULL) {
_this->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc(sizeof(*_this->hidden));
if (_this->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_zerop(_this->hidden);
/* These are the native supported audio PS2 configs */ // These are the native supported audio PS2 configs
switch (_this->spec.freq) { switch (device->spec.freq) {
case 11025: case 11025:
case 12000: case 12000:
case 22050: case 22050:
@ -53,93 +43,89 @@ static int PS2AUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
case 32000: case 32000:
case 44100: case 44100:
case 48000: case 48000:
_this->spec.freq = _this->spec.freq; break; // acceptable value, keep it
break;
default: default:
_this->spec.freq = 48000; device->spec.freq = 48000;
break; break;
} }
_this->spec.samples = 512; device->sample_frames = 512;
_this->spec.channels = _this->spec.channels == 1 ? 1 : 2; device->spec.channels = device->spec.channels == 1 ? 1 : 2;
_this->spec.format = _this->spec.format == SDL_AUDIO_S8 ? SDL_AUDIO_S8 : SDL_AUDIO_S16; device->spec.format = device->spec.format == SDL_AUDIO_S8 ? SDL_AUDIO_S8 : SDL_AUDIO_S16;
SDL_CalculateAudioSpec(&_this->spec); struct audsrv_fmt_t format;
format.bits = device->spec.format == SDL_AUDIO_S8 ? 8 : 16;
format.freq = device->spec.freq;
format.channels = device->spec.channels;
format.bits = _this->spec.format == SDL_AUDIO_S8 ? 8 : 16; device->hidden->channel = audsrv_set_format(&format);
format.freq = _this->spec.freq;
format.channels = _this->spec.channels;
_this->hidden->channel = audsrv_set_format(&format);
audsrv_set_volume(MAX_VOLUME); audsrv_set_volume(MAX_VOLUME);
if (_this->hidden->channel < 0) { if (device->hidden->channel < 0) {
SDL_aligned_free(_this->hidden->rawbuf);
_this->hidden->rawbuf = NULL;
return SDL_SetError("Couldn't reserve hardware channel"); return SDL_SetError("Couldn't reserve hardware channel");
} }
/* Update the fragment size as size in bytes. */ // Update the fragment size as size in bytes.
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
/* Allocate the mixing buffer. Its size and starting address must /* Allocate the mixing buffer. Its size and starting address must
be a multiple of 64 bytes. Our sample count is already a multiple of be a multiple of 64 bytes. Our sample count is already a multiple of
64, so spec->size should be a multiple of 64 as well. */ 64, so spec->size should be a multiple of 64 as well. */
mixlen = _this->spec.size * NUM_BUFFERS; const int mixlen = device->buffer_size * NUM_BUFFERS;
_this->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen); device->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen);
if (_this->hidden->rawbuf == NULL) { if (device->hidden->rawbuf == NULL) {
return SDL_SetError("Couldn't allocate mixing buffer"); return SDL_SetError("Couldn't allocate mixing buffer");
} }
SDL_memset(_this->hidden->rawbuf, 0, mixlen); SDL_memset(device->hidden->rawbuf, device->silence_value, mixlen);
for (i = 0; i < NUM_BUFFERS; i++) { for (int i = 0; i < NUM_BUFFERS; i++) {
_this->hidden->mixbufs[i] = &_this->hidden->rawbuf[i * _this->spec.size]; device->hidden->mixbufs[i] = &device->hidden->rawbuf[i * device->buffer_size];
} }
_this->hidden->next_buffer = 0;
return 0; return 0;
} }
static void PS2AUDIO_PlayDevice(SDL_AudioDevice *_this) static void PS2AUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{ {
uint8_t *mixbuf = _this->hidden->mixbufs[_this->hidden->next_buffer]; audsrv_play_audio((char *)buffer, buflen);
audsrv_play_audio((char *)mixbuf, _this->spec.size);
_this->hidden->next_buffer = (_this->hidden->next_buffer + 1) % NUM_BUFFERS;
} }
/* This function waits until it is possible to write a full sound buffer */ static void PS2AUDIO_WaitDevice(SDL_AudioDevice *device)
static void PS2AUDIO_WaitDevice(SDL_AudioDevice *_this)
{ {
audsrv_wait_audio(_this->spec.size); audsrv_wait_audio(device->buffer_size);
} }
static Uint8 *PS2AUDIO_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *PS2AUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
return _this->hidden->mixbufs[_this->hidden->next_buffer]; Uint8 *buffer = device->hidden->mixbufs[device->hidden->next_buffer];
device->hidden->next_buffer = (device->hidden->next_buffer + 1) % NUM_BUFFERS;
return buffer;
} }
static void PS2AUDIO_CloseDevice(SDL_AudioDevice *_this) static void PS2AUDIO_CloseDevice(SDL_AudioDevice *device)
{ {
if (_this->hidden->channel >= 0) { if (device->hidden) {
if (device->hidden->channel >= 0) {
audsrv_stop_audio(); audsrv_stop_audio();
_this->hidden->channel = -1; device->hidden->channel = -1;
} }
if (_this->hidden->rawbuf != NULL) { if (device->hidden->rawbuf != NULL) {
SDL_aligned_free(_this->hidden->rawbuf); SDL_aligned_free(device->hidden->rawbuf);
_this->hidden->rawbuf = NULL; device->hidden->rawbuf = NULL;
}
SDL_free(device->hidden);
device->hidden = NULL;
} }
} }
static void PS2AUDIO_ThreadInit(SDL_AudioDevice *_this) static void PS2AUDIO_ThreadInit(SDL_AudioDevice *device)
{ {
/* Increase the priority of this audio thread by 1 to put it /* Increase the priority of this audio thread by 1 to put it
ahead of other SDL threads. */ ahead of other SDL threads. */
int32_t thid; const int32_t thid = GetThreadId();
ee_thread_status_t status; ee_thread_status_t status;
thid = GetThreadId(); if (ReferThreadStatus(thid, &status) == 0) {
if (ReferThreadStatus(GetThreadId(), &status) == 0) {
ChangeThreadPriority(thid, status.current_priority - 1); ChangeThreadPriority(thid, status.current_priority - 1);
} }
} }
@ -155,7 +141,6 @@ static SDL_bool PS2AUDIO_Init(SDL_AudioDriverImpl *impl)
return SDL_FALSE; return SDL_FALSE;
} }
/* Set the function pointers */
impl->OpenDevice = PS2AUDIO_OpenDevice; impl->OpenDevice = PS2AUDIO_OpenDevice;
impl->PlayDevice = PS2AUDIO_PlayDevice; impl->PlayDevice = PS2AUDIO_PlayDevice;
impl->WaitDevice = PS2AUDIO_WaitDevice; impl->WaitDevice = PS2AUDIO_WaitDevice;
@ -164,7 +149,7 @@ static SDL_bool PS2AUDIO_Init(SDL_AudioDriverImpl *impl)
impl->ThreadInit = PS2AUDIO_ThreadInit; impl->ThreadInit = PS2AUDIO_ThreadInit;
impl->Deinitialize = PS2AUDIO_Deinitialize; impl->Deinitialize = PS2AUDIO_Deinitialize;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE; impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */ return SDL_TRUE; // this audio target is available.
} }
AudioBootStrap PS2AUDIO_bootstrap = { AudioBootStrap PS2AUDIO_bootstrap = {

View File

@ -34,41 +34,34 @@
#include <pspaudio.h> #include <pspaudio.h>
#include <pspthreadman.h> #include <pspthreadman.h>
/* The tag name used by PSP audio */
#define PSPAUDIO_DRIVER_NAME "psp"
static inline SDL_bool isBasicAudioConfig(const SDL_AudioSpec *spec) static inline SDL_bool isBasicAudioConfig(const SDL_AudioSpec *spec)
{ {
return spec->freq == 44100; return spec->freq == 44100;
} }
static int PSPAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int PSPAUDIO_OpenDevice(SDL_AudioDevice *device)
{ {
int format, mixlen, i; device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
if (device->hidden == NULL) {
_this->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc(sizeof(*_this->hidden));
if (_this->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_zerop(_this->hidden);
/* device only natively supports S16LSB */ // device only natively supports S16LSB
_this->spec.format = SDL_AUDIO_S16LSB; device->spec.format = SDL_AUDIO_S16LSB;
/* PSP has some limitations with the Audio. It fully supports 44.1KHz (Mono & Stereo), /* 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, however with frequencies different than 44.1KHz, it just supports Stereo,
so a resampler must be done for these scenarios */ so a resampler must be done for these scenarios */
if (isBasicAudioConfig(&_this->spec)) { if (isBasicAudioConfig(&device->spec)) {
/* The sample count must be a multiple of 64. */ // The sample count must be a multiple of 64.
_this->spec.samples = PSP_AUDIO_SAMPLE_ALIGN(_this->spec.samples); device->sample_frames = PSP_AUDIO_SAMPLE_ALIGN(device->sample_frames);
/* The number of channels (1 or 2). */ // The number of channels (1 or 2).
_this->spec.channels = _this->spec.channels == 1 ? 1 : 2; device->spec.channels = device->spec.channels == 1 ? 1 : 2;
format = _this->spec.channels == 1 ? PSP_AUDIO_FORMAT_MONO : PSP_AUDIO_FORMAT_STEREO; const int format = (device->spec.channels == 1) ? PSP_AUDIO_FORMAT_MONO : PSP_AUDIO_FORMAT_STEREO;
_this->hidden->channel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, _this->spec.samples, format); device->hidden->channel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, device->sample_frames, format);
} else { } else {
/* 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11050, 8000 */ // 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11050, 8000
switch (_this->spec.freq) { switch (device->spec.freq) {
case 8000: case 8000:
case 11025: case 11025:
case 12000: case 12000:
@ -78,93 +71,90 @@ static int PSPAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
case 32000: case 32000:
case 44100: case 44100:
case 48000: case 48000:
_this->spec.freq = _this->spec.freq; break; // acceptable, keep it
break;
default: default:
_this->spec.freq = 48000; device->spec.freq = 48000;
break; break;
} }
/* The number of samples to output in one output call (min 17, max 4111). */ // The number of samples to output in one output call (min 17, max 4111).
_this->spec.samples = _this->spec.samples < 17 ? 17 : (_this->spec.samples > 4111 ? 4111 : _this->spec.samples); device->sample_frames = device->sample_frames < 17 ? 17 : (device->sample_frames > 4111 ? 4111 : device->sample_frames);
_this->spec.channels = 2; /* we're forcing the hardware to stereo. */ device->spec.channels = 2; // we're forcing the hardware to stereo.
_this->hidden->channel = sceAudioSRCChReserve(_this->spec.samples, _this->spec.freq, 2); device->hidden->channel = sceAudioSRCChReserve(device->sample_frames, device->spec.freq, 2);
} }
if (_this->hidden->channel < 0) { if (device->hidden->channel < 0) {
SDL_aligned_free(_this->hidden->rawbuf);
_this->hidden->rawbuf = NULL;
return SDL_SetError("Couldn't reserve hardware channel"); return SDL_SetError("Couldn't reserve hardware channel");
} }
/* Update the fragment size as size in bytes. */ // Update the fragment size as size in bytes.
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
/* Allocate the mixing buffer. Its size and starting address must /* Allocate the mixing buffer. Its size and starting address must
be a multiple of 64 bytes. Our sample count is already a multiple of be a multiple of 64 bytes. Our sample count is already a multiple of
64, so spec->size should be a multiple of 64 as well. */ 64, so spec->size should be a multiple of 64 as well. */
mixlen = _this->spec.size * NUM_BUFFERS; const int mixlen = device->buffer_size * NUM_BUFFERS;
_this->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen); device->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen);
if (_this->hidden->rawbuf == NULL) { if (device->hidden->rawbuf == NULL) {
return SDL_SetError("Couldn't allocate mixing buffer"); return SDL_SetError("Couldn't allocate mixing buffer");
} }
SDL_memset(_this->hidden->rawbuf, 0, mixlen); SDL_memset(device->hidden->rawbuf, device->silence_value, mixlen);
for (i = 0; i < NUM_BUFFERS; i++) { for (int i = 0; i < NUM_BUFFERS; i++) {
_this->hidden->mixbufs[i] = &_this->hidden->rawbuf[i * _this->spec.size]; device->hidden->mixbufs[i] = &device->hidden->rawbuf[i * device->buffer_size];
} }
_this->hidden->next_buffer = 0;
return 0; return 0;
} }
static void PSPAUDIO_PlayDevice(SDL_AudioDevice *_this) static void PSPAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{ {
Uint8 *mixbuf = _this->hidden->mixbufs[_this->hidden->next_buffer]; if (!isBasicAudioConfig(&device->spec)) {
if (!isBasicAudioConfig(&_this->spec)) { SDL_assert(device->spec.channels == 2);
SDL_assert(_this->spec.channels == 2); sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, (void *) buffer);
sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, mixbuf);
} else { } else {
sceAudioOutputPannedBlocking(_this->hidden->channel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, mixbuf); sceAudioOutputPannedBlocking(device->hidden->channel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, (void *) buffer);
}
} }
_this->hidden->next_buffer = (_this->hidden->next_buffer + 1) % NUM_BUFFERS; static void PSPAUDIO_WaitDevice(SDL_AudioDevice *device)
}
/* This function waits until it is possible to write a full sound buffer */
static void PSPAUDIO_WaitDevice(SDL_AudioDevice *_this)
{ {
/* Because we block when sending audio, there's no need for this function to do anything. */ // Because we block when sending audio, there's no need for this function to do anything.
} }
static Uint8 *PSPAUDIO_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *PSPAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
return _this->hidden->mixbufs[_this->hidden->next_buffer]; Uint8 *buffer = device->hidden->mixbufs[device->hidden->next_buffer];
device->hidden->next_buffer = (device->hidden->next_buffer + 1) % NUM_BUFFERS;
return buffer;
} }
static void PSPAUDIO_CloseDevice(SDL_AudioDevice *_this) static void PSPAUDIO_CloseDevice(SDL_AudioDevice *device)
{ {
if (_this->hidden->channel >= 0) { if (device->hidden) {
if (!isBasicAudioConfig(&_this->spec)) { if (device->hidden->channel >= 0) {
if (!isBasicAudioConfig(&device->spec)) {
sceAudioSRCChRelease(); sceAudioSRCChRelease();
} else { } else {
sceAudioChRelease(_this->hidden->channel); sceAudioChRelease(device->hidden->channel);
} }
_this->hidden->channel = -1; device->hidden->channel = -1;
} }
if (_this->hidden->rawbuf != NULL) { if (device->hidden->rawbuf != NULL) {
SDL_aligned_free(_this->hidden->rawbuf); SDL_aligned_free(device->hidden->rawbuf);
_this->hidden->rawbuf = NULL; device->hidden->rawbuf = NULL;
}
SDL_free(device->hidden);
device->hidden = NULL;
} }
} }
static void PSPAUDIO_ThreadInit(SDL_AudioDevice *_this) static void PSPAUDIO_ThreadInit(SDL_AudioDevice *device)
{ {
/* Increase the priority of this audio thread by 1 to put it /* Increase the priority of this audio thread by 1 to put it
ahead of other SDL threads. */ ahead of other SDL threads. */
SceUID thid; const SceUID thid = sceKernelGetThreadId();
SceKernelThreadInfo status; SceKernelThreadInfo status;
thid = sceKernelGetThreadId();
status.size = sizeof(SceKernelThreadInfo); status.size = sizeof(SceKernelThreadInfo);
if (sceKernelReferThreadStatus(thid, &status) == 0) { if (sceKernelReferThreadStatus(thid, &status) == 0) {
sceKernelChangeThreadPriority(thid, status.currentPriority - 1); sceKernelChangeThreadPriority(thid, status.currentPriority - 1);
@ -173,25 +163,20 @@ static void PSPAUDIO_ThreadInit(SDL_AudioDevice *_this)
static SDL_bool PSPAUDIO_Init(SDL_AudioDriverImpl *impl) static SDL_bool PSPAUDIO_Init(SDL_AudioDriverImpl *impl)
{ {
/* Set the function pointers */
impl->OpenDevice = PSPAUDIO_OpenDevice; impl->OpenDevice = PSPAUDIO_OpenDevice;
impl->PlayDevice = PSPAUDIO_PlayDevice; impl->PlayDevice = PSPAUDIO_PlayDevice;
impl->WaitDevice = PSPAUDIO_WaitDevice; impl->WaitDevice = PSPAUDIO_WaitDevice;
impl->GetDeviceBuf = PSPAUDIO_GetDeviceBuf; impl->GetDeviceBuf = PSPAUDIO_GetDeviceBuf;
impl->CloseDevice = PSPAUDIO_CloseDevice; impl->CloseDevice = PSPAUDIO_CloseDevice;
impl->ThreadInit = PSPAUDIO_ThreadInit; impl->ThreadInit = PSPAUDIO_ThreadInit;
/* PSP audio device */
impl->OnlyHasDefaultOutputDevice = SDL_TRUE; impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
/* //impl->HasCaptureSupport = SDL_TRUE;
impl->HasCaptureSupport = SDL_TRUE; //impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE; return SDL_TRUE;
*/
return SDL_TRUE; /* this audio target is available. */
} }
AudioBootStrap PSPAUDIO_bootstrap = { AudioBootStrap PSPAUDIO_bootstrap = {
"psp", "PSP audio driver", PSPAUDIO_Init, SDL_FALSE "psp", "PSP audio driver", PSPAUDIO_Init, SDL_FALSE
}; };
#endif /* SDL_AUDIO_DRIVER_PSP */ #endif // SDL_AUDIO_DRIVER_PSP

View File

@ -43,13 +43,12 @@ static pa_context *pulseaudio_context = NULL;
static SDL_Thread *pulseaudio_hotplug_thread = NULL; static SDL_Thread *pulseaudio_hotplug_thread = NULL;
static SDL_AtomicInt pulseaudio_hotplug_thread_active; static SDL_AtomicInt pulseaudio_hotplug_thread_active;
/* These are the OS identifiers (i.e. ALSA strings)... */ // These are the OS identifiers (i.e. ALSA strings)...
static char *default_sink_path = NULL; static char *default_sink_path = NULL;
static char *default_source_path = NULL; static char *default_source_path = NULL;
/* ... and these are the descriptions we use in GetDefaultAudioInfo. */ // ... and these are the PulseAudio device indices of the default devices.
static char *default_sink_name = NULL; static uint32_t default_sink_index = 0;
static char *default_source_name = NULL; static uint32_t default_source_index = 0;
static const char *(*PULSEAUDIO_pa_get_library_version)(void); static const char *(*PULSEAUDIO_pa_get_library_version)(void);
static pa_channel_map *(*PULSEAUDIO_pa_channel_map_init_auto)( static pa_channel_map *(*PULSEAUDIO_pa_channel_map_init_auto)(
@ -359,12 +358,6 @@ failed:
return -1; return -1;
} }
/* This function waits until it is possible to write a full sound buffer */
static void PULSEAUDIO_WaitDevice(SDL_AudioDevice *_this)
{
/* this is a no-op; we wait in PULSEAUDIO_PlayDevice now. */
}
static void WriteCallback(pa_stream *p, size_t nbytes, void *userdata) static void WriteCallback(pa_stream *p, size_t nbytes, void *userdata)
{ {
struct SDL_PrivateAudioData *h = (struct SDL_PrivateAudioData *)userdata; struct SDL_PrivateAudioData *h = (struct SDL_PrivateAudioData *)userdata;
@ -373,50 +366,56 @@ static void WriteCallback(pa_stream *p, size_t nbytes, void *userdata)
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
} }
static void PULSEAUDIO_PlayDevice(SDL_AudioDevice *_this) /* This function waits until it is possible to write a full sound buffer */
static void PULSEAUDIO_WaitDevice(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *h = _this->hidden; struct SDL_PrivateAudioData *h = device->hidden;
int available = h->mixlen;
int written = 0;
int cpy;
/*printf("PULSEAUDIO PLAYDEVICE START! mixlen=%d\n", available);*/ /*printf("PULSEAUDIO PLAYDEVICE START! mixlen=%d\n", available);*/
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
while (SDL_AtomicGet(&_this->enabled) && (available > 0)) { while (!SDL_AtomicGet(&device->shutdown) && (h->bytes_requested < (device->buffer_size / 2))) {
cpy = SDL_min(h->bytes_requested, available); /*printf("PULSEAUDIO WAIT IN WAITDEVICE!\n");*/
if (cpy) {
if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf + written, cpy, NULL, 0LL, PA_SEEK_RELATIVE) < 0) {
SDL_OpenedAudioDeviceDisconnected(_this);
break;
}
/*printf("PULSEAUDIO FEED! nbytes=%u\n", (unsigned int) cpy);*/
h->bytes_requested -= cpy;
written += cpy;
available -= cpy;
}
if (available > 0) {
/* let WriteCallback fire if necessary. */
/*printf("PULSEAUDIO WAIT IN PLAYDEVICE!\n");*/
PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop);
if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) { if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) {
/*printf("PULSEAUDIO DEVICE FAILURE IN PLAYDEVICE!\n");*/ /*printf("PULSEAUDIO DEVICE FAILURE IN WAITDEVICE!\n");*/
SDL_OpenedAudioDeviceDisconnected(_this); SDL_AudioDeviceDisconnected(device);
break; break;
} }
} }
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
} }
static void PULSEAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
{
struct SDL_PrivateAudioData *h = device->hidden;
/*printf("PULSEAUDIO PLAYDEVICE START! mixlen=%d\n", available);*/
SDL_assert(h->bytes_requested >= buffer_size);
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
const int rc = PULSEAUDIO_pa_stream_write(h->stream, buffer, buffer_size, NULL, 0LL, PA_SEEK_RELATIVE);
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
if (rc < 0) {
SDL_AudioDeviceDisconnected(device);
return;
}
/*printf("PULSEAUDIO FEED! nbytes=%d\n", buffer_size);*/
h->bytes_requested -= buffer_size;
/*printf("PULSEAUDIO PLAYDEVICE END! written=%d\n", written);*/ /*printf("PULSEAUDIO PLAYDEVICE END! written=%d\n", written);*/
} }
static Uint8 *PULSEAUDIO_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *PULSEAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
return _this->hidden->mixbuf; struct SDL_PrivateAudioData *h = device->hidden;
*buffer_size = SDL_min(*buffer_size, h->bytes_requested);
return device->hidden->mixbuf;
} }
static void ReadCallback(pa_stream *p, size_t nbytes, void *userdata) static void ReadCallback(pa_stream *p, size_t nbytes, void *userdata)
@ -425,67 +424,70 @@ static void ReadCallback(pa_stream *p, size_t nbytes, void *userdata)
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); /* the capture code queries what it needs, we just need to signal to end any wait */ PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); /* the capture code queries what it needs, we just need to signal to end any wait */
} }
static int PULSEAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) static void PULSEAUDIO_WaitCaptureDevice(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *h = _this->hidden; struct SDL_PrivateAudioData *h = device->hidden;
const void *data = NULL;
size_t nbytes = 0; if (h->capturebuf != NULL) {
int retval = 0; return; // there's still data available to read.
}
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
while (SDL_AtomicGet(&_this->enabled)) { while (!SDL_AtomicGet(&device->shutdown)) {
if (h->capturebuf != NULL) {
const int cpy = SDL_min(buflen, h->capturelen);
SDL_memcpy(buffer, h->capturebuf, cpy);
/*printf("PULSEAUDIO: fed %d captured bytes\n", cpy);*/
h->capturebuf += cpy;
h->capturelen -= cpy;
if (h->capturelen == 0) {
h->capturebuf = NULL;
PULSEAUDIO_pa_stream_drop(h->stream); /* done with this fragment. */
}
retval = cpy; /* new data, return it. */
break;
}
while (SDL_AtomicGet(&_this->enabled) && (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0)) {
PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop);
if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) { if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) {
/*printf("PULSEAUDIO DEVICE FAILURE IN CAPTUREFROMDEVICE!\n");*/ //printf("PULSEAUDIO DEVICE FAILURE IN WAITCAPTUREDEVICE!\n");
SDL_OpenedAudioDeviceDisconnected(_this); SDL_AudioDeviceDisconnected(device);
retval = -1;
break; break;
} } else if (PULSEAUDIO_pa_stream_readable_size(h->stream) > 0) {
} // a new fragment is available!
const void *data = NULL;
if ((retval == -1) || !SDL_AtomicGet(&_this->enabled)) { /* in case this happened while we were blocking. */ size_t nbytes = 0;
retval = -1;
break;
}
/* a new fragment is available! */
PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes); PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes);
SDL_assert(nbytes > 0); SDL_assert(nbytes > 0);
/* If data == NULL, then the buffer had a hole, ignore that */ if (data == NULL) { // If NULL, then the buffer had a hole, ignore that
if (data == NULL) { PULSEAUDIO_pa_stream_drop(h->stream); // drop this fragment.
PULSEAUDIO_pa_stream_drop(h->stream); /* drop this fragment. */
} else { } else {
/* store this fragment's data, start feeding it to SDL. */ // store this fragment's data for use with CaptureFromDevice
/*printf("PULSEAUDIO: captured %d new bytes\n", (int) nbytes);*/ //printf("PULSEAUDIO: captured %d new bytes\n", (int) nbytes);
h->capturebuf = (const Uint8 *)data; h->capturebuf = (const Uint8 *)data;
h->capturelen = nbytes; h->capturelen = nbytes;
break;
}
} }
} }
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
return retval;
} }
static void PULSEAUDIO_FlushCapture(SDL_AudioDevice *_this) static int PULSEAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{ {
struct SDL_PrivateAudioData *h = _this->hidden; struct SDL_PrivateAudioData *h = device->hidden;
if (h->capturebuf != NULL) {
const int cpy = SDL_min(buflen, h->capturelen);
if (cpy > 0) {
//printf("PULSEAUDIO: fed %d captured bytes\n", cpy);
SDL_memcpy(buffer, h->capturebuf, cpy);
h->capturebuf += cpy;
h->capturelen -= cpy;
}
if (h->capturelen == 0) {
h->capturebuf = NULL;
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); // don't know if you _have_ to lock for this, but just in case.
PULSEAUDIO_pa_stream_drop(h->stream); // done with this fragment.
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
}
return cpy; /* new data, return it. */
}
return 0;
}
static void PULSEAUDIO_FlushCapture(SDL_AudioDevice *device)
{
struct SDL_PrivateAudioData *h = device->hidden;
const void *data = NULL; const void *data = NULL;
size_t nbytes = 0; size_t nbytes = 0;
@ -497,11 +499,11 @@ static void PULSEAUDIO_FlushCapture(SDL_AudioDevice *_this)
h->capturelen = 0; h->capturelen = 0;
} }
while (SDL_AtomicGet(&_this->enabled) && (PULSEAUDIO_pa_stream_readable_size(h->stream) > 0)) { while (!SDL_AtomicGet(&device->shutdown) && (PULSEAUDIO_pa_stream_readable_size(h->stream) > 0)) {
PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop);
if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) { if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) {
/*printf("PULSEAUDIO DEVICE FAILURE IN FLUSHCAPTURE!\n");*/ /*printf("PULSEAUDIO DEVICE FAILURE IN FLUSHCAPTURE!\n");*/
SDL_OpenedAudioDeviceDisconnected(_this); SDL_AudioDeviceDisconnected(device);
break; break;
} }
@ -515,22 +517,23 @@ static void PULSEAUDIO_FlushCapture(SDL_AudioDevice *_this)
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
} }
static void PULSEAUDIO_CloseDevice(SDL_AudioDevice *_this) static void PULSEAUDIO_CloseDevice(SDL_AudioDevice *device)
{ {
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
if (_this->hidden->stream) { if (device->hidden->stream) {
if (_this->hidden->capturebuf != NULL) { if (device->hidden->capturebuf != NULL) {
PULSEAUDIO_pa_stream_drop(_this->hidden->stream); PULSEAUDIO_pa_stream_drop(device->hidden->stream);
} }
PULSEAUDIO_pa_stream_disconnect(_this->hidden->stream); PULSEAUDIO_pa_stream_disconnect(device->hidden->stream);
PULSEAUDIO_pa_stream_unref(_this->hidden->stream); PULSEAUDIO_pa_stream_unref(device->hidden->stream);
} }
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // in case the device thread is waiting somewhere, this will unblock it.
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
SDL_free(_this->hidden->mixbuf); SDL_free(device->hidden->mixbuf);
SDL_free(_this->hidden->device_name); SDL_free(device->hidden->device_name);
SDL_free(_this->hidden); SDL_free(device->hidden);
} }
static void SinkDeviceNameCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data) static void SinkDeviceNameCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
@ -551,15 +554,13 @@ static void SourceDeviceNameCallback(pa_context *c, const pa_source_info *i, int
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
} }
static SDL_bool FindDeviceName(struct SDL_PrivateAudioData *h, const SDL_bool iscapture, void *handle) static SDL_bool FindDeviceName(SDL_AudioDevice *device)
{ {
const uint32_t idx = ((uint32_t)((intptr_t)handle)) - 1; struct SDL_PrivateAudioData *h = device->hidden;
SDL_assert(device->handle != NULL); // this was a thing in SDL2, but shouldn't be in SDL3.
const uint32_t idx = ((uint32_t)((intptr_t)device->handle)) - 1;
if (handle == NULL) { /* NULL == default device. */ if (device->iscapture) {
return SDL_TRUE;
}
if (iscapture) {
WaitForPulseOperation(PULSEAUDIO_pa_context_get_source_info_by_index(pulseaudio_context, idx, SourceDeviceNameCallback, &h->device_name)); WaitForPulseOperation(PULSEAUDIO_pa_context_get_source_info_by_index(pulseaudio_context, idx, SourceDeviceNameCallback, &h->device_name));
} else { } else {
WaitForPulseOperation(PULSEAUDIO_pa_context_get_sink_info_by_index(pulseaudio_context, idx, SinkDeviceNameCallback, &h->device_name)); WaitForPulseOperation(PULSEAUDIO_pa_context_get_sink_info_by_index(pulseaudio_context, idx, SinkDeviceNameCallback, &h->device_name));
@ -573,8 +574,9 @@ static void PulseStreamStateChangeCallback(pa_stream *stream, void *userdata)
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); /* just signal any waiting code, it can look up the details. */ PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); /* just signal any waiting code, it can look up the details. */
} }
static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *device)
{ {
const SDL_bool iscapture = device->iscapture;
struct SDL_PrivateAudioData *h = NULL; struct SDL_PrivateAudioData *h = NULL;
SDL_AudioFormat test_format; SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts; const SDL_AudioFormat *closefmts;
@ -582,7 +584,6 @@ static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
pa_buffer_attr paattr; pa_buffer_attr paattr;
pa_channel_map pacmap; pa_channel_map pacmap;
pa_stream_flags_t flags = 0; pa_stream_flags_t flags = 0;
SDL_bool iscapture = _this->iscapture;
int format = PA_SAMPLE_INVALID; int format = PA_SAMPLE_INVALID;
int retval = 0; int retval = 0;
@ -590,14 +591,13 @@ static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
SDL_assert(pulseaudio_context != NULL); SDL_assert(pulseaudio_context != NULL);
/* Initialize all variables that we clean on shutdown */ /* Initialize all variables that we clean on shutdown */
h = _this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*_this->hidden)); h = device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
if (_this->hidden == NULL) { if (device->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_zerop(_this->hidden);
/* Try for a closest match on audio format */ /* Try for a closest match on audio format */
closefmts = SDL_ClosestAudioFormats(_this->spec.format); closefmts = SDL_ClosestAudioFormats(device->spec.format);
while ((test_format = *(closefmts++)) != 0) { while ((test_format = *(closefmts++)) != 0) {
#ifdef DEBUG_AUDIO #ifdef DEBUG_AUDIO
fprintf(stderr, "Trying format 0x%4.4x\n", test_format); fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
@ -632,28 +632,27 @@ static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
if (!test_format) { if (!test_format) {
return SDL_SetError("pulseaudio: Unsupported audio format"); return SDL_SetError("pulseaudio: Unsupported audio format");
} }
_this->spec.format = test_format; device->spec.format = test_format;
paspec.format = format; paspec.format = format;
/* Calculate the final parameters for this audio specification */ /* Calculate the final parameters for this audio specification */
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
/* Allocate mixing buffer */ /* Allocate mixing buffer */
if (!iscapture) { if (!iscapture) {
h->mixlen = _this->spec.size; h->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
h->mixbuf = (Uint8 *)SDL_malloc(h->mixlen);
if (h->mixbuf == NULL) { if (h->mixbuf == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_memset(h->mixbuf, _this->spec.silence, _this->spec.size); SDL_memset(h->mixbuf, device->silence_value, device->buffer_size);
} }
paspec.channels = _this->spec.channels; paspec.channels = device->spec.channels;
paspec.rate = _this->spec.freq; paspec.rate = device->spec.freq;
/* Reduced prebuffering compared to the defaults. */ /* Reduced prebuffering compared to the defaults. */
paattr.fragsize = _this->spec.size; paattr.fragsize = device->buffer_size;
paattr.tlength = h->mixlen; paattr.tlength = device->buffer_size;
paattr.prebuf = -1; paattr.prebuf = -1;
paattr.maxlength = -1; paattr.maxlength = -1;
paattr.minreq = -1; paattr.minreq = -1;
@ -661,14 +660,13 @@ static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
if (!FindDeviceName(h, iscapture, _this->handle)) { if (!FindDeviceName(device)) {
retval = SDL_SetError("Requested PulseAudio sink/source missing?"); retval = SDL_SetError("Requested PulseAudio sink/source missing?");
} else { } else {
const char *name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME); const char *name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME);
/* The SDL ALSA output hints us that we use Windows' channel mapping */ /* The SDL ALSA output hints us that we use Windows' channel mapping */
/* https://bugzilla.libsdl.org/show_bug.cgi?id=110 */ /* https://bugzilla.libsdl.org/show_bug.cgi?id=110 */
PULSEAUDIO_pa_channel_map_init_auto(&pacmap, _this->spec.channels, PULSEAUDIO_pa_channel_map_init_auto(&pacmap, device->spec.channels, PA_CHANNEL_MAP_WAVEEX);
PA_CHANNEL_MAP_WAVEEX);
h->stream = PULSEAUDIO_pa_stream_new( h->stream = PULSEAUDIO_pa_stream_new(
pulseaudio_context, pulseaudio_context,
@ -684,11 +682,8 @@ static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
PULSEAUDIO_pa_stream_set_state_callback(h->stream, PulseStreamStateChangeCallback, NULL); PULSEAUDIO_pa_stream_set_state_callback(h->stream, PulseStreamStateChangeCallback, NULL);
/* now that we have multi-device support, don't move a stream from // SDL manages device moves if the default changes, so don't ever let Pulse automatically migrate this stream.
a device that was unplugged to something else, unless we're default. */
if (h->device_name != NULL) {
flags |= PA_STREAM_DONT_MOVE; flags |= PA_STREAM_DONT_MOVE;
}
if (iscapture) { if (iscapture) {
PULSEAUDIO_pa_stream_set_read_callback(h->stream, ReadCallback, h); PULSEAUDIO_pa_stream_set_read_callback(h->stream, ReadCallback, h);
@ -744,62 +739,46 @@ static SDL_AudioFormat PulseFormatToSDLFormat(pa_sample_format_t format)
} }
} }
/* This is called when PulseAudio adds an output ("sink") device. */ // This is called when PulseAudio adds an output ("sink") device.
// !!! FIXME: this is almost identical to SourceInfoCallback, merge the two.
static void SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data) static void SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
{ {
SDL_AudioSpec spec;
SDL_bool add = (SDL_bool)((intptr_t)data);
if (i) { if (i) {
spec.freq = i->sample_spec.rate; const SDL_bool add = (SDL_bool) ((intptr_t)data);
spec.channels = i->sample_spec.channels;
spec.format = PulseFormatToSDLFormat(i->sample_spec.format);
spec.silence = 0;
spec.samples = 0;
spec.size = 0;
spec.callback = NULL;
spec.userdata = NULL;
if (add) { if (add) {
SDL_AudioSpec spec;
spec.format = PulseFormatToSDLFormat(i->sample_spec.format);
spec.channels = i->sample_spec.channels;
spec.freq = i->sample_spec.rate;
SDL_AddAudioDevice(SDL_FALSE, i->description, &spec, (void *)((intptr_t)i->index + 1)); SDL_AddAudioDevice(SDL_FALSE, i->description, &spec, (void *)((intptr_t)i->index + 1));
} }
if (default_sink_path != NULL && SDL_strcmp(i->name, default_sink_path) == 0) { if (default_sink_path != NULL && SDL_strcmp(i->name, default_sink_path) == 0) {
if (default_sink_name != NULL) { default_sink_index = i->index;
SDL_free(default_sink_name);
}
default_sink_name = SDL_strdup(i->description);
} }
} }
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
} }
/* This is called when PulseAudio adds a capture ("source") device. */ /* This is called when PulseAudio adds a capture ("source") device. */
// !!! FIXME: this is almost identical to SinkInfoCallback, merge the two.
static void SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *data) static void SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
{ {
SDL_AudioSpec spec;
SDL_bool add = (SDL_bool)((intptr_t)data);
if (i) {
/* Maybe skip "monitor" sources. These are just output from other sinks. */ /* Maybe skip "monitor" sources. These are just output from other sinks. */
if (include_monitors || (i->monitor_of_sink == PA_INVALID_INDEX)) { if (i && (include_monitors || (i->monitor_of_sink == PA_INVALID_INDEX))) {
spec.freq = i->sample_spec.rate; const SDL_bool add = (SDL_bool) ((intptr_t)data);
spec.channels = i->sample_spec.channels;
spec.format = PulseFormatToSDLFormat(i->sample_spec.format);
spec.silence = 0;
spec.samples = 0;
spec.size = 0;
spec.callback = NULL;
spec.userdata = NULL;
if (add) { if (add) {
SDL_AudioSpec spec;
spec.format = PulseFormatToSDLFormat(i->sample_spec.format);
spec.channels = i->sample_spec.channels;
spec.freq = i->sample_spec.rate;
SDL_AddAudioDevice(SDL_TRUE, i->description, &spec, (void *)((intptr_t)i->index + 1)); SDL_AddAudioDevice(SDL_TRUE, i->description, &spec, (void *)((intptr_t)i->index + 1));
} }
if (default_source_path != NULL && SDL_strcmp(i->name, default_source_path) == 0) { if (default_source_path != NULL && SDL_strcmp(i->name, default_source_path) == 0) {
if (default_source_name != NULL) { default_source_index = i->index;
SDL_free(default_source_name);
}
default_source_name = SDL_strdup(i->description);
}
} }
} }
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
@ -807,14 +786,22 @@ static void SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_la
static void ServerInfoCallback(pa_context *c, const pa_server_info *i, void *data) static void ServerInfoCallback(pa_context *c, const pa_server_info *i, void *data)
{ {
if (!default_sink_path || (SDL_strcmp(i->default_sink_name, default_sink_path) != 0)) {
/*printf("DEFAULT SINK PATH CHANGED TO '%s'\n", i->default_sink_name);*/
SDL_free(default_sink_path); SDL_free(default_sink_path);
SDL_free(default_source_path);
default_sink_path = SDL_strdup(i->default_sink_name); default_sink_path = SDL_strdup(i->default_sink_name);
}
if (!default_source_path || (SDL_strcmp(i->default_source_name, default_source_path) != 0)) {
/*printf("DEFAULT SOURCE PATH CHANGED TO '%s'\n", i->default_source_name);*/
SDL_free(default_source_path);
default_source_path = SDL_strdup(i->default_source_name); default_source_path = SDL_strdup(i->default_source_name);
}
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
} }
/* This is called when PulseAudio has a device connected/removed/changed. */ // This is called when PulseAudio has a device connected/removed/changed. */
static void HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *data) static void HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *data)
{ {
const SDL_bool added = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW); const SDL_bool added = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW);
@ -825,29 +812,41 @@ static void HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint3
const SDL_bool sink = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK); const SDL_bool sink = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK);
const SDL_bool source = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE); const SDL_bool source = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
/* adds need sink details from the PulseAudio server. Another callback... */
/* (just unref all these operations right away, because we aren't going to wait on them and their callbacks will handle any work, so they can free as soon as that happens.) */
if ((added || changed) && sink) {
if (changed) { if (changed) {
PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_server_info(pulseaudio_context, ServerInfoCallback, NULL)); PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_server_info(pulseaudio_context, ServerInfoCallback, NULL));
} }
/* adds need sink details from the PulseAudio server. Another callback...
(just unref all these operations right away, because we aren't going to wait on them
and their callbacks will handle any work, so they can free as soon as that happens.) */
if ((added || changed) && sink) {
PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_sink_info_by_index(pulseaudio_context, idx, SinkInfoCallback, (void *)((intptr_t)added))); PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_sink_info_by_index(pulseaudio_context, idx, SinkInfoCallback, (void *)((intptr_t)added)));
} else if ((added || changed) && source) { } else if ((added || changed) && source) {
if (changed) {
PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_server_info(pulseaudio_context, ServerInfoCallback, NULL));
}
PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_source_info_by_index(pulseaudio_context, idx, SourceInfoCallback, (void *)((intptr_t)added))); PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_source_info_by_index(pulseaudio_context, idx, SourceInfoCallback, (void *)((intptr_t)added)));
} else if (removed && (sink || source)) { } else if (removed && (sink || source)) {
/* removes we can handle just with the device index. */ // removes we can handle just with the device index.
SDL_RemoveAudioDevice(source != 0, (void *)((intptr_t)idx + 1)); SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle((void *)((intptr_t)idx + 1)));
} }
} }
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
} }
/* this runs as a thread while the Pulse target is initialized to catch hotplug events. */ static void CheckDefaultDevice(uint32_t *prev_default, uint32_t new_default)
{
if (*prev_default != new_default) {
SDL_AudioDevice *device = SDL_FindPhysicalAudioDeviceByHandle((void *)((intptr_t)new_default + 1));
if (device) {
*prev_default = new_default;
SDL_DefaultAudioDeviceChanged(device);
}
}
}
// this runs as a thread while the Pulse target is initialized to catch hotplug events.
static int SDLCALL HotplugThread(void *data) static int SDLCALL HotplugThread(void *data)
{ {
uint32_t prev_default_sink_index = default_sink_index;
uint32_t prev_default_source_index = default_source_index;
pa_operation *op; pa_operation *op;
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW); SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
@ -855,7 +854,7 @@ static int SDLCALL HotplugThread(void *data)
PULSEAUDIO_pa_context_set_subscribe_callback(pulseaudio_context, HotplugCallback, NULL); PULSEAUDIO_pa_context_set_subscribe_callback(pulseaudio_context, HotplugCallback, NULL);
/* don't WaitForPulseOperation on the subscription; when it's done we'll be able to get hotplug events, but waiting doesn't changing anything. */ /* don't WaitForPulseOperation on the subscription; when it's done we'll be able to get hotplug events, but waiting doesn't changing anything. */
op = PULSEAUDIO_pa_context_subscribe(pulseaudio_context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, NULL, NULL); op = PULSEAUDIO_pa_context_subscribe(pulseaudio_context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SERVER, NULL, NULL);
SDL_PostSemaphore((SDL_Semaphore *) data); SDL_PostSemaphore((SDL_Semaphore *) data);
@ -865,6 +864,13 @@ static int SDLCALL HotplugThread(void *data)
PULSEAUDIO_pa_operation_unref(op); PULSEAUDIO_pa_operation_unref(op);
op = NULL; op = NULL;
} }
// Update default devices; don't hold the pulse lock during this, since it could deadlock vs a playing device that we're about to lock here.
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
CheckDefaultDevice(&prev_default_sink_index, default_sink_index);
CheckDefaultDevice(&prev_default_source_index, default_source_index);
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
} }
if (op) { if (op) {
@ -874,9 +880,11 @@ static int SDLCALL HotplugThread(void *data)
PULSEAUDIO_pa_context_set_subscribe_callback(pulseaudio_context, NULL, NULL); PULSEAUDIO_pa_context_set_subscribe_callback(pulseaudio_context, NULL, NULL);
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
return 0; return 0;
} }
static void PULSEAUDIO_DetectDevices(void) static void PULSEAUDIO_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{ {
SDL_Semaphore *ready_sem = SDL_CreateSemaphore(0); SDL_Semaphore *ready_sem = SDL_CreateSemaphore(0);
@ -886,44 +894,24 @@ static void PULSEAUDIO_DetectDevices(void)
WaitForPulseOperation(PULSEAUDIO_pa_context_get_source_info_list(pulseaudio_context, SourceInfoCallback, (void *)((intptr_t)SDL_TRUE))); WaitForPulseOperation(PULSEAUDIO_pa_context_get_source_info_list(pulseaudio_context, SourceInfoCallback, (void *)((intptr_t)SDL_TRUE)));
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
SDL_AudioDevice *device;
device = SDL_FindPhysicalAudioDeviceByHandle((void *)((intptr_t)default_sink_index + 1));
if (device) {
*default_output = device;
}
device = SDL_FindPhysicalAudioDeviceByHandle((void *)((intptr_t)default_source_index + 1));
if (device) {
*default_capture = device;
}
/* ok, we have a sane list, let's set up hotplug notifications now... */ /* ok, we have a sane list, let's set up hotplug notifications now... */
SDL_AtomicSet(&pulseaudio_hotplug_thread_active, 1); SDL_AtomicSet(&pulseaudio_hotplug_thread_active, 1);
pulseaudio_hotplug_thread = SDL_CreateThreadInternal(HotplugThread, "PulseHotplug", 256 * 1024, ready_sem); /* !!! FIXME: this can probably survive in significantly less stack space. */ pulseaudio_hotplug_thread = SDL_CreateThreadInternal(HotplugThread, "PulseHotplug", 256 * 1024, ready_sem); // !!! FIXME: this can probably survive in significantly less stack space.
SDL_WaitSemaphore(ready_sem); SDL_WaitSemaphore(ready_sem);
SDL_DestroySemaphore(ready_sem); SDL_DestroySemaphore(ready_sem);
} }
static int PULSEAUDIO_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
{
int i;
int numdevices;
char *target;
if (iscapture) {
if (default_source_name == NULL) {
return SDL_SetError("PulseAudio could not find a default source");
}
target = default_source_name;
} else {
if (default_sink_name == NULL) {
return SDL_SetError("PulseAudio could not find a default sink");
}
target = default_sink_name;
}
numdevices = SDL_GetNumAudioDevices(iscapture);
for (i = 0; i < numdevices; i += 1) {
if (SDL_strcmp(SDL_GetAudioDeviceName(i, iscapture), target) == 0) {
if (name != NULL) {
*name = SDL_strdup(target);
}
SDL_GetAudioDeviceSpec(i, iscapture, spec);
return 0;
}
}
return SDL_SetError("Could not find default PulseAudio device");
}
static void PULSEAUDIO_Deinitialize(void) static void PULSEAUDIO_Deinitialize(void)
{ {
if (pulseaudio_hotplug_thread) { if (pulseaudio_hotplug_thread) {
@ -941,10 +929,9 @@ static void PULSEAUDIO_Deinitialize(void)
default_sink_path = NULL; default_sink_path = NULL;
SDL_free(default_source_path); SDL_free(default_source_path);
default_source_path = NULL; default_source_path = NULL;
SDL_free(default_sink_name);
default_sink_name = NULL; default_source_index = 0;
SDL_free(default_source_name); default_sink_index = 0;
default_source_name = NULL;
UnloadPulseAudioLibrary(); UnloadPulseAudioLibrary();
} }
@ -968,12 +955,11 @@ static SDL_bool PULSEAUDIO_Init(SDL_AudioDriverImpl *impl)
impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf; impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
impl->CloseDevice = PULSEAUDIO_CloseDevice; impl->CloseDevice = PULSEAUDIO_CloseDevice;
impl->Deinitialize = PULSEAUDIO_Deinitialize; impl->Deinitialize = PULSEAUDIO_Deinitialize;
impl->WaitCaptureDevice = PULSEAUDIO_WaitCaptureDevice;
impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice; impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice;
impl->FlushCapture = PULSEAUDIO_FlushCapture; impl->FlushCapture = PULSEAUDIO_FlushCapture;
impl->GetDefaultAudioInfo = PULSEAUDIO_GetDefaultAudioInfo;
impl->HasCaptureSupport = SDL_TRUE; impl->HasCaptureSupport = SDL_TRUE;
impl->SupportsNonPow2Samples = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */ return SDL_TRUE; /* this audio target is available. */
} }

View File

@ -36,7 +36,6 @@ struct SDL_PrivateAudioData
/* Raw mixing buffer */ /* Raw mixing buffer */
Uint8 *mixbuf; Uint8 *mixbuf;
int mixlen;
int bytes_requested; /* bytes of data the hardware wants _now_. */ int bytes_requested; /* bytes of data the hardware wants _now_. */

View File

@ -19,13 +19,7 @@
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
*/ */
/* // !!! FIXME: can this target support hotplugging?
* !!! FIXME: streamline this a little by removing all the
* !!! FIXME: if (capture) {} else {} sections that are identical
* !!! FIXME: except for one flag.
*/
/* !!! FIXME: can this target support hotplugging? */
#include "../../SDL_internal.h" #include "../../SDL_internal.h"
@ -48,7 +42,7 @@
#include "../SDL_audio_c.h" #include "../SDL_audio_c.h"
#include "SDL_qsa_audio.h" #include "SDL_qsa_audio.h"
/* default channel communication parameters */ // default channel communication parameters
#define DEFAULT_CPARAMS_RATE 44100 #define DEFAULT_CPARAMS_RATE 44100
#define DEFAULT_CPARAMS_VOICES 1 #define DEFAULT_CPARAMS_VOICES 1
@ -56,32 +50,17 @@
#define DEFAULT_CPARAMS_FRAGS_MIN 1 #define DEFAULT_CPARAMS_FRAGS_MIN 1
#define DEFAULT_CPARAMS_FRAGS_MAX 1 #define DEFAULT_CPARAMS_FRAGS_MAX 1
/* List of found devices */ #define QSA_MAX_NAME_LENGTH 81+16 // Hardcoded in QSA, can't be changed
#define QSA_MAX_DEVICES 32
#define QSA_MAX_NAME_LENGTH 81+16 /* Hardcoded in QSA, can't be changed */
typedef struct _QSA_Device
{
char name[QSA_MAX_NAME_LENGTH]; /* Long audio device name for SDL */
int cardno;
int deviceno;
} QSA_Device;
QSA_Device qsa_playback_device[QSA_MAX_DEVICES];
uint32_t qsa_playback_devices;
QSA_Device qsa_capture_device[QSA_MAX_DEVICES];
uint32_t qsa_capture_devices;
static int QSA_SetError(const char *fn, int status) static int QSA_SetError(const char *fn, int status)
{ {
return SDL_SetError("QSA: %s() failed: %s", fn, snd_strerror(status)); return SDL_SetError("QSA: %s() failed: %s", fn, snd_strerror(status));
} }
/* !!! FIXME: does this need to be here? Does the SDL version not work? */ // !!! FIXME: does this need to be here? Does the SDL version not work?
static void QSA_ThreadInit(SDL_AudioDevice *_this) static void QSA_ThreadInit(SDL_AudioDevice *device)
{ {
/* Increase default 10 priority to 25 to avoid jerky sound */ // Increase default 10 priority to 25 to avoid jerky sound
struct sched_param param; struct sched_param param;
if (SchedGet(0, 0, &param) != -1) { if (SchedGet(0, 0, &param) != -1) {
param.sched_priority = param.sched_curpriority + 15; param.sched_priority = param.sched_curpriority + 15;
@ -89,7 +68,7 @@ static void QSA_ThreadInit(SDL_AudioDevice *_this)
} }
} }
/* PCM channel parameters initialize function */ // PCM channel parameters initialize function
static void QSA_InitAudioParams(snd_pcm_channel_params_t * cpars) static void QSA_InitAudioParams(snd_pcm_channel_params_t * cpars)
{ {
SDL_zerop(cpars); SDL_zerop(cpars);
@ -106,209 +85,150 @@ static void QSA_InitAudioParams(snd_pcm_channel_params_t * cpars)
cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX; cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX;
} }
/* This function waits until it is possible to write a full sound buffer */ // This function waits until it is possible to write a full sound buffer
static void QSA_WaitDevice(SDL_AudioDevice *_this) static void QSA_WaitDevice(SDL_AudioDevice *device)
{ {
int result; int result;
/* Setup timeout for playing one fragment equal to 2 seconds */ // Setup timeout for playing one fragment equal to 2 seconds
/* If timeout occurred than something wrong with hardware or driver */ // If timeout occurred than something wrong with hardware or driver
/* For example, Vortex 8820 audio driver stucks on second DAC because */ // For example, Vortex 8820 audio driver stucks on second DAC because
/* it doesn't exist ! */ // it doesn't exist !
result = SDL_IOReady(_this->hidden->audio_fd, result = SDL_IOReady(device->hidden->audio_fd,
_this->hidden->iscapture ? SDL_IOR_READ : SDL_IOR_WRITE, device->iscapture ? SDL_IOR_READ : SDL_IOR_WRITE,
2 * 1000); 2 * 1000);
switch (result) { switch (result) {
case -1: case -1:
SDL_SetError("QSA: SDL_IOReady() failed: %s", strerror(errno)); SDL_SetError("QSA: SDL_IOReady() failed: %s", strerror(errno)); // !!! FIXME: Should we just disconnect the device in this case?
break; break;
case 0: case 0:
SDL_SetError("QSA: timeout on buffer waiting occurred"); device->hidden->timeout_on_wait = SDL_TRUE; // !!! FIXME: Should we just disconnect the device in this case?
_this->hidden->timeout_on_wait = 1;
break; break;
default: default:
_this->hidden->timeout_on_wait = 0; device->hidden->timeout_on_wait = SDL_FALSE;
break; break;
} }
} }
static void QSA_PlayDevice(SDL_AudioDevice *_this) static void QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{ {
snd_pcm_channel_status_t cstatus; if (SDL_AtomicGet(&device->shutdown) || !device->hidden) {
int written;
int status;
int towrite;
void *pcmbuffer;
if (!SDL_AtomicGet(&_this->enabled) || !_this->hidden) {
return; return;
} }
towrite = _this->spec.size; int towrite = buflen;
pcmbuffer = _this->hidden->pcm_buf;
/* Write the audio data, checking for EAGAIN (buffer full) and underrun */ // Write the audio data, checking for EAGAIN (buffer full) and underrun
do { while ((towrite > 0) && !SDL_AtomicGet(&device->shutdown));
written = const int bw = snd_pcm_plugin_write(device->hidden->audio_handle, buffer, towrite);
snd_pcm_plugin_write(_this->hidden->audio_handle, pcmbuffer, if (bw != towrite) {
towrite); // Check if samples playback got stuck somewhere in hardware or in the audio device driver
if (written != towrite) { if ((errno == EAGAIN) && (bw == 0)) {
/* Check if samples playback got stuck somewhere in hardware or in */ if (device->hidden->timeout_on_wait) {
/* the audio device driver */ return; // oh well, try again next time. !!! FIXME: Should we just disconnect the device in this case?
if ((errno == EAGAIN) && (written == 0)) {
if (_this->hidden->timeout_on_wait != 0) {
SDL_SetError("QSA: buffer playback timeout");
return;
} }
} }
/* Check for errors or conditions */ // Check for errors or conditions
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
/* Let a little CPU time go by and try to write again */ SDL_Delay(1); // Let a little CPU time go by and try to write again
SDL_Delay(1);
/* if we wrote some data */ // if we wrote some data
towrite -= written; towrite -= bw;
pcmbuffer += written * _this->spec.channels; buffer += bw * device->spec.channels;
continue; continue;
} else { } else if ((errno == EINVAL) || (errno == EIO)) {
if ((errno == EINVAL) || (errno == EIO)) { snd_pcm_channel_status_t cstatus;
SDL_zero(cstatus); SDL_zero(cstatus);
if (!_this->hidden->iscapture) { cstatus.channel = device->iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK;
cstatus.channel = SND_PCM_CHANNEL_PLAYBACK;
} else {
cstatus.channel = SND_PCM_CHANNEL_CAPTURE;
}
status = int status = snd_pcm_plugin_status(device->hidden->audio_handle, &cstatus);
snd_pcm_plugin_status(_this->hidden->audio_handle,
&cstatus);
if (status < 0) { if (status < 0) {
QSA_SetError("snd_pcm_plugin_status", status); QSA_SetError("snd_pcm_plugin_status", status);
return; return; // !!! FIXME: disconnect the device?
} } 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 ((cstatus.status == SND_PCM_STATUS_UNDERRUN) ||
(cstatus.status == SND_PCM_STATUS_READY)) {
if (!_this->hidden->iscapture) {
status =
snd_pcm_plugin_prepare(_this->hidden->
audio_handle,
SND_PCM_CHANNEL_PLAYBACK);
} else {
status =
snd_pcm_plugin_prepare(_this->hidden->
audio_handle,
SND_PCM_CHANNEL_CAPTURE);
}
if (status < 0) { if (status < 0) {
QSA_SetError("snd_pcm_plugin_prepare", status); QSA_SetError("snd_pcm_plugin_prepare", status);
return; return; // !!! FIXME: disconnect the device?
} }
} }
continue; continue;
} else { } else {
return; return; // !!! FIXME: disconnect the device?
}
} }
} else { } else {
/* we wrote all remaining data */ // we wrote all remaining data
towrite -= written; towrite -= bw;
pcmbuffer += written * _this->spec.channels; buffer += bw * device->spec.channels;
}
} }
} while ((towrite > 0) && SDL_AtomicGet(&_this->enabled));
/* If we couldn't write, assume fatal error for now */ // If we couldn't write, assume fatal error for now
if (towrite != 0) { if (towrite != 0) {
SDL_OpenedAudioDeviceDisconnected(_this); SDL_AudioDeviceDisconnected(device);
} }
} }
static Uint8 *QSA_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *QSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
return _this->hidden->pcm_buf; return device->hidden->pcm_buf;
} }
static void QSA_CloseDevice(SDL_AudioDevice *_this) static void QSA_CloseDevice(SDL_AudioDevice *device)
{ {
if (_this->hidden->audio_handle != NULL) { if (device->hidden) {
if (device->hidden->audio_handle != NULL) {
#if _NTO_VERSION < 710 #if _NTO_VERSION < 710
if (!_this->hidden->iscapture) { // Finish playing available samples or cancel unread samples during capture
/* Finish playing available samples */ snd_pcm_plugin_flush(device->hidden->audio_handle, device->iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK);
snd_pcm_plugin_flush(_this->hidden->audio_handle,
SND_PCM_CHANNEL_PLAYBACK);
} else {
/* Cancel unread samples during capture */
snd_pcm_plugin_flush(_this->hidden->audio_handle,
SND_PCM_CHANNEL_CAPTURE);
}
#endif #endif
snd_pcm_close(_this->hidden->audio_handle); snd_pcm_close(device->hidden->audio_handle);
} }
SDL_free(_this->hidden->pcm_buf); SDL_free(device->hidden->pcm_buf);
SDL_free(_this->hidden); SDL_free(device->hidden);
device->hidden = NULL;
}
} }
static int QSA_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int QSA_OpenDevice(SDL_AudioDevice *device)
{ {
#if 0 if (device->iscapture) {
/* !!! FIXME: SDL2 used to pass this handle. What's the alternative? */ return SDL_SetError("SDL capture support isn't available on QNX atm"); // !!! FIXME: most of this code has support for capture devices, but there's no CaptureFromDevice, etc functions. Fill them in!
const QSA_Device *device = (const QSA_Device *) handle; }
#else
const QSA_Device *device = NULL;
#endif
int status = 0;
int format = 0;
SDL_AudioFormat test_format = 0;
const SDL_AudioFormat *closefmts;
snd_pcm_channel_setup_t csetup;
snd_pcm_channel_params_t cparams;
SDL_bool iscapture = _this->iscapture;
/* Initialize all variables that we clean on shutdown */ SDL_assert(device->handle != NULL); // NULL used to mean "system default device" in SDL2; it does not mean that in SDL3.
_this->hidden = const Uint32 sdlhandle = (Uint32) ((size_t) device->handle);
(struct SDL_PrivateAudioData *) SDL_calloc(1, const uint32_t cardno = (uint32_t) (sdlhandle & 0xFFFF);
(sizeof const uint32_t deviceno = (uint32_t) ((sdlhandle >> 16) & 0xFFFF);
(struct const SDL_bool iscapture = device->iscapture;
SDL_PrivateAudioData))); int status = 0;
if (_this->hidden == NULL) {
// Initialize all variables that we clean on shutdown
device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, (sizeof (struct SDL_PrivateAudioData)));
if (device->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
/* Initialize channel transfer parameters to default */ // Initialize channel transfer parameters to default
snd_pcm_channel_params_t cparams;
QSA_InitAudioParams(&cparams); QSA_InitAudioParams(&cparams);
/* Initialize channel direction: capture or playback */ // Open requested audio device
_this->hidden->iscapture = iscapture ? SDL_TRUE : SDL_FALSE; status = snd_pcm_open(&device->hidden->audio_handle, cardno, deviceno, iscapture ? SND_PCM_OPEN_CAPTURE : SND_PCM_OPEN_PLAYBACK);
if (device != NULL) {
/* Open requested audio device */
_this->hidden->deviceno = device->deviceno;
_this->hidden->cardno = device->cardno;
status = snd_pcm_open(&_this->hidden->audio_handle,
device->cardno, device->deviceno,
iscapture ? SND_PCM_OPEN_CAPTURE : SND_PCM_OPEN_PLAYBACK);
} else {
/* Open system default audio device */
status = snd_pcm_open_preferred(&_this->hidden->audio_handle,
&_this->hidden->cardno,
&_this->hidden->deviceno,
iscapture ? SND_PCM_OPEN_CAPTURE : SND_PCM_OPEN_PLAYBACK);
}
/* Check if requested device is opened */
if (status < 0) { if (status < 0) {
_this->hidden->audio_handle = NULL; device->hidden->audio_handle = NULL;
return QSA_SetError("snd_pcm_open", status); return QSA_SetError("snd_pcm_open", status);
} }
/* Try for a closest match on audio format */ // Try for a closest match on audio format
closefmts = SDL_ClosestAudioFormats(_this->spec.format); SDL_AudioFormat test_format = 0;
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
while ((test_format = *(closefmts++)) != 0) { while ((test_format = *(closefmts++)) != 0) {
/* if match found set format to equivalent QSA format */ // if match found set format to equivalent QSA format
switch (test_format) { switch (test_format) {
#define CHECKFMT(sdlfmt, qsafmt) case SDL_AUDIO_##sdlfmt: format = SND_PCM_SFMT_##qsafmt; break #define CHECKFMT(sdlfmt, qsafmt) case SDL_AUDIO_##sdlfmt: cparams.format.format = SND_PCM_SFMT_##qsafmt; break
CHECKFMT(U8, U8); CHECKFMT(U8, U8);
CHECKFMT(S8, S8); CHECKFMT(S8, S8);
CHECKFMT(S16LSB, S16_LE); CHECKFMT(S16LSB, S16_LE);
@ -323,161 +243,151 @@ static int QSA_OpenDevice(SDL_AudioDevice *_this, const char *devname)
break; break;
} }
/* assumes test_format not 0 on success */ // assumes test_format not 0 on success
if (test_format == 0) { if (test_format == 0) {
return SDL_SetError("QSA: Couldn't find any hardware audio formats"); return SDL_SetError("QSA: Couldn't find any hardware audio formats");
} }
_this->spec.format = test_format; device->spec.format = test_format;
/* Set the audio format */ // Set mono/stereo/4ch/6ch/8ch audio
cparams.format.format = format; cparams.format.voices = device->spec.channels;
/* Set mono/stereo/4ch/6ch/8ch audio */ // Set rate
cparams.format.voices = _this->spec.channels; cparams.format.rate = device->spec.freq;
/* Set rate */ // Setup the transfer parameters according to cparams
cparams.format.rate = _this->spec.freq; status = snd_pcm_plugin_params(device->hidden->audio_handle, &cparams);
/* Setup the transfer parameters according to cparams */
status = snd_pcm_plugin_params(_this->hidden->audio_handle, &cparams);
if (status < 0) { if (status < 0) {
return QSA_SetError("snd_pcm_plugin_params", status); return QSA_SetError("snd_pcm_plugin_params", status);
} }
/* Make sure channel is setup right one last time */ // Make sure channel is setup right one last time
snd_pcm_channel_setup_t csetup;
SDL_zero(csetup); SDL_zero(csetup);
if (!_this->hidden->iscapture) { csetup.channel = iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK;
csetup.channel = SND_PCM_CHANNEL_PLAYBACK; if (snd_pcm_plugin_setup(device->hidden->audio_handle, &csetup) < 0) {
} else {
csetup.channel = SND_PCM_CHANNEL_CAPTURE;
}
/* Setup an audio channel */
if (snd_pcm_plugin_setup(_this->hidden->audio_handle, &csetup) < 0) {
return SDL_SetError("QSA: Unable to setup channel"); return SDL_SetError("QSA: Unable to setup channel");
} }
/* Calculate the final parameters for this audio specification */ device->sample_frames = csetup.buf.block.frag_size;
SDL_CalculateAudioSpec(&_this->spec);
_this->hidden->pcm_len = _this->spec.size; // Calculate the final parameters for this audio specification
SDL_UpdatedAudioDeviceFormat(device);
if (_this->hidden->pcm_len == 0) { device->hidden->pcm_buf = (Uint8 *) SDL_malloc(device->buffer_size);
_this->hidden->pcm_len = if (device->hidden->pcm_buf == NULL) {
csetup.buf.block.frag_size * _this->spec.channels *
(snd_pcm_format_width(format) / 8);
}
/*
* Allocate memory to the audio buffer and initialize with silence
* (Note that buffer size must be a multiple of fragment size, so find
* closest multiple)
*/
_this->hidden->pcm_buf =
(Uint8 *) SDL_malloc(_this->hidden->pcm_len);
if (_this->hidden->pcm_buf == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_memset(_this->hidden->pcm_buf, _this->spec.silence, SDL_memset(device->hidden->pcm_buf, device->silence_value, device->buffer_size);
_this->hidden->pcm_len);
/* get the file descriptor */ // get the file descriptor
if (!_this->hidden->iscapture) { device->hidden->audio_fd = snd_pcm_file_descriptor(device->hidden->audio_handle, csetup.channel);
_this->hidden->audio_fd = if (device->hidden->audio_fd < 0) {
snd_pcm_file_descriptor(_this->hidden->audio_handle, return QSA_SetError("snd_pcm_file_descriptor", device->hidden->audio_fd);
SND_PCM_CHANNEL_PLAYBACK);
} else {
_this->hidden->audio_fd =
snd_pcm_file_descriptor(_this->hidden->audio_handle,
SND_PCM_CHANNEL_CAPTURE);
}
if (_this->hidden->audio_fd < 0) {
return QSA_SetError("snd_pcm_file_descriptor", status);
}
/* Prepare an audio channel */
if (!_this->hidden->iscapture) {
/* Prepare audio playback */
status =
snd_pcm_plugin_prepare(_this->hidden->audio_handle,
SND_PCM_CHANNEL_PLAYBACK);
} else {
/* Prepare audio capture */
status =
snd_pcm_plugin_prepare(_this->hidden->audio_handle,
SND_PCM_CHANNEL_CAPTURE);
} }
// Prepare an audio channel
status = snd_pcm_plugin_prepare(device->hidden->audio_handle, csetup.channel)
if (status < 0) { if (status < 0) {
return QSA_SetError("snd_pcm_plugin_prepare", status); return QSA_SetError("snd_pcm_plugin_prepare", status);
} }
/* We're really ready to rock and roll. :-) */ return 0; // We're really ready to rock and roll. :-)
return 0;
} }
static void QSA_DetectDevices(void) static SDL_AudioFormat QnxFormatToSDLFormat(const int32_t qnxfmt)
{ {
uint32_t it; switch (qnxfmt) {
uint32_t cards; #define CHECKFMT(sdlfmt, qsafmt) case SND_PCM_SFMT_##qsafmt: return SDL_AUDIO_##sdlfmt
uint32_t devices; CHECKFMT(U8, U8);
int32_t status; CHECKFMT(S8, S8);
CHECKFMT(S16LSB, S16_LE);
CHECKFMT(S16MSB, S16_BE);
CHECKFMT(S32LSB, S32_LE);
CHECKFMT(S32MSB, S32_BE);
CHECKFMT(F32LSB, FLOAT_LE);
CHECKFMT(F32MSB, FLOAT_BE);
#undef CHECKFMT
default: break;
}
return SDL_AUDIO_S16SYS; // oh well.
}
/* Detect amount of available devices */ static void QSA_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
/* this value can be changed in the runtime */ {
cards = snd_cards(); // Detect amount of available devices
// this value can be changed in the runtime
int num_cards = 0;
(void) snd_cards_list(NULL, 0, &alloc_num_cards);
SDL_bool isstack = SDL_FALSE;
int *cards = SDL_small_alloc(int, num_cards, &isstack);
if (!cards) {
return; // we're in trouble.
}
int overflow_cards = 0;
const int total_num_cards = snd_cards_list(cards, num_cards, &overflow_cards);
// if overflow_cards > 0 or total_num_cards > num_cards, it changed at the last moment; oh well, we lost some.
num_cards = SDL_min(num_cards, total_num_cards); // ...but make sure it didn't _shrink_.
/* If io-audio manager is not running we will get 0 as number */ // If io-audio manager is not running we will get 0 as number of available audio devices
/* of available audio devices */ if (num_cards == 0) { // not any available audio devices?
if (cards == 0) { SDL_small_free(cards, isstack);
/* We have no any available audio devices */
return; return;
} }
/* !!! FIXME: code duplication */ // Find requested devices by type
/* Find requested devices by type */ for (int it = 0; it < num_cards; it++) {
{ /* output devices */ const int card = cards[it];
/* Playback devices enumeration requested */ for (uint32_t deviceno = 0; ; deviceno++) {
for (it = 0; it < cards; it++) { int32_t status;
devices = 0; char name[QSA_MAX_NAME_LENGTH];
do {
status = status = snd_card_get_longname(card, name, sizeof (name));
snd_card_get_longname(it,
qsa_playback_device
[qsa_playback_devices].name,
QSA_MAX_NAME_LENGTH);
if (status == EOK) { if (status == EOK) {
snd_pcm_t *handle; snd_pcm_t *handle;
/* Add device number to device name */ // Add device number to device name
sprintf(qsa_playback_device[qsa_playback_devices].name + char fullname[QSA_MAX_NAME_LENGTH + 32];
SDL_strlen(qsa_playback_device SDL_snprintf(fullname, sizeof (fullname), "%s d%d", name, (int) deviceno);
[qsa_playback_devices].name), " d%d",
devices);
/* Store associated card number id */ // Check if this device id could play anything
qsa_playback_device[qsa_playback_devices].cardno = it; SDL_bool iscapture = SDL_FALSE;
status = snd_pcm_open(&handle, card, deviceno, SND_PCM_OPEN_PLAYBACK);
/* Check if this device id could play anything */ if (status != EOK) { // no? See if it's a capture device instead.
status = #if 0 // !!! FIXME: most of this code has support for capture devices, but there's no CaptureFromDevice, etc functions. Fill them in!
snd_pcm_open(&handle, it, devices, status = snd_pcm_open(&handle, card, deviceno, SND_PCM_OPEN_CAPTURE);
SND_PCM_OPEN_PLAYBACK);
if (status == EOK) { if (status == EOK) {
qsa_playback_device[qsa_playback_devices].deviceno = iscapture = SDL_TRUE;
devices; }
#endif
}
if (status == EOK) {
SDL_AudioSpec spec;
SDL_AudioSpec *pspec = &spec;
snd_pcm_channel_setup_t csetup;
SDL_zero(csetup);
csetup.channel = iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK;
if (snd_pcm_plugin_setup(device->hidden->audio_handle, &csetup) < 0) {
pspec = NULL; // go on without spec info.
} else {
spec.format = QnxFormatToSDLFormat(csetup.format.format);
spec.channels = csetup.format.channels;
spec.freq = csetup.format.rate;
}
status = snd_pcm_close(handle); status = snd_pcm_close(handle);
if (status == EOK) { if (status == EOK) {
/* Note that spec is NULL, because we are required to open the device before // !!! FIXME: I'm assuming each of these values are way less than 0xFFFF. Fix this if not.
* acquiring the mix format, making this information inaccessible at SDL_assert(card <= 0xFFFF);
* enumeration time SDL_assert(deviceno <= 0xFFFF);
*/ const Uint32 sdlhandle = ((Uint32) card) | (((Uint32) deviceno) << 16);
SDL_AddAudioDevice(SDL_FALSE, qsa_playback_device[qsa_playback_devices].name, NULL, &qsa_playback_device[qsa_playback_devices]); SDL_AddAudioDevice(iscapture, fullname, pspec, (void *) ((size_t) sdlhandle));
qsa_playback_devices++;
} }
} else { } else {
/* Check if we got end of devices list */ // Check if we got end of devices list
if (status == -ENOENT) { if (status == -ENOENT) {
break; break;
} }
@ -485,105 +395,40 @@ static void QSA_DetectDevices(void)
} else { } else {
break; break;
} }
/* Check if we reached maximum devices count */
if (qsa_playback_devices >= QSA_MAX_DEVICES) {
break;
}
devices++;
} while (1);
/* Check if we reached maximum devices count */
if (qsa_playback_devices >= QSA_MAX_DEVICES) {
break;
}
}
}
{ /* capture devices */
/* Capture devices enumeration requested */
for (it = 0; it < cards; it++) {
devices = 0;
do {
status =
snd_card_get_longname(it,
qsa_capture_device
[qsa_capture_devices].name,
QSA_MAX_NAME_LENGTH);
if (status == EOK) {
snd_pcm_t *handle;
/* Add device number to device name */
sprintf(qsa_capture_device[qsa_capture_devices].name +
SDL_strlen(qsa_capture_device
[qsa_capture_devices].name), " d%d",
devices);
/* Store associated card number id */
qsa_capture_device[qsa_capture_devices].cardno = it;
/* Check if this device id could play anything */
status =
snd_pcm_open(&handle, it, devices,
SND_PCM_OPEN_CAPTURE);
if (status == EOK) {
qsa_capture_device[qsa_capture_devices].deviceno =
devices;
status = snd_pcm_close(handle);
if (status == EOK) {
/* Note that spec is NULL, because we are required to open the device before
* acquiring the mix format, making this information inaccessible at
* enumeration time
*/
SDL_AddAudioDevice(SDL_TRUE, qsa_capture_device[qsa_capture_devices].name, NULL, &qsa_capture_device[qsa_capture_devices]);
qsa_capture_devices++;
}
} else {
/* Check if we got end of devices list */
if (status == -ENOENT) {
break;
} }
} }
/* Check if we reached maximum devices count */ SDL_small_free(cards, isstack);
if (qsa_capture_devices >= QSA_MAX_DEVICES) {
break;
}
} else {
break;
}
devices++;
} while (1);
/* Check if we reached maximum devices count */ // Try to open the "preferred" devices, which will tell us the card/device pairs for the default devices.
if (qsa_capture_devices >= QSA_MAX_DEVICES) { snd_pcm_t handle;
break; int cardno, deviceno;
} if (snd_pcm_open_preferred(&handle, &cardno, &deviceno, SND_PCM_OPEN_PLAYBACK) == 0) {
snd_pcm_close(handle);
// !!! FIXME: I'm assuming each of these values are way less than 0xFFFF. Fix this if not.
SDL_assert(cardno <= 0xFFFF);
SDL_assert(deviceno <= 0xFFFF);
const Uint32 sdlhandle = ((Uint32) card) | (((Uint32) deviceno) << 16);
*default_output = SDL_FindPhysicalAudioDeviceByHandle((void *) ((size_t) sdlhandle));
} }
if (snd_pcm_open_preferred(&handle, &cardno, &deviceno, SND_PCM_OPEN_CAPTURE) == 0) {
snd_pcm_close(handle);
// !!! FIXME: I'm assuming each of these values are way less than 0xFFFF. Fix this if not.
SDL_assert(cardno <= 0xFFFF);
SDL_assert(deviceno <= 0xFFFF);
const Uint32 sdlhandle = ((Uint32) card) | (((Uint32) deviceno) << 16);
*default_capture = SDL_FindPhysicalAudioDeviceByHandle((void *) ((size_t) sdlhandle));
} }
} }
static void QSA_Deinitialize(void) static void QSA_Deinitialize(void)
{ {
/* Clear devices array on shutdown */ // nothing to do here atm.
/* !!! FIXME: we zero these on init...any reason to do it here? */
SDL_zeroa(qsa_playback_device);
SDL_zeroa(qsa_capture_device);
qsa_playback_devices = 0;
qsa_capture_devices = 0;
} }
static SDL_bool QSA_Init(SDL_AudioDriverImpl * impl) static SDL_bool QSA_Init(SDL_AudioDriverImpl * impl)
{ {
/* Clear devices array */
SDL_zeroa(qsa_playback_device);
SDL_zeroa(qsa_capture_device);
qsa_playback_devices = 0;
qsa_capture_devices = 0;
/* Set function pointers */
/* DeviceLock and DeviceUnlock functions are used default, */
/* provided by SDL, which uses pthread_mutex for lock/unlock */
impl->DetectDevices = QSA_DetectDevices; impl->DetectDevices = QSA_DetectDevices;
impl->OpenDevice = QSA_OpenDevice; impl->OpenDevice = QSA_OpenDevice;
impl->ThreadInit = QSA_ThreadInit; impl->ThreadInit = QSA_ThreadInit;
@ -592,21 +437,16 @@ static SDL_bool QSA_Init(SDL_AudioDriverImpl * impl)
impl->GetDeviceBuf = QSA_GetDeviceBuf; impl->GetDeviceBuf = QSA_GetDeviceBuf;
impl->CloseDevice = QSA_CloseDevice; impl->CloseDevice = QSA_CloseDevice;
impl->Deinitialize = QSA_Deinitialize; impl->Deinitialize = QSA_Deinitialize;
impl->LockDevice = NULL;
impl->UnlockDevice = NULL;
impl->ProvidesOwnCallbackThread = 0; // !!! FIXME: most of this code has support for capture devices, but there's no CaptureFromDevice, etc functions. Fill them in!
impl->HasCaptureSupport = 1; //impl->HasCaptureSupport = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = 0;
impl->OnlyHasDefaultCaptureDevice = 0;
return SDL_TRUE; /* this audio target is available. */ return SDL_TRUE;
} }
AudioBootStrap QSAAUDIO_bootstrap = { AudioBootStrap QSAAUDIO_bootstrap = {
"qsa", "QNX QSA Audio", QSA_Init, 0 "qsa", "QNX QSA Audio", QSA_Init, 0
}; };
#endif /* SDL_AUDIO_DRIVER_QNX */ #endif // SDL_AUDIO_DRIVER_QNX
/* vi: set ts=4 sw=4 expandtab: */

View File

@ -30,23 +30,10 @@
struct SDL_PrivateAudioData struct SDL_PrivateAudioData
{ {
/* SDL capture state */ snd_pcm_t *audio_handle; // The audio device handle
SDL_bool iscapture; int audio_fd; // The audio file descriptor, for selecting on
SDL_bool timeout_on_wait; // Select timeout status
/* The audio device handle */ Uint8 *pcm_buf; // Raw mixing buffer
int cardno;
int deviceno;
snd_pcm_t *audio_handle;
/* The audio file descriptor */
int audio_fd;
/* Select timeout status */
uint32_t timeout_on_wait;
/* Raw mixing buffer */
Uint8 *pcm_buf;
Uint32 pcm_len;
}; };
#endif /* __SDL_QSA_AUDIO_H__ */ #endif /* __SDL_QSA_AUDIO_H__ */

View File

@ -23,7 +23,7 @@
#ifdef SDL_AUDIO_DRIVER_SNDIO #ifdef SDL_AUDIO_DRIVER_SNDIO
/* OpenBSD sndio target */ // OpenBSD sndio target
#ifdef HAVE_STDIO_H #ifdef HAVE_STDIO_H
#include <stdio.h> #include <stdio.h>
@ -72,14 +72,13 @@ static int load_sndio_sym(const char *fn, void **addr)
{ {
*addr = SDL_LoadFunction(sndio_handle, fn); *addr = SDL_LoadFunction(sndio_handle, fn);
if (*addr == NULL) { if (*addr == NULL) {
/* Don't call SDL_SetError(): SDL_LoadFunction already did. */ return 0; // Don't call SDL_SetError(): SDL_LoadFunction already did.
return 0;
} }
return 1; return 1;
} }
/* cast funcs to char* first, to please GCC's strict aliasing rules. */ // cast funcs to char* first, to please GCC's strict aliasing rules.
#define SDL_SNDIO_SYM(x) \ #define SDL_SNDIO_SYM(x) \
if (!load_sndio_sym(#x, (void **)(char *)&SNDIO_##x)) \ if (!load_sndio_sym(#x, (void **)(char *)&SNDIO_##x)) \
return -1 return -1
@ -123,8 +122,7 @@ static int LoadSNDIOLibrary(void)
if (sndio_handle == NULL) { if (sndio_handle == NULL) {
sndio_handle = SDL_LoadObject(sndio_library); sndio_handle = SDL_LoadObject(sndio_library);
if (sndio_handle == NULL) { if (sndio_handle == NULL) {
retval = -1; retval = -1; // Don't call SDL_SetError(): SDL_LoadObject already did.
/* Don't call SDL_SetError(): SDL_LoadObject already did. */
} else { } else {
retval = load_sndio_syms(); retval = load_sndio_syms();
if (retval < 0) { if (retval < 0) {
@ -147,129 +145,128 @@ static int LoadSNDIOLibrary(void)
return 0; return 0;
} }
#endif /* SDL_AUDIO_DRIVER_SNDIO_DYNAMIC */ #endif // SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
static void SNDIO_WaitDevice(SDL_AudioDevice *_this) static void SNDIO_WaitDevice(SDL_AudioDevice *device)
{ {
/* no-op; SNDIO_sio_write() blocks if necessary. */ const SDL_bool iscapture = device->iscapture;
while (!SDL_AtomicGet(&device->shutdown)) {
if (SNDIO_sio_eof(device->hidden->dev)) {
SDL_AudioDeviceDisconnected(device);
return;
} }
static void SNDIO_PlayDevice(SDL_AudioDevice *_this) const int nfds = SNDIO_sio_pollfd(device->hidden->dev, device->hidden->pfd, iscapture ? POLLIN : POLLOUT);
{ if (nfds <= 0 || poll(device->hidden->pfd, nfds, 10) < 0) {
const int written = SNDIO_sio_write(_this->hidden->dev, SDL_AudioDeviceDisconnected(device);
_this->hidden->mixbuf, return;
_this->hidden->mixlen); }
/* If we couldn't write, assume fatal error for now */ const int revents = SNDIO_sio_revents(device->hidden->dev, device->hidden->pfd);
if (written == 0) { if (iscapture && (revents & POLLIN)) {
SDL_OpenedAudioDeviceDisconnected(_this); return;
} else if (!iscapture && (revents & POLLOUT)) {
return;
} else if (revents & POLLHUP) {
SDL_AudioDeviceDisconnected(device);
return;
}
}
}
static void 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
} }
#ifdef DEBUG_AUDIO #ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", written); fprintf(stderr, "Wrote %d bytes of audio data\n", written);
#endif #endif
} }
static int SNDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) static int SNDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{ {
size_t r; // We set capture devices non-blocking; this can safely return 0 in SDL3, but we'll check for EOF to cause a device disconnect.
int revents; const size_t br = SNDIO_sio_read(device->hidden->dev, buffer, buflen);
int nfds; if ((br == 0) && SNDIO_sio_eof(device->hidden->dev)) {
/* Emulate a blocking read */
r = SNDIO_sio_read(_this->hidden->dev, buffer, buflen);
while (r == 0 && !SNDIO_sio_eof(_this->hidden->dev)) {
nfds = SNDIO_sio_pollfd(_this->hidden->dev, _this->hidden->pfd, POLLIN);
if (nfds <= 0 || poll(_this->hidden->pfd, nfds, INFTIM) < 0) {
return -1; return -1;
} }
revents = SNDIO_sio_revents(_this->hidden->dev, _this->hidden->pfd); return (int) br;
if (revents & POLLIN) {
r = SNDIO_sio_read(_this->hidden->dev, buffer, buflen);
}
if (revents & POLLHUP) {
break;
}
}
return (int)r;
} }
static void SNDIO_FlushCapture(SDL_AudioDevice *_this) static void SNDIO_FlushCapture(SDL_AudioDevice *device)
{ {
char buf[512]; char buf[512];
while (!SDL_AtomicGet(&device->shutdown) && (SNDIO_sio_read(device->hidden->dev, buf, sizeof(buf)) > 0)) {
while (SNDIO_sio_read(_this->hidden->dev, buf, sizeof(buf)) != 0) { // do nothing
/* do nothing */;
} }
} }
static Uint8 *SNDIO_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *SNDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
return _this->hidden->mixbuf; return device->hidden->mixbuf;
} }
static void SNDIO_CloseDevice(SDL_AudioDevice *_this) static void SNDIO_CloseDevice(SDL_AudioDevice *device)
{ {
if (_this->hidden->pfd != NULL) { if (device->hidden) {
SDL_free(_this->hidden->pfd); if (device->hidden->dev != NULL) {
SNDIO_sio_stop(device->hidden->dev);
SNDIO_sio_close(device->hidden->dev);
} }
if (_this->hidden->dev != NULL) { SDL_free(device->hidden->pfd);
SNDIO_sio_stop(_this->hidden->dev); SDL_free(device->hidden->mixbuf);
SNDIO_sio_close(_this->hidden->dev); SDL_free(device->hidden);
device->hidden = NULL;
} }
SDL_free(_this->hidden->mixbuf);
SDL_free(_this->hidden);
} }
static int SNDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int SNDIO_OpenDevice(SDL_AudioDevice *device)
{ {
SDL_AudioFormat test_format; device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
const SDL_AudioFormat *closefmts; if (device->hidden == NULL) {
struct sio_par par;
SDL_bool iscapture = _this->iscapture;
_this->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc(sizeof(*_this->hidden));
if (_this->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_zerop(_this->hidden);
_this->hidden->mixlen = _this->spec.size; // !!! FIXME: we really should standardize this on a specific SDL hint.
const char *audiodev = SDL_getenv("AUDIODEV");
/* Capture devices must be non-blocking for SNDIO_FlushCapture */ // Capture devices must be non-blocking for SNDIO_FlushCapture
_this->hidden->dev = SNDIO_sio_open(devname != NULL ? devname : SIO_DEVANY, device->hidden->dev = SNDIO_sio_open(audiodev != NULL ? audiodev : SIO_DEVANY,
iscapture ? SIO_REC : SIO_PLAY, iscapture); device->iscapture ? SIO_REC : SIO_PLAY, device->iscapture);
if (_this->hidden->dev == NULL) { if (device->hidden->dev == NULL) {
return SDL_SetError("sio_open() failed"); return SDL_SetError("sio_open() failed");
} }
/* Allocate the pollfd array for capture devices */ device->hidden->pfd = SDL_malloc(sizeof(struct pollfd) * SNDIO_sio_nfds(device->hidden->dev));
if (iscapture) { if (device->hidden->pfd == NULL) {
_this->hidden->pfd = SDL_malloc(sizeof(struct pollfd) * SNDIO_sio_nfds(_this->hidden->dev));
if (_this->hidden->pfd == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
}
struct sio_par par;
SNDIO_sio_initpar(&par); SNDIO_sio_initpar(&par);
par.rate = _this->spec.freq; par.rate = device->spec.freq;
par.pchan = _this->spec.channels; par.pchan = device->spec.channels;
par.round = _this->spec.samples; par.round = device->sample_frames;
par.appbufsz = par.round * 2; par.appbufsz = par.round * 2;
/* Try for a closest match on audio format */ // Try for a closest match on audio format
closefmts = SDL_ClosestAudioFormats(_this->spec.format); SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
while ((test_format = *(closefmts++)) != 0) { while ((test_format = *(closefmts++)) != 0) {
if (!SDL_AUDIO_ISFLOAT(test_format)) { if (!SDL_AUDIO_ISFLOAT(test_format)) {
par.le = SDL_AUDIO_ISLITTLEENDIAN(test_format) ? 1 : 0; par.le = SDL_AUDIO_ISLITTLEENDIAN(test_format) ? 1 : 0;
par.sig = SDL_AUDIO_ISSIGNED(test_format) ? 1 : 0; par.sig = SDL_AUDIO_ISSIGNED(test_format) ? 1 : 0;
par.bits = SDL_AUDIO_BITSIZE(test_format); par.bits = SDL_AUDIO_BITSIZE(test_format);
if (SNDIO_sio_setpar(_this->hidden->dev, &par) == 0) { if (SNDIO_sio_setpar(device->hidden->dev, &par) == 0) {
continue; continue;
} }
if (SNDIO_sio_getpar(_this->hidden->dev, &par) == 0) { if (SNDIO_sio_getpar(device->hidden->dev, &par) == 0) {
return SDL_SetError("sio_getpar() failed"); return SDL_SetError("sio_getpar() failed");
} }
if (par.bps != SIO_BPS(par.bits)) { if (par.bps != SIO_BPS(par.bits)) {
@ -282,46 +279,44 @@ static int SNDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
} }
if (!test_format) { if (!test_format) {
return SDL_SetError("%s: Unsupported audio format", "sndio"); return SDL_SetError("sndio: Unsupported audio format");
} }
if ((par.bps == 4) && (par.sig) && (par.le)) { if ((par.bps == 4) && (par.sig) && (par.le)) {
_this->spec.format = SDL_AUDIO_S32LSB; device->spec.format = SDL_AUDIO_S32LSB;
} else if ((par.bps == 4) && (par.sig) && (!par.le)) { } else if ((par.bps == 4) && (par.sig) && (!par.le)) {
_this->spec.format = SDL_AUDIO_S32MSB; device->spec.format = SDL_AUDIO_S32MSB;
} else if ((par.bps == 2) && (par.sig) && (par.le)) { } else if ((par.bps == 2) && (par.sig) && (par.le)) {
_this->spec.format = SDL_AUDIO_S16LSB; device->spec.format = SDL_AUDIO_S16LSB;
} else if ((par.bps == 2) && (par.sig) && (!par.le)) { } else if ((par.bps == 2) && (par.sig) && (!par.le)) {
_this->spec.format = SDL_AUDIO_S16MSB; device->spec.format = SDL_AUDIO_S16MSB;
} else if ((par.bps == 1) && (par.sig)) { } else if ((par.bps == 1) && (par.sig)) {
_this->spec.format = SDL_AUDIO_S8; device->spec.format = SDL_AUDIO_S8;
} else if ((par.bps == 1) && (!par.sig)) { } else if ((par.bps == 1) && (!par.sig)) {
_this->spec.format = SDL_AUDIO_U8; device->spec.format = SDL_AUDIO_U8;
} else { } else {
return SDL_SetError("sndio: Got unsupported hardware audio format."); return SDL_SetError("sndio: Got unsupported hardware audio format.");
} }
_this->spec.freq = par.rate; device->spec.freq = par.rate;
_this->spec.channels = par.pchan; device->spec.channels = par.pchan;
_this->spec.samples = par.round; device->sample_frames = par.round;
/* Calculate the final parameters for this audio specification */ // Calculate the final parameters for this audio specification
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
/* Allocate mixing buffer */ // Allocate mixing buffer
_this->hidden->mixlen = _this->spec.size; device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->hidden->mixlen); if (device->hidden->mixbuf == NULL) {
if (_this->hidden->mixbuf == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->hidden->mixlen); SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
if (!SNDIO_sio_start(_this->hidden->dev)) { if (!SNDIO_sio_start(device->hidden->dev)) {
return SDL_SetError("sio_start() failed"); return SDL_SetError("sio_start() failed");
} }
/* We're ready to rock and roll. :-) */ return 0; // We're ready to rock and roll. :-)
return 0;
} }
static void SNDIO_Deinitialize(void) static void SNDIO_Deinitialize(void)
@ -329,10 +324,10 @@ static void SNDIO_Deinitialize(void)
UnloadSNDIOLibrary(); UnloadSNDIOLibrary();
} }
static void SNDIO_DetectDevices(void) static void SNDIO_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{ {
SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *)0x1); *default_output = SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *)0x1);
SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *)0x2); *default_capture = SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *)0x2);
} }
static SDL_bool SNDIO_Init(SDL_AudioDriverImpl *impl) static SDL_bool SNDIO_Init(SDL_AudioDriverImpl *impl)
@ -341,12 +336,12 @@ static SDL_bool SNDIO_Init(SDL_AudioDriverImpl *impl)
return SDL_FALSE; return SDL_FALSE;
} }
/* Set the function pointers */
impl->OpenDevice = SNDIO_OpenDevice; impl->OpenDevice = SNDIO_OpenDevice;
impl->WaitDevice = SNDIO_WaitDevice; impl->WaitDevice = SNDIO_WaitDevice;
impl->PlayDevice = SNDIO_PlayDevice; impl->PlayDevice = SNDIO_PlayDevice;
impl->GetDeviceBuf = SNDIO_GetDeviceBuf; impl->GetDeviceBuf = SNDIO_GetDeviceBuf;
impl->CloseDevice = SNDIO_CloseDevice; impl->CloseDevice = SNDIO_CloseDevice;
impl->WaitCaptureDevice = SNDIO_WaitDevice;
impl->CaptureFromDevice = SNDIO_CaptureFromDevice; impl->CaptureFromDevice = SNDIO_CaptureFromDevice;
impl->FlushCapture = SNDIO_FlushCapture; impl->FlushCapture = SNDIO_FlushCapture;
impl->Deinitialize = SNDIO_Deinitialize; impl->Deinitialize = SNDIO_Deinitialize;
@ -355,11 +350,11 @@ static SDL_bool SNDIO_Init(SDL_AudioDriverImpl *impl)
impl->AllowsArbitraryDeviceNames = SDL_TRUE; impl->AllowsArbitraryDeviceNames = SDL_TRUE;
impl->HasCaptureSupport = SDL_TRUE; impl->HasCaptureSupport = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */ return SDL_TRUE;
} }
AudioBootStrap SNDIO_bootstrap = { AudioBootStrap SNDIO_bootstrap = {
"sndio", "OpenBSD sndio", SNDIO_Init, SDL_FALSE "sndio", "OpenBSD sndio", SNDIO_Init, SDL_FALSE
}; };
#endif /* SDL_AUDIO_DRIVER_SNDIO */ #endif // SDL_AUDIO_DRIVER_SNDIO

View File

@ -30,15 +30,9 @@
struct SDL_PrivateAudioData struct SDL_PrivateAudioData
{ {
/* The audio device handle */ struct sio_hdl *dev; // The audio device handle
struct sio_hdl *dev; Uint8 *mixbuf; // Raw mixing buffer
struct pollfd *pfd; // Polling structures for non-blocking sndio devices
/* Raw mixing buffer */
Uint8 *mixbuf;
int mixlen;
/* Polling structures for non-blocking sndio devices */
struct pollfd *pfd;
}; };
#endif /* SDL_sndioaudio_h_ */ #endif /* SDL_sndioaudio_h_ */

View File

@ -38,41 +38,41 @@
#define SCE_AUDIO_SAMPLE_ALIGN(s) (((s) + 63) & ~63) #define SCE_AUDIO_SAMPLE_ALIGN(s) (((s) + 63) & ~63)
#define SCE_AUDIO_MAX_VOLUME 0x8000 #define SCE_AUDIO_MAX_VOLUME 0x8000
static int VITAAUD_OpenCaptureDevice(SDL_AudioDevice *_this) static int VITAAUD_OpenCaptureDevice(SDL_AudioDevice *device)
{ {
_this->spec.freq = 16000; device->spec.freq = 16000;
_this->spec.samples = 512; device->spec.channels = 1;
_this->spec.channels = 1; device->sample_frames = 512;
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
_this->hidden->port = sceAudioInOpenPort(SCE_AUDIO_IN_PORT_TYPE_VOICE, 512, 16000, SCE_AUDIO_IN_PARAM_FORMAT_S16_MONO); device->hidden->port = sceAudioInOpenPort(SCE_AUDIO_IN_PORT_TYPE_VOICE, 512, 16000, SCE_AUDIO_IN_PARAM_FORMAT_S16_MONO);
if (_this->hidden->port < 0) { if (device->hidden->port < 0) {
return SDL_SetError("Couldn't open audio in port: %x", _this->hidden->port); return SDL_SetError("Couldn't open audio in port: %x", device->hidden->port);
} }
return 0; return 0;
} }
static int VITAAUD_OpenDevice(SDL_AudioDevice *_this, const char *devname) static int VITAAUD_OpenDevice(SDL_AudioDevice *device)
{ {
int format, mixlen, i, port = SCE_AUDIO_OUT_PORT_TYPE_MAIN; int format, mixlen, i, port = SCE_AUDIO_OUT_PORT_TYPE_MAIN;
int vols[2] = { SCE_AUDIO_MAX_VOLUME, SCE_AUDIO_MAX_VOLUME }; int vols[2] = { SCE_AUDIO_MAX_VOLUME, SCE_AUDIO_MAX_VOLUME };
SDL_AudioFormat test_format; SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts; const SDL_AudioFormat *closefmts;
_this->hidden = (struct SDL_PrivateAudioData *) device->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc(sizeof(*_this->hidden)); SDL_malloc(sizeof(*device->hidden));
if (_this->hidden == NULL) { if (device->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_memset(_this->hidden, 0, sizeof(*_this->hidden)); SDL_memset(device->hidden, 0, sizeof(*device->hidden));
closefmts = SDL_ClosestAudioFormats(_this->spec.format); closefmts = SDL_ClosestAudioFormats(device->spec.format);
while ((test_format = *(closefmts++)) != 0) { while ((test_format = *(closefmts++)) != 0) {
if (test_format == SDL_AUDIO_S16LSB) { if (test_format == SDL_AUDIO_S16LSB) {
_this->spec.format = test_format; device->spec.format = test_format;
break; break;
} }
} }
@ -81,106 +81,127 @@ static int VITAAUD_OpenDevice(SDL_AudioDevice *_this, const char *devname)
return SDL_SetError("Unsupported audio format"); return SDL_SetError("Unsupported audio format");
} }
if (_this->iscapture) { if (device->iscapture) {
return VITAAUD_OpenCaptureDevice(_this); return VITAAUD_OpenCaptureDevice(device);
} }
/* The sample count must be a multiple of 64. */ // The sample count must be a multiple of 64.
_this->spec.samples = SCE_AUDIO_SAMPLE_ALIGN(_this->spec.samples); device->sample_frames = SCE_AUDIO_SAMPLE_ALIGN(device->sample_frames);
/* Update the fragment size as size in bytes. */ // Update the fragment size as size in bytes.
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
/* Allocate the mixing buffer. Its size and starting address must /* Allocate the mixing buffer. Its size and starting address must
be a multiple of 64 bytes. Our sample count is already a multiple of be a multiple of 64 bytes. Our sample count is already a multiple of
64, so spec->size should be a multiple of 64 as well. */ 64, so spec->size should be a multiple of 64 as well. */
mixlen = _this->spec.size * NUM_BUFFERS; mixlen = device->buffer_size * NUM_BUFFERS;
_this->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen); device->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen);
if (_this->hidden->rawbuf == NULL) { if (device->hidden->rawbuf == NULL) {
return SDL_SetError("Couldn't allocate mixing buffer"); return SDL_SetError("Couldn't allocate mixing buffer");
} }
/* Setup the hardware channel. */ // Setup the hardware channel.
if (_this->spec.channels == 1) { if (device->spec.channels == 1) {
format = SCE_AUDIO_OUT_MODE_MONO; format = SCE_AUDIO_OUT_MODE_MONO;
} else { } else {
format = SCE_AUDIO_OUT_MODE_STEREO; format = SCE_AUDIO_OUT_MODE_STEREO;
} }
if (_this->spec.freq < 48000) { // the main port requires 48000Hz audio, so this drops to the background music port if necessary
if (device->spec.freq < 48000) {
port = SCE_AUDIO_OUT_PORT_TYPE_BGM; port = SCE_AUDIO_OUT_PORT_TYPE_BGM;
} }
_this->hidden->port = sceAudioOutOpenPort(port, _this->spec.samples, _this->spec.freq, format); device->hidden->port = sceAudioOutOpenPort(port, device->sample_frames, device->spec.freq, format);
if (_this->hidden->port < 0) { if (device->hidden->port < 0) {
SDL_aligned_free(_this->hidden->rawbuf); SDL_aligned_free(device->hidden->rawbuf);
_this->hidden->rawbuf = NULL; device->hidden->rawbuf = NULL;
return SDL_SetError("Couldn't open audio out port: %x", _this->hidden->port); return SDL_SetError("Couldn't open audio out port: %x", device->hidden->port);
} }
sceAudioOutSetVolume(_this->hidden->port, SCE_AUDIO_VOLUME_FLAG_L_CH | SCE_AUDIO_VOLUME_FLAG_R_CH, vols); sceAudioOutSetVolume(device->hidden->port, SCE_AUDIO_VOLUME_FLAG_L_CH | SCE_AUDIO_VOLUME_FLAG_R_CH, vols);
SDL_memset(_this->hidden->rawbuf, 0, mixlen); SDL_memset(device->hidden->rawbuf, 0, mixlen);
for (i = 0; i < NUM_BUFFERS; i++) { for (i = 0; i < NUM_BUFFERS; i++) {
_this->hidden->mixbufs[i] = &_this->hidden->rawbuf[i * _this->spec.size]; device->hidden->mixbufs[i] = &device->hidden->rawbuf[i * device->buffer_size];
} }
_this->hidden->next_buffer = 0; device->hidden->next_buffer = 0;
return 0; return 0;
} }
static void VITAAUD_PlayDevice(SDL_AudioDevice *_this) static void VITAAUD_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
{ {
Uint8 *mixbuf = _this->hidden->mixbufs[_this->hidden->next_buffer]; sceAudioOutOutput(device->hidden->port, buffer);
sceAudioOutOutput(_this->hidden->port, mixbuf);
_this->hidden->next_buffer = (_this->hidden->next_buffer + 1) % NUM_BUFFERS;
} }
/* This function waits until it is possible to write a full sound buffer */ // This function waits until it is possible to write a full sound buffer
static void VITAAUD_WaitDevice(SDL_AudioDevice *_this) static void VITAAUD_WaitDevice(SDL_AudioDevice *device)
{ {
/* Because we block when sending audio, there's no need for this function to do anything. */ // !!! FIXME: we might just need to sleep roughly as long as playback buffers take to process, based on sample rate, etc.
while (!SDL_AtomicGet(&device->shutdown) && (sceAudioOutGetRestSample(device->hidden->port) >= device->buffer_size)) {
SDL_Delay(1);
}
} }
static Uint8 *VITAAUD_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *VITAAUD_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
return _this->hidden->mixbufs[_this->hidden->next_buffer]; Uint8 *retval = device->hidden->mixbufs[device->hidden->next_buffer];
device->hidden->next_buffer = (device->hidden->next_buffer + 1) % NUM_BUFFERS;
return retval;
} }
static void VITAAUD_CloseDevice(SDL_AudioDevice *_this) static void VITAAUD_CloseDevice(SDL_AudioDevice *device)
{ {
if (_this->hidden->port >= 0) { if (device->hidden) {
if (_this->iscapture) { if (device->hidden->port >= 0) {
sceAudioInReleasePort(_this->hidden->port); if (device->iscapture) {
sceAudioInReleasePort(device->hidden->port);
} else { } else {
sceAudioOutReleasePort(_this->hidden->port); sceAudioOutReleasePort(device->hidden->port);
} }
_this->hidden->port = -1; device->hidden->port = -1;
} }
if (!_this->iscapture && _this->hidden->rawbuf != NULL) { if (!device->iscapture && device->hidden->rawbuf != NULL) {
SDL_aligned_free(_this->hidden->rawbuf); SDL_aligned_free(device->hidden->rawbuf); // this uses memalign(), not SDL_malloc().
_this->hidden->rawbuf = NULL; device->hidden->rawbuf = NULL;
}
SDL_free(device->hidden);
device->hidden = NULL;
} }
} }
static int VITAAUD_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) static void VITAAUD_WaitCaptureDevice(SDL_AudioDevice *device)
{
// there's only a blocking call to obtain more data, so we'll just sleep as
// long as a buffer would run.
const Uint64 endticks = SDL_GetTicks() + ((device->sample_frames * 1000) / device->spec.freq);
while (!SDL_AtomicGet(&device->shutdown) && (SDL_GetTicks() < endticks)) {
SDL_Delay(1);
}
}
static int VITAAUD_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{ {
int ret; int ret;
SDL_assert(buflen == _this->spec.size); SDL_assert(buflen == device->buffer_size);
ret = sceAudioInInput(_this->hidden->port, buffer); ret = sceAudioInInput(device->hidden->port, buffer);
if (ret < 0) { if (ret < 0) {
return SDL_SetError("Failed to capture from device: %x", ret); return SDL_SetError("Failed to capture from device: %x", ret);
} }
return _this->spec.size; return device->buffer_size;
} }
static void VITAAUD_ThreadInit(SDL_AudioDevice *_this) static void VITAAUD_FlushCapture(SDL_AudioDevice *device)
{ {
/* Increase the priority of this audio thread by 1 to put it // just grab the latest and dump it.
ahead of other SDL threads. */ sceAudioInInput(device->hidden->port, device->work_buffer);
}
static void VITAAUD_ThreadInit(SDL_AudioDevice *device)
{
// Increase the priority of this audio thread by 1 to put it ahead of other SDL threads.
SceUID thid; SceUID thid;
SceKernelThreadInfo info; SceKernelThreadInfo info;
thid = sceKernelGetThreadId(); thid = sceKernelGetThreadId();
@ -192,26 +213,25 @@ static void VITAAUD_ThreadInit(SDL_AudioDevice *_this)
static SDL_bool VITAAUD_Init(SDL_AudioDriverImpl *impl) static SDL_bool VITAAUD_Init(SDL_AudioDriverImpl *impl)
{ {
/* Set the function pointers */
impl->OpenDevice = VITAAUD_OpenDevice; impl->OpenDevice = VITAAUD_OpenDevice;
impl->PlayDevice = VITAAUD_PlayDevice; impl->PlayDevice = VITAAUD_PlayDevice;
impl->WaitDevice = VITAAUD_WaitDevice; impl->WaitDevice = VITAAUD_WaitDevice;
impl->GetDeviceBuf = VITAAUD_GetDeviceBuf; impl->GetDeviceBuf = VITAAUD_GetDeviceBuf;
impl->CloseDevice = VITAAUD_CloseDevice; impl->CloseDevice = VITAAUD_CloseDevice;
impl->ThreadInit = VITAAUD_ThreadInit; impl->ThreadInit = VITAAUD_ThreadInit;
impl->WaitCaptureDevice = VITAAUD_WaitCaptureDevice;
impl->FlushCapture = VITAAUD_FlushCapture;
impl->CaptureFromDevice = VITAAUD_CaptureFromDevice; impl->CaptureFromDevice = VITAAUD_CaptureFromDevice;
/* and the capabilities */
impl->HasCaptureSupport = SDL_TRUE; impl->HasCaptureSupport = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE; impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE; impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */ return SDL_TRUE;
} }
AudioBootStrap VITAAUD_bootstrap = { AudioBootStrap VITAAUD_bootstrap = {
"vita", "VITA audio driver", VITAAUD_Init, SDL_FALSE "vita", "VITA audio driver", VITAAUD_Init, SDL_FALSE
}; };
#endif /* SDL_AUDIO_DRIVER_VITA */ #endif // SDL_AUDIO_DRIVER_VITA

File diff suppressed because it is too large Load Diff

View File

@ -31,37 +31,38 @@ extern "C" {
struct SDL_PrivateAudioData struct SDL_PrivateAudioData
{ {
SDL_AtomicInt refcount;
WCHAR *devid; WCHAR *devid;
WAVEFORMATEX *waveformat; WAVEFORMATEX *waveformat;
IAudioClient *client; IAudioClient *client;
IAudioRenderClient *render; IAudioRenderClient *render;
IAudioCaptureClient *capture; IAudioCaptureClient *capture;
SDL_AudioStream *capturestream;
HANDLE event; HANDLE event;
HANDLE task; HANDLE task;
SDL_bool coinitialized; SDL_bool coinitialized;
int framesize; int framesize;
int default_device_generation;
SDL_bool device_lost; SDL_bool device_lost;
void *activation_handler; void *activation_handler;
SDL_AtomicInt just_activated;
}; };
/* win32 and winrt implementations call into these. */ /* win32 and winrt implementations call into these. */
int WASAPI_PrepDevice(SDL_AudioDevice *_this, const SDL_bool updatestream); int WASAPI_PrepDevice(SDL_AudioDevice *device);
void WASAPI_RefDevice(SDL_AudioDevice *_this); void WASAPI_DisconnectDevice(SDL_AudioDevice *device);
void WASAPI_UnrefDevice(SDL_AudioDevice *_this);
// BE CAREFUL: if you are holding the device lock and proxy to the management thread with wait_until_complete, and grab the lock again, you will deadlock.
typedef int (*ManagementThreadTask)(void *userdata);
int WASAPI_ProxyToManagementThread(ManagementThreadTask task, void *userdata, int *wait_until_complete);
/* These are functions that are implemented differently for Windows vs WinRT. */ /* These are functions that are implemented differently for Windows vs WinRT. */
// UNLESS OTHERWISE NOTED THESE ALL HAPPEN ON THE MANAGEMENT THREAD.
int WASAPI_PlatformInit(void); int WASAPI_PlatformInit(void);
void WASAPI_PlatformDeinit(void); void WASAPI_PlatformDeinit(void);
void WASAPI_EnumerateEndpoints(void); void WASAPI_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture); int WASAPI_ActivateDevice(SDL_AudioDevice *device);
int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery); void WASAPI_PlatformThreadInit(SDL_AudioDevice *device); // this happens on the audio device thread, not the management thread.
void WASAPI_PlatformThreadInit(SDL_AudioDevice *_this); void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device); // this happens on the audio device thread, not the management thread.
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *_this);
void WASAPI_PlatformDeleteActivationHandler(void *handler); void WASAPI_PlatformDeleteActivationHandler(void *handler);
void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -49,7 +49,7 @@ static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x
int WASAPI_PlatformInit(void) int WASAPI_PlatformInit(void)
{ {
if (SDL_IMMDevice_Init() < 0) { if (SDL_IMMDevice_Init() < 0) { // this will call WIN_CoInitialize for us!
return -1; /* This is set by SDL_IMMDevice_Init */ return -1; /* This is set by SDL_IMMDevice_Init */
} }
@ -72,72 +72,68 @@ void WASAPI_PlatformDeinit(void)
pAvSetMmThreadCharacteristicsW = NULL; pAvSetMmThreadCharacteristicsW = NULL;
pAvRevertMmThreadCharacteristics = NULL; pAvRevertMmThreadCharacteristics = NULL;
SDL_IMMDevice_Quit(); SDL_IMMDevice_Quit(); // This will call WIN_CoUninitialize for us!
} }
void WASAPI_PlatformThreadInit(SDL_AudioDevice *_this) void WASAPI_PlatformThreadInit(SDL_AudioDevice *device)
{ {
/* this thread uses COM. */ /* this thread uses COM. */
if (SUCCEEDED(WIN_CoInitialize())) { /* can't report errors, hope it worked! */ if (SUCCEEDED(WIN_CoInitialize())) { /* can't report errors, hope it worked! */
_this->hidden->coinitialized = SDL_TRUE; device->hidden->coinitialized = SDL_TRUE;
} }
/* Set this thread to very high "Pro Audio" priority. */ /* Set this thread to very high "Pro Audio" priority. */
if (pAvSetMmThreadCharacteristicsW) { if (pAvSetMmThreadCharacteristicsW) {
DWORD idx = 0; DWORD idx = 0;
_this->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx); device->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx);
} } else {
SDL_SetThreadPriority(device->iscapture ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL);
} }
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *_this) }
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device)
{ {
/* Set this thread back to normal priority. */ /* Set this thread back to normal priority. */
if (_this->hidden->task && pAvRevertMmThreadCharacteristics) { if (device->hidden->task && pAvRevertMmThreadCharacteristics) {
pAvRevertMmThreadCharacteristics(_this->hidden->task); pAvRevertMmThreadCharacteristics(device->hidden->task);
_this->hidden->task = NULL; device->hidden->task = NULL;
} }
if (_this->hidden->coinitialized) { if (device->hidden->coinitialized) {
WIN_CoUninitialize(); WIN_CoUninitialize();
_this->hidden->coinitialized = SDL_FALSE; device->hidden->coinitialized = SDL_FALSE;
} }
} }
int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery) int WASAPI_ActivateDevice(SDL_AudioDevice *device)
{ {
IMMDevice *device = NULL; IMMDevice *immdevice = NULL;
HRESULT ret; if (SDL_IMMDevice_Get(device, &immdevice, device->iscapture) < 0) {
device->hidden->client = NULL;
if (SDL_IMMDevice_Get(_this->hidden->devid, &device, _this->iscapture) < 0) {
_this->hidden->client = NULL;
return -1; /* This is already set by SDL_IMMDevice_Get */ return -1; /* This is already set by SDL_IMMDevice_Get */
} }
/* this is not async in standard win32, yay! */ /* this is _not_ async in standard win32, yay! */
ret = IMMDevice_Activate(device, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&_this->hidden->client); HRESULT ret = IMMDevice_Activate(immdevice, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&device->hidden->client);
IMMDevice_Release(device); IMMDevice_Release(immdevice);
if (FAILED(ret)) { if (FAILED(ret)) {
SDL_assert(_this->hidden->client == NULL); SDL_assert(device->hidden->client == NULL);
return WIN_SetErrorFromHRESULT("WASAPI can't activate audio endpoint", ret); return WIN_SetErrorFromHRESULT("WASAPI can't activate audio endpoint", ret);
} }
SDL_assert(_this->hidden->client != NULL); SDL_assert(device->hidden->client != NULL);
if (WASAPI_PrepDevice(_this, isrecovery) == -1) { /* not async, fire it right away. */ if (WASAPI_PrepDevice(device) == -1) { /* not async, fire it right away. */
return -1; return -1;
} }
return 0; /* good to go. */ return 0; /* good to go. */
} }
void WASAPI_EnumerateEndpoints(void) void WASAPI_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{ {
SDL_IMMDevice_EnumerateEndpoints(SDL_FALSE); SDL_IMMDevice_EnumerateEndpoints(default_output, default_capture);
}
int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
{
return SDL_IMMDevice_GetDefaultAudioInfo(name, spec, iscapture);
} }
void WASAPI_PlatformDeleteActivationHandler(void *handler) void WASAPI_PlatformDeleteActivationHandler(void *handler)
@ -146,4 +142,9 @@ void WASAPI_PlatformDeleteActivationHandler(void *handler)
SDL_assert(!"This function should have only been called on WinRT."); SDL_assert(!"This function should have only been called on WinRT.");
} }
void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device)
{
SDL_IMMDevice_FreeDeviceHandle(device);
}
#endif /* SDL_AUDIO_DRIVER_WASAPI && !defined(__WINRT__) */ #endif /* SDL_AUDIO_DRIVER_WASAPI && !defined(__WINRT__) */

View File

@ -53,21 +53,16 @@ using namespace Microsoft::WRL;
static Platform::String ^ SDL_PKEY_AudioEngine_DeviceFormat = L"{f19f064d-082c-4e27-bc73-6882a1bb8e4c} 0"; static Platform::String ^ SDL_PKEY_AudioEngine_DeviceFormat = L"{f19f064d-082c-4e27-bc73-6882a1bb8e4c} 0";
static void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid);
static void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid); static SDL_bool FindWinRTAudioDeviceCallback(SDL_AudioDevice *device, void *userdata)
extern "C" { {
SDL_AtomicInt SDL_IMMDevice_DefaultPlaybackGeneration; return (SDL_wcscmp((LPCWSTR) device->handle, (LPCWSTR) userdata) == 0) ? SDL_TRUE : SDL_FALSE;
SDL_AtomicInt SDL_IMMDevice_DefaultCaptureGeneration;
} }
/* This is a list of device id strings we have inflight, so we have consistent pointers to the same device. */ static SDL_AudioDevice *FindWinRTAudioDevice(LPCWSTR devid)
typedef struct DevIdList
{ {
WCHAR *str; return SDL_FindPhysicalAudioDeviceByCallback(FindWinRTAudioDeviceCallback, (void *) devid);
struct DevIdList *next; }
} DevIdList;
static DevIdList *deviceid_list = NULL;
class SDL_WasapiDeviceEventHandler class SDL_WasapiDeviceEventHandler
{ {
@ -80,9 +75,10 @@ class SDL_WasapiDeviceEventHandler
void OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args); void OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args);
void OnDefaultRenderDeviceChanged(Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args); void OnDefaultRenderDeviceChanged(Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args);
void OnDefaultCaptureDeviceChanged(Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args); void OnDefaultCaptureDeviceChanged(Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args);
SDL_Semaphore *completed; void WaitForCompletion();
private: private:
SDL_Semaphore *completed_semaphore;
const SDL_bool iscapture; const SDL_bool iscapture;
DeviceWatcher ^ watcher; DeviceWatcher ^ watcher;
Windows::Foundation::EventRegistrationToken added_handler; Windows::Foundation::EventRegistrationToken added_handler;
@ -93,10 +89,11 @@ class SDL_WasapiDeviceEventHandler
}; };
SDL_WasapiDeviceEventHandler::SDL_WasapiDeviceEventHandler(const SDL_bool _iscapture) SDL_WasapiDeviceEventHandler::SDL_WasapiDeviceEventHandler(const SDL_bool _iscapture)
: iscapture(_iscapture), completed(SDL_CreateSemaphore(0)) : iscapture(_iscapture), completed_semaphore(SDL_CreateSemaphore(0))
{ {
if (!completed) if (!completed_semaphore) {
return; // uhoh. return; // uhoh.
}
Platform::String ^ selector = _iscapture ? MediaDevice::GetAudioCaptureSelector() : MediaDevice::GetAudioRenderSelector(); Platform::String ^ selector = _iscapture ? MediaDevice::GetAudioCaptureSelector() : MediaDevice::GetAudioRenderSelector();
Platform::Collections::Vector<Platform::String ^> properties; Platform::Collections::Vector<Platform::String ^> properties;
@ -128,9 +125,10 @@ SDL_WasapiDeviceEventHandler::~SDL_WasapiDeviceEventHandler()
watcher->Stop(); watcher->Stop();
watcher = nullptr; watcher = nullptr;
} }
if (completed) {
SDL_DestroySemaphore(completed); if (completed_semaphore) {
completed = nullptr; SDL_DestroySemaphore(completed_semaphore);
completed_semaphore = nullptr;
} }
if (iscapture) { if (iscapture) {
@ -142,21 +140,34 @@ SDL_WasapiDeviceEventHandler::~SDL_WasapiDeviceEventHandler()
void SDL_WasapiDeviceEventHandler::OnDeviceAdded(DeviceWatcher ^ sender, DeviceInformation ^ info) void SDL_WasapiDeviceEventHandler::OnDeviceAdded(DeviceWatcher ^ sender, DeviceInformation ^ info)
{ {
/* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever).
In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for
phones and tablets, where you might have an internal speaker and a headphone jack and expect both to be
available and switch automatically. (!!! FIXME...?) */
SDL_assert(sender == this->watcher); SDL_assert(sender == this->watcher);
char *utf8dev = WIN_StringToUTF8(info->Name->Data()); char *utf8dev = WIN_StringToUTF8(info->Name->Data());
if (utf8dev) { if (utf8dev) {
WAVEFORMATEXTENSIBLE fmt; SDL_AudioSpec spec;
SDL_zero(spec);
Platform::Object ^ obj = info->Properties->Lookup(SDL_PKEY_AudioEngine_DeviceFormat); Platform::Object ^ obj = info->Properties->Lookup(SDL_PKEY_AudioEngine_DeviceFormat);
if (obj) { if (obj) {
IPropertyValue ^ property = (IPropertyValue ^) obj; IPropertyValue ^ property = (IPropertyValue ^) obj;
Platform::Array<unsigned char> ^ data; Platform::Array<unsigned char> ^ data;
property->GetUInt8Array(&data); property->GetUInt8Array(&data);
SDL_memcpy(&fmt, data->Data, SDL_min(data->Length, sizeof(WAVEFORMATEXTENSIBLE))); WAVEFORMATEXTENSIBLE fmt;
} else {
SDL_zero(fmt); SDL_zero(fmt);
SDL_memcpy(&fmt, data->Data, SDL_min(data->Length, sizeof(WAVEFORMATEXTENSIBLE)));
spec.channels = (Uint8)fmt.Format.nChannels;
spec.freq = fmt.Format.nSamplesPerSec;
spec.format = SDL_WaveFormatExToSDLFormat((WAVEFORMATEX *)&fmt);
} }
WASAPI_AddDevice(this->iscapture, utf8dev, &fmt, info->Id->Data()); LPWSTR devid = SDL_wcsdup(info->Id->Data());
if (devid) {
SDL_AddAudioDevice(this->iscapture, utf8dev, spec.channels ? &spec : NULL, devid);
}
SDL_free(utf8dev); SDL_free(utf8dev);
} }
} }
@ -164,7 +175,7 @@ void SDL_WasapiDeviceEventHandler::OnDeviceAdded(DeviceWatcher ^ sender, DeviceI
void SDL_WasapiDeviceEventHandler::OnDeviceRemoved(DeviceWatcher ^ sender, DeviceInformationUpdate ^ info) void SDL_WasapiDeviceEventHandler::OnDeviceRemoved(DeviceWatcher ^ sender, DeviceInformationUpdate ^ info)
{ {
SDL_assert(sender == this->watcher); SDL_assert(sender == this->watcher);
WASAPI_RemoveDevice(this->iscapture, info->Id->Data()); WASAPI_DisconnectDevice(FindWinRTAudioDevice(info->Id->Data()));
} }
void SDL_WasapiDeviceEventHandler::OnDeviceUpdated(DeviceWatcher ^ sender, DeviceInformationUpdate ^ args) void SDL_WasapiDeviceEventHandler::OnDeviceUpdated(DeviceWatcher ^ sender, DeviceInformationUpdate ^ args)
@ -175,19 +186,30 @@ void SDL_WasapiDeviceEventHandler::OnDeviceUpdated(DeviceWatcher ^ sender, Devic
void SDL_WasapiDeviceEventHandler::OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args) void SDL_WasapiDeviceEventHandler::OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args)
{ {
SDL_assert(sender == this->watcher); SDL_assert(sender == this->watcher);
SDL_PostSemaphore(this->completed); if (this->completed_semaphore) {
SDL_PostSemaphore(this->completed_semaphore);
}
} }
void SDL_WasapiDeviceEventHandler::OnDefaultRenderDeviceChanged(Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args) void SDL_WasapiDeviceEventHandler::OnDefaultRenderDeviceChanged(Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args)
{ {
SDL_assert(!this->iscapture); SDL_assert(!this->iscapture);
SDL_AtomicAdd(&SDL_IMMDevice_DefaultPlaybackGeneration, 1); SDL_DefaultAudioDeviceChanged(FindWinRTAudioDevice(args->Id->Data()));
} }
void SDL_WasapiDeviceEventHandler::OnDefaultCaptureDeviceChanged(Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args) void SDL_WasapiDeviceEventHandler::OnDefaultCaptureDeviceChanged(Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args)
{ {
SDL_assert(this->iscapture); SDL_assert(this->iscapture);
SDL_AtomicAdd(&SDL_IMMDevice_DefaultCaptureGeneration, 1); SDL_DefaultAudioDeviceChanged(FindWinRTAudioDevice(args->Id->Data()));
}
void SDL_WasapiDeviceEventHandler::WaitForCompletion()
{
if (this->completed_semaphore) {
SDL_WaitSemaphore(this->completed_semaphore);
SDL_DestroySemaphore(this->completed_semaphore);
this->completed_semaphore = nullptr;
}
} }
static SDL_WasapiDeviceEventHandler *playback_device_event_handler; static SDL_WasapiDeviceEventHandler *playback_device_event_handler;
@ -195,54 +217,63 @@ static SDL_WasapiDeviceEventHandler *capture_device_event_handler;
int WASAPI_PlatformInit(void) int WASAPI_PlatformInit(void)
{ {
SDL_AtomicSet(&SDL_IMMDevice_DefaultPlaybackGeneration, 1);
SDL_AtomicSet(&SDL_IMMDevice_DefaultCaptureGeneration, 1);
return 0; return 0;
} }
void WASAPI_PlatformDeinit(void) void WASAPI_PlatformDeinit(void)
{ {
DevIdList *devidlist;
DevIdList *next;
delete playback_device_event_handler; delete playback_device_event_handler;
playback_device_event_handler = nullptr; playback_device_event_handler = nullptr;
delete capture_device_event_handler; delete capture_device_event_handler;
capture_device_event_handler = nullptr; capture_device_event_handler = nullptr;
for (devidlist = deviceid_list; devidlist; devidlist = next) {
next = devidlist->next;
SDL_free(devidlist->str);
SDL_free(devidlist);
}
deviceid_list = NULL;
} }
void WASAPI_EnumerateEndpoints(void) void WASAPI_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{ {
Platform::String ^ defdevid;
// DeviceWatchers will fire an Added event for each existing device at // DeviceWatchers will fire an Added event for each existing device at
// startup, so we don't need to enumerate them separately before // startup, so we don't need to enumerate them separately before
// listening for updates. // listening for updates.
playback_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_FALSE); playback_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_FALSE);
capture_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_TRUE); playback_device_event_handler->WaitForCompletion();
SDL_WaitSemaphore(playback_device_event_handler->completed); defdevid = MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default);
SDL_WaitSemaphore(capture_device_event_handler->completed); if (defdevid) {
*default_output = FindWinRTAudioDevice(defdevid->Data());
} }
struct SDL_WasapiActivationHandler : public RuntimeClass<RuntimeClassFlags<ClassicCom>, FtmBase, IActivateAudioInterfaceCompletionHandler> capture_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_TRUE);
capture_device_event_handler->WaitForCompletion();
defdevid = MediaDevice::GetDefaultAudioCaptureId(AudioDeviceRole::Default);
if (defdevid) {
*default_capture = FindWinRTAudioDevice(defdevid->Data());
}
}
class SDL_WasapiActivationHandler : public RuntimeClass<RuntimeClassFlags<ClassicCom>, FtmBase, IActivateAudioInterfaceCompletionHandler>
{ {
SDL_WasapiActivationHandler() : device(nullptr) {} public:
STDMETHOD(ActivateCompleted) SDL_WasapiActivationHandler() : completion_semaphore(SDL_CreateSemaphore(0)) { SDL_assert(completion_semaphore != NULL); }
(IActivateAudioInterfaceAsyncOperation *operation); STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation *operation);
SDL_AudioDevice *device; void WaitForCompletion();
private:
SDL_Semaphore *completion_semaphore;
}; };
void SDL_WasapiActivationHandler::WaitForCompletion()
{
if (completion_semaphore) {
SDL_WaitSemaphore(completion_semaphore);
SDL_DestroySemaphore(completion_semaphore);
completion_semaphore = NULL;
}
}
HRESULT HRESULT
SDL_WasapiActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation *async) SDL_WasapiActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation *async)
{ {
// Just set a flag, since we're probably in a different thread. We'll pick it up and init everything on our own thread to prevent races. // Just set a flag, since we're probably in a different thread. We'll pick it up and init everything on our own thread to prevent races.
SDL_AtomicSet(&device->hidden->just_activated, 1); SDL_PostSemaphore(completion_semaphore);
WASAPI_UnrefDevice(device);
return S_OK; return S_OK;
} }
@ -251,24 +282,10 @@ void WASAPI_PlatformDeleteActivationHandler(void *handler)
((SDL_WasapiActivationHandler *)handler)->Release(); ((SDL_WasapiActivationHandler *)handler)->Release();
} }
int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture) int WASAPI_ActivateDevice(SDL_AudioDevice *device)
{ {
return SDL_Unsupported(); LPCWSTR devid = (LPCWSTR) device->handle;
} SDL_assert(devid != NULL);
int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery)
{
LPCWSTR devid = _this->hidden->devid;
Platform::String ^ defdevid;
if (devid == nullptr) {
defdevid = _this->iscapture ? MediaDevice::GetDefaultAudioCaptureId(AudioDeviceRole::Default) : MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default);
if (defdevid) {
devid = defdevid->Data();
}
}
SDL_AtomicSet(&_this->hidden->just_activated, 0);
ComPtr<SDL_WasapiActivationHandler> handler = Make<SDL_WasapiActivationHandler>(); ComPtr<SDL_WasapiActivationHandler> handler = Make<SDL_WasapiActivationHandler>();
if (handler == nullptr) { if (handler == nullptr) {
@ -276,10 +293,8 @@ int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery)
} }
handler.Get()->AddRef(); // we hold a reference after ComPtr destructs on return, causing a Release, and Release ourselves in WASAPI_PlatformDeleteActivationHandler(), etc. handler.Get()->AddRef(); // we hold a reference after ComPtr destructs on return, causing a Release, and Release ourselves in WASAPI_PlatformDeleteActivationHandler(), etc.
handler.Get()->device = _this; device->hidden->activation_handler = handler.Get();
_this->hidden->activation_handler = handler.Get();
WASAPI_RefDevice(_this); /* completion handler will unref it. */
IActivateAudioInterfaceAsyncOperation *async = nullptr; IActivateAudioInterfaceAsyncOperation *async = nullptr;
const HRESULT ret = ActivateAudioInterfaceAsync(devid, __uuidof(IAudioClient), nullptr, handler.Get(), &async); const HRESULT ret = ActivateAudioInterfaceAsync(devid, __uuidof(IAudioClient), nullptr, handler.Get(), &async);
@ -288,21 +303,11 @@ int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery)
async->Release(); async->Release();
} }
handler.Get()->Release(); handler.Get()->Release();
WASAPI_UnrefDevice(_this);
return WIN_SetErrorFromHRESULT("WASAPI can't activate requested audio endpoint", ret); return WIN_SetErrorFromHRESULT("WASAPI can't activate requested audio endpoint", ret);
} }
/* Spin until the async operation is complete. // !!! FIXME: the problems in SDL2 that needed this to be synchronous are _probably_ solved by SDL3, and this can block indefinitely if a user prompt is shown to get permission to use a microphone.
* If we don't PrepDevice before leaving this function, the bug list gets LONG: handler.Get()->WaitForCompletion(); // block here until we have an answer, so this is synchronous to us after all.
* - device.spec is not filled with the correct information
* - The 'obtained' spec will be wrong for ALLOW_CHANGE properties
* - SDL_AudioStreams will/will not be allocated at the right time
* - SDL_assert(device->callbackspec.size == device->spec.size) will fail
* - When the assert is ignored, skipping or a buffer overflow will occur
*/
while (!SDL_AtomicCAS(&_this->hidden->just_activated, 1, 0)) {
SDL_Delay(1);
}
HRESULT activateRes = S_OK; HRESULT activateRes = S_OK;
IUnknown *iunknown = nullptr; IUnknown *iunknown = nullptr;
@ -314,114 +319,32 @@ int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery)
return WIN_SetErrorFromHRESULT("Failed to activate WASAPI device", activateRes); return WIN_SetErrorFromHRESULT("Failed to activate WASAPI device", activateRes);
} }
iunknown->QueryInterface(IID_PPV_ARGS(&_this->hidden->client)); iunknown->QueryInterface(IID_PPV_ARGS(&device->hidden->client));
if (!_this->hidden->client) { if (!device->hidden->client) {
return SDL_SetError("Failed to query WASAPI client interface"); return SDL_SetError("Failed to query WASAPI client interface");
} }
if (WASAPI_PrepDevice(_this, isrecovery) == -1) { if (WASAPI_PrepDevice(device) == -1) {
return -1; return -1;
} }
return 0; return 0;
} }
void WASAPI_PlatformThreadInit(SDL_AudioDevice *_this) void WASAPI_PlatformThreadInit(SDL_AudioDevice *device)
{
// !!! FIXME: set this thread to "Pro Audio" priority.
SDL_SetThreadPriority(device->iscapture ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL);
}
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device)
{ {
// !!! FIXME: set this thread to "Pro Audio" priority. // !!! FIXME: set this thread to "Pro Audio" priority.
} }
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *_this) void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device)
{ {
// !!! FIXME: set this thread to "Pro Audio" priority. SDL_free(device->handle);
}
/* Everything below was copied from SDL_wasapi.c, before it got moved to SDL_immdevice.c! */
static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
extern "C" SDL_AudioFormat
WaveFormatToSDLFormat(WAVEFORMATEX *waveformat)
{
if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
return SDL_AUDIO_F32SYS;
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
return SDL_AUDIO_S16SYS;
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
return SDL_AUDIO_S32SYS;
} 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;
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
return SDL_AUDIO_S16SYS;
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
return SDL_AUDIO_S32SYS;
}
}
return 0;
}
static void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid)
{
DevIdList *i;
DevIdList *next;
DevIdList *prev = NULL;
for (i = deviceid_list; i; i = next) {
next = i->next;
if (SDL_wcscmp(i->str, devid) == 0) {
if (prev) {
prev->next = next;
} else {
deviceid_list = next;
}
SDL_RemoveAudioDevice(iscapture, i->str);
SDL_free(i->str);
SDL_free(i);
} else {
prev = i;
}
}
}
static void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid)
{
DevIdList *devidlist;
SDL_AudioSpec spec;
/* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever).
In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for
phones and tablets, where you might have an internal speaker and a headphone jack and expect both to be
available and switch automatically. (!!! FIXME...?) */
/* see if we already have this one. */
for (devidlist = deviceid_list; devidlist; devidlist = devidlist->next) {
if (SDL_wcscmp(devidlist->str, devid) == 0) {
return; /* we already have this. */
}
}
devidlist = (DevIdList *)SDL_malloc(sizeof(*devidlist));
if (devidlist == NULL) {
return; /* oh well. */
}
devid = SDL_wcsdup(devid);
if (!devid) {
SDL_free(devidlist);
return; /* oh well. */
}
devidlist->str = (WCHAR *)devid;
devidlist->next = deviceid_list;
deviceid_list = devidlist;
SDL_zero(spec);
spec.channels = (Uint8)fmt->Format.nChannels;
spec.freq = fmt->Format.nSamplesPerSec;
spec.format = WaveFormatToSDLFormat((WAVEFORMATEX *)fmt);
SDL_AddAudioDevice(iscapture, devname, &spec, (void *)devid);
} }
#endif // SDL_AUDIO_DRIVER_WASAPI && defined(__WINRT__) #endif // SDL_AUDIO_DRIVER_WASAPI && defined(__WINRT__)

View File

@ -233,7 +233,7 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
JNIEnv *env, jclass jcls); JNIEnv *env, jclass jcls);
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture, SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture, jstring name,
jint device_id); jint device_id);
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
@ -242,7 +242,7 @@ JNIEXPORT void JNICALL
static JNINativeMethod SDLAudioManager_tab[] = { static JNINativeMethod SDLAudioManager_tab[] = {
{ "nativeSetupJNI", "()I", SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI) }, { "nativeSetupJNI", "()I", SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI) },
{ "addAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(addAudioDevice) }, { "addAudioDevice", "(ZLjava/lang/String;I)V", SDL_JAVA_AUDIO_INTERFACE(addAudioDevice) },
{ "removeAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice) } { "removeAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice) }
}; };
@ -350,8 +350,8 @@ static jmethodID midSupportsRelativeMouse;
static jclass mAudioManagerClass; static jclass mAudioManagerClass;
/* method signatures */ /* method signatures */
static jmethodID midGetAudioOutputDevices; static jmethodID midRegisterAudioDeviceCallback;
static jmethodID midGetAudioInputDevices; static jmethodID midUnregisterAudioDeviceCallback;
static jmethodID midAudioOpen; static jmethodID midAudioOpen;
static jmethodID midAudioWriteByteBuffer; static jmethodID midAudioWriteByteBuffer;
static jmethodID midAudioWriteShortBuffer; static jmethodID midAudioWriteShortBuffer;
@ -681,12 +681,12 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jcl
mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls)); mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
midGetAudioOutputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass, midRegisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"getAudioOutputDevices", "registerAudioDeviceCallback",
"()[I"); "()V");
midGetAudioInputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass, midUnregisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"getAudioInputDevices", "unregisterAudioDeviceCallback",
"()[I"); "()V");
midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass, midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"audioOpen", "(IIIII)[I"); "audioOpen", "(IIIII)[I");
midAudioWriteByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass, midAudioWriteByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
@ -710,7 +710,7 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jcl
midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass, midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"audioSetThreadPriority", "(ZI)V"); "audioSetThreadPriority", "(ZI)V");
if (!midGetAudioOutputDevices || !midGetAudioInputDevices || !midAudioOpen || if (!midRegisterAudioDeviceCallback || !midUnregisterAudioDeviceCallback || !midAudioOpen ||
!midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer || !midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer ||
!midAudioClose || !midAudioClose ||
!midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer || !midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer ||
@ -1002,18 +1002,17 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)(
SDL_AtomicSet(&bPermissionRequestPending, SDL_FALSE); SDL_AtomicSet(&bPermissionRequestPending, SDL_FALSE);
} }
extern void SDL_AddAudioDevice(const SDL_bool iscapture, const char *name, SDL_AudioSpec *spec, void *handle);
extern void SDL_RemoveAudioDevice(const SDL_bool iscapture, void *handle);
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture, SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture,
jint device_id) jstring name, jint device_id)
{ {
if (SDL_GetCurrentAudioDriver() != NULL) { if (SDL_GetCurrentAudioDriver() != NULL) {
char device_name[64]; void *handle = (void *)((size_t)device_id);
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id); if (!SDL_FindPhysicalAudioDeviceByHandle(handle)) {
SDL_Log("Adding device with name %s, capture %d", device_name, is_capture); const char *utf8name = (*env)->GetStringUTFChars(env, name, NULL);
SDL_AddAudioDevice(is_capture, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1)); SDL_AddAudioDevice(is_capture ? SDL_TRUE : SDL_FALSE, SDL_strdup(utf8name), NULL, handle);
(*env)->ReleaseStringUTFChars(env, name, utf8name);
}
} }
} }
@ -1022,8 +1021,8 @@ SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice)(JNIEnv *env, jclass jcls, jboolean i
jint device_id) jint device_id)
{ {
if (SDL_GetCurrentAudioDriver() != NULL) { if (SDL_GetCurrentAudioDriver() != NULL) {
SDL_Log("Removing device with handle %d, capture %d", device_id + 1, is_capture); SDL_Log("Removing device with handle %d, capture %d", device_id, is_capture);
SDL_RemoveAudioDevice(is_capture, (void *)((size_t)device_id + 1)); SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle((void *)((size_t)device_id)));
} }
} }
@ -1152,7 +1151,6 @@ retry:
SDL_LockMutex(Android_ActivityMutex); SDL_LockMutex(Android_ActivityMutex);
if (Android_Window) { if (Android_Window) {
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_WindowData *data = Android_Window->driverdata; SDL_WindowData *data = Android_Window->driverdata;
/* Wait for Main thread being paused and context un-activated to release 'egl_surface' */ /* Wait for Main thread being paused and context un-activated to release 'egl_surface' */
@ -1169,7 +1167,7 @@ retry:
#ifdef SDL_VIDEO_OPENGL_EGL #ifdef SDL_VIDEO_OPENGL_EGL
if (data->egl_surface != EGL_NO_SURFACE) { if (data->egl_surface != EGL_NO_SURFACE) {
SDL_EGL_DestroySurface(_this, data->egl_surface); SDL_EGL_DestroySurface(SDL_GetVideoDevice(), data->egl_surface);
data->egl_surface = EGL_NO_SURFACE; data->egl_surface = EGL_NO_SURFACE;
} }
#endif #endif
@ -1564,58 +1562,25 @@ static void *audioBufferPinned = NULL;
static int captureBufferFormat = 0; static int captureBufferFormat = 0;
static jobject captureBuffer = NULL; static jobject captureBuffer = NULL;
static void Android_JNI_GetAudioDevices(int *devices, int *length, int max_len, int is_input) void Android_StartAudioHotplug(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{ {
JNIEnv *env = Android_JNI_GetEnv(); JNIEnv *env = Android_JNI_GetEnv();
jintArray result; // this will fire the callback for each existing device right away (which will eventually SDL_AddAudioDevice), and again later when things change.
(*env)->CallStaticVoidMethod(env, mAudioManagerClass, midRegisterAudioDeviceCallback);
if (is_input) { *default_output = *default_capture = NULL; // !!! FIXME: how do you decide the default device id?
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioInputDevices);
} else {
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioOutputDevices);
} }
*length = (*env)->GetArrayLength(env, result); void Android_StopAudioHotplug(void)
*length = SDL_min(*length, max_len);
(*env)->GetIntArrayRegion(env, result, 0, *length, devices);
}
void Android_DetectDevices(void)
{ {
int inputs[100]; JNIEnv *env = Android_JNI_GetEnv();
int outputs[100]; (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midUnregisterAudioDeviceCallback);
int inputs_length = 0;
int outputs_length = 0;
SDL_zeroa(inputs);
Android_JNI_GetAudioDevices(inputs, &inputs_length, 100, 1 /* input devices */);
for (int i = 0; i < inputs_length; ++i) {
int device_id = inputs[i];
char device_name[64];
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id);
SDL_Log("Adding input device with name %s", device_name);
SDL_AddAudioDevice(SDL_TRUE, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1));
} }
SDL_zeroa(outputs); int Android_JNI_OpenAudioDevice(SDL_AudioDevice *device)
Android_JNI_GetAudioDevices(outputs, &outputs_length, 100, 0 /* output devices */);
for (int i = 0; i < outputs_length; ++i) {
int device_id = outputs[i];
char device_name[64];
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id);
SDL_Log("Adding output device with name %s", device_name);
SDL_AddAudioDevice(SDL_FALSE, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1));
}
}
int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spec)
{ {
const SDL_bool iscapture = device->iscapture;
SDL_AudioSpec *spec = &device->spec;
const int device_id = (int) ((size_t) device->handle);
int audioformat; int audioformat;
jobject jbufobj = NULL; jobject jbufobj = NULL;
jobject result; jobject result;
@ -1640,10 +1605,10 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
if (iscapture) { if (iscapture) {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture"); __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, spec->samples, device_id); result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, device->sample_frames, device_id);
} else { } else {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output"); __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, spec->samples, device_id); result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, device->sample_frames, device_id);
} }
if (result == NULL) { if (result == NULL) {
/* Error during audio initialization, error printed from Java */ /* Error during audio initialization, error printed from Java */
@ -1668,10 +1633,10 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
spec->format = SDL_AUDIO_F32; spec->format = SDL_AUDIO_F32;
break; break;
default: default:
return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat); return SDL_SetError("Unexpected audio format from Java: %d", audioformat);
} }
spec->channels = resultElements[2]; spec->channels = resultElements[2];
spec->samples = resultElements[3]; device->sample_frames = resultElements[3];
(*env)->ReleaseIntArrayElements(env, (jintArray)result, resultElements, JNI_ABORT); (*env)->ReleaseIntArrayElements(env, (jintArray)result, resultElements, JNI_ABORT);
(*env)->DeleteLocalRef(env, result); (*env)->DeleteLocalRef(env, result);
@ -1680,7 +1645,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
switch (audioformat) { switch (audioformat) {
case ENCODING_PCM_8BIT: case ENCODING_PCM_8BIT:
{ {
jbyteArray audioBufferLocal = (*env)->NewByteArray(env, spec->samples * spec->channels); jbyteArray audioBufferLocal = (*env)->NewByteArray(env, device->sample_frames * spec->channels);
if (audioBufferLocal) { if (audioBufferLocal) {
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal); jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
(*env)->DeleteLocalRef(env, audioBufferLocal); (*env)->DeleteLocalRef(env, audioBufferLocal);
@ -1688,7 +1653,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
} break; } break;
case ENCODING_PCM_16BIT: case ENCODING_PCM_16BIT:
{ {
jshortArray audioBufferLocal = (*env)->NewShortArray(env, spec->samples * spec->channels); jshortArray audioBufferLocal = (*env)->NewShortArray(env, device->sample_frames * spec->channels);
if (audioBufferLocal) { if (audioBufferLocal) {
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal); jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
(*env)->DeleteLocalRef(env, audioBufferLocal); (*env)->DeleteLocalRef(env, audioBufferLocal);
@ -1696,7 +1661,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
} break; } break;
case ENCODING_PCM_FLOAT: case ENCODING_PCM_FLOAT:
{ {
jfloatArray audioBufferLocal = (*env)->NewFloatArray(env, spec->samples * spec->channels); jfloatArray audioBufferLocal = (*env)->NewFloatArray(env, device->sample_frames * spec->channels);
if (audioBufferLocal) { if (audioBufferLocal) {
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal); jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
(*env)->DeleteLocalRef(env, audioBufferLocal); (*env)->DeleteLocalRef(env, audioBufferLocal);
@ -1887,12 +1852,17 @@ void Android_JNI_CloseAudioDevice(const int iscapture)
} }
} }
void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id) static void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id)
{ {
JNIEnv *env = Android_JNI_GetEnv(); JNIEnv *env = Android_JNI_GetEnv();
(*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioSetThreadPriority, iscapture, device_id); (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioSetThreadPriority, iscapture, device_id);
} }
void Android_AudioThreadInit(SDL_AudioDevice *device)
{
Android_JNI_AudioSetThreadPriority((int) device->iscapture, (int)device->instance_id);
}
/* Test for an exception and call SDL_SetError with its detail if one occurs */ /* Test for an exception and call SDL_SetError with its detail if one occurs */
/* If the parameter silent is truthy then SDL_SetError() will not be called. */ /* If the parameter silent is truthy then SDL_SetError() will not be called. */
static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent) static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)
@ -2013,13 +1983,18 @@ int Android_JNI_FileOpen(SDL_RWops *ctx,
return 0; return 0;
} }
Sint64 Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, Sint64 size) size_t Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, size_t size)
{ {
AAsset *asset = (AAsset *)ctx->hidden.androidio.asset; AAsset *asset = (AAsset *)ctx->hidden.androidio.asset;
return (Sint64) AAsset_read(asset, buffer, (size_t) size); int bytes = AAsset_read(asset, buffer, size);
if (bytes < 0) {
SDL_SetError("AAsset_read() failed");
return 0;
}
return (size_t)bytes;
} }
Sint64 Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer, Sint64 size) size_t Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer, size_t size)
{ {
return SDL_SetError("Cannot write to Android package filesystem"); return SDL_SetError("Cannot write to Android package filesystem");
} }

View File

@ -30,6 +30,8 @@ extern "C" {
#include <EGL/eglplatform.h> #include <EGL/eglplatform.h>
#include <android/native_window_jni.h> #include <android/native_window_jni.h>
#include "../../audio/SDL_sysaudio.h"
/* Interface from the SDL library into the Android Java activity */ /* Interface from the SDL library into the Android Java activity */
extern void Android_JNI_SetActivityTitle(const char *title); extern void Android_JNI_SetActivityTitle(const char *title);
extern void Android_JNI_SetWindowStyle(SDL_bool fullscreen); extern void Android_JNI_SetWindowStyle(SDL_bool fullscreen);
@ -47,14 +49,15 @@ extern SDL_DisplayOrientation Android_JNI_GetDisplayNaturalOrientation(void);
extern SDL_DisplayOrientation Android_JNI_GetDisplayCurrentOrientation(void); extern SDL_DisplayOrientation Android_JNI_GetDisplayCurrentOrientation(void);
/* Audio support */ /* Audio support */
extern void Android_DetectDevices(void); void Android_StartAudioHotplug(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
extern int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spec); void Android_StopAudioHotplug(void);
extern void Android_AudioThreadInit(SDL_AudioDevice *device);
extern int Android_JNI_OpenAudioDevice(SDL_AudioDevice *device);
extern void *Android_JNI_GetAudioBuffer(void); extern void *Android_JNI_GetAudioBuffer(void);
extern void Android_JNI_WriteAudioBuffer(void); extern void Android_JNI_WriteAudioBuffer(void);
extern int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen); extern int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen);
extern void Android_JNI_FlushCapturedAudio(void); extern void Android_JNI_FlushCapturedAudio(void);
extern void Android_JNI_CloseAudioDevice(const int iscapture); extern void Android_JNI_CloseAudioDevice(const int iscapture);
extern void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id);
/* Detecting device type */ /* Detecting device type */
extern SDL_bool Android_IsDeXMode(void); extern SDL_bool Android_IsDeXMode(void);
@ -63,8 +66,8 @@ extern SDL_bool Android_IsChromebook(void);
int Android_JNI_FileOpen(SDL_RWops *ctx, const char *fileName, const char *mode); int Android_JNI_FileOpen(SDL_RWops *ctx, const char *fileName, const char *mode);
Sint64 Android_JNI_FileSize(SDL_RWops *ctx); Sint64 Android_JNI_FileSize(SDL_RWops *ctx);
Sint64 Android_JNI_FileSeek(SDL_RWops *ctx, Sint64 offset, int whence); Sint64 Android_JNI_FileSeek(SDL_RWops *ctx, Sint64 offset, int whence);
Sint64 Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, Sint64 size); size_t Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, size_t size);
Sint64 Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer, Sint64 size); size_t Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer, size_t size);
int Android_JNI_FileClose(SDL_RWops *ctx); int Android_JNI_FileClose(SDL_RWops *ctx);
/* Environment support */ /* Environment support */

View File

@ -27,6 +27,12 @@
#include "../../audio/SDL_sysaudio.h" #include "../../audio/SDL_sysaudio.h"
#include <objbase.h> /* For CLSIDFromString */ #include <objbase.h> /* For CLSIDFromString */
typedef struct SDL_IMMDevice_HandleData
{
LPWSTR immdevice_id;
GUID directsound_guid;
} SDL_IMMDevice_HandleData;
static const ERole SDL_IMMDevice_role = eConsole; /* !!! FIXME: should this be eMultimedia? Should be a hint? */ static const ERole SDL_IMMDevice_role = eConsole; /* !!! FIXME: should this be eMultimedia? Should be a hint? */
/* This is global to the WASAPI target, to handle hotplug and default device lookup. */ /* This is global to the WASAPI target, to handle hotplug and default device lookup. */
@ -47,13 +53,28 @@ static const IID SDL_IID_IMMEndpoint = { 0x1be09788, 0x6894, 0x4089,{ 0x85, 0x86
static const PROPERTYKEY SDL_PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd,{ 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, } }, 14 }; static const PROPERTYKEY SDL_PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd,{ 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, } }, 14 };
static const PROPERTYKEY SDL_PKEY_AudioEngine_DeviceFormat = { { 0xf19f064d, 0x82c, 0x4e27,{ 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c, } }, 0 }; static const PROPERTYKEY SDL_PKEY_AudioEngine_DeviceFormat = { { 0xf19f064d, 0x82c, 0x4e27,{ 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c, } }, 0 };
static const PROPERTYKEY SDL_PKEY_AudioEndpoint_GUID = { { 0x1da5d803, 0xd492, 0x4edd,{ 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e, } }, 4 }; static const PROPERTYKEY SDL_PKEY_AudioEndpoint_GUID = { { 0x1da5d803, 0xd492, 0x4edd,{ 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e, } }, 4 };
static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
/* *INDENT-ON* */ /* clang-format on */ /* *INDENT-ON* */ /* clang-format on */
/* these increment as default devices change. Opened default devices pick up changes in their threads. */ static SDL_bool FindByDevIDCallback(SDL_AudioDevice *device, void *userdata)
SDL_AtomicInt SDL_IMMDevice_DefaultPlaybackGeneration; {
SDL_AtomicInt SDL_IMMDevice_DefaultCaptureGeneration; const SDL_IMMDevice_HandleData *handle = (const SDL_IMMDevice_HandleData *) device->handle;
return (SDL_wcscmp(handle->immdevice_id, (LPCWSTR) userdata) == 0) ? SDL_TRUE : SDL_FALSE;
}
static SDL_AudioDevice *SDL_IMMDevice_FindByDevID(LPCWSTR devid)
{
return SDL_FindPhysicalAudioDeviceByCallback(FindByDevIDCallback, (void *) devid);
}
LPGUID SDL_IMMDevice_GetDirectSoundGUID(SDL_AudioDevice *device)
{
return (device && device->handle) ? &(((SDL_IMMDevice_HandleData *) device->handle)->directsound_guid) : NULL;
}
LPCWSTR SDL_IMMDevice_GetDevID(SDL_AudioDevice *device)
{
return (device && device->handle) ? ((const SDL_IMMDevice_HandleData *) device->handle)->immdevice_id : NULL;
}
static void GetMMDeviceInfo(IMMDevice *device, char **utf8dev, WAVEFORMATEXTENSIBLE *fmt, GUID *guid) static void GetMMDeviceInfo(IMMDevice *device, char **utf8dev, WAVEFORMATEXTENSIBLE *fmt, GUID *guid)
{ {
@ -82,94 +103,54 @@ static void GetMMDeviceInfo(IMMDevice *device, char **utf8dev, WAVEFORMATEXTENSI
} }
} }
/* This is a list of device id strings we have inflight, so we have consistent pointers to the same device. */ void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device)
typedef struct DevIdList
{ {
LPWSTR str; if (device && device->handle) {
LPGUID guid; SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *) device->handle;
struct DevIdList *next; SDL_free(handle->immdevice_id);
} DevIdList; SDL_free(handle);
device->handle = NULL;
}
}
static DevIdList *deviceid_list = NULL; static SDL_AudioDevice *SDL_IMMDevice_Add(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid, GUID *dsoundguid)
static void SDL_IMMDevice_Remove(const SDL_bool iscapture, LPCWSTR devid, SDL_bool useguid)
{ {
DevIdList *i;
DevIdList *next;
DevIdList *prev = NULL;
for (i = deviceid_list; i; i = next) {
next = i->next;
if (SDL_wcscmp(i->str, devid) == 0) {
if (prev) {
prev->next = next;
} else {
deviceid_list = next;
}
SDL_RemoveAudioDevice(iscapture, useguid ? ((void *)i->guid) : ((void *)i->str));
SDL_free(i->str);
SDL_free(i);
} else {
prev = i;
}
}
}
static void SDL_IMMDevice_Add(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid, GUID *dsoundguid, SDL_bool useguid)
{
DevIdList *devidlist;
SDL_AudioSpec spec;
LPWSTR devidcopy;
LPGUID cpyguid;
LPVOID driverdata;
/* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever). /* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever).
In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for
phones and tablets, where you might have an internal speaker and a headphone jack and expect both to be phones and tablets, where you might have an internal speaker and a headphone jack and expect both to be
available and switch automatically. (!!! FIXME...?) */ available and switch automatically. (!!! FIXME...?) */
/* see if we already have this one. */ if (!devname) {
for (devidlist = deviceid_list; devidlist; devidlist = devidlist->next) { return NULL;
if (SDL_wcscmp(devidlist->str, devid) == 0) {
return; /* we already have this. */
}
} }
devidlist = (DevIdList *)SDL_malloc(sizeof(*devidlist)); // see if we already have this one first.
if (devidlist == NULL) { SDL_AudioDevice *device = SDL_IMMDevice_FindByDevID(devid);
return; /* oh well. */ if (!device) {
// handle is freed by SDL_IMMDevice_FreeDeviceHandle!
SDL_IMMDevice_HandleData *handle = SDL_malloc(sizeof(SDL_IMMDevice_HandleData));
if (!handle) {
SDL_OutOfMemory();
return NULL;
} }
handle->immdevice_id = SDL_wcsdup(devid);
devidcopy = SDL_wcsdup(devid); if (!handle->immdevice_id) {
if (!devidcopy) { SDL_OutOfMemory();
SDL_free(devidlist); SDL_free(handle);
return; /* oh well. */ return NULL;
} }
SDL_memcpy(&handle->directsound_guid, dsoundguid, sizeof(GUID));
if (useguid) { SDL_AudioSpec spec;
/* This is freed by DSOUND_FreeDeviceData! */
cpyguid = (LPGUID)SDL_malloc(sizeof(GUID));
if (!cpyguid) {
SDL_free(devidlist);
SDL_free(devidcopy);
return; /* oh well. */
}
SDL_memcpy(cpyguid, dsoundguid, sizeof(GUID));
driverdata = cpyguid;
} else {
cpyguid = NULL;
driverdata = devidcopy;
}
devidlist->str = devidcopy;
devidlist->guid = cpyguid;
devidlist->next = deviceid_list;
deviceid_list = devidlist;
SDL_zero(spec); SDL_zero(spec);
spec.channels = (Uint8)fmt->Format.nChannels; spec.channels = (Uint8)fmt->Format.nChannels;
spec.freq = fmt->Format.nSamplesPerSec; spec.freq = fmt->Format.nSamplesPerSec;
spec.format = WaveFormatToSDLFormat((WAVEFORMATEX *)fmt); spec.format = SDL_WaveFormatExToSDLFormat((WAVEFORMATEX *)fmt);
SDL_AddAudioDevice(iscapture, devname, &spec, driverdata);
device = SDL_AddAudioDevice(iscapture, devname, &spec, handle);
}
return device;
} }
/* We need a COM subclass of IMMNotificationClient for hotplug support, which is /* We need a COM subclass of IMMNotificationClient for hotplug support, which is
@ -181,14 +162,13 @@ typedef struct SDLMMNotificationClient
{ {
const IMMNotificationClientVtbl *lpVtbl; const IMMNotificationClientVtbl *lpVtbl;
SDL_AtomicInt refcount; SDL_AtomicInt refcount;
SDL_bool useguid;
} SDLMMNotificationClient; } SDLMMNotificationClient;
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotificationClient *this, REFIID iid, void **ppv) static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotificationClient *client, REFIID iid, void **ppv)
{ {
if ((WIN_IsEqualIID(iid, &IID_IUnknown)) || (WIN_IsEqualIID(iid, &SDL_IID_IMMNotificationClient))) { if ((WIN_IsEqualIID(iid, &IID_IUnknown)) || (WIN_IsEqualIID(iid, &SDL_IID_IMMNotificationClient))) {
*ppv = this; *ppv = client;
this->lpVtbl->AddRef(this); client->lpVtbl->AddRef(client);
return S_OK; return S_OK;
} }
@ -196,55 +176,34 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotif
return E_NOINTERFACE; return E_NOINTERFACE;
} }
static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_AddRef(IMMNotificationClient *ithis) static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_AddRef(IMMNotificationClient *iclient)
{ {
SDLMMNotificationClient *this = (SDLMMNotificationClient *)ithis; SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient;
return (ULONG)(SDL_AtomicIncRef(&this->refcount) + 1); return (ULONG)(SDL_AtomicIncRef(&client->refcount) + 1);
} }
static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_Release(IMMNotificationClient *ithis) static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_Release(IMMNotificationClient *iclient)
{ {
/* this is a static object; we don't ever free it. */ /* client is a static object; we don't ever free it. */
SDLMMNotificationClient *this = (SDLMMNotificationClient *)ithis; SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient;
const ULONG retval = SDL_AtomicDecRef(&this->refcount); const ULONG retval = SDL_AtomicDecRef(&client->refcount);
if (retval == 0) { if (retval == 0) {
SDL_AtomicSet(&this->refcount, 0); /* uhh... */ SDL_AtomicSet(&client->refcount, 0); /* uhh... */
return 0; return 0;
} }
return retval - 1; return retval - 1;
} }
/* These are the entry points called when WASAPI device endpoints change. */ // These are the entry points called when WASAPI device endpoints change.
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *ithis, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *iclient, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
{ {
if (role != SDL_IMMDevice_role) { if (role == SDL_IMMDevice_role) {
return S_OK; /* ignore it. */ SDL_DefaultAudioDeviceChanged(SDL_IMMDevice_FindByDevID(pwstrDeviceId));
} }
/* Increment the "generation," so opened devices will pick this up in their threads. */
switch (flow) {
case eRender:
SDL_AtomicAdd(&SDL_IMMDevice_DefaultPlaybackGeneration, 1);
break;
case eCapture:
SDL_AtomicAdd(&SDL_IMMDevice_DefaultCaptureGeneration, 1);
break;
case eAll:
SDL_AtomicAdd(&SDL_IMMDevice_DefaultPlaybackGeneration, 1);
SDL_AtomicAdd(&SDL_IMMDevice_DefaultCaptureGeneration, 1);
break;
default:
SDL_assert(!"uhoh, unexpected OnDefaultDeviceChange flow!");
break;
}
return S_OK; return S_OK;
} }
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceAdded(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId) static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceAdded(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId)
{ {
/* we ignore this; devices added here then progress to ACTIVE, if appropriate, in /* we ignore this; devices added here then progress to ACTIVE, if appropriate, in
OnDeviceStateChange, making that a better place to deal with device adds. More OnDeviceStateChange, making that a better place to deal with device adds. More
@ -254,13 +213,12 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceAdded(IMMNotifi
return S_OK; return S_OK;
} }
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceRemoved(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId) static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceRemoved(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId)
{ {
/* See notes in OnDeviceAdded handler about why we ignore this. */ return S_OK; // See notes in OnDeviceAdded handler about why we ignore this.
return S_OK;
} }
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId, DWORD dwNewState) static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId, DWORD dwNewState)
{ {
IMMDevice *device = NULL; IMMDevice *device = NULL;
@ -270,18 +228,17 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IM
EDataFlow flow; EDataFlow flow;
if (SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) { if (SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) {
const SDL_bool iscapture = (flow == eCapture); const SDL_bool iscapture = (flow == eCapture);
const SDLMMNotificationClient *client = (SDLMMNotificationClient *)ithis;
if (dwNewState == DEVICE_STATE_ACTIVE) { if (dwNewState == DEVICE_STATE_ACTIVE) {
char *utf8dev; char *utf8dev;
WAVEFORMATEXTENSIBLE fmt; WAVEFORMATEXTENSIBLE fmt;
GUID dsoundguid; GUID dsoundguid;
GetMMDeviceInfo(device, &utf8dev, &fmt, &dsoundguid); GetMMDeviceInfo(device, &utf8dev, &fmt, &dsoundguid);
if (utf8dev) { if (utf8dev) {
SDL_IMMDevice_Add(iscapture, utf8dev, &fmt, pwstrDeviceId, &dsoundguid, client->useguid); SDL_IMMDevice_Add(iscapture, utf8dev, &fmt, pwstrDeviceId, &dsoundguid);
SDL_free(utf8dev); SDL_free(utf8dev);
} }
} else { } else {
SDL_IMMDevice_Remove(iscapture, pwstrDeviceId, client->useguid); SDL_AudioDeviceDisconnected(SDL_IMMDevice_FindByDevID(pwstrDeviceId));
} }
} }
IMMEndpoint_Release(endpoint); IMMEndpoint_Release(endpoint);
@ -292,9 +249,9 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IM
return S_OK; return S_OK;
} }
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *this, LPCWSTR pwstrDeviceId, const PROPERTYKEY key) static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *client, LPCWSTR pwstrDeviceId, const PROPERTYKEY key)
{ {
return S_OK; /* we don't care about these. */ return S_OK; // we don't care about these.
} }
static const IMMNotificationClientVtbl notification_client_vtbl = { static const IMMNotificationClientVtbl notification_client_vtbl = {
@ -314,31 +271,25 @@ int SDL_IMMDevice_Init(void)
{ {
HRESULT ret; HRESULT ret;
SDL_AtomicSet(&SDL_IMMDevice_DefaultPlaybackGeneration, 1);
SDL_AtomicSet(&SDL_IMMDevice_DefaultCaptureGeneration, 1);
/* just skip the discussion with COM here. */ /* just skip the discussion with COM here. */
if (!WIN_IsWindowsVistaOrGreater()) { if (!WIN_IsWindowsVistaOrGreater()) {
return SDL_SetError("WASAPI support requires Windows Vista or later"); return SDL_SetError("IMMDevice support requires Windows Vista or later");
} }
if (FAILED(WIN_CoInitialize())) { if (FAILED(WIN_CoInitialize())) {
return SDL_SetError("WASAPI: CoInitialize() failed"); return SDL_SetError("IMMDevice: CoInitialize() failed");
} }
ret = CoCreateInstance(&SDL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &SDL_IID_IMMDeviceEnumerator, (LPVOID *)&enumerator); ret = CoCreateInstance(&SDL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &SDL_IID_IMMDeviceEnumerator, (LPVOID *)&enumerator);
if (FAILED(ret)) { if (FAILED(ret)) {
WIN_CoUninitialize(); WIN_CoUninitialize();
return WIN_SetErrorFromHRESULT("WASAPI CoCreateInstance(MMDeviceEnumerator)", ret); return WIN_SetErrorFromHRESULT("IMMDevice CoCreateInstance(MMDeviceEnumerator)", ret);
} }
return 0; return 0;
} }
void SDL_IMMDevice_Quit(void) void SDL_IMMDevice_Quit(void)
{ {
DevIdList *devidlist;
DevIdList *next;
if (enumerator) { if (enumerator) {
IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)&notification_client); IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)&notification_client);
IMMDeviceEnumerator_Release(enumerator); IMMDeviceEnumerator_Release(enumerator);
@ -346,197 +297,99 @@ void SDL_IMMDevice_Quit(void)
} }
WIN_CoUninitialize(); WIN_CoUninitialize();
for (devidlist = deviceid_list; devidlist; devidlist = next) {
next = devidlist->next;
SDL_free(devidlist->str);
SDL_free(devidlist);
}
deviceid_list = NULL;
} }
int SDL_IMMDevice_Get(LPCWSTR devid, IMMDevice **device, SDL_bool iscapture) int SDL_IMMDevice_Get(SDL_AudioDevice *device, IMMDevice **immdevice, SDL_bool iscapture)
{ {
const Uint64 timeout = SDL_GetTicks() + 8000; /* intel's audio drivers can fail for up to EIGHT SECONDS after a device is connected or we wake from sleep. */ const Uint64 timeout = SDL_GetTicks() + 8000; /* intel's audio drivers can fail for up to EIGHT SECONDS after a device is connected or we wake from sleep. */
HRESULT ret;
SDL_assert(device != NULL); SDL_assert(device != NULL);
SDL_assert(immdevice != NULL);
while (SDL_TRUE) { LPCWSTR devid = SDL_IMMDevice_GetDevID(device);
if (devid == NULL) { SDL_assert(devid != NULL);
const EDataFlow dataflow = iscapture ? eCapture : eRender;
ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_IMMDevice_role, device);
} else {
ret = IMMDeviceEnumerator_GetDevice(enumerator, devid, device);
}
if (SUCCEEDED(ret)) { HRESULT ret;
break; while ((ret = IMMDeviceEnumerator_GetDevice(enumerator, devid, immdevice)) == E_NOTFOUND) {
}
if (ret == E_NOTFOUND) {
const Uint64 now = SDL_GetTicks(); const Uint64 now = SDL_GetTicks();
if (timeout > now) { if (timeout > now) {
const Uint64 ticksleft = timeout - now; const Uint64 ticksleft = timeout - now;
SDL_Delay((Uint32)SDL_min(ticksleft, 300)); /* wait awhile and try again. */ SDL_Delay((Uint32)SDL_min(ticksleft, 300)); /* wait awhile and try again. */
continue; continue;
} }
}
return WIN_SetErrorFromHRESULT("WASAPI can't find requested audio endpoint", ret);
}
return 0;
}
typedef struct
{
LPWSTR devid;
char *devname;
WAVEFORMATEXTENSIBLE fmt;
GUID dsoundguid;
} EndpointItem;
static int SDLCALL sort_endpoints(const void *_a, const void *_b)
{
LPWSTR a = ((const EndpointItem *)_a)->devid;
LPWSTR b = ((const EndpointItem *)_b)->devid;
if (!a && !b) {
return 0;
} else if (!a && b) {
return -1;
} else if (a && !b) {
return 1;
}
while (SDL_TRUE) {
if (*a < *b) {
return -1;
} else if (*a > *b) {
return 1;
} else if (*a == 0) {
break; break;
} }
a++;
b++; return SUCCEEDED(ret) ? 0 : WIN_SetErrorFromHRESULT("WASAPI can't find requested audio endpoint", ret);
} }
return 0; static void EnumerateEndpointsForFlow(const SDL_bool iscapture, SDL_AudioDevice **default_device)
}
static void EnumerateEndpointsForFlow(const SDL_bool iscapture)
{ {
IMMDeviceCollection *collection = NULL;
EndpointItem *items;
UINT i, total;
/* Note that WASAPI separates "adapter devices" from "audio endpoint devices" /* Note that WASAPI separates "adapter devices" from "audio endpoint devices"
...one adapter device ("SoundBlaster Pro") might have multiple endpoint devices ("Speakers", "Line-Out"). */ ...one adapter device ("SoundBlaster Pro") might have multiple endpoint devices ("Speakers", "Line-Out"). */
IMMDeviceCollection *collection = NULL;
if (FAILED(IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, iscapture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &collection))) { if (FAILED(IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, iscapture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &collection))) {
return; return;
} }
UINT total = 0;
if (FAILED(IMMDeviceCollection_GetCount(collection, &total))) { if (FAILED(IMMDeviceCollection_GetCount(collection, &total))) {
IMMDeviceCollection_Release(collection); IMMDeviceCollection_Release(collection);
return; return;
} }
items = (EndpointItem *)SDL_calloc(total, sizeof(EndpointItem)); LPWSTR default_devid = NULL;
if (items == NULL) { if (default_device) {
return; /* oh well. */ IMMDevice *default_immdevice = NULL;
const EDataFlow dataflow = iscapture ? eCapture : eRender;
if (SUCCEEDED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_IMMDevice_role, &default_immdevice))) {
LPWSTR devid = NULL;
if (SUCCEEDED(IMMDevice_GetId(default_immdevice, &devid))) {
default_devid = SDL_wcsdup(devid); // if this fails, oh well.
CoTaskMemFree(devid);
} }
IMMDevice_Release(default_immdevice);
for (i = 0; i < total; i++) {
EndpointItem *item = items + i;
IMMDevice *device = NULL;
if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &device))) {
if (SUCCEEDED(IMMDevice_GetId(device, &item->devid))) {
GetMMDeviceInfo(device, &item->devname, &item->fmt, &item->dsoundguid);
}
IMMDevice_Release(device);
} }
} }
/* sort the list of devices by their guid so list is consistent between runs */ for (UINT i = 0; i < total; i++) {
SDL_qsort(items, total, sizeof(*items), sort_endpoints); IMMDevice *immdevice = NULL;
if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &immdevice))) {
/* Send the sorted list on to the SDL's higher level. */ LPWSTR devid = NULL;
for (i = 0; i < total; i++) { if (SUCCEEDED(IMMDevice_GetId(immdevice, &devid))) {
EndpointItem *item = items + i; char *devname = NULL;
if ((item->devid) && (item->devname)) { WAVEFORMATEXTENSIBLE fmt;
SDL_IMMDevice_Add(iscapture, item->devname, &item->fmt, item->devid, &item->dsoundguid, notification_client.useguid); GUID dsoundguid;
SDL_zero(fmt);
SDL_zero(dsoundguid);
GetMMDeviceInfo(immdevice, &devname, &fmt, &dsoundguid);
if (devname) {
SDL_AudioDevice *sdldevice = SDL_IMMDevice_Add(iscapture, devname, &fmt, devid, &dsoundguid);
if (default_device && default_devid && SDL_wcscmp(default_devid, devid) == 0) {
*default_device = sdldevice;
}
SDL_free(devname);
}
CoTaskMemFree(devid);
}
IMMDevice_Release(immdevice);
} }
SDL_free(item->devname);
CoTaskMemFree(item->devid);
} }
SDL_free(items); SDL_free(default_devid);
IMMDeviceCollection_Release(collection); IMMDeviceCollection_Release(collection);
} }
void SDL_IMMDevice_EnumerateEndpoints(SDL_bool useguid) void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{ {
notification_client.useguid = useguid; EnumerateEndpointsForFlow(SDL_FALSE, default_output); /* playback */
EnumerateEndpointsForFlow(SDL_TRUE, default_capture); /* capture */
EnumerateEndpointsForFlow(SDL_FALSE); /* playback */
EnumerateEndpointsForFlow(SDL_TRUE); /* capture */
/* if this fails, we just won't get hotplug events. Carry on anyhow. */ /* if this fails, we just won't get hotplug events. Carry on anyhow. */
IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)&notification_client); IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)&notification_client);
} }
int SDL_IMMDevice_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
{
WAVEFORMATEXTENSIBLE fmt;
IMMDevice *device = NULL;
char *filler;
GUID morefiller;
const EDataFlow dataflow = iscapture ? eCapture : eRender;
HRESULT ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_IMMDevice_role, &device);
if (FAILED(ret)) {
SDL_assert(device == NULL);
return WIN_SetErrorFromHRESULT("WASAPI can't find default audio endpoint", ret);
}
if (name == NULL) {
name = &filler;
}
SDL_zero(fmt);
GetMMDeviceInfo(device, name, &fmt, &morefiller);
IMMDevice_Release(device);
if (name == &filler) {
SDL_free(filler);
}
SDL_zerop(spec);
spec->channels = (Uint8)fmt.Format.nChannels;
spec->freq = fmt.Format.nSamplesPerSec;
spec->format = WaveFormatToSDLFormat((WAVEFORMATEX *)&fmt);
return 0;
}
SDL_AudioFormat WaveFormatToSDLFormat(WAVEFORMATEX *waveformat)
{
if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
return SDL_AUDIO_F32SYS;
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
return SDL_AUDIO_S16SYS;
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
return SDL_AUDIO_S32SYS;
} 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;
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
return SDL_AUDIO_S16SYS;
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
return SDL_AUDIO_S32SYS;
}
}
return 0;
}
#endif /* (defined(__WIN32__) || defined(__GDK__)) && defined(HAVE_MMDEVICEAPI_H) */ #endif /* (defined(__WIN32__) || defined(__GDK__)) && defined(HAVE_MMDEVICEAPI_H) */

View File

@ -26,16 +26,14 @@
#include <mmdeviceapi.h> #include <mmdeviceapi.h>
#include <mmreg.h> #include <mmreg.h>
typedef struct SDL_AudioDevice SDL_AudioDevice; // this is defined in src/audio/SDL_sysaudio.h
int SDL_IMMDevice_Init(void); int SDL_IMMDevice_Init(void);
void SDL_IMMDevice_Quit(void); void SDL_IMMDevice_Quit(void);
int SDL_IMMDevice_Get(LPCWSTR devid, IMMDevice **device, SDL_bool iscapture); int SDL_IMMDevice_Get(SDL_AudioDevice *device, IMMDevice **immdevice, SDL_bool iscapture);
void SDL_IMMDevice_EnumerateEndpoints(SDL_bool useguid); void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
int SDL_IMMDevice_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture); LPGUID SDL_IMMDevice_GetDirectSoundGUID(SDL_AudioDevice *device);
LPCWSTR SDL_IMMDevice_GetDevID(SDL_AudioDevice *device);
SDL_AudioFormat WaveFormatToSDLFormat(WAVEFORMATEX *waveformat); void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device);
/* these increment as default devices change. Opened default devices pick up changes in their threads. */
extern SDL_AtomicInt SDL_IMMDevice_DefaultPlaybackGeneration;
extern SDL_AtomicInt SDL_IMMDevice_DefaultCaptureGeneration;
#endif /* SDL_IMMDEVICE_H */ #endif /* SDL_IMMDEVICE_H */

View File

@ -335,6 +335,33 @@ BOOL WIN_IsRectEmpty(const RECT *rect)
return (rect->right <= rect->left) || (rect->bottom <= rect->top); return (rect->right <= rect->left) || (rect->bottom <= rect->top);
} }
/* Some GUIDs we need to know without linking to libraries that aren't available before Vista. */
/* *INDENT-OFF* */ /* clang-format off */
static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
/* *INDENT-ON* */ /* clang-format on */
SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat)
{
if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
return SDL_AUDIO_F32SYS;
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
return SDL_AUDIO_S16SYS;
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
return SDL_AUDIO_S32SYS;
} 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;
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
return SDL_AUDIO_S16SYS;
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
return SDL_AUDIO_S32SYS;
}
}
return 0;
}
/* Win32-specific SDL_RunApp(), which does most of the SDL_main work, /* Win32-specific SDL_RunApp(), which does most of the SDL_main work,
based on SDL_windows_main.c, placed in the public domain by Sam Lantinga 4/13/98 */ based on SDL_windows_main.c, placed in the public domain by Sam Lantinga 4/13/98 */
#ifdef __WIN32__ #ifdef __WIN32__
@ -348,7 +375,7 @@ static int OutOfMemory(void)
return -1; return -1;
} }
DECLSPEC int SDL_RunApp(int _argc, char* _argv[], SDL_main_func mainFunction, void * reserved) DECLSPEC int MINGW32_FORCEALIGN SDL_RunApp(int _argc, char* _argv[], SDL_main_func mainFunction, void * reserved)
{ {
/* Gets the arguments with GetCommandLine, converts them to argc and argv /* Gets the arguments with GetCommandLine, converts them to argc and argv

View File

@ -76,8 +76,22 @@
#define WINVER _WIN32_WINNT #define WINVER _WIN32_WINNT
#endif #endif
/* See https://github.com/libsdl-org/SDL/pull/7607 */
/* force_align_arg_pointer attribute requires gcc >= 4.2.x. */
#if defined(__clang__)
#define HAVE_FORCE_ALIGN_ARG_POINTER
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
#define HAVE_FORCE_ALIGN_ARG_POINTER
#endif
#if defined(__GNUC__) && defined(__i386__) && defined(HAVE_FORCE_ALIGN_ARG_POINTER)
#define MINGW32_FORCEALIGN __attribute__((force_align_arg_pointer))
#else
#define MINGW32_FORCEALIGN
#endif
#include <windows.h> #include <windows.h>
#include <basetyps.h> /* for REFIID with broken mingw.org headers */ #include <basetyps.h> /* for REFIID with broken mingw.org headers */
#include <mmreg.h>
/* Older Visual C++ headers don't have the Win64-compatible typedefs... */ /* Older Visual C++ headers don't have the Win64-compatible typedefs... */
#if defined(_MSC_VER) && (_MSC_VER <= 1200) #if defined(_MSC_VER) && (_MSC_VER <= 1200)
@ -154,6 +168,8 @@ extern void WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect);
/* Returns SDL_TRUE if the rect is empty */ /* Returns SDL_TRUE if the rect is empty */
extern BOOL WIN_IsRectEmpty(const RECT *rect); extern BOOL WIN_IsRectEmpty(const RECT *rect);
extern SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat);
/* Ends C function definitions when using C++ */ /* Ends C function definitions when using C++ */
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -35,22 +35,17 @@ SDL3_0.0.0 {
SDL_BroadcastCondition; SDL_BroadcastCondition;
SDL_CaptureMouse; SDL_CaptureMouse;
SDL_CleanupTLS; SDL_CleanupTLS;
SDL_ClearAudioStream;
SDL_ClearComposition; SDL_ClearComposition;
SDL_ClearError; SDL_ClearError;
SDL_ClearHints; SDL_ClearHints;
SDL_ClearQueuedAudio;
SDL_CloseAudioDevice;
SDL_CloseGamepad; SDL_CloseGamepad;
SDL_CloseJoystick; SDL_CloseJoystick;
SDL_CloseSensor; SDL_CloseSensor;
SDL_ComposeCustomBlendMode; SDL_ComposeCustomBlendMode;
SDL_ConvertAudioSamples;
SDL_ConvertEventToRenderCoordinates; SDL_ConvertEventToRenderCoordinates;
SDL_ConvertPixels; SDL_ConvertPixels;
SDL_ConvertSurface; SDL_ConvertSurface;
SDL_ConvertSurfaceFormat; SDL_ConvertSurfaceFormat;
SDL_CreateAudioStream;
SDL_CreateColorCursor; SDL_CreateColorCursor;
SDL_CreateCondition; SDL_CreateCondition;
SDL_CreateCursor; SDL_CreateCursor;
@ -82,8 +77,6 @@ SDL3_0.0.0 {
SDL_DelHintCallback; SDL_DelHintCallback;
SDL_Delay; SDL_Delay;
SDL_DelayNS; SDL_DelayNS;
SDL_DequeueAudio;
SDL_DestroyAudioStream;
SDL_DestroyCondition; SDL_DestroyCondition;
SDL_DestroyCursor; SDL_DestroyCursor;
SDL_DestroyMutex; SDL_DestroyMutex;
@ -114,7 +107,6 @@ SDL3_0.0.0 {
SDL_FillSurfaceRects; SDL_FillSurfaceRects;
SDL_FilterEvents; SDL_FilterEvents;
SDL_FlashWindow; SDL_FlashWindow;
SDL_FlushAudioStream;
SDL_FlushEvent; SDL_FlushEvent;
SDL_FlushEvents; SDL_FlushEvents;
SDL_GDKGetTaskQueue; SDL_GDKGetTaskQueue;
@ -150,27 +142,18 @@ SDL3_0.0.0 {
SDL_GetAndroidSDKVersion; SDL_GetAndroidSDKVersion;
SDL_GetAssertionHandler; SDL_GetAssertionHandler;
SDL_GetAssertionReport; SDL_GetAssertionReport;
SDL_GetAudioDeviceName;
SDL_GetAudioDeviceSpec;
SDL_GetAudioDeviceStatus;
SDL_GetAudioDriver;
SDL_GetAudioStreamAvailable;
SDL_GetAudioStreamData;
SDL_GetAudioStreamFormat;
SDL_GetBasePath; SDL_GetBasePath;
SDL_GetCPUCacheLineSize; SDL_GetCPUCacheLineSize;
SDL_GetCPUCount; SDL_GetCPUCount;
SDL_GetClipboardData; SDL_GetClipboardData;
SDL_GetClipboardText; SDL_GetClipboardText;
SDL_GetClosestFullscreenDisplayMode; SDL_GetClosestFullscreenDisplayMode;
SDL_GetCurrentAudioDriver;
SDL_GetCurrentDisplayMode; SDL_GetCurrentDisplayMode;
SDL_GetCurrentDisplayOrientation; SDL_GetCurrentDisplayOrientation;
SDL_GetCurrentRenderOutputSize; SDL_GetCurrentRenderOutputSize;
SDL_GetCurrentVideoDriver; SDL_GetCurrentVideoDriver;
SDL_GetCursor; SDL_GetCursor;
SDL_GetDefaultAssertionHandler; SDL_GetDefaultAssertionHandler;
SDL_GetDefaultAudioInfo;
SDL_GetDefaultCursor; SDL_GetDefaultCursor;
SDL_GetDesktopDisplayMode; SDL_GetDesktopDisplayMode;
SDL_GetDisplayBounds; SDL_GetDisplayBounds;
@ -267,8 +250,6 @@ SDL3_0.0.0 {
SDL_GetMouseState; SDL_GetMouseState;
SDL_GetNaturalDisplayOrientation; SDL_GetNaturalDisplayOrientation;
SDL_GetNumAllocations; SDL_GetNumAllocations;
SDL_GetNumAudioDevices;
SDL_GetNumAudioDrivers;
SDL_GetNumGamepadMappings; SDL_GetNumGamepadMappings;
SDL_GetNumGamepadTouchpadFingers; SDL_GetNumGamepadTouchpadFingers;
SDL_GetNumGamepadTouchpads; SDL_GetNumGamepadTouchpads;
@ -280,7 +261,7 @@ SDL3_0.0.0 {
SDL_GetNumTouchFingers; SDL_GetNumTouchFingers;
SDL_GetNumVideoDrivers; SDL_GetNumVideoDrivers;
SDL_GetOriginalMemoryFunctions; SDL_GetOriginalMemoryFunctions;
SDL_GetPath; SDL_GetUserFolder;
SDL_GetPerformanceCounter; SDL_GetPerformanceCounter;
SDL_GetPerformanceFrequency; SDL_GetPerformanceFrequency;
SDL_GetPixelFormatEnumForMasks; SDL_GetPixelFormatEnumForMasks;
@ -291,7 +272,6 @@ SDL3_0.0.0 {
SDL_GetPreferredLocales; SDL_GetPreferredLocales;
SDL_GetPrimaryDisplay; SDL_GetPrimaryDisplay;
SDL_GetPrimarySelectionText; SDL_GetPrimarySelectionText;
SDL_GetQueuedAudioSize;
SDL_GetRGB; SDL_GetRGB;
SDL_GetRGBA; SDL_GetRGBA;
SDL_GetRectAndLineIntersection; SDL_GetRectAndLineIntersection;
@ -461,8 +441,6 @@ SDL3_0.0.0 {
SDL_LoadFile_RW; SDL_LoadFile_RW;
SDL_LoadFunction; SDL_LoadFunction;
SDL_LoadObject; SDL_LoadObject;
SDL_LoadWAV_RW;
SDL_LockAudioDevice;
SDL_LockJoysticks; SDL_LockJoysticks;
SDL_LockMutex; SDL_LockMutex;
SDL_LockRWLockForReading; SDL_LockRWLockForReading;
@ -494,7 +472,6 @@ SDL3_0.0.0 {
SDL_Metal_DestroyView; SDL_Metal_DestroyView;
SDL_Metal_GetLayer; SDL_Metal_GetLayer;
SDL_MinimizeWindow; SDL_MinimizeWindow;
SDL_MixAudioFormat;
SDL_MouseIsHaptic; SDL_MouseIsHaptic;
SDL_NumHaptics; SDL_NumHaptics;
SDL_OnApplicationDidBecomeActive; SDL_OnApplicationDidBecomeActive;
@ -504,22 +481,17 @@ SDL3_0.0.0 {
SDL_OnApplicationWillEnterForeground; SDL_OnApplicationWillEnterForeground;
SDL_OnApplicationWillResignActive; SDL_OnApplicationWillResignActive;
SDL_OnApplicationWillTerminate; SDL_OnApplicationWillTerminate;
SDL_OpenAudioDevice;
SDL_OpenGamepad; SDL_OpenGamepad;
SDL_OpenJoystick; SDL_OpenJoystick;
SDL_OpenSensor; SDL_OpenSensor;
SDL_OpenURL; SDL_OpenURL;
SDL_PauseAudioDevice;
SDL_PeepEvents; SDL_PeepEvents;
SDL_PlayAudioDevice;
SDL_PollEvent; SDL_PollEvent;
SDL_PostSemaphore; SDL_PostSemaphore;
SDL_PremultiplyAlpha; SDL_PremultiplyAlpha;
SDL_PumpEvents; SDL_PumpEvents;
SDL_PushEvent; SDL_PushEvent;
SDL_PutAudioStreamData;
SDL_QueryTexture; SDL_QueryTexture;
SDL_QueueAudio;
SDL_Quit; SDL_Quit;
SDL_QuitSubSystem; SDL_QuitSubSystem;
SDL_RWFromConstMem; SDL_RWFromConstMem;
@ -532,12 +504,12 @@ SDL3_0.0.0 {
SDL_RWtell; SDL_RWtell;
SDL_RWwrite; SDL_RWwrite;
SDL_RaiseWindow; SDL_RaiseWindow;
SDL_ReadBE16; SDL_ReadU16BE;
SDL_ReadBE32; SDL_ReadU32BE;
SDL_ReadBE64; SDL_ReadU64BE;
SDL_ReadLE16; SDL_ReadU16LE;
SDL_ReadLE32; SDL_ReadU32LE;
SDL_ReadLE64; SDL_ReadU64LE;
SDL_ReadU8; SDL_ReadU8;
SDL_RegisterApp; SDL_RegisterApp;
SDL_RegisterEvents; SDL_RegisterEvents;
@ -672,7 +644,6 @@ SDL3_0.0.0 {
SDL_TryLockRWLockForWriting; SDL_TryLockRWLockForWriting;
SDL_TryWaitSemaphore; SDL_TryWaitSemaphore;
SDL_UnloadObject; SDL_UnloadObject;
SDL_UnlockAudioDevice;
SDL_UnlockJoysticks; SDL_UnlockJoysticks;
SDL_UnlockMutex; SDL_UnlockMutex;
SDL_UnlockRWLock; SDL_UnlockRWLock;
@ -705,12 +676,12 @@ SDL3_0.0.0 {
SDL_WinRTGetDeviceFamily; SDL_WinRTGetDeviceFamily;
SDL_WinRTGetFSPathUNICODE; SDL_WinRTGetFSPathUNICODE;
SDL_WinRTGetFSPathUTF8; SDL_WinRTGetFSPathUTF8;
SDL_WriteBE16; SDL_WriteU16BE;
SDL_WriteBE32; SDL_WriteU32BE;
SDL_WriteBE64; SDL_WriteU64BE;
SDL_WriteLE16; SDL_WriteU16LE;
SDL_WriteLE32; SDL_WriteU32LE;
SDL_WriteLE64; SDL_WriteU64LE;
SDL_WriteU8; SDL_WriteU8;
SDL_abs; SDL_abs;
SDL_acos; SDL_acos;
@ -879,6 +850,55 @@ SDL3_0.0.0 {
SDL_strnlen; SDL_strnlen;
SDL_AddGamepadMappingsFromFile; SDL_AddGamepadMappingsFromFile;
SDL_ReloadGamepadMappings; SDL_ReloadGamepadMappings;
SDL_GetNumAudioDrivers;
SDL_GetAudioDriver;
SDL_GetCurrentAudioDriver;
SDL_GetAudioOutputDevices;
SDL_GetAudioCaptureDevices;
SDL_GetAudioDeviceName;
SDL_GetAudioDeviceFormat;
SDL_OpenAudioDevice;
SDL_CloseAudioDevice;
SDL_BindAudioStreams;
SDL_BindAudioStream;
SDL_UnbindAudioStreams;
SDL_UnbindAudioStream;
SDL_CreateAudioStream;
SDL_GetAudioStreamFormat;
SDL_SetAudioStreamFormat;
SDL_PutAudioStreamData;
SDL_GetAudioStreamData;
SDL_GetAudioStreamAvailable;
SDL_FlushAudioStream;
SDL_ClearAudioStream;
SDL_LockAudioStream;
SDL_UnlockAudioStream;
SDL_SetAudioStreamGetCallback;
SDL_SetAudioStreamPutCallback;
SDL_DestroyAudioStream;
SDL_CreateAndBindAudioStream;
SDL_LoadWAV_RW;
SDL_LoadWAV;
SDL_MixAudioFormat;
SDL_ConvertAudioSamples;
SDL_GetSilenceValueForFormat;
SDL_LoadWAV;
SDL_PauseAudioDevice;
SDL_ResumeAudioDevice;
SDL_IsAudioDevicePaused;
SDL_GetAudioStreamBinding;
SDL_ShowWindowSystemMenu;
SDL_ReadS16LE;
SDL_ReadS16BE;
SDL_ReadS32LE;
SDL_ReadS32BE;
SDL_ReadS64LE;
SDL_ReadS64BE;
SDL_WriteS16LE;
SDL_WriteS16BE;
SDL_WriteS32LE;
SDL_WriteS32BE;
SDL_WriteS64LE;
# extra symbols go here (don't modify this line) # extra symbols go here (don't modify this line)
local: *; local: *;
}; };

View File

@ -59,22 +59,17 @@
#define SDL_BroadcastCondition SDL_BroadcastCondition_REAL #define SDL_BroadcastCondition SDL_BroadcastCondition_REAL
#define SDL_CaptureMouse SDL_CaptureMouse_REAL #define SDL_CaptureMouse SDL_CaptureMouse_REAL
#define SDL_CleanupTLS SDL_CleanupTLS_REAL #define SDL_CleanupTLS SDL_CleanupTLS_REAL
#define SDL_ClearAudioStream SDL_ClearAudioStream_REAL
#define SDL_ClearComposition SDL_ClearComposition_REAL #define SDL_ClearComposition SDL_ClearComposition_REAL
#define SDL_ClearError SDL_ClearError_REAL #define SDL_ClearError SDL_ClearError_REAL
#define SDL_ClearHints SDL_ClearHints_REAL #define SDL_ClearHints SDL_ClearHints_REAL
#define SDL_ClearQueuedAudio SDL_ClearQueuedAudio_REAL
#define SDL_CloseAudioDevice SDL_CloseAudioDevice_REAL
#define SDL_CloseGamepad SDL_CloseGamepad_REAL #define SDL_CloseGamepad SDL_CloseGamepad_REAL
#define SDL_CloseJoystick SDL_CloseJoystick_REAL #define SDL_CloseJoystick SDL_CloseJoystick_REAL
#define SDL_CloseSensor SDL_CloseSensor_REAL #define SDL_CloseSensor SDL_CloseSensor_REAL
#define SDL_ComposeCustomBlendMode SDL_ComposeCustomBlendMode_REAL #define SDL_ComposeCustomBlendMode SDL_ComposeCustomBlendMode_REAL
#define SDL_ConvertAudioSamples SDL_ConvertAudioSamples_REAL
#define SDL_ConvertEventToRenderCoordinates SDL_ConvertEventToRenderCoordinates_REAL #define SDL_ConvertEventToRenderCoordinates SDL_ConvertEventToRenderCoordinates_REAL
#define SDL_ConvertPixels SDL_ConvertPixels_REAL #define SDL_ConvertPixels SDL_ConvertPixels_REAL
#define SDL_ConvertSurface SDL_ConvertSurface_REAL #define SDL_ConvertSurface SDL_ConvertSurface_REAL
#define SDL_ConvertSurfaceFormat SDL_ConvertSurfaceFormat_REAL #define SDL_ConvertSurfaceFormat SDL_ConvertSurfaceFormat_REAL
#define SDL_CreateAudioStream SDL_CreateAudioStream_REAL
#define SDL_CreateColorCursor SDL_CreateColorCursor_REAL #define SDL_CreateColorCursor SDL_CreateColorCursor_REAL
#define SDL_CreateCondition SDL_CreateCondition_REAL #define SDL_CreateCondition SDL_CreateCondition_REAL
#define SDL_CreateCursor SDL_CreateCursor_REAL #define SDL_CreateCursor SDL_CreateCursor_REAL
@ -106,8 +101,6 @@
#define SDL_DelHintCallback SDL_DelHintCallback_REAL #define SDL_DelHintCallback SDL_DelHintCallback_REAL
#define SDL_Delay SDL_Delay_REAL #define SDL_Delay SDL_Delay_REAL
#define SDL_DelayNS SDL_DelayNS_REAL #define SDL_DelayNS SDL_DelayNS_REAL
#define SDL_DequeueAudio SDL_DequeueAudio_REAL
#define SDL_DestroyAudioStream SDL_DestroyAudioStream_REAL
#define SDL_DestroyCondition SDL_DestroyCondition_REAL #define SDL_DestroyCondition SDL_DestroyCondition_REAL
#define SDL_DestroyCursor SDL_DestroyCursor_REAL #define SDL_DestroyCursor SDL_DestroyCursor_REAL
#define SDL_DestroyMutex SDL_DestroyMutex_REAL #define SDL_DestroyMutex SDL_DestroyMutex_REAL
@ -138,7 +131,6 @@
#define SDL_FillSurfaceRects SDL_FillSurfaceRects_REAL #define SDL_FillSurfaceRects SDL_FillSurfaceRects_REAL
#define SDL_FilterEvents SDL_FilterEvents_REAL #define SDL_FilterEvents SDL_FilterEvents_REAL
#define SDL_FlashWindow SDL_FlashWindow_REAL #define SDL_FlashWindow SDL_FlashWindow_REAL
#define SDL_FlushAudioStream SDL_FlushAudioStream_REAL
#define SDL_FlushEvent SDL_FlushEvent_REAL #define SDL_FlushEvent SDL_FlushEvent_REAL
#define SDL_FlushEvents SDL_FlushEvents_REAL #define SDL_FlushEvents SDL_FlushEvents_REAL
#define SDL_GDKGetTaskQueue SDL_GDKGetTaskQueue_REAL #define SDL_GDKGetTaskQueue SDL_GDKGetTaskQueue_REAL
@ -174,27 +166,18 @@
#define SDL_GetAndroidSDKVersion SDL_GetAndroidSDKVersion_REAL #define SDL_GetAndroidSDKVersion SDL_GetAndroidSDKVersion_REAL
#define SDL_GetAssertionHandler SDL_GetAssertionHandler_REAL #define SDL_GetAssertionHandler SDL_GetAssertionHandler_REAL
#define SDL_GetAssertionReport SDL_GetAssertionReport_REAL #define SDL_GetAssertionReport SDL_GetAssertionReport_REAL
#define SDL_GetAudioDeviceName SDL_GetAudioDeviceName_REAL
#define SDL_GetAudioDeviceSpec SDL_GetAudioDeviceSpec_REAL
#define SDL_GetAudioDeviceStatus SDL_GetAudioDeviceStatus_REAL
#define SDL_GetAudioDriver SDL_GetAudioDriver_REAL
#define SDL_GetAudioStreamAvailable SDL_GetAudioStreamAvailable_REAL
#define SDL_GetAudioStreamData SDL_GetAudioStreamData_REAL
#define SDL_GetAudioStreamFormat SDL_GetAudioStreamFormat_REAL
#define SDL_GetBasePath SDL_GetBasePath_REAL #define SDL_GetBasePath SDL_GetBasePath_REAL
#define SDL_GetCPUCacheLineSize SDL_GetCPUCacheLineSize_REAL #define SDL_GetCPUCacheLineSize SDL_GetCPUCacheLineSize_REAL
#define SDL_GetCPUCount SDL_GetCPUCount_REAL #define SDL_GetCPUCount SDL_GetCPUCount_REAL
#define SDL_GetClipboardData SDL_GetClipboardData_REAL #define SDL_GetClipboardData SDL_GetClipboardData_REAL
#define SDL_GetClipboardText SDL_GetClipboardText_REAL #define SDL_GetClipboardText SDL_GetClipboardText_REAL
#define SDL_GetClosestFullscreenDisplayMode SDL_GetClosestFullscreenDisplayMode_REAL #define SDL_GetClosestFullscreenDisplayMode SDL_GetClosestFullscreenDisplayMode_REAL
#define SDL_GetCurrentAudioDriver SDL_GetCurrentAudioDriver_REAL
#define SDL_GetCurrentDisplayMode SDL_GetCurrentDisplayMode_REAL #define SDL_GetCurrentDisplayMode SDL_GetCurrentDisplayMode_REAL
#define SDL_GetCurrentDisplayOrientation SDL_GetCurrentDisplayOrientation_REAL #define SDL_GetCurrentDisplayOrientation SDL_GetCurrentDisplayOrientation_REAL
#define SDL_GetCurrentRenderOutputSize SDL_GetCurrentRenderOutputSize_REAL #define SDL_GetCurrentRenderOutputSize SDL_GetCurrentRenderOutputSize_REAL
#define SDL_GetCurrentVideoDriver SDL_GetCurrentVideoDriver_REAL #define SDL_GetCurrentVideoDriver SDL_GetCurrentVideoDriver_REAL
#define SDL_GetCursor SDL_GetCursor_REAL #define SDL_GetCursor SDL_GetCursor_REAL
#define SDL_GetDefaultAssertionHandler SDL_GetDefaultAssertionHandler_REAL #define SDL_GetDefaultAssertionHandler SDL_GetDefaultAssertionHandler_REAL
#define SDL_GetDefaultAudioInfo SDL_GetDefaultAudioInfo_REAL
#define SDL_GetDefaultCursor SDL_GetDefaultCursor_REAL #define SDL_GetDefaultCursor SDL_GetDefaultCursor_REAL
#define SDL_GetDesktopDisplayMode SDL_GetDesktopDisplayMode_REAL #define SDL_GetDesktopDisplayMode SDL_GetDesktopDisplayMode_REAL
#define SDL_GetDisplayBounds SDL_GetDisplayBounds_REAL #define SDL_GetDisplayBounds SDL_GetDisplayBounds_REAL
@ -291,8 +274,6 @@
#define SDL_GetMouseState SDL_GetMouseState_REAL #define SDL_GetMouseState SDL_GetMouseState_REAL
#define SDL_GetNaturalDisplayOrientation SDL_GetNaturalDisplayOrientation_REAL #define SDL_GetNaturalDisplayOrientation SDL_GetNaturalDisplayOrientation_REAL
#define SDL_GetNumAllocations SDL_GetNumAllocations_REAL #define SDL_GetNumAllocations SDL_GetNumAllocations_REAL
#define SDL_GetNumAudioDevices SDL_GetNumAudioDevices_REAL
#define SDL_GetNumAudioDrivers SDL_GetNumAudioDrivers_REAL
#define SDL_GetNumGamepadMappings SDL_GetNumGamepadMappings_REAL #define SDL_GetNumGamepadMappings SDL_GetNumGamepadMappings_REAL
#define SDL_GetNumGamepadTouchpadFingers SDL_GetNumGamepadTouchpadFingers_REAL #define SDL_GetNumGamepadTouchpadFingers SDL_GetNumGamepadTouchpadFingers_REAL
#define SDL_GetNumGamepadTouchpads SDL_GetNumGamepadTouchpads_REAL #define SDL_GetNumGamepadTouchpads SDL_GetNumGamepadTouchpads_REAL
@ -304,7 +285,7 @@
#define SDL_GetNumTouchFingers SDL_GetNumTouchFingers_REAL #define SDL_GetNumTouchFingers SDL_GetNumTouchFingers_REAL
#define SDL_GetNumVideoDrivers SDL_GetNumVideoDrivers_REAL #define SDL_GetNumVideoDrivers SDL_GetNumVideoDrivers_REAL
#define SDL_GetOriginalMemoryFunctions SDL_GetOriginalMemoryFunctions_REAL #define SDL_GetOriginalMemoryFunctions SDL_GetOriginalMemoryFunctions_REAL
#define SDL_GetPath SDL_GetPath_REAL #define SDL_GetUserFolder SDL_GetUserFolder_REAL
#define SDL_GetPerformanceCounter SDL_GetPerformanceCounter_REAL #define SDL_GetPerformanceCounter SDL_GetPerformanceCounter_REAL
#define SDL_GetPerformanceFrequency SDL_GetPerformanceFrequency_REAL #define SDL_GetPerformanceFrequency SDL_GetPerformanceFrequency_REAL
#define SDL_GetPixelFormatEnumForMasks SDL_GetPixelFormatEnumForMasks_REAL #define SDL_GetPixelFormatEnumForMasks SDL_GetPixelFormatEnumForMasks_REAL
@ -315,7 +296,6 @@
#define SDL_GetPreferredLocales SDL_GetPreferredLocales_REAL #define SDL_GetPreferredLocales SDL_GetPreferredLocales_REAL
#define SDL_GetPrimaryDisplay SDL_GetPrimaryDisplay_REAL #define SDL_GetPrimaryDisplay SDL_GetPrimaryDisplay_REAL
#define SDL_GetPrimarySelectionText SDL_GetPrimarySelectionText_REAL #define SDL_GetPrimarySelectionText SDL_GetPrimarySelectionText_REAL
#define SDL_GetQueuedAudioSize SDL_GetQueuedAudioSize_REAL
#define SDL_GetRGB SDL_GetRGB_REAL #define SDL_GetRGB SDL_GetRGB_REAL
#define SDL_GetRGBA SDL_GetRGBA_REAL #define SDL_GetRGBA SDL_GetRGBA_REAL
#define SDL_GetRectAndLineIntersection SDL_GetRectAndLineIntersection_REAL #define SDL_GetRectAndLineIntersection SDL_GetRectAndLineIntersection_REAL
@ -485,8 +465,6 @@
#define SDL_LoadFile_RW SDL_LoadFile_RW_REAL #define SDL_LoadFile_RW SDL_LoadFile_RW_REAL
#define SDL_LoadFunction SDL_LoadFunction_REAL #define SDL_LoadFunction SDL_LoadFunction_REAL
#define SDL_LoadObject SDL_LoadObject_REAL #define SDL_LoadObject SDL_LoadObject_REAL
#define SDL_LoadWAV_RW SDL_LoadWAV_RW_REAL
#define SDL_LockAudioDevice SDL_LockAudioDevice_REAL
#define SDL_LockJoysticks SDL_LockJoysticks_REAL #define SDL_LockJoysticks SDL_LockJoysticks_REAL
#define SDL_LockMutex SDL_LockMutex_REAL #define SDL_LockMutex SDL_LockMutex_REAL
#define SDL_LockRWLockForReading SDL_LockRWLockForReading_REAL #define SDL_LockRWLockForReading SDL_LockRWLockForReading_REAL
@ -518,7 +496,6 @@
#define SDL_Metal_DestroyView SDL_Metal_DestroyView_REAL #define SDL_Metal_DestroyView SDL_Metal_DestroyView_REAL
#define SDL_Metal_GetLayer SDL_Metal_GetLayer_REAL #define SDL_Metal_GetLayer SDL_Metal_GetLayer_REAL
#define SDL_MinimizeWindow SDL_MinimizeWindow_REAL #define SDL_MinimizeWindow SDL_MinimizeWindow_REAL
#define SDL_MixAudioFormat SDL_MixAudioFormat_REAL
#define SDL_MouseIsHaptic SDL_MouseIsHaptic_REAL #define SDL_MouseIsHaptic SDL_MouseIsHaptic_REAL
#define SDL_NumHaptics SDL_NumHaptics_REAL #define SDL_NumHaptics SDL_NumHaptics_REAL
#define SDL_OnApplicationDidBecomeActive SDL_OnApplicationDidBecomeActive_REAL #define SDL_OnApplicationDidBecomeActive SDL_OnApplicationDidBecomeActive_REAL
@ -528,22 +505,17 @@
#define SDL_OnApplicationWillEnterForeground SDL_OnApplicationWillEnterForeground_REAL #define SDL_OnApplicationWillEnterForeground SDL_OnApplicationWillEnterForeground_REAL
#define SDL_OnApplicationWillResignActive SDL_OnApplicationWillResignActive_REAL #define SDL_OnApplicationWillResignActive SDL_OnApplicationWillResignActive_REAL
#define SDL_OnApplicationWillTerminate SDL_OnApplicationWillTerminate_REAL #define SDL_OnApplicationWillTerminate SDL_OnApplicationWillTerminate_REAL
#define SDL_OpenAudioDevice SDL_OpenAudioDevice_REAL
#define SDL_OpenGamepad SDL_OpenGamepad_REAL #define SDL_OpenGamepad SDL_OpenGamepad_REAL
#define SDL_OpenJoystick SDL_OpenJoystick_REAL #define SDL_OpenJoystick SDL_OpenJoystick_REAL
#define SDL_OpenSensor SDL_OpenSensor_REAL #define SDL_OpenSensor SDL_OpenSensor_REAL
#define SDL_OpenURL SDL_OpenURL_REAL #define SDL_OpenURL SDL_OpenURL_REAL
#define SDL_PauseAudioDevice SDL_PauseAudioDevice_REAL
#define SDL_PeepEvents SDL_PeepEvents_REAL #define SDL_PeepEvents SDL_PeepEvents_REAL
#define SDL_PlayAudioDevice SDL_PlayAudioDevice_REAL
#define SDL_PollEvent SDL_PollEvent_REAL #define SDL_PollEvent SDL_PollEvent_REAL
#define SDL_PostSemaphore SDL_PostSemaphore_REAL #define SDL_PostSemaphore SDL_PostSemaphore_REAL
#define SDL_PremultiplyAlpha SDL_PremultiplyAlpha_REAL #define SDL_PremultiplyAlpha SDL_PremultiplyAlpha_REAL
#define SDL_PumpEvents SDL_PumpEvents_REAL #define SDL_PumpEvents SDL_PumpEvents_REAL
#define SDL_PushEvent SDL_PushEvent_REAL #define SDL_PushEvent SDL_PushEvent_REAL
#define SDL_PutAudioStreamData SDL_PutAudioStreamData_REAL
#define SDL_QueryTexture SDL_QueryTexture_REAL #define SDL_QueryTexture SDL_QueryTexture_REAL
#define SDL_QueueAudio SDL_QueueAudio_REAL
#define SDL_Quit SDL_Quit_REAL #define SDL_Quit SDL_Quit_REAL
#define SDL_QuitSubSystem SDL_QuitSubSystem_REAL #define SDL_QuitSubSystem SDL_QuitSubSystem_REAL
#define SDL_RWFromConstMem SDL_RWFromConstMem_REAL #define SDL_RWFromConstMem SDL_RWFromConstMem_REAL
@ -556,12 +528,12 @@
#define SDL_RWtell SDL_RWtell_REAL #define SDL_RWtell SDL_RWtell_REAL
#define SDL_RWwrite SDL_RWwrite_REAL #define SDL_RWwrite SDL_RWwrite_REAL
#define SDL_RaiseWindow SDL_RaiseWindow_REAL #define SDL_RaiseWindow SDL_RaiseWindow_REAL
#define SDL_ReadBE16 SDL_ReadBE16_REAL #define SDL_ReadU16BE SDL_ReadU16BE_REAL
#define SDL_ReadBE32 SDL_ReadBE32_REAL #define SDL_ReadU32BE SDL_ReadU32BE_REAL
#define SDL_ReadBE64 SDL_ReadBE64_REAL #define SDL_ReadU64BE SDL_ReadU64BE_REAL
#define SDL_ReadLE16 SDL_ReadLE16_REAL #define SDL_ReadU16LE SDL_ReadU16LE_REAL
#define SDL_ReadLE32 SDL_ReadLE32_REAL #define SDL_ReadU32LE SDL_ReadU32LE_REAL
#define SDL_ReadLE64 SDL_ReadLE64_REAL #define SDL_ReadU64LE SDL_ReadU64LE_REAL
#define SDL_ReadU8 SDL_ReadU8_REAL #define SDL_ReadU8 SDL_ReadU8_REAL
#define SDL_RegisterApp SDL_RegisterApp_REAL #define SDL_RegisterApp SDL_RegisterApp_REAL
#define SDL_RegisterEvents SDL_RegisterEvents_REAL #define SDL_RegisterEvents SDL_RegisterEvents_REAL
@ -605,7 +577,6 @@
#define SDL_SendGamepadEffect SDL_SendGamepadEffect_REAL #define SDL_SendGamepadEffect SDL_SendGamepadEffect_REAL
#define SDL_SendJoystickEffect SDL_SendJoystickEffect_REAL #define SDL_SendJoystickEffect SDL_SendJoystickEffect_REAL
#define SDL_SetAssertionHandler SDL_SetAssertionHandler_REAL #define SDL_SetAssertionHandler SDL_SetAssertionHandler_REAL
#define SDL_SetAudioStreamFormat SDL_SetAudioStreamFormat_REAL
#define SDL_SetClipboardData SDL_SetClipboardData_REAL #define SDL_SetClipboardData SDL_SetClipboardData_REAL
#define SDL_SetClipboardText SDL_SetClipboardText_REAL #define SDL_SetClipboardText SDL_SetClipboardText_REAL
#define SDL_SetCursor SDL_SetCursor_REAL #define SDL_SetCursor SDL_SetCursor_REAL
@ -696,7 +667,6 @@
#define SDL_TryLockRWLockForWriting SDL_TryLockRWLockForWriting_REAL #define SDL_TryLockRWLockForWriting SDL_TryLockRWLockForWriting_REAL
#define SDL_TryWaitSemaphore SDL_TryWaitSemaphore_REAL #define SDL_TryWaitSemaphore SDL_TryWaitSemaphore_REAL
#define SDL_UnloadObject SDL_UnloadObject_REAL #define SDL_UnloadObject SDL_UnloadObject_REAL
#define SDL_UnlockAudioDevice SDL_UnlockAudioDevice_REAL
#define SDL_UnlockJoysticks SDL_UnlockJoysticks_REAL #define SDL_UnlockJoysticks SDL_UnlockJoysticks_REAL
#define SDL_UnlockMutex SDL_UnlockMutex_REAL #define SDL_UnlockMutex SDL_UnlockMutex_REAL
#define SDL_UnlockRWLock SDL_UnlockRWLock_REAL #define SDL_UnlockRWLock SDL_UnlockRWLock_REAL
@ -729,12 +699,12 @@
#define SDL_WinRTGetDeviceFamily SDL_WinRTGetDeviceFamily_REAL #define SDL_WinRTGetDeviceFamily SDL_WinRTGetDeviceFamily_REAL
#define SDL_WinRTGetFSPathUNICODE SDL_WinRTGetFSPathUNICODE_REAL #define SDL_WinRTGetFSPathUNICODE SDL_WinRTGetFSPathUNICODE_REAL
#define SDL_WinRTGetFSPathUTF8 SDL_WinRTGetFSPathUTF8_REAL #define SDL_WinRTGetFSPathUTF8 SDL_WinRTGetFSPathUTF8_REAL
#define SDL_WriteBE16 SDL_WriteBE16_REAL #define SDL_WriteU16BE SDL_WriteU16BE_REAL
#define SDL_WriteBE32 SDL_WriteBE32_REAL #define SDL_WriteU32BE SDL_WriteU32BE_REAL
#define SDL_WriteBE64 SDL_WriteBE64_REAL #define SDL_WriteU64BE SDL_WriteU64BE_REAL
#define SDL_WriteLE16 SDL_WriteLE16_REAL #define SDL_WriteU16LE SDL_WriteU16LE_REAL
#define SDL_WriteLE32 SDL_WriteLE32_REAL #define SDL_WriteU32LE SDL_WriteU32LE_REAL
#define SDL_WriteLE64 SDL_WriteLE64_REAL #define SDL_WriteU64LE SDL_WriteU64LE_REAL
#define SDL_WriteU8 SDL_WriteU8_REAL #define SDL_WriteU8 SDL_WriteU8_REAL
#define SDL_abs SDL_abs_REAL #define SDL_abs SDL_abs_REAL
#define SDL_acos SDL_acos_REAL #define SDL_acos SDL_acos_REAL
@ -905,3 +875,52 @@
#define SDL_strnlen SDL_strnlen_REAL #define SDL_strnlen SDL_strnlen_REAL
#define SDL_AddGamepadMappingsFromFile SDL_AddGamepadMappingsFromFile_REAL #define SDL_AddGamepadMappingsFromFile SDL_AddGamepadMappingsFromFile_REAL
#define SDL_ReloadGamepadMappings SDL_ReloadGamepadMappings_REAL #define SDL_ReloadGamepadMappings SDL_ReloadGamepadMappings_REAL
#define SDL_GetNumAudioDrivers SDL_GetNumAudioDrivers_REAL
#define SDL_GetAudioDriver SDL_GetAudioDriver_REAL
#define SDL_GetCurrentAudioDriver SDL_GetCurrentAudioDriver_REAL
#define SDL_GetAudioOutputDevices SDL_GetAudioOutputDevices_REAL
#define SDL_GetAudioCaptureDevices SDL_GetAudioCaptureDevices_REAL
#define SDL_GetAudioDeviceName SDL_GetAudioDeviceName_REAL
#define SDL_GetAudioDeviceFormat SDL_GetAudioDeviceFormat_REAL
#define SDL_OpenAudioDevice SDL_OpenAudioDevice_REAL
#define SDL_CloseAudioDevice SDL_CloseAudioDevice_REAL
#define SDL_BindAudioStreams SDL_BindAudioStreams_REAL
#define SDL_BindAudioStream SDL_BindAudioStream_REAL
#define SDL_UnbindAudioStreams SDL_UnbindAudioStreams_REAL
#define SDL_UnbindAudioStream SDL_UnbindAudioStream_REAL
#define SDL_CreateAudioStream SDL_CreateAudioStream_REAL
#define SDL_GetAudioStreamFormat SDL_GetAudioStreamFormat_REAL
#define SDL_SetAudioStreamFormat SDL_SetAudioStreamFormat_REAL
#define SDL_PutAudioStreamData SDL_PutAudioStreamData_REAL
#define SDL_GetAudioStreamData SDL_GetAudioStreamData_REAL
#define SDL_GetAudioStreamAvailable SDL_GetAudioStreamAvailable_REAL
#define SDL_FlushAudioStream SDL_FlushAudioStream_REAL
#define SDL_ClearAudioStream SDL_ClearAudioStream_REAL
#define SDL_LockAudioStream SDL_LockAudioStream_REAL
#define SDL_UnlockAudioStream SDL_UnlockAudioStream_REAL
#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_LoadWAV_RW SDL_LoadWAV_RW_REAL
#define SDL_LoadWAV SDL_LoadWAV_REAL
#define SDL_MixAudioFormat SDL_MixAudioFormat_REAL
#define SDL_ConvertAudioSamples SDL_ConvertAudioSamples_REAL
#define SDL_GetSilenceValueForFormat SDL_GetSilenceValueForFormat_REAL
#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_ShowWindowSystemMenu SDL_ShowWindowSystemMenu_REAL
#define SDL_ReadS16LE SDL_ReadS16LE_REAL
#define SDL_ReadS16BE SDL_ReadS16BE_REAL
#define SDL_ReadS32LE SDL_ReadS32LE_REAL
#define SDL_ReadS32BE SDL_ReadS32BE_REAL
#define SDL_ReadS64LE SDL_ReadS64LE_REAL
#define SDL_ReadS64BE SDL_ReadS64BE_REAL
#define SDL_WriteS16LE SDL_WriteS16LE_REAL
#define SDL_WriteS16BE SDL_WriteS16BE_REAL
#define SDL_WriteS32LE SDL_WriteS32LE_REAL
#define SDL_WriteS32BE SDL_WriteS32BE_REAL
#define SDL_WriteS64LE SDL_WriteS64LE_REAL

View File

@ -119,7 +119,7 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_IsDeXMode,(void),(),return)
SDL_DYNAPI_PROC(void,SDL_AddEventWatch,(SDL_EventFilter a, void *b),(a,b),) SDL_DYNAPI_PROC(void,SDL_AddEventWatch,(SDL_EventFilter a, void *b),(a,b),)
SDL_DYNAPI_PROC(int,SDL_AddGamepadMapping,(const char *a),(a),return) SDL_DYNAPI_PROC(int,SDL_AddGamepadMapping,(const char *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_AddGamepadMappingsFromRW,(SDL_RWops *a, int b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_AddGamepadMappingsFromRW,(SDL_RWops *a, SDL_bool b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_AddHintCallback,(const char *a, SDL_HintCallback b, void *c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_AddHintCallback,(const char *a, SDL_HintCallback b, void *c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_TimerID,SDL_AddTimer,(Uint32 a, SDL_TimerCallback b, void *c),(a,b,c),return) SDL_DYNAPI_PROC(SDL_TimerID,SDL_AddTimer,(Uint32 a, SDL_TimerCallback b, void *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_AtomicAdd,(SDL_AtomicInt *a, int b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_AtomicAdd,(SDL_AtomicInt *a, int b),(a,b),return)
@ -141,22 +141,17 @@ SDL_DYNAPI_PROC(int,SDL_BlitSurfaceUncheckedScaled,(SDL_Surface *a, const SDL_Re
SDL_DYNAPI_PROC(int,SDL_BroadcastCondition,(SDL_Condition *a),(a),return) SDL_DYNAPI_PROC(int,SDL_BroadcastCondition,(SDL_Condition *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_CaptureMouse,(SDL_bool a),(a),return) SDL_DYNAPI_PROC(int,SDL_CaptureMouse,(SDL_bool a),(a),return)
SDL_DYNAPI_PROC(void,SDL_CleanupTLS,(void),(),) SDL_DYNAPI_PROC(void,SDL_CleanupTLS,(void),(),)
SDL_DYNAPI_PROC(int,SDL_ClearAudioStream,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(void,SDL_ClearComposition,(void),(),) SDL_DYNAPI_PROC(void,SDL_ClearComposition,(void),(),)
SDL_DYNAPI_PROC(void,SDL_ClearError,(void),(),) SDL_DYNAPI_PROC(void,SDL_ClearError,(void),(),)
SDL_DYNAPI_PROC(void,SDL_ClearHints,(void),(),) SDL_DYNAPI_PROC(void,SDL_ClearHints,(void),(),)
SDL_DYNAPI_PROC(int,SDL_ClearQueuedAudio,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(void,SDL_CloseAudioDevice,(SDL_AudioDeviceID a),(a),)
SDL_DYNAPI_PROC(void,SDL_CloseGamepad,(SDL_Gamepad *a),(a),) SDL_DYNAPI_PROC(void,SDL_CloseGamepad,(SDL_Gamepad *a),(a),)
SDL_DYNAPI_PROC(void,SDL_CloseJoystick,(SDL_Joystick *a),(a),) SDL_DYNAPI_PROC(void,SDL_CloseJoystick,(SDL_Joystick *a),(a),)
SDL_DYNAPI_PROC(void,SDL_CloseSensor,(SDL_Sensor *a),(a),) SDL_DYNAPI_PROC(void,SDL_CloseSensor,(SDL_Sensor *a),(a),)
SDL_DYNAPI_PROC(SDL_BlendMode,SDL_ComposeCustomBlendMode,(SDL_BlendFactor a, SDL_BlendFactor b, SDL_BlendOperation c, SDL_BlendFactor d, SDL_BlendFactor e, SDL_BlendOperation f),(a,b,c,d,e,f),return) SDL_DYNAPI_PROC(SDL_BlendMode,SDL_ComposeCustomBlendMode,(SDL_BlendFactor a, SDL_BlendFactor b, SDL_BlendOperation c, SDL_BlendFactor d, SDL_BlendFactor e, SDL_BlendOperation f),(a,b,c,d,e,f),return)
SDL_DYNAPI_PROC(int,SDL_ConvertAudioSamples,(SDL_AudioFormat a, Uint8 b, int c, const Uint8 *d, int e, SDL_AudioFormat f, Uint8 g, int h, Uint8 **i, int *j),(a,b,c,d,e,f,g,h,i,j),return)
SDL_DYNAPI_PROC(int,SDL_ConvertEventToRenderCoordinates,(SDL_Renderer *a, SDL_Event *b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_ConvertEventToRenderCoordinates,(SDL_Renderer *a, SDL_Event *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_ConvertPixels,(int a, int b, Uint32 c, const void *d, int e, Uint32 f, void *g, int h),(a,b,c,d,e,f,g,h),return) SDL_DYNAPI_PROC(int,SDL_ConvertPixels,(int a, int b, Uint32 c, const void *d, int e, Uint32 f, void *g, int h),(a,b,c,d,e,f,g,h),return)
SDL_DYNAPI_PROC(SDL_Surface*,SDL_ConvertSurface,(SDL_Surface *a, const SDL_PixelFormat *b),(a,b),return) SDL_DYNAPI_PROC(SDL_Surface*,SDL_ConvertSurface,(SDL_Surface *a, const SDL_PixelFormat *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_Surface*,SDL_ConvertSurfaceFormat,(SDL_Surface *a, Uint32 b),(a,b),return) SDL_DYNAPI_PROC(SDL_Surface*,SDL_ConvertSurfaceFormat,(SDL_Surface *a, Uint32 b),(a,b),return)
SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_CreateAudioStream,(SDL_AudioFormat a, int b, int c, SDL_AudioFormat d, int e, int f),(a,b,c,d,e,f),return)
SDL_DYNAPI_PROC(SDL_Cursor*,SDL_CreateColorCursor,(SDL_Surface *a, int b, int c),(a,b,c),return) SDL_DYNAPI_PROC(SDL_Cursor*,SDL_CreateColorCursor,(SDL_Surface *a, int b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_Condition*,SDL_CreateCondition,(void),(),return) SDL_DYNAPI_PROC(SDL_Condition*,SDL_CreateCondition,(void),(),return)
SDL_DYNAPI_PROC(SDL_Cursor*,SDL_CreateCursor,(const Uint8 *a, const Uint8 *b, int c, int d, int e, int f),(a,b,c,d,e,f),return) SDL_DYNAPI_PROC(SDL_Cursor*,SDL_CreateCursor,(const Uint8 *a, const Uint8 *b, int c, int d, int e, int f),(a,b,c,d,e,f),return)
@ -185,8 +180,6 @@ SDL_DYNAPI_PROC(void,SDL_DelEventWatch,(SDL_EventFilter a, void *b),(a,b),)
SDL_DYNAPI_PROC(void,SDL_DelHintCallback,(const char *a, SDL_HintCallback b, void *c),(a,b,c),) SDL_DYNAPI_PROC(void,SDL_DelHintCallback,(const char *a, SDL_HintCallback b, void *c),(a,b,c),)
SDL_DYNAPI_PROC(void,SDL_Delay,(Uint32 a),(a),) SDL_DYNAPI_PROC(void,SDL_Delay,(Uint32 a),(a),)
SDL_DYNAPI_PROC(void,SDL_DelayNS,(Uint64 a),(a),) SDL_DYNAPI_PROC(void,SDL_DelayNS,(Uint64 a),(a),)
SDL_DYNAPI_PROC(Uint32,SDL_DequeueAudio,(SDL_AudioDeviceID a, void *b, Uint32 c),(a,b,c),return)
SDL_DYNAPI_PROC(void,SDL_DestroyAudioStream,(SDL_AudioStream *a),(a),)
SDL_DYNAPI_PROC(void,SDL_DestroyCondition,(SDL_Condition *a),(a),) SDL_DYNAPI_PROC(void,SDL_DestroyCondition,(SDL_Condition *a),(a),)
SDL_DYNAPI_PROC(void,SDL_DestroyCursor,(SDL_Cursor *a),(a),) SDL_DYNAPI_PROC(void,SDL_DestroyCursor,(SDL_Cursor *a),(a),)
SDL_DYNAPI_PROC(void,SDL_DestroyMutex,(SDL_Mutex *a),(a),) SDL_DYNAPI_PROC(void,SDL_DestroyMutex,(SDL_Mutex *a),(a),)
@ -216,7 +209,6 @@ SDL_DYNAPI_PROC(int,SDL_FillSurfaceRect,(SDL_Surface *a, const SDL_Rect *b, Uint
SDL_DYNAPI_PROC(int,SDL_FillSurfaceRects,(SDL_Surface *a, const SDL_Rect *b, int c, Uint32 d),(a,b,c,d),return) SDL_DYNAPI_PROC(int,SDL_FillSurfaceRects,(SDL_Surface *a, const SDL_Rect *b, int c, Uint32 d),(a,b,c,d),return)
SDL_DYNAPI_PROC(void,SDL_FilterEvents,(SDL_EventFilter a, void *b),(a,b),) SDL_DYNAPI_PROC(void,SDL_FilterEvents,(SDL_EventFilter a, void *b),(a,b),)
SDL_DYNAPI_PROC(int,SDL_FlashWindow,(SDL_Window *a, SDL_FlashOperation b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_FlashWindow,(SDL_Window *a, SDL_FlashOperation b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_FlushAudioStream,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(void,SDL_FlushEvent,(Uint32 a),(a),) SDL_DYNAPI_PROC(void,SDL_FlushEvent,(Uint32 a),(a),)
SDL_DYNAPI_PROC(void,SDL_FlushEvents,(Uint32 a, Uint32 b),(a,b),) SDL_DYNAPI_PROC(void,SDL_FlushEvents,(Uint32 a, Uint32 b),(a,b),)
SDL_DYNAPI_PROC(int,SDL_GL_BindTexture,(SDL_Texture *a, float *b, float *c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_GL_BindTexture,(SDL_Texture *a, float *b, float *c),(a,b,c),return)
@ -249,27 +241,18 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadHasSensor,(SDL_Gamepad *a, SDL_SensorType b)
SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadSensorEnabled,(SDL_Gamepad *a, SDL_SensorType b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadSensorEnabled,(SDL_Gamepad *a, SDL_SensorType b),(a,b),return)
SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetAssertionHandler,(void **a),(a),return) SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetAssertionHandler,(void **a),(a),return)
SDL_DYNAPI_PROC(const SDL_AssertData*,SDL_GetAssertionReport,(void),(),return) SDL_DYNAPI_PROC(const SDL_AssertData*,SDL_GetAssertionReport,(void),(),return)
SDL_DYNAPI_PROC(const char*,SDL_GetAudioDeviceName,(int a, int b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceSpec,(int a, int b, SDL_AudioSpec *c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_AudioStatus,SDL_GetAudioDeviceStatus,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_GetAudioDriver,(int a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamAvailable,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamData,(SDL_AudioStream *a, void *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioFormat *b, int *c, int *d, SDL_AudioFormat *e, int *f, int *g),(a,b,c,d,e,f,g),return)
SDL_DYNAPI_PROC(char*,SDL_GetBasePath,(void),(),return) SDL_DYNAPI_PROC(char*,SDL_GetBasePath,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetCPUCacheLineSize,(void),(),return) SDL_DYNAPI_PROC(int,SDL_GetCPUCacheLineSize,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetCPUCount,(void),(),return) SDL_DYNAPI_PROC(int,SDL_GetCPUCount,(void),(),return)
SDL_DYNAPI_PROC(void*,SDL_GetClipboardData,(const char *a, size_t *b),(a,b),return) SDL_DYNAPI_PROC(void*,SDL_GetClipboardData,(const char *a, size_t *b),(a,b),return)
SDL_DYNAPI_PROC(char*,SDL_GetClipboardText,(void),(),return) SDL_DYNAPI_PROC(char*,SDL_GetClipboardText,(void),(),return)
SDL_DYNAPI_PROC(const SDL_DisplayMode*,SDL_GetClosestFullscreenDisplayMode,(SDL_DisplayID a, int b, int c, float d, SDL_bool e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(const SDL_DisplayMode*,SDL_GetClosestFullscreenDisplayMode,(SDL_DisplayID a, int b, int c, float d, SDL_bool e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(const char*,SDL_GetCurrentAudioDriver,(void),(),return)
SDL_DYNAPI_PROC(const SDL_DisplayMode*,SDL_GetCurrentDisplayMode,(SDL_DisplayID a),(a),return) SDL_DYNAPI_PROC(const SDL_DisplayMode*,SDL_GetCurrentDisplayMode,(SDL_DisplayID a),(a),return)
SDL_DYNAPI_PROC(SDL_DisplayOrientation,SDL_GetCurrentDisplayOrientation,(SDL_DisplayID a),(a),return) SDL_DYNAPI_PROC(SDL_DisplayOrientation,SDL_GetCurrentDisplayOrientation,(SDL_DisplayID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetCurrentRenderOutputSize,(SDL_Renderer *a, int *b, int *c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_GetCurrentRenderOutputSize,(SDL_Renderer *a, int *b, int *c),(a,b,c),return)
SDL_DYNAPI_PROC(const char*,SDL_GetCurrentVideoDriver,(void),(),return) SDL_DYNAPI_PROC(const char*,SDL_GetCurrentVideoDriver,(void),(),return)
SDL_DYNAPI_PROC(SDL_Cursor*,SDL_GetCursor,(void),(),return) SDL_DYNAPI_PROC(SDL_Cursor*,SDL_GetCursor,(void),(),return)
SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetDefaultAssertionHandler,(void),(),return) SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetDefaultAssertionHandler,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetDefaultAudioInfo,(char **a, SDL_AudioSpec *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_Cursor*,SDL_GetDefaultCursor,(void),(),return) SDL_DYNAPI_PROC(SDL_Cursor*,SDL_GetDefaultCursor,(void),(),return)
SDL_DYNAPI_PROC(const SDL_DisplayMode*,SDL_GetDesktopDisplayMode,(SDL_DisplayID a),(a),return) SDL_DYNAPI_PROC(const SDL_DisplayMode*,SDL_GetDesktopDisplayMode,(SDL_DisplayID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetDisplayBounds,(SDL_DisplayID a, SDL_Rect *b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_GetDisplayBounds,(SDL_DisplayID a, SDL_Rect *b),(a,b),return)
@ -366,8 +349,6 @@ SDL_DYNAPI_PROC(SDL_Window*,SDL_GetMouseFocus,(void),(),return)
SDL_DYNAPI_PROC(Uint32,SDL_GetMouseState,(float *a, float *b),(a,b),return) SDL_DYNAPI_PROC(Uint32,SDL_GetMouseState,(float *a, float *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_DisplayOrientation,SDL_GetNaturalDisplayOrientation,(SDL_DisplayID a),(a),return) SDL_DYNAPI_PROC(SDL_DisplayOrientation,SDL_GetNaturalDisplayOrientation,(SDL_DisplayID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetNumAllocations,(void),(),return) SDL_DYNAPI_PROC(int,SDL_GetNumAllocations,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetNumAudioDevices,(int a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetNumAudioDrivers,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetNumGamepadMappings,(void),(),return) SDL_DYNAPI_PROC(int,SDL_GetNumGamepadMappings,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetNumGamepadTouchpadFingers,(SDL_Gamepad *a, int b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_GetNumGamepadTouchpadFingers,(SDL_Gamepad *a, int b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GetNumGamepadTouchpads,(SDL_Gamepad *a),(a),return) SDL_DYNAPI_PROC(int,SDL_GetNumGamepadTouchpads,(SDL_Gamepad *a),(a),return)
@ -379,7 +360,7 @@ SDL_DYNAPI_PROC(int,SDL_GetNumTouchDevices,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetNumTouchFingers,(SDL_TouchID a),(a),return) SDL_DYNAPI_PROC(int,SDL_GetNumTouchFingers,(SDL_TouchID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetNumVideoDrivers,(void),(),return) SDL_DYNAPI_PROC(int,SDL_GetNumVideoDrivers,(void),(),return)
SDL_DYNAPI_PROC(void,SDL_GetOriginalMemoryFunctions,(SDL_malloc_func *a, SDL_calloc_func *b, SDL_realloc_func *c, SDL_free_func *d),(a,b,c,d),) SDL_DYNAPI_PROC(void,SDL_GetOriginalMemoryFunctions,(SDL_malloc_func *a, SDL_calloc_func *b, SDL_realloc_func *c, SDL_free_func *d),(a,b,c,d),)
SDL_DYNAPI_PROC(char*,SDL_GetPath,(SDL_Folder a),(a),return) SDL_DYNAPI_PROC(char*,SDL_GetUserFolder,(SDL_Folder a),(a),return)
SDL_DYNAPI_PROC(Uint64,SDL_GetPerformanceCounter,(void),(),return) SDL_DYNAPI_PROC(Uint64,SDL_GetPerformanceCounter,(void),(),return)
SDL_DYNAPI_PROC(Uint64,SDL_GetPerformanceFrequency,(void),(),return) SDL_DYNAPI_PROC(Uint64,SDL_GetPerformanceFrequency,(void),(),return)
SDL_DYNAPI_PROC(Uint32,SDL_GetPixelFormatEnumForMasks,(int a, Uint32 b, Uint32 c, Uint32 d, Uint32 e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(Uint32,SDL_GetPixelFormatEnumForMasks,(int a, Uint32 b, Uint32 c, Uint32 d, Uint32 e),(a,b,c,d,e),return)
@ -390,7 +371,6 @@ SDL_DYNAPI_PROC(char*,SDL_GetPrefPath,(const char *a, const char *b),(a,b),retur
SDL_DYNAPI_PROC(SDL_Locale*,SDL_GetPreferredLocales,(void),(),return) SDL_DYNAPI_PROC(SDL_Locale*,SDL_GetPreferredLocales,(void),(),return)
SDL_DYNAPI_PROC(SDL_DisplayID,SDL_GetPrimaryDisplay,(void),(),return) SDL_DYNAPI_PROC(SDL_DisplayID,SDL_GetPrimaryDisplay,(void),(),return)
SDL_DYNAPI_PROC(char*,SDL_GetPrimarySelectionText,(void),(),return) SDL_DYNAPI_PROC(char*,SDL_GetPrimarySelectionText,(void),(),return)
SDL_DYNAPI_PROC(Uint32,SDL_GetQueuedAudioSize,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(void,SDL_GetRGB,(Uint32 a, const SDL_PixelFormat *b, Uint8 *c, Uint8 *d, Uint8 *e),(a,b,c,d,e),) SDL_DYNAPI_PROC(void,SDL_GetRGB,(Uint32 a, const SDL_PixelFormat *b, Uint8 *c, Uint8 *d, Uint8 *e),(a,b,c,d,e),)
SDL_DYNAPI_PROC(void,SDL_GetRGBA,(Uint32 a, const SDL_PixelFormat *b, Uint8 *c, Uint8 *d, Uint8 *e, Uint8 *f),(a,b,c,d,e,f),) SDL_DYNAPI_PROC(void,SDL_GetRGBA,(Uint32 a, const SDL_PixelFormat *b, Uint8 *c, Uint8 *d, Uint8 *e, Uint8 *f),(a,b,c,d,e,f),)
SDL_DYNAPI_PROC(SDL_bool,SDL_GetRectAndLineIntersection,(const SDL_Rect *a, int *b, int *c, int *d, int *e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(SDL_bool,SDL_GetRectAndLineIntersection,(const SDL_Rect *a, int *b, int *c, int *d, int *e),(a,b,c,d,e),return)
@ -553,8 +533,6 @@ SDL_DYNAPI_PROC(void*,SDL_LoadFile,(const char *a, size_t *b),(a,b),return)
SDL_DYNAPI_PROC(void*,SDL_LoadFile_RW,(SDL_RWops *a, size_t *b, SDL_bool c),(a,b,c),return) SDL_DYNAPI_PROC(void*,SDL_LoadFile_RW,(SDL_RWops *a, size_t *b, SDL_bool c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_FunctionPointer,SDL_LoadFunction,(void *a, const char *b),(a,b),return) SDL_DYNAPI_PROC(SDL_FunctionPointer,SDL_LoadFunction,(void *a, const char *b),(a,b),return)
SDL_DYNAPI_PROC(void*,SDL_LoadObject,(const char *a),(a),return) SDL_DYNAPI_PROC(void*,SDL_LoadObject,(const char *a),(a),return)
SDL_DYNAPI_PROC(SDL_AudioSpec*,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_LockAudioDevice,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(void,SDL_LockJoysticks,(void),(),) SDL_DYNAPI_PROC(void,SDL_LockJoysticks,(void),(),)
SDL_DYNAPI_PROC(int,SDL_LockMutex,(SDL_Mutex *a),(a),return) SDL_DYNAPI_PROC(int,SDL_LockMutex,(SDL_Mutex *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_LockRWLockForReading,(SDL_RWLock *a),(a),return) SDL_DYNAPI_PROC(int,SDL_LockRWLockForReading,(SDL_RWLock *a),(a),return)
@ -578,7 +556,6 @@ SDL_DYNAPI_PROC(SDL_MetalView,SDL_Metal_CreateView,(SDL_Window *a),(a),return)
SDL_DYNAPI_PROC(void,SDL_Metal_DestroyView,(SDL_MetalView a),(a),) SDL_DYNAPI_PROC(void,SDL_Metal_DestroyView,(SDL_MetalView a),(a),)
SDL_DYNAPI_PROC(void*,SDL_Metal_GetLayer,(SDL_MetalView a),(a),return) SDL_DYNAPI_PROC(void*,SDL_Metal_GetLayer,(SDL_MetalView a),(a),return)
SDL_DYNAPI_PROC(int,SDL_MinimizeWindow,(SDL_Window *a),(a),return) SDL_DYNAPI_PROC(int,SDL_MinimizeWindow,(SDL_Window *a),(a),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_MouseIsHaptic,(void),(),return) SDL_DYNAPI_PROC(int,SDL_MouseIsHaptic,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_NumHaptics,(void),(),return) SDL_DYNAPI_PROC(int,SDL_NumHaptics,(void),(),return)
SDL_DYNAPI_PROC(void,SDL_OnApplicationDidBecomeActive,(void),(),) SDL_DYNAPI_PROC(void,SDL_OnApplicationDidBecomeActive,(void),(),)
@ -587,41 +564,36 @@ SDL_DYNAPI_PROC(void,SDL_OnApplicationDidReceiveMemoryWarning,(void),(),)
SDL_DYNAPI_PROC(void,SDL_OnApplicationWillEnterForeground,(void),(),) SDL_DYNAPI_PROC(void,SDL_OnApplicationWillEnterForeground,(void),(),)
SDL_DYNAPI_PROC(void,SDL_OnApplicationWillResignActive,(void),(),) SDL_DYNAPI_PROC(void,SDL_OnApplicationWillResignActive,(void),(),)
SDL_DYNAPI_PROC(void,SDL_OnApplicationWillTerminate,(void),(),) SDL_DYNAPI_PROC(void,SDL_OnApplicationWillTerminate,(void),(),)
SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_OpenAudioDevice,(const char *a, int b, const SDL_AudioSpec *c, SDL_AudioSpec *d, int e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(SDL_Gamepad*,SDL_OpenGamepad,(SDL_JoystickID a),(a),return) SDL_DYNAPI_PROC(SDL_Gamepad*,SDL_OpenGamepad,(SDL_JoystickID a),(a),return)
SDL_DYNAPI_PROC(SDL_Joystick*,SDL_OpenJoystick,(SDL_JoystickID a),(a),return) SDL_DYNAPI_PROC(SDL_Joystick*,SDL_OpenJoystick,(SDL_JoystickID a),(a),return)
SDL_DYNAPI_PROC(SDL_Sensor*,SDL_OpenSensor,(SDL_SensorID a),(a),return) SDL_DYNAPI_PROC(SDL_Sensor*,SDL_OpenSensor,(SDL_SensorID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_OpenURL,(const char *a),(a),return) SDL_DYNAPI_PROC(int,SDL_OpenURL,(const char *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_PauseAudioDevice,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_PeepEvents,(SDL_Event *a, int b, SDL_eventaction c, Uint32 d, Uint32 e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(int,SDL_PeepEvents,(SDL_Event *a, int b, SDL_eventaction c, Uint32 d, Uint32 e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_PlayAudioDevice,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_PollEvent,(SDL_Event *a),(a),return) SDL_DYNAPI_PROC(int,SDL_PollEvent,(SDL_Event *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_PostSemaphore,(SDL_Semaphore *a),(a),return) SDL_DYNAPI_PROC(int,SDL_PostSemaphore,(SDL_Semaphore *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_PremultiplyAlpha,(int a, int b, Uint32 c, const void *d, int e, Uint32 f, void *g, int h),(a,b,c,d,e,f,g,h),return) SDL_DYNAPI_PROC(int,SDL_PremultiplyAlpha,(int a, int b, Uint32 c, const void *d, int e, Uint32 f, void *g, int h),(a,b,c,d,e,f,g,h),return)
SDL_DYNAPI_PROC(void,SDL_PumpEvents,(void),(),) SDL_DYNAPI_PROC(void,SDL_PumpEvents,(void),(),)
SDL_DYNAPI_PROC(int,SDL_PushEvent,(SDL_Event *a),(a),return) SDL_DYNAPI_PROC(int,SDL_PushEvent,(SDL_Event *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_PutAudioStreamData,(SDL_AudioStream *a, const void *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_QueryTexture,(SDL_Texture *a, Uint32 *b, int *c, int *d, int *e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(int,SDL_QueryTexture,(SDL_Texture *a, Uint32 *b, int *c, int *d, int *e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_QueueAudio,(SDL_AudioDeviceID a, const void *b, Uint32 c),(a,b,c),return)
SDL_DYNAPI_PROC(void,SDL_Quit,(void),(),) SDL_DYNAPI_PROC(void,SDL_Quit,(void),(),)
SDL_DYNAPI_PROC(void,SDL_QuitSubSystem,(Uint32 a),(a),) SDL_DYNAPI_PROC(void,SDL_QuitSubSystem,(Uint32 a),(a),)
SDL_DYNAPI_PROC(SDL_RWops*,SDL_RWFromConstMem,(const void *a, size_t b),(a,b),return) SDL_DYNAPI_PROC(SDL_RWops*,SDL_RWFromConstMem,(const void *a, size_t b),(a,b),return)
SDL_DYNAPI_PROC(SDL_RWops*,SDL_RWFromFile,(const char *a, const char *b),(a,b),return) SDL_DYNAPI_PROC(SDL_RWops*,SDL_RWFromFile,(const char *a, const char *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_RWops*,SDL_RWFromMem,(void *a, size_t b),(a,b),return) SDL_DYNAPI_PROC(SDL_RWops*,SDL_RWFromMem,(void *a, size_t b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_RWclose,(SDL_RWops *a),(a),return) SDL_DYNAPI_PROC(int,SDL_RWclose,(SDL_RWops *a),(a),return)
SDL_DYNAPI_PROC(Sint64,SDL_RWread,(SDL_RWops *a, void *b, Sint64 c),(a,b,c),return) SDL_DYNAPI_PROC(size_t,SDL_RWread,(SDL_RWops *a, void *b, size_t c),(a,b,c),return)
SDL_DYNAPI_PROC(Sint64,SDL_RWseek,(SDL_RWops *a, Sint64 b, int c),(a,b,c),return) SDL_DYNAPI_PROC(Sint64,SDL_RWseek,(SDL_RWops *a, Sint64 b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(Sint64,SDL_RWsize,(SDL_RWops *a),(a),return) SDL_DYNAPI_PROC(Sint64,SDL_RWsize,(SDL_RWops *a),(a),return)
SDL_DYNAPI_PROC(Sint64,SDL_RWtell,(SDL_RWops *a),(a),return) SDL_DYNAPI_PROC(Sint64,SDL_RWtell,(SDL_RWops *a),(a),return)
SDL_DYNAPI_PROC(Sint64,SDL_RWwrite,(SDL_RWops *a, const void *b, Sint64 c),(a,b,c),return) SDL_DYNAPI_PROC(size_t,SDL_RWwrite,(SDL_RWops *a, const void *b, size_t c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_RaiseWindow,(SDL_Window *a),(a),return) SDL_DYNAPI_PROC(int,SDL_RaiseWindow,(SDL_Window *a),(a),return)
SDL_DYNAPI_PROC(Uint16,SDL_ReadBE16,(SDL_RWops *a),(a),return) SDL_DYNAPI_PROC(SDL_bool,SDL_ReadU16BE,(SDL_RWops *a, Uint16 *b),(a,b),return)
SDL_DYNAPI_PROC(Uint32,SDL_ReadBE32,(SDL_RWops *a),(a),return) SDL_DYNAPI_PROC(SDL_bool,SDL_ReadU32BE,(SDL_RWops *a, Uint32 *b),(a,b),return)
SDL_DYNAPI_PROC(Uint64,SDL_ReadBE64,(SDL_RWops *a),(a),return) SDL_DYNAPI_PROC(SDL_bool,SDL_ReadU64BE,(SDL_RWops *a, Uint64 *b),(a,b),return)
SDL_DYNAPI_PROC(Uint16,SDL_ReadLE16,(SDL_RWops *a),(a),return) SDL_DYNAPI_PROC(SDL_bool,SDL_ReadU16LE,(SDL_RWops *a, Uint16 *b),(a,b),return)
SDL_DYNAPI_PROC(Uint32,SDL_ReadLE32,(SDL_RWops *a),(a),return) SDL_DYNAPI_PROC(SDL_bool,SDL_ReadU32LE,(SDL_RWops *a, Uint32 *b),(a,b),return)
SDL_DYNAPI_PROC(Uint64,SDL_ReadLE64,(SDL_RWops *a),(a),return) SDL_DYNAPI_PROC(SDL_bool,SDL_ReadU64LE,(SDL_RWops *a, Uint64 *b),(a,b),return)
SDL_DYNAPI_PROC(Uint8,SDL_ReadU8,(SDL_RWops *a),(a),return) SDL_DYNAPI_PROC(SDL_bool,SDL_ReadU8,(SDL_RWops *a, Uint8 *b),(a,b),return)
SDL_DYNAPI_PROC(Uint32,SDL_RegisterEvents,(int a),(a),return) SDL_DYNAPI_PROC(Uint32,SDL_RegisterEvents,(int a),(a),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_RemoveTimer,(SDL_TimerID a),(a),return) SDL_DYNAPI_PROC(SDL_bool,SDL_RemoveTimer,(SDL_TimerID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_RenderClear,(SDL_Renderer *a),(a),return) SDL_DYNAPI_PROC(int,SDL_RenderClear,(SDL_Renderer *a),(a),return)
@ -656,13 +628,12 @@ SDL_DYNAPI_PROC(int,SDL_RumbleJoystickTriggers,(SDL_Joystick *a, Uint16 b, Uint1
SDL_DYNAPI_PROC(int,SDL_RunApp,(int a, char *b[], SDL_main_func c, void *d),(a,b,c,d),return) SDL_DYNAPI_PROC(int,SDL_RunApp,(int a, char *b[], SDL_main_func c, void *d),(a,b,c,d),return)
SDL_DYNAPI_PROC(size_t,SDL_SIMDGetAlignment,(void),(),return) SDL_DYNAPI_PROC(size_t,SDL_SIMDGetAlignment,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_SaveBMP,(SDL_Surface *a, const char *b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_SaveBMP,(SDL_Surface *a, const char *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_SaveBMP_RW,(SDL_Surface *a, SDL_RWops *b, int c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_SaveBMP_RW,(SDL_Surface *a, SDL_RWops *b, SDL_bool c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_ScreenKeyboardShown,(SDL_Window *a),(a),return) SDL_DYNAPI_PROC(SDL_bool,SDL_ScreenKeyboardShown,(SDL_Window *a),(a),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_ScreenSaverEnabled,(void),(),return) SDL_DYNAPI_PROC(SDL_bool,SDL_ScreenSaverEnabled,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_SendGamepadEffect,(SDL_Gamepad *a, const void *b, int c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_SendGamepadEffect,(SDL_Gamepad *a, const void *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SendJoystickEffect,(SDL_Joystick *a, const void *b, int c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_SendJoystickEffect,(SDL_Joystick *a, const void *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(void,SDL_SetAssertionHandler,(SDL_AssertionHandler a, void *b),(a,b),) SDL_DYNAPI_PROC(void,SDL_SetAssertionHandler,(SDL_AssertionHandler a, void *b),(a,b),)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioFormat b, int c, int d, SDL_AudioFormat e, int f, int g),(a,b,c,d,e,f,g),return)
SDL_DYNAPI_PROC(int,SDL_SetClipboardData,(SDL_ClipboardDataCallback a, SDL_ClipboardCleanupCallback b, void *c, const char **d, size_t e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(int,SDL_SetClipboardData,(SDL_ClipboardDataCallback a, SDL_ClipboardCleanupCallback b, void *c, const char **d, size_t e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_SetClipboardText,(const char *a),(a),return) SDL_DYNAPI_PROC(int,SDL_SetClipboardText,(const char *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_SetCursor,(SDL_Cursor *a),(a),return) SDL_DYNAPI_PROC(int,SDL_SetCursor,(SDL_Cursor *a),(a),return)
@ -751,7 +722,6 @@ SDL_DYNAPI_PROC(int,SDL_TryLockRWLockForReading,(SDL_RWLock *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_TryLockRWLockForWriting,(SDL_RWLock *a),(a),return) SDL_DYNAPI_PROC(int,SDL_TryLockRWLockForWriting,(SDL_RWLock *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_TryWaitSemaphore,(SDL_Semaphore *a),(a),return) SDL_DYNAPI_PROC(int,SDL_TryWaitSemaphore,(SDL_Semaphore *a),(a),return)
SDL_DYNAPI_PROC(void,SDL_UnloadObject,(void *a),(a),) SDL_DYNAPI_PROC(void,SDL_UnloadObject,(void *a),(a),)
SDL_DYNAPI_PROC(void,SDL_UnlockAudioDevice,(SDL_AudioDeviceID a),(a),)
SDL_DYNAPI_PROC(void,SDL_UnlockJoysticks,(void),(),) SDL_DYNAPI_PROC(void,SDL_UnlockJoysticks,(void),(),)
SDL_DYNAPI_PROC(int,SDL_UnlockMutex,(SDL_Mutex *a),(a),return) SDL_DYNAPI_PROC(int,SDL_UnlockMutex,(SDL_Mutex *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_UnlockRWLock,(SDL_RWLock *a),(a),return) SDL_DYNAPI_PROC(int,SDL_UnlockRWLock,(SDL_RWLock *a),(a),return)
@ -780,13 +750,13 @@ SDL_DYNAPI_PROC(void,SDL_WaitThread,(SDL_Thread *a, int *b),(a,b),)
SDL_DYNAPI_PROC(int,SDL_WarpMouseGlobal,(float a, float b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_WarpMouseGlobal,(float a, float b),(a,b),return)
SDL_DYNAPI_PROC(void,SDL_WarpMouseInWindow,(SDL_Window *a, float b, float c),(a,b,c),) SDL_DYNAPI_PROC(void,SDL_WarpMouseInWindow,(SDL_Window *a, float b, float c),(a,b,c),)
SDL_DYNAPI_PROC(Uint32,SDL_WasInit,(Uint32 a),(a),return) SDL_DYNAPI_PROC(Uint32,SDL_WasInit,(Uint32 a),(a),return)
SDL_DYNAPI_PROC(size_t,SDL_WriteBE16,(SDL_RWops *a, Uint16 b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU16BE,(SDL_RWops *a, Uint16 b),(a,b),return)
SDL_DYNAPI_PROC(size_t,SDL_WriteBE32,(SDL_RWops *a, Uint32 b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU32BE,(SDL_RWops *a, Uint32 b),(a,b),return)
SDL_DYNAPI_PROC(size_t,SDL_WriteBE64,(SDL_RWops *a, Uint64 b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU64BE,(SDL_RWops *a, Uint64 b),(a,b),return)
SDL_DYNAPI_PROC(size_t,SDL_WriteLE16,(SDL_RWops *a, Uint16 b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU16LE,(SDL_RWops *a, Uint16 b),(a,b),return)
SDL_DYNAPI_PROC(size_t,SDL_WriteLE32,(SDL_RWops *a, Uint32 b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU32LE,(SDL_RWops *a, Uint32 b),(a,b),return)
SDL_DYNAPI_PROC(size_t,SDL_WriteLE64,(SDL_RWops *a, Uint64 b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU64LE,(SDL_RWops *a, Uint64 b),(a,b),return)
SDL_DYNAPI_PROC(size_t,SDL_WriteU8,(SDL_RWops *a, Uint8 b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU8,(SDL_RWops *a, Uint8 b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_abs,(int a),(a),return) SDL_DYNAPI_PROC(int,SDL_abs,(int a),(a),return)
SDL_DYNAPI_PROC(double,SDL_acos,(double a),(a),return) SDL_DYNAPI_PROC(double,SDL_acos,(double a),(a),return)
SDL_DYNAPI_PROC(float,SDL_acosf,(float a),(a),return) SDL_DYNAPI_PROC(float,SDL_acosf,(float a),(a),return)
@ -950,3 +920,51 @@ SDL_DYNAPI_PROC(size_t,SDL_wcsnlen,(const wchar_t *a, size_t b),(a,b),return)
SDL_DYNAPI_PROC(size_t,SDL_strnlen,(const char *a, size_t b),(a,b),return) SDL_DYNAPI_PROC(size_t,SDL_strnlen,(const char *a, size_t b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_AddGamepadMappingsFromFile,(const char *a),(a),return) SDL_DYNAPI_PROC(int,SDL_AddGamepadMappingsFromFile,(const char *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_ReloadGamepadMappings,(void),(),return) SDL_DYNAPI_PROC(int,SDL_ReloadGamepadMappings,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetNumAudioDrivers,(void),(),return)
SDL_DYNAPI_PROC(const char*,SDL_GetAudioDriver,(int a),(a),return)
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(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)
SDL_DYNAPI_PROC(int,SDL_BindAudioStream,(SDL_AudioDeviceID a, SDL_AudioStream *b),(a,b),return)
SDL_DYNAPI_PROC(void,SDL_UnbindAudioStreams,(SDL_AudioStream **a, int b),(a,b),)
SDL_DYNAPI_PROC(void,SDL_UnbindAudioStream,(SDL_AudioStream *a),(a),)
SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_CreateAudioStream,(const SDL_AudioSpec *a, const SDL_AudioSpec *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioSpec *b, SDL_AudioSpec *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamFormat,(SDL_AudioStream *a, const SDL_AudioSpec *b, const SDL_AudioSpec *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_PutAudioStreamData,(SDL_AudioStream *a, const void *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamData,(SDL_AudioStream *a, void *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamAvailable,(SDL_AudioStream *a),(a),return)
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(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(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)
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(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)
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadS32LE,(SDL_RWops *a, Sint32 *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadS32BE,(SDL_RWops *a, Sint32 *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadS64LE,(SDL_RWops *a, Sint64 *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadS64BE,(SDL_RWops *a, Sint64 *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS16LE,(SDL_RWops *a, Sint16 b),(a,b),return)
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)

View File

@ -580,8 +580,6 @@ int SDL_StartEventLoop(void)
SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, SDL_FALSE); SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, SDL_FALSE);
SDL_SetEventEnabled(SDL_EVENT_DROP_TEXT, SDL_FALSE); SDL_SetEventEnabled(SDL_EVENT_DROP_TEXT, SDL_FALSE);
#endif #endif
SDL_SetEventEnabled(SDL_EVENT_JOYSTICK_UPDATE_COMPLETE, SDL_FALSE);
SDL_SetEventEnabled(SDL_EVENT_GAMEPAD_UPDATE_COMPLETE, SDL_FALSE);
SDL_EventQ.active = SDL_TRUE; SDL_EventQ.active = SDL_TRUE;
SDL_UnlockMutex(SDL_EventQ.lock); SDL_UnlockMutex(SDL_EventQ.lock);

View File

@ -33,8 +33,9 @@
typedef enum typedef enum
{ {
KEYBOARD_HARDWARE = 0x01, KEYBOARD_HARDWARE = 0x01,
KEYBOARD_AUTORELEASE = 0x02, KEYBOARD_VIRTUAL = 0x02,
KEYBOARD_IGNOREMODIFIERS = 0x04 KEYBOARD_AUTORELEASE = 0x04,
KEYBOARD_IGNOREMODIFIERS = 0x08
} SDL_KeyboardFlags; } SDL_KeyboardFlags;
#define KEYBOARD_SOURCE_MASK (KEYBOARD_HARDWARE | KEYBOARD_AUTORELEASE) #define KEYBOARD_SOURCE_MASK (KEYBOARD_HARDWARE | KEYBOARD_AUTORELEASE)
@ -50,6 +51,7 @@ struct SDL_Keyboard
Uint8 keystate[SDL_NUM_SCANCODES]; Uint8 keystate[SDL_NUM_SCANCODES];
SDL_Keycode keymap[SDL_NUM_SCANCODES]; SDL_Keycode keymap[SDL_NUM_SCANCODES];
SDL_bool autorelease_pending; SDL_bool autorelease_pending;
Uint64 hardware_timestamp;
}; };
static SDL_Keyboard SDL_keyboard; static SDL_Keyboard SDL_keyboard;
@ -875,7 +877,9 @@ static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, SDL_KeyboardFlags flags
keycode = keyboard->keymap[scancode]; keycode = keyboard->keymap[scancode];
} }
if (source == KEYBOARD_AUTORELEASE) { if (source == KEYBOARD_HARDWARE) {
keyboard->hardware_timestamp = SDL_GetTicks();
} else if (source == KEYBOARD_AUTORELEASE) {
keyboard->autorelease_pending = SDL_TRUE; keyboard->autorelease_pending = SDL_TRUE;
} }
@ -978,20 +982,25 @@ int SDL_SendKeyboardUnicodeKey(Uint64 timestamp, Uint32 ch)
if (mod & SDL_KMOD_SHIFT) { if (mod & SDL_KMOD_SHIFT) {
/* If the character uses shift, press shift down */ /* If the character uses shift, press shift down */
SDL_SendKeyboardKey(timestamp, SDL_PRESSED, SDL_SCANCODE_LSHIFT); SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_PRESSED, SDL_SCANCODE_LSHIFT, SDLK_UNKNOWN);
} }
/* Send a keydown and keyup for the character */ /* Send a keydown and keyup for the character */
SDL_SendKeyboardKey(timestamp, SDL_PRESSED, code); SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_PRESSED, code, SDLK_UNKNOWN);
SDL_SendKeyboardKey(timestamp, SDL_RELEASED, code); SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_RELEASED, code, SDLK_UNKNOWN);
if (mod & SDL_KMOD_SHIFT) { if (mod & SDL_KMOD_SHIFT) {
/* If the character uses shift, release shift */ /* If the character uses shift, release shift */
SDL_SendKeyboardKey(timestamp, SDL_RELEASED, SDL_SCANCODE_LSHIFT); SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_RELEASED, SDL_SCANCODE_LSHIFT, SDLK_UNKNOWN);
} }
return 0; return 0;
} }
int SDL_SendVirtualKeyboardKey(Uint64 timestamp, Uint8 state, SDL_Scancode scancode)
{
return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, state, scancode, SDLK_UNKNOWN);
}
int SDL_SendKeyboardKey(Uint64 timestamp, Uint8 state, SDL_Scancode scancode) int SDL_SendKeyboardKey(Uint64 timestamp, Uint8 state, SDL_Scancode scancode)
{ {
return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, state, scancode, SDLK_UNKNOWN); return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, state, scancode, SDLK_UNKNOWN);
@ -1025,6 +1034,13 @@ void SDL_ReleaseAutoReleaseKeys(void)
} }
keyboard->autorelease_pending = SDL_FALSE; keyboard->autorelease_pending = SDL_FALSE;
} }
if (keyboard->hardware_timestamp) {
/* Keep hardware keyboard "active" for 250 ms */
if (SDL_GetTicks() >= keyboard->hardware_timestamp + 250) {
keyboard->hardware_timestamp = 0;
}
}
} }
SDL_bool SDL_HardwareKeyboardKeyPressed(void) SDL_bool SDL_HardwareKeyboardKeyPressed(void)
@ -1037,7 +1053,8 @@ SDL_bool SDL_HardwareKeyboardKeyPressed(void)
return SDL_TRUE; return SDL_TRUE;
} }
} }
return SDL_FALSE;
return keyboard->hardware_timestamp ? SDL_TRUE : SDL_FALSE;
} }
int SDL_SendKeyboardText(const char *text) int SDL_SendKeyboardText(const char *text)

View File

@ -49,6 +49,9 @@ extern int SDL_SetKeyboardFocus(SDL_Window *window);
*/ */
extern int SDL_SendKeyboardUnicodeKey(Uint64 timestamp, Uint32 ch); extern int SDL_SendKeyboardUnicodeKey(Uint64 timestamp, Uint32 ch);
/* Send a key from a virtual key source, like an on-screen keyboard */
extern int SDL_SendVirtualKeyboardKey(Uint64 timestamp, Uint8 state, SDL_Scancode scancode);
/* Send a keyboard key event */ /* Send a keyboard key event */
extern int SDL_SendKeyboardKey(Uint64 timestamp, Uint8 state, SDL_Scancode scancode); extern int SDL_SendKeyboardKey(Uint64 timestamp, Uint8 state, SDL_Scancode scancode);
extern int SDL_SendKeyboardKeyAutoRelease(Uint64 timestamp, SDL_Scancode scancode); extern int SDL_SendKeyboardKeyAutoRelease(Uint64 timestamp, SDL_Scancode scancode);

View File

@ -162,7 +162,7 @@ static void SDLCALL SDL_MouseRelativeWarpMotionChanged(void *userdata, const cha
} }
/* Public functions */ /* Public functions */
int SDL_InitMouse(void) int SDL_PreInitMouse(void)
{ {
SDL_Mouse *mouse = SDL_GetMouse(); SDL_Mouse *mouse = SDL_GetMouse();
@ -203,8 +203,17 @@ int SDL_InitMouse(void)
mouse->was_touch_mouse_events = SDL_FALSE; /* no touch to mouse movement event pending */ mouse->was_touch_mouse_events = SDL_FALSE; /* no touch to mouse movement event pending */
mouse->cursor_shown = SDL_TRUE; mouse->cursor_shown = SDL_TRUE;
return 0;
}
if (!mouse->CreateCursor) { void SDL_PostInitMouse(void)
{
SDL_Mouse *mouse = SDL_GetMouse();
/* Create a dummy mouse cursor for video backends that don't support true cursors,
* so that mouse grab and focus functionality will work.
*/
if (!mouse->def_cursor) {
SDL_Surface *surface = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_ARGB8888); SDL_Surface *surface = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_ARGB8888);
if (surface) { if (surface) {
SDL_memset(surface->pixels, 0, (size_t)surface->h * surface->pitch); SDL_memset(surface->pixels, 0, (size_t)surface->h * surface->pitch);
@ -212,14 +221,29 @@ int SDL_InitMouse(void)
SDL_DestroySurface(surface); SDL_DestroySurface(surface);
} }
} }
return 0;
} }
void SDL_SetDefaultCursor(SDL_Cursor *cursor) void SDL_SetDefaultCursor(SDL_Cursor *cursor)
{ {
SDL_Mouse *mouse = SDL_GetMouse(); SDL_Mouse *mouse = SDL_GetMouse();
if (cursor == mouse->def_cursor) {
return;
}
if (mouse->def_cursor) {
SDL_Cursor *default_cursor = mouse->def_cursor;
if (mouse->cur_cursor == mouse->def_cursor) {
mouse->cur_cursor = NULL;
}
mouse->def_cursor = NULL;
SDL_DestroyCursor(default_cursor);
}
mouse->def_cursor = cursor; mouse->def_cursor = cursor;
if (!mouse->cur_cursor) { if (!mouse->cur_cursor) {
SDL_SetCursor(cursor); SDL_SetCursor(cursor);
} }
@ -845,6 +869,10 @@ void SDL_QuitMouse(void)
SDL_SetRelativeMouseMode(SDL_FALSE); SDL_SetRelativeMouseMode(SDL_FALSE);
SDL_ShowCursor(); SDL_ShowCursor();
if (mouse->def_cursor) {
SDL_SetDefaultCursor(NULL);
}
cursor = mouse->cursors; cursor = mouse->cursors;
while (cursor) { while (cursor) {
next = cursor->next; next = cursor->next;
@ -854,15 +882,6 @@ void SDL_QuitMouse(void)
mouse->cursors = NULL; mouse->cursors = NULL;
mouse->cur_cursor = NULL; mouse->cur_cursor = NULL;
if (mouse->def_cursor) {
if (mouse->FreeCursor) {
mouse->FreeCursor(mouse->def_cursor);
} else {
SDL_free(mouse->def_cursor);
}
mouse->def_cursor = NULL;
}
if (mouse->sources) { if (mouse->sources) {
SDL_free(mouse->sources); SDL_free(mouse->sources);
mouse->sources = NULL; mouse->sources = NULL;
@ -1377,7 +1396,7 @@ void SDL_DestroyCursor(SDL_Cursor *cursor)
mouse->cursors = curr->next; mouse->cursors = curr->next;
} }
if (mouse->FreeCursor) { if (mouse->FreeCursor && curr->driverdata) {
mouse->FreeCursor(curr); mouse->FreeCursor(curr);
} else { } else {
SDL_free(curr); SDL_free(curr);

Some files were not shown because too many files have changed in this diff Show More