update sdl Merge commit '4d48f9d23713d94b861da7b5d41baf2a41334994'

This commit is contained in:
Green Sky 2023-08-12 20:17:29 +02:00
commit 6df0417667
No known key found for this signature in database
215 changed files with 12672 additions and 17114 deletions

View File

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

12
external/sdl/SDL/.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
matrix:
platform:
- { name: iOS, target: Static Library-iOS, sdk: iphoneos }
- { name: tvOS, target: Static Library-tvOS, sdk: appletvos }
- { name: iOS, target: SDL3, sdk: iphoneos }
- { name: tvOS, target: SDL3, sdk: appletvos }
steps:
- 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',
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: 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' }
steps:
@ -130,7 +130,7 @@ jobs:
${{ matrix.platform.source_cmd }}
cmake -S cmake/test -B cmake_config_build -G Ninja \
-DTEST_SHARED=ON \
-DTEST_STATIC=ON \
-DTEST_STATIC=${{ !matrix.platform.no-static }} \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=$(echo "${{ github.workspace }}/cmake_prefix" | sed -e 's#\\#/#g')
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 (ARM), flags: -A ARM, artifact: 'SDL-VC-arm32' }
- { 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' }
steps:
@ -70,7 +70,6 @@ jobs:
run: |
cmake --build build/ --config Release --target PACKAGE
- name: Verify CMake configuration files
if: ${{ !contains(matrix.platform.name, 'UWP') }} # FIXME: cmake/test/CMakeLists.txt should support UWP
run: |
cmake -S cmake/test -B cmake_config_build `
-DCMAKE_PREFIX_PATH=${{ env.SDL3_DIR }} `

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

View File

@ -50,6 +50,9 @@ cmake-build-*
xcuserdata
*.xcworkspace
# for Visual Studio Code
.vscode/
# for Visual C++
.vs
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 default to yes on iOS.
# 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)
else()
set(SDL_HIDAPI_LIBUSB_AVAILABLE TRUE)
@ -198,6 +198,7 @@ set(SDL_SHARED_DEFAULT ON)
set(SDL_STATIC_DEFAULT ON)
set(SDL_SHARED_AVAILABLE ON)
set(SDL_STATIC_AVAILABLE ON)
# All these *_DEFAULT vars will default to ON if not specified,
# 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})
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.
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)
@ -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_DUMMYVIDEO "Use dummy video driver" ON)
dep_option(SDL_IBUS "Enable IBus support" ON ${UNIX_SYS} OFF)
set_option(SDL_OPENGL "Include OpenGL support" ON)
set_option(SDL_OPENGLES "Include OpenGL ES support" ON)
dep_option(SDL_OPENGL "Include OpenGL support" ON "NOT VISIONOS" OFF)
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})
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)
@ -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)
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)
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_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)
@ -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")
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})
option(SDL_TEST "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)
cmake_dependent_option(SDL_STATIC "Build a static version of the library" ${SDL_STATIC_DEFAULT} ${SDL_STATIC_AVAILABLE} OFF)
option(SDL_TEST_LIBRARY "Build the SDL3_test library" ON)
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_INSTALL_TESTS "Install test-cases" OFF "NOT SDL_DISABLE_INSTALL;NOT SDL_FRAMEWORK" 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;NOT WINDOWS_STORE" OFF)
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")
@ -391,7 +394,7 @@ if(SDL_STATIC)
target_compile_features(SDL3-static PRIVATE c_std_99)
endif()
if(SDL_TEST)
if(SDL_TEST_LIBRARY)
add_library(SDL3_test STATIC)
add_library(SDL3::SDL3_test ALIAS SDL3_test)
SDL_AddCommonCompilerFlags(SDL3_test)
@ -2028,7 +2031,7 @@ elseif(APPLE)
endif()
if(SDL_MISC)
if(IOS OR TVOS)
if(IOS OR TVOS OR VISIONOS)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/misc/ios/*.m")
else()
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/misc/macos/*.m")
@ -2051,10 +2054,10 @@ elseif(APPLE)
if(SDL_JOYSTICK)
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")
set(SDL_JOYSTICK_MFI 1)
if(IOS)
if(IOS OR VISIONOS)
set(SDL_FRAMEWORK_COREMOTION 1)
endif()
set(SDL_FRAMEWORK_GAMECONTROLLER 1)
@ -2086,15 +2089,17 @@ elseif(APPLE)
set(SDL_FRAMEWORK_GAMECONTROLLER 1)
set(SDL_FRAMEWORK_COREHAPTICS 1)
endif()
if(NOT VISIONOS)
set(SDL_JOYSTICK_IOKIT 1)
set(SDL_FRAMEWORK_IOKIT 1)
endif()
set(SDL_FRAMEWORK_FF 1)
endif()
set(HAVE_SDL_JOYSTICK TRUE)
endif()
if(SDL_HAPTIC)
if (IOS OR TVOS)
if (IOS OR TVOS OR VISIONOS)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/haptic/dummy/*.c")
set(SDL_HAPTIC_DUMMY 1)
else()
@ -2107,7 +2112,7 @@ elseif(APPLE)
endif()
if(SDL_POWER)
if (IOS OR TVOS)
if (IOS OR TVOS OR VISIONOS)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/power/uikit/*.m")
set(SDL_POWER_UIKIT 1)
else()
@ -2136,7 +2141,7 @@ elseif(APPLE)
endif()
if(SDL_SENSOR)
if(IOS)
if(IOS OR VISIONOS)
set(SDL_SENSOR_COREMOTION 1)
set(HAVE_SDL_SENSORS TRUE)
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/ ?
if(SDL_VIDEO)
if (IOS OR TVOS)
if (IOS OR TVOS OR VISIONOS)
set(SDL_VIDEO_DRIVER_UIKIT 1)
set(SDL_FRAMEWORK_COREGRAPHICS 1)
set(SDL_FRAMEWORK_QUARTZCORE 1)
@ -2165,7 +2170,7 @@ elseif(APPLE)
endif()
if(SDL_OPENGLES)
if(IOS OR TVOS)
if(IOS OR TVOS OR VISIONOS)
set(SDL_FRAMEWORK_OPENGLES 1)
set(SDL_VIDEO_OPENGL_ES 1)
else()
@ -2250,7 +2255,7 @@ elseif(APPLE)
endif()
endif()
if(SDL_FRAMEWORK_METAL)
if(IOS OR TVOS)
if(IOS OR TVOS OR VISIONOS)
sdl_link_dependency(metal LINK_OPTIONS "-Wl,-framework,Metal")
else()
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")
endif()
if(SDL_FRAMEWORK_QUARTZCORE)
if(IOS OR TVOS)
if(IOS OR TVOS OR VISIONOS)
sdl_link_dependency(quartz_core LINK_OPTIONS "-Wl,-framework,QuartzCore")
else()
sdl_link_dependency(metal LINK_OPTIONS "-Wl,-weak_framework,QuartzCore")
@ -2796,6 +2801,8 @@ foreach(_hdr IN LISTS SDL3_INCLUDE_FILES)
endif()
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")
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION.txt")
# If VERSION exists, it contains the SDL version
@ -2809,6 +2816,7 @@ else()
endif()
endif()
set(SDL_REVISION "SDL-${SDL_REVISION_CENTER}${SDL_REVISION_SUFFIX}")
endif()
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)
@ -3094,18 +3102,6 @@ if(SDL_STATIC)
OUTPUT_NAME "${sdl_static_libname}"
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_link_libraries(SDL3-static PRIVATE ${SDL_CMAKE_DEPENDS})
target_include_directories(SDL3-static
@ -3135,7 +3131,7 @@ sdl_compile_definitions(
##### Tests #####
if(SDL_TEST)
if(SDL_TEST_LIBRARY)
file(GLOB TEST_SOURCES "${SDL3_SOURCE_DIR}/src/test/*.c")
target_sources(SDL3_test PRIVATE ${TEST_SOURCES})
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_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_CMAKEDIR "${SDL_SDLtest_INSTALL_RESOURCEDIR}/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")
endif()
if(SDL_TEST)
if(SDL_TEST_LIBRARY)
export(TARGETS SDL3_test NAMESPACE "SDL3::" FILE "SDL3testTargets.cmake")
endif()
@ -3285,7 +3275,7 @@ if(NOT SDL_DISABLE_INSTALL)
)
endif()
if(SDL_TEST)
if(SDL_TEST_LIBRARY)
install(TARGETS SDL3_test EXPORT SDL3testTargets
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
FRAMEWORK DESTINATION "."
@ -3317,7 +3307,7 @@ if(NOT SDL_DISABLE_INSTALL)
)
endif()
if(SDL_TEST)
if(SDL_TEST_LIBRARY)
install(EXPORT SDL3testTargets
FILE "${SDL_SDLtest_INSTALL_CMAKEFILENAME}"
NAMESPACE SDL3::
@ -3336,7 +3326,7 @@ if(NOT SDL_DISABLE_INSTALL)
install(FILES ${SDL3_INCLUDE_FILES}
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/SDL3"
)
if(SDL_TEST)
if(SDL_TEST_LIBRARY)
install(FILES ${SDL3_TEST_INCLUDE_FILES}
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/SDL3"
)

View File

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

View File

@ -100,6 +100,7 @@
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>OldStyle</DebugInformationFormat>
<OmitDefaultLibName>true</OmitDefaultLibName>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -116,6 +117,7 @@
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>OldStyle</DebugInformationFormat>
<OmitDefaultLibName>true</OmitDefaultLibName>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
@ -134,6 +136,7 @@
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>OldStyle</DebugInformationFormat>
<OmitDefaultLibName>true</OmitDefaultLibName>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -150,6 +153,7 @@
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>OldStyle</DebugInformationFormat>
<OmitDefaultLibName>true</OmitDefaultLibName>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
</ItemDefinitionGroup>
<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
// Include any optional config for this build
// This allows you to set DEVELOPMENT_TEAM for all targets, for example.
#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[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
CONFIG_FRAMEWORK_LDFLAGS = -lSDL3_test

View File

@ -21,8 +21,6 @@ public class SDLAudioManager {
protected static AudioRecord mAudioRecord;
protected static Context mContext;
private static final int[] NO_DEVICES = {};
private static AudioDeviceCallback mAudioDeviceCallback;
public static void initialize() {
@ -36,7 +34,7 @@ public class SDLAudioManager {
@Override
public void onAudioDevicesAdded(AudioDeviceInfo[] 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) {
mContext = context;
if (context != null) {
registerAudioDeviceCallback();
}
}
public static void release(Context context) {
unregisterAudioDeviceCallback(context);
// no-op atm
}
// Audio
@ -311,65 +306,30 @@ public class SDLAudioManager {
return null;
}
private static void registerAudioDeviceCallback() {
public static void registerAudioDeviceCallback() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
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);
}
}
private static void unregisterAudioDeviceCallback(Context context) {
public static void unregisterAudioDeviceCallback() {
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);
}
}
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.
*/
@ -535,6 +495,6 @@ public class SDLAudioManager {
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, 0)
+ SDL_PlayAudioDevice(e)
+ SDL_ResumeAudioDevice(e)
|
- SDL_PauseAudioDevice(e, SDL_FALSE)
+ SDL_PlayAudioDevice(e)
+ SDL_ResumeAudioDevice(e)
)
@@
@ -321,7 +321,7 @@ expression e, pause_on;
+ if (pause_on) {
+ SDL_PauseAudioDevice(e);
+ } else {
+ SDL_PlayAudioDevice(e);
+ SDL_ResumeAudioDevice(e);
+ }
@ -940,19 +940,19 @@ typedef SDL_ControllerTouchpadEvent, SDL_GamepadTouchpadEvent;
@@
@@
- SDL_CONTROLLER_BUTTON_PADDLE1
+ SDL_GAMEPAD_BUTTON_PADDLE1
+ SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1
@@
@@
- SDL_CONTROLLER_BUTTON_PADDLE2
+ SDL_GAMEPAD_BUTTON_PADDLE2
+ SDL_GAMEPAD_BUTTON_LEFT_PADDLE1
@@
@@
- SDL_CONTROLLER_BUTTON_PADDLE3
+ SDL_GAMEPAD_BUTTON_PADDLE3
+ SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2
@@
@@
- SDL_CONTROLLER_BUTTON_PADDLE4
+ SDL_GAMEPAD_BUTTON_PADDLE4
+ SDL_GAMEPAD_BUTTON_LEFT_PADDLE2
@@
@@
- SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
@ -2690,3 +2690,63 @@ typedef SDL_cond, SDL_Condition;
- SDL_strtokr
+ 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})
if(PC_LIBUSB_FOUND)
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)
cmake_pop_check_state()
if(HAVE_LIBUSB_H)
@ -1041,7 +1041,7 @@ macro(CheckHIDAPI)
if(USB_1.0_LIB)
set(SDL_LIBUSB_DYNAMIC "\"${USB_1.0_LIB_SONAME}\"")
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()
@ -1079,17 +1079,22 @@ endmacro()
# - n/a
macro(CheckRPI)
if(SDL_RPI)
# presence of bcm_host means raspberry pi
find_package(RPi_BcmHost)
if(RPi_BcmHost_FOUND)
set(BCM_HOST_PKG_CONFIG_SPEC bcm_host)
set(BRCMEGL_PKG_CONFIG_SPEC brcmegl)
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)
sdl_link_dependency(rpi LIBS RPi_BcmHost::RPi_BcmHost CMAKE_MODULE RPi_BcmHost PKG_CONFIG_SPECS ${RPi_BcmHost_PKG_CONFIG_SPEC})
find_package(RPi_BrcmEGL)
if(SDL_VIDEO AND RPi_BrcmEGL_FOUND)
if(SDL_VIDEO)
set(HAVE_SDL_VIDEO TRUE)
set(SDL_VIDEO_DRIVER_RPI 1)
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()

View File

@ -14,6 +14,9 @@ macro(SDL_DetectCMakePlatform)
set(SDL_CMAKE_PLATFORM tvOS)
elseif(CMAKE_SYSTEM_NAME MATCHES ".*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()
elseif(CMAKE_SYSTEM_NAME MATCHES "Haiku.*")
set(SDL_CMAKE_PLATFORM Haiku)

View File

@ -2,6 +2,11 @@
cmake_minimum_required(VERSION 3.12)
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)

View File

@ -17,7 +17,7 @@ def main():
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))
bytes_written = 0
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.
### 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
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
```
## 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!
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
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_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_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:
```c
@ -75,8 +190,9 @@ should be changed to:
```c
Uint8 *dst_data = NULL;
int dst_len = 0;
if (SDL_ConvertAudioSamples(src_format, src_channels, src_rate, src_data, src_len
dst_format, dst_channels, dst_rate, &dst_data, &dst_len) < 0) {
const SDL_AudioSpec src_spec = { src_format, src_channels, src_rate };
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 */
}
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_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:
* SDL_AudioStreamAvailable() => SDL_GetAudioStreamAvailable()
@ -118,17 +238,25 @@ The following functions have been renamed:
The following functions have been removed:
* SDL_GetNumAudioDevices()
* SDL_GetAudioDeviceSpec()
* SDL_ConvertAudio()
* SDL_BuildAudioCVT()
* SDL_OpenAudio()
* SDL_CloseAudio()
* SDL_PauseAudio()
* SDL_GetAudioStatus()
* SDL_GetAudioDeviceStatus()
* SDL_GetDefaultAudioInfo()
* SDL_LockAudio()
* SDL_LockAudioDevice()
* SDL_UnlockAudio()
* SDL_UnlockAudioDevice()
* SDL_MixAudio()
Use the SDL_AudioDevice functions instead.
* SDL_QueueAudio()
* SDL_DequeueAudio()
* SDL_ClearAudioQueue()
* SDL_GetQueuedAudioSize()
The following symbols have been renamed:
* 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_MAX => SDL_GAMEPAD_BUTTON_MAX
* SDL_CONTROLLER_BUTTON_MISC1 => SDL_GAMEPAD_BUTTON_MISC1
* SDL_CONTROLLER_BUTTON_PADDLE1 => SDL_GAMEPAD_BUTTON_PADDLE1
* SDL_CONTROLLER_BUTTON_PADDLE2 => SDL_GAMEPAD_BUTTON_PADDLE2
* SDL_CONTROLLER_BUTTON_PADDLE3 => SDL_GAMEPAD_BUTTON_PADDLE3
* SDL_CONTROLLER_BUTTON_PADDLE4 => SDL_GAMEPAD_BUTTON_PADDLE4
* SDL_CONTROLLER_BUTTON_PADDLE1 => SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1
* SDL_CONTROLLER_BUTTON_PADDLE2 => SDL_GAMEPAD_BUTTON_LEFT_PADDLE1
* SDL_CONTROLLER_BUTTON_PADDLE3 => SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2
* SDL_CONTROLLER_BUTTON_PADDLE4 => SDL_GAMEPAD_BUTTON_LEFT_PADDLE2
* SDL_CONTROLLER_BUTTON_RIGHTSHOULDER => SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER
* SDL_CONTROLLER_BUTTON_RIGHTSTICK => SDL_GAMEPAD_BUTTON_RIGHT_STICK
* 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
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
the window in points. For high DPI displays, this will set up scaling from points to
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:
```c
Sint64 SDL_RWread(SDL_RWops *context, void *ptr, Sint64 size);
Sint64 SDL_RWwrite(SDL_RWops *context, const void *ptr, Sint64 size);
size_t SDL_RWread(SDL_RWops *context, void *ptr, size_t 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:
```
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:
```
size_t custom_read(void *ptr, size_t size, size_t nitems, SDL_RWops *stream)
{
Sint64 amount = SDL_RWread(stream, ptr, size * nitems);
if (amount <= 0) {
if (size > 0 && nitems > 0) {
return SDL_RWread(stream, ptr, size * nitems) / size;
}
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.
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>
static Sint64 SDLCALL
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)
static Sint64 SDLCALL stdio_seek(SDL_RWops *context, Sint64 offset, int whence)
{
int stdiowhence;
@ -858,54 +967,46 @@ stdio_seek(SDL_RWops * context, Sint64 offset, int whence)
return SDL_Error(SDL_EFSEEK);
}
static Sint64 SDLCALL
stdio_read(SDL_RWops * context, void *ptr, Sint64 size)
static size_t SDLCALL stdio_read(SDL_RWops *context, void *ptr, size_t size)
{
size_t nread;
size_t bytes;
nread = fread(ptr, 1, (size_t) size, (FILE *)context->hidden.stdio.fp);
if (nread == 0 && ferror((FILE *)context->hidden.stdio.fp)) {
return SDL_Error(SDL_EFREAD);
bytes = fread(ptr, 1, size, (FILE *)context->hidden.stdio.fp);
if (bytes == 0 && ferror((FILE *)context->hidden.stdio.fp)) {
SDL_Error(SDL_EFREAD);
}
return (Sint64) nread;
return bytes;
}
static Sint64 SDLCALL
stdio_write(SDL_RWops * context, const void *ptr, Sint64 size)
static size_t SDLCALL stdio_write(SDL_RWops *context, const void *ptr, size_t size)
{
size_t nwrote;
size_t bytes;
nwrote = fwrite(ptr, 1, (size_t) size, (FILE *)context->hidden.stdio.fp);
if (nwrote == 0 && ferror((FILE *)context->hidden.stdio.fp)) {
return SDL_Error(SDL_EFWRITE);
bytes = fwrite(ptr, 1, size, (FILE *)context->hidden.stdio.fp);
if (bytes == 0 && ferror((FILE *)context->hidden.stdio.fp)) {
SDL_Error(SDL_EFWRITE);
}
return (Sint64) nwrote;
return bytes;
}
static int SDLCALL
stdio_close(SDL_RWops * context)
static int SDLCALL stdio_close(SDL_RWops *context)
{
int status = 0;
if (context) {
if (context->hidden.stdio.autoclose) {
/* WARNING: Check the return value here! */
if (fclose((FILE *)context->hidden.stdio.fp) != 0) {
status = SDL_Error(SDL_EFWRITE);
}
}
SDL_DestroyRW(context);
}
return status;
}
SDL_RWops *
SDL_RWFromFP(void *fp, SDL_bool autoclose)
SDL_RWops *SDL_RWFromFP(void *fp, SDL_bool autoclose)
{
SDL_RWops *rwops = NULL;
rwops = SDL_CreateRW();
if (rwops != NULL) {
rwops->size = stdio_size;
rwops->seek = stdio_seek;
rwops->read = stdio_read;
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:
* SDL_AllocRW() => SDL_CreateRW()
* 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

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,8 @@
#ifndef SDL_DEPRECATED
# if defined(__GNUC__) && (__GNUC__ >= 4) /* technically, this arrived in gcc 3.1, but oh well. */
# define SDL_DEPRECATED __attribute__((deprecated))
# elif defined(_MSC_VER)
# define SDL_DEPRECATED __declspec(deprecated)
# else
# define SDL_DEPRECATED
# 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_REMOVED, /**< An opened joystick has been removed */
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 */
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_UP, /**< Gamepad touchpad finger was lifted */
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 */
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 */
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 padding1;
Uint8 padding2;

View File

@ -159,11 +159,11 @@ extern DECLSPEC char *SDLCALL SDL_GetPrefPath(const char *org, const char *app);
* | SAVEDGAMES | Vista+ | | | | | |
* | SCREENSHOTS | Vista+ | | | | | |
* | 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
{
@ -206,13 +206,18 @@ typedef enum
} SDL_Folder;
/**
* Finds the most suitable OS-provided folder for @p folder, and returns its
* path in OS-specific notation.
* Finds the most suitable user folder for @p purpose, and returns its path in
* OS-specific notation.
*
* Many OSes provide certain standard folders for certain purposes, such as
* storing pictures, music or videos for a certain user. This function gives
* 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
* beginning of the execution and kept for as long as needed.
*
@ -229,7 +234,7 @@ typedef enum
*
* \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++ */
#ifdef __cplusplus

View File

@ -95,11 +95,11 @@ typedef enum
SDL_GAMEPAD_BUTTON_DPAD_DOWN,
SDL_GAMEPAD_BUTTON_DPAD_LEFT,
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_PADDLE1, /* Xbox Elite paddle P1 (upper left, facing the back) */
SDL_GAMEPAD_BUTTON_PADDLE2, /* Xbox Elite paddle P3 (upper right, facing the back) */
SDL_GAMEPAD_BUTTON_PADDLE3, /* Xbox Elite paddle P2 (lower left, facing the back) */
SDL_GAMEPAD_BUTTON_PADDLE4, /* Xbox Elite paddle P4 (lower right, facing the back) */
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_RIGHT_PADDLE1, /* Upper or primary paddle, under your right hand (e.g. Xbox Elite paddle P1) */
SDL_GAMEPAD_BUTTON_LEFT_PADDLE1, /* Upper or primary paddle, under your left hand (e.g. Xbox Elite paddle P3) */
SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2, /* Lower or secondary paddle, under your right hand (e.g. Xbox Elite paddle P2) */
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_MAX
} SDL_GamepadButton;
@ -176,7 +176,8 @@ extern DECLSPEC int SDLCALL SDL_AddGamepadMapping(const char *mapping);
* constrained environment.
*
* \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()
* for more information.
*
@ -186,7 +187,7 @@ extern DECLSPEC int SDLCALL SDL_AddGamepadMapping(const char *mapping);
* \sa SDL_AddGamepadMappingsFromFile
* \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.
@ -321,7 +322,6 @@ extern DECLSPEC SDL_JoystickID *SDLCALL SDL_GetGamepads(int *count);
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetGamepadNameForIndex
* \sa SDL_OpenGamepad
*/
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.
*
* \sa SDL_CloseGamepad
* \sa SDL_GetGamepadNameForIndex
* \sa SDL_IsGamepad
*/
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.
*
* \sa SDL_GetGamepadNameForIndex
* \sa SDL_GetGamepadInstanceName
* \sa SDL_OpenGamepad
*/
extern DECLSPEC const char *SDLCALL SDL_GetGamepadName(SDL_Gamepad *gamepad);

View File

@ -34,6 +34,10 @@
/******************************************************************************/
/* Enable thread safety attributes only with clang.
* 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) && \
defined(__clang__) && (!defined(SWIG))

View File

@ -174,10 +174,10 @@
#define SDL_CONTROLLER_BUTTON_LEFTSTICK SDL_GAMEPAD_BUTTON_LEFT_STICK
#define SDL_CONTROLLER_BUTTON_MAX SDL_GAMEPAD_BUTTON_MAX
#define SDL_CONTROLLER_BUTTON_MISC1 SDL_GAMEPAD_BUTTON_MISC1
#define SDL_CONTROLLER_BUTTON_PADDLE1 SDL_GAMEPAD_BUTTON_PADDLE1
#define SDL_CONTROLLER_BUTTON_PADDLE2 SDL_GAMEPAD_BUTTON_PADDLE2
#define SDL_CONTROLLER_BUTTON_PADDLE3 SDL_GAMEPAD_BUTTON_PADDLE3
#define SDL_CONTROLLER_BUTTON_PADDLE4 SDL_GAMEPAD_BUTTON_PADDLE4
#define SDL_CONTROLLER_BUTTON_PADDLE1 SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1
#define SDL_CONTROLLER_BUTTON_PADDLE2 SDL_GAMEPAD_BUTTON_LEFT_PADDLE1
#define SDL_CONTROLLER_BUTTON_PADDLE3 SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2
#define SDL_CONTROLLER_BUTTON_PADDLE4 SDL_GAMEPAD_BUTTON_LEFT_PADDLE2
#define SDL_CONTROLLER_BUTTON_RIGHTSHOULDER SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER
#define SDL_CONTROLLER_BUTTON_RIGHTSTICK SDL_GAMEPAD_BUTTON_RIGHT_STICK
#define SDL_CONTROLLER_BUTTON_START SDL_GAMEPAD_BUTTON_START
@ -423,6 +423,18 @@
#define RW_SEEK_SET SDL_RW_SEEK_SET
#define SDL_AllocRW SDL_CreateRW
#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 */
#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_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_PADDLE1 SDL_CONTROLLER_BUTTON_PADDLE1_renamed_SDL_GAMEPAD_BUTTON_PADDLE1
#define SDL_CONTROLLER_BUTTON_PADDLE2 SDL_CONTROLLER_BUTTON_PADDLE2_renamed_SDL_GAMEPAD_BUTTON_PADDLE2
#define SDL_CONTROLLER_BUTTON_PADDLE3 SDL_CONTROLLER_BUTTON_PADDLE3_renamed_SDL_GAMEPAD_BUTTON_PADDLE3
#define SDL_CONTROLLER_BUTTON_PADDLE4 SDL_CONTROLLER_BUTTON_PADDLE4_renamed_SDL_GAMEPAD_BUTTON_PADDLE4
#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_LEFT_PADDLE1
#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_LEFT_PADDLE2
#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_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 SDL_AllocRW SDL_AllocRW_renamed_SDL_CreateRW
#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 */
#define SDL_SensorClose SDL_SensorClose_renamed_SDL_CloseSensor

View File

@ -91,6 +91,8 @@ typedef enum
{
SDL_ARRAYORDER_NONE,
SDL_ARRAYORDER_RGB,
SDL_ARRAYORDER_UNUSED1, /* Left for compatibility with SDL2 */
SDL_ARRAYORDER_UNUSED2, /* Left for compatibility with SDL2 */
SDL_ARRAYORDER_BGR
} SDL_ArrayOrder;
@ -263,11 +265,19 @@ typedef enum
SDL_PIXELFORMAT_ARGB32 = SDL_PIXELFORMAT_ARGB8888,
SDL_PIXELFORMAT_BGRA32 = SDL_PIXELFORMAT_BGRA8888,
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
SDL_PIXELFORMAT_RGBA32 = SDL_PIXELFORMAT_ABGR8888,
SDL_PIXELFORMAT_ARGB32 = SDL_PIXELFORMAT_BGRA8888,
SDL_PIXELFORMAT_BGRA32 = SDL_PIXELFORMAT_ARGB8888,
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
SDL_PIXELFORMAT_YV12 = /**< Planar mode: Y + V + U (3 planes) */

View File

@ -94,6 +94,9 @@
#ifndef TARGET_OS_SIMULATOR
#define TARGET_OS_SIMULATOR 0
#endif
#ifndef TARGET_OS_XR
#define TARGET_OS_XR 0
#endif
#if TARGET_OS_TV
#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.
*
* 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.
* To stop rendering to a texture and render to the window again, call this
* 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
* SDL a drawable to render to, which might happen if the window is
* 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
* \returns an `id<MTLRenderCommandEncoder>` on success, or NULL if the

View File

@ -38,13 +38,21 @@
extern "C" {
#endif
/* RWops Types */
#define SDL_RWOPS_UNKNOWN 0U /**< Unknown stream type */
#define SDL_RWOPS_WINFILE 1U /**< Win32 file */
#define SDL_RWOPS_STDFILE 2U /**< Stdio file */
#define SDL_RWOPS_JNIFILE 3U /**< Android asset */
#define SDL_RWOPS_MEMORY 4U /**< Memory stream */
#define SDL_RWOPS_MEMORY_RO 5U /**< Read-Only memory stream */
/* RWops types */
#define SDL_RWOPS_UNKNOWN 0 /**< Unknown stream type */
#define SDL_RWOPS_WINFILE 1 /**< Win32 file */
#define SDL_RWOPS_STDFILE 2 /**< Stdio file */
#define SDL_RWOPS_JNIFILE 3 /**< Android asset */
#define SDL_RWOPS_MEMORY 4 /**< 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.
@ -52,7 +60,9 @@ extern "C" {
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);
@ -62,36 +72,23 @@ typedef struct SDL_RWops
*
* \return the final offset in the data stream, or -1 on error.
*/
Sint64 (SDLCALL * seek) (struct SDL_RWops * context, Sint64 offset,
int whence);
Sint64 (SDLCALL *seek)(struct SDL_RWops *context, Sint64 offset, int whence);
/**
* Read up to \c size bytes from the data stream to the area pointed
* at by \c ptr.
*
* It is an error to use a negative \c size, but this parameter is
* 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.
* \return the number of bytes read
*/
Sint64 (SDLCALL * read) (struct SDL_RWops * context, void *ptr,
Sint64 size);
size_t (SDLCALL *read)(struct SDL_RWops *context, void *ptr, size_t size);
/**
* 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,
* etc). Returns -1 on error when nothing was written.
* to data stream.
*
* It is an error to use a negative \c size, but this parameter is
* 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.
* \return the number of bytes written
*/
Sint64 (SDLCALL * write) (struct SDL_RWops * context, const void *ptr,
Sint64 size);
size_t (SDLCALL *write)(struct SDL_RWops *context, const void *ptr, size_t size);
/**
* Close and free an allocated SDL_RWops structure.
@ -101,6 +98,7 @@ typedef struct SDL_RWops
int (SDLCALL *close)(struct SDL_RWops *context);
Uint32 type;
Uint32 status;
union
{
#ifdef __ANDROID__
@ -109,7 +107,7 @@ typedef struct SDL_RWops
void *asset;
} androidio;
#elif defined(__WIN32__) || defined(__GDK__)
#elif defined(__WIN32__) || defined(__GDK__) || defined(__WINRT__)
struct
{
SDL_bool append;
@ -213,8 +211,7 @@ typedef struct SDL_RWops
* \sa SDL_RWtell
* \sa SDL_RWwrite
*/
extern DECLSPEC SDL_RWops *SDLCALL SDL_RWFromFile(const char *file,
const char *mode);
extern DECLSPEC SDL_RWops *SDLCALL SDL_RWFromFile(const char *file, const char *mode);
/**
* 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_RWtell
*/
extern DECLSPEC SDL_RWops *SDLCALL SDL_RWFromConstMem(const void *mem,
size_t size);
extern DECLSPEC SDL_RWops *SDLCALL SDL_RWFromConstMem(const void *mem, size_t size);
/* @} *//* 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.
*
* 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
* implementations in SDL, like SDL_RWFromFile() or SDL_RWFromMem(), etc.
*
@ -315,7 +311,7 @@ extern DECLSPEC SDL_RWops *SDLCALL SDL_CreateRW(void);
* SDL_CreateRW().
*
* 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
* implementations in SDL, like SDL_RWFromFile() or SDL_RWFromMem(), etc, and
* 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
* **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.
*
* \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_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.
*
* 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
* \returns the size of the data stream in the SDL_RWops on success, -1 if
* unknown or a negative error code on failure; call SDL_GetError()
* for more information.
* \returns the size of the data stream in the SDL_RWops 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.
*/
@ -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
* `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 offset an offset in bytes, relative to **whence** location; can be
* negative
* \param whence any of `SDL_RW_SEEK_SET`, `SDL_RW_SEEK_CUR`,
* `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.
*
@ -388,8 +381,7 @@ extern DECLSPEC Sint64 SDLCALL SDL_RWsize(SDL_RWops *context);
* \sa SDL_RWtell
* \sa SDL_RWwrite
*/
extern DECLSPEC Sint64 SDLCALL SDL_RWseek(SDL_RWops *context,
Sint64 offset, int whence);
extern DECLSPEC Sint64 SDLCALL SDL_RWseek(SDL_RWops *context, Sint64 offset, int whence);
/**
* 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
* application development.
*
* Prior to SDL 2.0.10, this function was a macro.
*
* \param context a SDL_RWops data stream object from which to get the current
* offset
* \param context an 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
* 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 ptr a pointer to a buffer to read data into
* \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
* for data not ready with a non-blocking context.
* \returns the number of bytes read, or 0 on end of file or other error.
*
* \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_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.
@ -488,8 +477,7 @@ extern DECLSPEC Sint64 SDLCALL SDL_RWread(SDL_RWops *context, void *ptr, Sint64
* \sa SDL_RWread
* \sa SDL_RWseek
*/
extern DECLSPEC Sint64 SDLCALL SDL_RWwrite(SDL_RWops *context,
const void *ptr, Sint64 size);
extern DECLSPEC size_t SDLCALL SDL_RWwrite(SDL_RWops *context, const void *ptr, size_t size);
/**
* 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
* 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
* \returns 0 on success or a negative error code on failure; call
* 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.
*/
extern DECLSPEC void *SDLCALL SDL_LoadFile_RW(SDL_RWops *src,
size_t *datasize,
SDL_bool freesrc);
extern DECLSPEC void *SDLCALL SDL_LoadFile_RW(SDL_RWops *src, size_t *datasize, SDL_bool freesrc);
/**
* 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().
*
* 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 datasize if not NULL, will store the number of bytes read
* \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.
*
* \param src the SDL_RWops to read from
* \returns the read byte on success or 0 on failure; call SDL_GetError() for
* more information.
* \param value a pointer filled in with the data read
* \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.
*
* \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
@ -588,13 +568,29 @@ extern DECLSPEC Uint8 SDLCALL SDL_ReadU8(SDL_RWops * src);
* the native byte order.
*
* \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.
*
* \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
@ -604,13 +600,29 @@ extern DECLSPEC Uint16 SDLCALL SDL_ReadLE16(SDL_RWops * src);
* the native byte order.
*
* \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.
*
* \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
@ -620,13 +632,29 @@ extern DECLSPEC Uint16 SDLCALL SDL_ReadBE16(SDL_RWops * src);
* the native byte order.
*
* \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.
*
* \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
@ -636,13 +664,29 @@ extern DECLSPEC Uint32 SDLCALL SDL_ReadLE32(SDL_RWops * src);
* the native byte order.
*
* \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.
*
* \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
@ -652,13 +696,29 @@ extern DECLSPEC Uint32 SDLCALL SDL_ReadBE32(SDL_RWops * src);
* the native byte order.
*
* \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.
*
* \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
@ -668,13 +728,29 @@ extern DECLSPEC Uint64 SDLCALL SDL_ReadLE64(SDL_RWops * src);
* the native byte order.
*
* \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.
*
* \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 */
/**
@ -689,17 +765,15 @@ extern DECLSPEC Uint64 SDLCALL SDL_ReadBE64(SDL_RWops * src);
*
* \param dst the SDL_RWops to write to
* \param value the byte value to write
* \returns 1 on success or 0 on failure; call SDL_GetError() for more
* information.
* \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.
*
* \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.
*
* 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 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.
*
* \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
* 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
* Use this function to write 16 bits in native format to an SDL_RWops as
* little-endian data.
*
* 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 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.
*
* \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.
*
* 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 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.
*
* \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.
*
* 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 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.
*
* \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.
*
* 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 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.
*
* \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 */
/* 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);
/**
* Allocate a new RGB surface with with a specific pixel format and existing
* pixel data.
* Allocate a new RGB surface with a specific pixel format and existing pixel
* data.
*
* 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.
*
* 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
* 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 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
* 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_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.

View File

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

View File

@ -155,7 +155,7 @@ SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn,
pfnSDL_CurrentBeginThread pfnBeginThread,
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
#undef SDL_CreateThread
#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_CreateThreadWithStackSize(fn, name, stacksize, data) SDL_CreateThreadWithStackSize(fn, name, stacksize, data, (pfnSDL_CurrentBeginThread)SDL_beginthread, (pfnSDL_CurrentEndThread)SDL_endthread)
#endif
#endif /* !__BUILDING_SDL2_COMPAT__ */
#else

View File

@ -118,6 +118,7 @@ typedef enum
* \sa SDL_SetWindowResizable()
* \sa SDL_SetWindowTitle()
* \sa SDL_ShowWindow()
* \sa SDL_ShowWindowSystemMenu()
*/
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);
/**
* 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.
*

View File

@ -632,9 +632,9 @@ SDL_bool SDL_IsTablet(void)
#ifdef __WIN32__
#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) {
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_
#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
#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);
#include "SDL_sysaudio.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"
#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;
if ((stat(fname, &sb) == 0) && (S_ISCHR(sb.st_mode))) {
const int audio_fd = open(fname, flags | O_CLOEXEC, 0);
if (audio_fd >= 0) {
const int okay = test(audio_fd);
const SDL_bool okay = test(audio_fd);
close(audio_fd);
if (okay) {
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 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_FALSE, classic, test);

View File

@ -36,6 +36,6 @@
#define OPEN_FLAGS_INPUT (O_RDONLY | O_NONBLOCK)
#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_ */

View File

@ -18,151 +18,269 @@
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_sysaudio_h_
#define SDL_sysaudio_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_INPUT_DEVNAME "System audio capture device"
/* The SDL audio driver */
typedef struct SDL_AudioDevice SDL_AudioDevice;
// these are used when no better specifics are known. We default to CD audio quality.
#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
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
its list of available devices. */
extern void SDL_RemoveAudioDevice(const SDL_bool iscapture, void *handle);
/* Backends should call this if an opened audio device is lost.
This can happen due to i/o errors, or a device being unplugged, etc. */
extern void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device);
/* Audio targets should call this if an opened audio device is lost while
being used. This can happen due to i/o errors, or a device being unplugged,
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);
// Backends should call this if the system default device changes.
extern void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device);
/* This is the size of a packet when using SDL_QueueAudio(). We allocate
these as necessary and pool them, under the assumption that we'll
eventually end up with a handful that keep recycling, meeting whatever
the app needs. We keep packing data tightly as more arrives to avoid
wasting space, and if we get a giant block of data, we'll split them
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
if this is crippling for some embedded system, we can #ifdef this.
The system preallocates enough packets for 2 callbacks' worth of data. */
#define SDL_AUDIOBUFFERQUEUE_PACKETLEN (8 * 1024)
// 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.
extern int SDL_AudioDeviceFormatChanged(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames);
// Same as above, but assume the device is already locked.
extern int SDL_AudioDeviceFormatChangedAlreadyLocked(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames);
// Find the SDL_AudioDevice associated with the handle supplied to SDL_AddAudioDevice. NULL if not found. DOES NOT LOCK THE DEVICE.
extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByHandle(void *handle);
// 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
{
void (*DetectDevices)(void);
int (*OpenDevice)(SDL_AudioDevice *_this, const char *devname);
void (*ThreadInit)(SDL_AudioDevice *_this); /* Called by audio thread at start */
void (*ThreadDeinit)(SDL_AudioDevice *_this); /* Called by audio thread at end */
void (*WaitDevice)(SDL_AudioDevice *_this);
void (*PlayDevice)(SDL_AudioDevice *_this);
Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *_this);
int (*CaptureFromDevice)(SDL_AudioDevice *_this, void *buffer, int buflen);
void (*FlushCapture)(SDL_AudioDevice *_this);
void (*CloseDevice)(SDL_AudioDevice *_this);
void (*LockDevice)(SDL_AudioDevice *_this);
void (*UnlockDevice)(SDL_AudioDevice *_this);
void (*FreeDeviceHandle)(void *handle); /**< SDL is done with handle from SDL_AddAudioDevice() */
void (*DetectDevices)(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
int (*OpenDevice)(SDL_AudioDevice *device);
void (*ThreadInit)(SDL_AudioDevice *device); // Called by audio thread at start
void (*ThreadDeinit)(SDL_AudioDevice *device); // Called by audio thread at end
void (*WaitDevice)(SDL_AudioDevice *device);
void (*PlayDevice)(SDL_AudioDevice *device, const Uint8 *buffer, int buflen); // buffer and buflen are always from GetDeviceBuf, passed here for convenience.
Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *device, int *buffer_size);
void (*WaitCaptureDevice)(SDL_AudioDevice *device);
int (*CaptureFromDevice)(SDL_AudioDevice *device, void *buffer, int buflen);
void (*FlushCapture)(SDL_AudioDevice *device);
void (*CloseDevice)(SDL_AudioDevice *device);
void (*FreeDeviceHandle)(SDL_AudioDevice *device); // SDL is done with this device; free the handle from SDL_AddAudioDevice()
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;
// 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.
SDL_bool HasCaptureSupport;
SDL_bool OnlyHasDefaultOutputDevice;
SDL_bool OnlyHasDefaultCaptureDevice;
SDL_bool AllowsArbitraryDeviceNames;
SDL_bool SupportsNonPow2Samples;
} 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
{
/* * * */
/* The name of this audio driver */
const char *name;
/* * * */
/* The description of this audio driver */
const char *desc;
SDL_AudioDriverImpl impl;
/* A mutex for device detection */
SDL_Mutex *detectionLock;
SDL_bool captureDevicesRemoved;
SDL_bool outputDevicesRemoved;
int outputDeviceCount;
int inputDeviceCount;
SDL_AudioDeviceItem *outputDevices;
SDL_AudioDeviceItem *inputDevices;
const char *name; // The name of this audio driver
const char *desc; // The description of this audio driver
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.
SDL_AudioDevice *capture_devices; // the list of currently-available audio capture devices.
SDL_AudioDeviceID default_output_device_id;
SDL_AudioDeviceID default_capture_device_id;
SDL_AtomicInt output_device_count;
SDL_AtomicInt capture_device_count;
SDL_AtomicInt last_device_instance_id; // increments on each device add to provide unique instance IDs
SDL_AtomicInt shutting_down; // non-zero during SDL_Quit, so we known not to accept any last-minute device hotplugs.
} 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
{
/* * * */
/* Data common to all devices */
SDL_AudioDeviceID id;
// A mutex for locking access to this struct
SDL_Mutex *lock;
/* 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;
int buffer_size;
/* The callback's expected audio specification (converted vs device's spec). */
SDL_AudioSpec callbackspec;
// The device's default audio specification
SDL_AudioSpec default_spec;
/* Stream that converts and resamples. NULL if not needed. */
SDL_AudioStream *stream;
// Number of sample frames the devices wants per-buffer.
int sample_frames;
/* Current state flags */
SDL_AtomicInt shutdown; /* true if we are signaling the play thread to end. */
SDL_AtomicInt enabled; /* true if device is functioning and connected. */
SDL_AtomicInt paused;
// Value to use for SDL_memset to silence a buffer in this device's format
int silence_value;
// 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;
/* Scratch buffer used in the bridge between SDL and the user callback. */
// Scratch buffer used for mixing.
Uint8 *work_buffer;
/* Size, in bytes, of work_buffer. */
Uint32 work_buffer_len;
/* A mutex for locking the mixing buffers */
SDL_Mutex *mixer_lock;
/* A thread to feed the audio device */
// A thread to feed the audio device
SDL_Thread *thread;
SDL_threadID threadid;
/* Queued buffers (if app not using callback). */
SDL_DataQueue *buffer_queue;
// SDL_TRUE if this physical device is currently opened by the backend.
SDL_bool is_opened;
/* * * */
/* Data private to this driver */
// Data private to this driver
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
@ -170,10 +288,10 @@ typedef struct AudioBootStrap
const char *name;
const char *desc;
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;
/* 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 PULSEAUDIO_bootstrap;
extern AudioBootStrap ALSA_bootstrap;
@ -188,8 +306,8 @@ extern AudioBootStrap HAIKUAUDIO_bootstrap;
extern AudioBootStrap COREAUDIO_bootstrap;
extern AudioBootStrap DISKAUDIO_bootstrap;
extern AudioBootStrap DUMMYAUDIO_bootstrap;
extern AudioBootStrap aaudio_bootstrap;
extern AudioBootStrap openslES_bootstrap;
extern AudioBootStrap AAUDIO_bootstrap;
extern AudioBootStrap openslES_bootstrap; // !!! FIXME: capitalize this to match the others
extern AudioBootStrap ANDROIDAUDIO_bootstrap;
extern AudioBootStrap PS2AUDIO_bootstrap;
extern AudioBootStrap PSPAUDIO_bootstrap;
@ -201,4 +319,4 @@ extern AudioBootStrap QSAAUDIO_bootstrap;
extern SDL_AudioDevice *get_audio_dev(SDL_AudioDeviceID id);
extern int get_max_num_audio_dev(void);
#endif /* SDL_sysaudio_h_ */
#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;
/* 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.
*/
i = sample_count;
@ -1553,7 +1553,7 @@ static int WaveReadPartialChunkData(SDL_RWops *src, WaveChunk *chunk, size_t len
return -2;
}
chunk->size = (size_t) SDL_RWread(src, chunk->data, length);
chunk->size = SDL_RWread(src, chunk->data, length);
if (chunk->size != length) {
/* Expected to be handled by the caller. */
}
@ -1614,16 +1614,20 @@ static int WaveReadFormat(WaveFile *file)
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->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. */
if (fmtlen >= 16) {
format->bitspersample = SDL_ReadLE16(fmtsrc);
if (!SDL_ReadU16LE(fmtsrc, &format->bitspersample)) {
return -1;
}
} else if (format->encoding == PCM_CODE) {
SDL_RWclose(fmtsrc);
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. */
if (fmtlen >= 18) {
format->extsize = SDL_ReadLE16(fmtsrc);
if (!SDL_ReadU16LE(fmtsrc, &format->extsize)) {
return -1;
}
}
if (format->formattag == EXTENSIBLE_CODE) {
@ -1647,10 +1653,11 @@ static int WaveReadFormat(WaveFile *file)
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->channelmask = SDL_ReadLE32(fmtsrc);
SDL_RWread(fmtsrc, format->subformat, 16);
format->encoding = WaveGetFormatGUIDEncoding(format);
}
@ -1667,15 +1674,11 @@ static int WaveCheckFormat(WaveFile *file, size_t datalength)
if (format->channels == 0) {
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) {
return SDL_SetError("Invalid sample rate");
} else if (format->frequency > INT_MAX) {
/* Limit given by SDL_AudioSpec.freq. */
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) {
Uint32 formtype;
/* 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");
} else if (SDL_SwapLE32(formtype) != WAVE) {
} else if (formtype != WAVE) {
return SDL_SetError("RIFF form type is not WAVE (not a Waveform file)");
}
} else if (RIFFchunk.fourcc == WAVE) {
@ -1895,10 +1898,8 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 *
} else {
/* Let's use src directly, it's just too convenient. */
Sint64 position = SDL_RWseek(src, chunk->position, SDL_RW_SEEK_SET);
Uint32 samplelength;
if (position == chunk->position && SDL_RWread(src, &samplelength, sizeof(Uint32)) == sizeof(Uint32)) {
if (position == chunk->position && SDL_ReadU32LE(src, &file->fact.samplelength)) {
file->fact.status = 1;
file->fact.samplelength = SDL_SwapLE32(samplelength);
} else {
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;
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");
} else if (SDL_RWread(src, &tmp, 1) != 1) {
} else if (!SDL_ReadU8(src, &tmp)) {
return SDL_SetError("RIFF size truncates chunk");
}
}
@ -2025,13 +2026,12 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 *
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.
*/
SDL_zerop(spec);
spec->freq = format->frequency;
spec->channels = (Uint8)format->channels;
spec->samples = 4096; /* Good default buffer size */
spec->format = 0;
switch (format->encoding) {
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);
}
break;
default:
return SDL_SetError("Unexpected data format");
}
spec->silence = SDL_GetSilenceValueForFormat(spec->format);
/* Report the end position back to the cleanup code. */
if (RIFFlengthknown) {
chunk->position = RIFFend;
@ -2075,17 +2075,14 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 *
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;
WaveFile file;
SDL_zero(file);
/* Make sure we are passed a valid data source */
if (src == NULL) {
/* Error may come from RWops. */
goto done;
goto done; /* Error may come from RWops. */
} else if (spec == NULL) {
SDL_InvalidParamError("spec");
goto done;
@ -2100,6 +2097,7 @@ SDL_AudioSpec *SDL_LoadWAV_RW(SDL_RWops *src, SDL_bool freesrc, SDL_AudioSpec *s
*audio_buf = NULL;
*audio_len = 0;
SDL_zero(file);
file.riffhint = WaveGetRiffSizeHint();
file.trunchint = WaveGetTruncationHint();
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);
if (result < 0) {
SDL_free(*audio_buf);
spec = NULL;
audio_buf = NULL;
audio_len = 0;
}
@ -2118,14 +2115,15 @@ SDL_AudioSpec *SDL_LoadWAV_RW(SDL_RWops *src, SDL_bool freesrc, SDL_AudioSpec *s
}
WaveFreeChunkData(&file.chunk);
SDL_free(file.decoderdata);
done:
if (freesrc && src) {
SDL_RWclose(src);
}
if (result == 0) {
return spec;
} else {
return NULL;
return result;
}
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 <aaudio/AAudio.h>
#if __ANDROID_API__ < 31
#define AAUDIO_FORMAT_PCM_I32 4
#endif
struct SDL_PrivateAudioData
{
AAudioStream *stream;
/* Raw mixing buffer */
Uint8 *mixbuf;
int mixlen;
int frame_size;
/* Resume device if it was paused automatically */
int resume;
Uint8 *mixbuf; // Raw mixing buffer
SDL_Semaphore *semaphore;
SDL_AtomicInt error_callback_triggered;
int resume; // Resume device if it was paused automatically
};
/* Debug */
// Debug
#if 0
#define LOGI(...) SDL_Log(__VA_ARGS__);
#else
#define LOGI(...)
#endif
#define LIB_AAUDIO_SO "libaaudio.so"
typedef struct AAUDIO_Data
{
AAudioStreamBuilder *builder;
void *handle;
#define SDL_PROC(ret, func, params) ret (*func) params;
#include "SDL_aaudiofuncs.h"
#undef SDL_PROC
} AAUDIO_Data;
static AAUDIO_Data ctx;
static int aaudio_LoadFunctions(AAUDIO_Data *data)
static int AAUDIO_LoadFunctions(AAUDIO_Data *data)
{
#define SDL_PROC(ret, func, params) \
do { \
@ -70,23 +70,95 @@ static int aaudio_LoadFunctions(AAUDIO_Data *data)
} \
} while (0);
#include "SDL_aaudiofuncs.h"
#undef SDL_PROC
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"
static int aaudio_OpenDevice(SDL_AudioDevice *_this, const char *devname)
// 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
// 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_bool iscapture = _this->iscapture;
SDL_AudioDevice *device = (SDL_AudioDevice *) userData;
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;
SDL_assert(device->handle != NULL); // AAUDIO_UNSPECIFIED is zero, so legit devices should all be non-zero.
LOGI(__func__);
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));
if (_this->hidden == NULL) {
hidden = device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
if (hidden == NULL) {
return SDL_OutOfMemory();
}
private = _this->hidden;
ctx.AAudioStreamBuilder_setSampleRate(ctx.builder, _this->spec.freq);
ctx.AAudioStreamBuilder_setChannelCount(ctx.builder, _this->spec.channels);
if(devname != NULL) {
int aaudio_device_id = SDL_atoi(devname);
SDL_AtomicSet(&hidden->error_callback_triggered, 0);
AAudioStreamBuilder *builder = NULL;
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);
ctx.AAudioStreamBuilder_setDeviceId(ctx.builder, aaudio_device_id);
}
{
aaudio_direction_t direction = (iscapture ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT);
ctx.AAudioStreamBuilder_setDirection(ctx.builder, direction);
}
{
aaudio_format_t format = AAUDIO_FORMAT_PCM_FLOAT;
if (_this->spec.format == SDL_AUDIO_S16SYS) {
format = AAUDIO_FORMAT_PCM_I16;
} else if (_this->spec.format == SDL_AUDIO_S16SYS) {
ctx.AAudioStreamBuilder_setDeviceId(builder, aaudio_device_id);
const aaudio_direction_t direction = (iscapture ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT);
ctx.AAudioStreamBuilder_setDirection(builder, direction);
aaudio_format_t format;
if ((device->spec.format == SDL_AUDIO_S32SYS) && (SDL_GetAndroidSDKVersion() >= 31)) {
format = AAUDIO_FORMAT_PCM_I32;
} else if (device->spec.format == SDL_AUDIO_F32SYS) {
format = AAUDIO_FORMAT_PCM_FLOAT;
}
ctx.AAudioStreamBuilder_setFormat(ctx.builder, format);
} else {
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",
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format),
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples);
device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
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) {
LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res);
ctx.AAudioStreamBuilder_delete(builder);
return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
_this->spec.freq = ctx.AAudioStream_getSampleRate(private->stream);
_this->spec.channels = ctx.AAudioStream_getChannelCount(private->stream);
{
aaudio_format_t fmt = ctx.AAudioStream_getFormat(private->stream);
if (fmt == AAUDIO_FORMAT_PCM_I16) {
_this->spec.format = SDL_AUDIO_S16SYS;
} else if (fmt == AAUDIO_FORMAT_PCM_FLOAT) {
_this->spec.format = SDL_AUDIO_F32SYS;
device->sample_frames = (int) ctx.AAudioStream_getFramesPerDataCallback(hidden->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.
device->sample_frames = (int) (ctx.AAudioStream_getBufferCapacityInFrames(hidden->stream) / 4);
LOGI("AAUDIO: Got a stream with unspecified sample frames per data callback! Retrying with %d frames...", device->sample_frames);
ctx.AAudioStream_close(hidden->stream);
ctx.AAudioStreamBuilder_setFramesPerDataCallback(builder, device->sample_frames);
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",
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format),
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples);
ctx.AAudioStreamBuilder_delete(builder);
SDL_CalculateAudioSpec(&_this->spec);
device->spec.freq = ctx.AAudioStream_getSampleRate(hidden->stream);
device->spec.channels = ctx.AAudioStream_getChannelCount(hidden->stream);
/* Allocate mixing buffer */
if (!iscapture) {
private->mixlen = _this->spec.size;
private->mixbuf = (Uint8 *)SDL_malloc(private->mixlen);
if (private->mixbuf == NULL) {
format = ctx.AAudioStream_getFormat(hidden->stream);
if (format == AAUDIO_FORMAT_PCM_I16) {
device->spec.format = SDL_AUDIO_S16SYS;
} else if (format == AAUDIO_FORMAT_PCM_I32) {
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();
}
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(private->stream);
res = ctx.AAudioStream_requestStart(hidden->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestStart %d iscapture:%d", res, iscapture);
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;
}
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;
LOGI(__func__);
if (private->stream) {
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));
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));
if (device->iscapture) {
// Pause() isn't implemented for 'capture', use Stop()
res = ctx.AAudioStream_requestStop(hidden->stream);
} 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
/* Log under-run count */
{
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
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestPause %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
static int aaudio_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
{
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;
SDL_LockMutex(device->lock);
hidden->resume = SDL_TRUE;
}
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__);
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);
}
ctx.handle = NULL;
ctx.builder = NULL;
SDL_zero(ctx);
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__);
/* 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);
if (ctx.handle == NULL) {
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;
}
AudioBootStrap aaudio_bootstrap = {
"AAudio", "AAudio audio driver", aaudio_Init, SDL_FALSE
if (AAUDIO_LoadFunctions(&ctx) < 0) {
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 */
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 */
#endif // SDL_AUDIO_DRIVER_AAUDIO

View File

@ -25,15 +25,15 @@
#ifdef SDL_AUDIO_DRIVER_AAUDIO
void aaudio_ResumeDevices(void);
void aaudio_PauseDevices(void);
SDL_bool aaudio_DetectBrokenPlayState(void);
void AAUDIO_ResumeDevices(void);
void AAUDIO_PauseDevices(void);
SDL_bool AAUDIO_DetectBrokenPlayState(void);
#else
static void aaudio_ResumeDevices(void) {}
static void aaudio_PauseDevices(void) {}
static SDL_bool aaudio_DetectBrokenPlayState(void) { return SDL_FALSE; }
#define AAUDIO_ResumeDevices()
#define AAUDIO_PauseDevices()
#define AAUDIO_DetectBrokenPlayState() (SDL_FALSE)
#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(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_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_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_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_setPrivacySensitive, (AAudioStreamBuilder * builder, bool privacySensitive)) /* API 30 */
SDL_PROC_UNUSED(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_setDataCallback, (AAudioStreamBuilder * builder, AAudioStream_dataCallback callback, void *userData))
SDL_PROC(void, AAudioStreamBuilder_setFramesPerDataCallback, (AAudioStreamBuilder * builder, int32_t numFrames))
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_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_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(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_read, (AAudioStream * stream, 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(int32_t, AAudioStream_getBufferSizeInFrames, (AAudioStream * stream))
SDL_PROC_UNUSED(int32_t, AAudioStream_getFramesPerBurst, (AAudioStream * stream))
SDL_PROC_UNUSED(int32_t, AAudioStream_getBufferCapacityInFrames, (AAudioStream * stream))
SDL_PROC_UNUSED(int32_t, AAudioStream_getFramesPerDataCallback, (AAudioStream * stream))
SDL_PROC(int32_t, AAudioStream_getXRunCount, (AAudioStream * stream))
SDL_PROC(int32_t, AAudioStream_getBufferCapacityInFrames, (AAudioStream * stream))
SDL_PROC(int32_t, AAudioStream_getFramesPerDataCallback, (AAudioStream * stream))
SDL_PROC_UNUSED(int32_t, AAudioStream_getXRunCount, (AAudioStream * stream))
SDL_PROC(int32_t, AAudioStream_getSampleRate, (AAudioStream * stream))
SDL_PROC(int32_t, AAudioStream_getChannelCount, (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_allowed_capture_policy_t, AAudioStream_getAllowedCapturePolicy, (AAudioStream * stream)) /* API 29 */
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"
// !!! FIXME: Clean out the fprintf and printf calls, replace with SDL_Log
#ifdef SDL_AUDIO_DRIVER_ALSA
#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_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_readi)(snd_pcm_t *, void *, snd_pcm_uframes_t);
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_close);
SDL_ALSA_SYM(snd_pcm_start);
SDL_ALSA_SYM(snd_pcm_writei);
SDL_ALSA_SYM(snd_pcm_readi);
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)
{
const char *device;
SDL_assert(handle != NULL); // SDL2 used NULL to mean "default" but that's not true in SDL3.
if (handle != NULL) {
return (const char *)handle;
}
/* !!! FIXME: we also check "SDL_AUDIO_DEVICE_NAME" at the higher level. */
device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */
if (SDL_strcmp((const char *) handle, "default") == 0) {
const char *device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */
if (device != NULL) {
return device;
}
if (channels == 6) {
} else if (channels == 6) {
return "plug:surround51";
} else if (channels == 4) {
return "plug:surround40";
}
return "default";
}
/* This function waits until it is possible to write a full sound buffer */
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
return (const char *)handle;
}
/* !!! FIXME: is there a channel swizzler in alsalib instead? */
@ -311,15 +288,15 @@ CHANNEL_SWIZZLE(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.
*/
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) \
case chans: \
switch ((_this->spec.format & (0xFF))) { \
switch ((device->spec.format & (0xFF))) { \
case 8: \
swizzle_alsa_channels_##chans##_Uint8(buffer, bufferlen); \
break; \
@ -348,22 +325,44 @@ static void swizzle_alsa_channels(SDL_AudioDevice *_this, void *buffer, Uint32 b
#ifdef SND_CHMAP_API_VERSION
/* 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 */
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 int frame_size = ((SDL_AUDIO_BITSIZE(_this->spec.format)) / 8) *
_this->spec.channels;
snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t)_this->spec.samples);
const snd_pcm_sframes_t needed = (snd_pcm_sframes_t)device->sample_frames;
while (!SDL_AtomicGet(&device->shutdown)) {
const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(device->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_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)) {
int status = ALSA_snd_pcm_writei(_this->hidden->pcm_handle,
device->hidden->swizzle_func(device, sample_buf, frames_left);
while ((frames_left > 0) && !SDL_AtomicGet(&device->shutdown)) {
int status = ALSA_snd_pcm_writei(device->hidden->pcm_handle,
sample_buf, frames_left);
if (status < 0) {
@ -373,21 +372,20 @@ static void ALSA_PlayDevice(SDL_AudioDevice *_this)
SDL_Delay(1);
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) {
/* Hmm, not much we can do - abort */
SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
"ALSA write failed (unrecoverable): %s\n",
"ALSA write failed (unrecoverable): %s",
ALSA_snd_strerror(status));
SDL_OpenedAudioDeviceDisconnected(_this);
SDL_AudioDeviceDisconnected(device);
return;
}
continue;
} else if (status == 0) {
/* No frames were written (no available space in pcm device).
Allow other threads to catch up. */
Uint32 delay = (frames_left / 2 * 1000) / _this->spec.freq;
SDL_Delay(delay);
SDL_Delay((frames_left / 2 * 1000) / device->spec.freq);
}
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;
const int frame_size = ((SDL_AUDIO_BITSIZE(_this->spec.format)) / 8) *
_this->spec.channels;
const int frame_size = ((SDL_AUDIO_BITSIZE(device->spec.format)) / 8) *
device->spec.channels;
const int total_frames = buflen / frame_size;
snd_pcm_uframes_t frames_left = total_frames;
snd_pcm_uframes_t wait_time = frame_size / 2;
SDL_assert((buflen % frame_size) == 0);
while (frames_left > 0 && SDL_AtomicGet(&_this->enabled)) {
int status;
status = ALSA_snd_pcm_readi(_this->hidden->pcm_handle,
while ((frames_left > 0) && !SDL_AtomicGet(&device->shutdown)) {
int status = ALSA_snd_pcm_readi(device->hidden->pcm_handle,
sample_buf, frames_left);
if (status == -EAGAIN) {
ALSA_snd_pcm_wait(_this->hidden->pcm_handle, wait_time);
status = 0;
break; // Can this even happen? Go back to WaitCaptureDevice, where the device lock isn't held.
} else if (status < 0) {
/*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) {
/* Hmm, not much we can do - abort */
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));
return -1;
}
continue;
break; // Go back to WaitCaptureDevice, where the device lock isn't held.
}
/*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;
}
_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;
}
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
ALSA_snd_pcm_drop() can hang, so don't use that.
*/
Uint32 delay = ((_this->spec.samples * 1000) / _this->spec.freq) * 2;
SDL_Delay(delay);
ALSA_snd_pcm_close(_this->hidden->pcm_handle);
SDL_Delay(((device->sample_frames * 1000) / device->spec.freq) * 2);
ALSA_snd_pcm_close(device->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;
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);
/* 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(
_this->hidden->pcm_handle, hwparams, &persize, NULL);
device->hidden->pcm_handle, hwparams, &persize, NULL);
if (status < 0) {
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 */
periods = 2;
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) {
return -1;
}
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) {
return -1;
}
/* "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) {
return -1;
}
_this->spec.samples = persize;
device->sample_frames = persize;
/* This is useful for debugging */
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;
}
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;
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 */
_this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(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();
}
SDL_zerop(_this->hidden);
/* Open the audio device */
/* Name of device should depend on # channels in spec */
snd_pcm_t *pcm_handle = NULL;
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,
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));
}
_this->hidden->pcm_handle = pcm_handle;
device->hidden->pcm_handle = pcm_handle;
/* Figure out what the hardware is capable of */
snd_pcm_hw_params_t *hwparams = NULL;
snd_pcm_hw_params_alloca(&hwparams);
status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams);
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 */
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) {
switch (test_format) {
case SDL_AUDIO_U8:
@ -605,21 +590,22 @@ static int ALSA_OpenDevice(SDL_AudioDevice *_this, const char *devname)
}
}
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
* 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
chmap = ALSA_snd_pcm_get_chmap(pcm_handle);
snd_pcm_chmap_t *chmap = ALSA_snd_pcm_get_chmap(pcm_handle);
if (chmap) {
char chmap_str[64];
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 ||
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() */
@ -628,38 +614,39 @@ static int ALSA_OpenDevice(SDL_AudioDevice *_this, const char *devname)
/* Set the number of channels */
status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
_this->spec.channels);
channels = _this->spec.channels;
device->spec.channels);
unsigned int channels = device->spec.channels;
if (status < 0) {
status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
if (status < 0) {
return SDL_SetError("ALSA: Couldn't set audio channels");
}
_this->spec.channels = channels;
device->spec.channels = channels;
}
/* 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,
&rate, NULL);
if (status < 0) {
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 */
status = ALSA_set_buffer_size(_this, hwparams);
status = ALSA_set_buffer_size(device, hwparams);
if (status < 0) {
return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
}
/* Set the software parameters */
snd_pcm_sw_params_t *swparams = NULL;
snd_pcm_sw_params_alloca(&swparams);
status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
if (status < 0) {
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) {
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));
}
/* Calculate the final parameters for this audio specification */
SDL_CalculateAudioSpec(&_this->spec);
// Calculate the final parameters for this audio specification
SDL_UpdatedAudioDeviceFormat(device);
/* Allocate mixing buffer */
// Allocate mixing buffer
if (!iscapture) {
_this->hidden->mixlen = _this->spec.size;
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->hidden->mixlen);
if (_this->hidden->mixbuf == NULL) {
device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
if (device->hidden->mixbuf == NULL) {
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
@ -692,6 +678,8 @@ static int ALSA_OpenDevice(SDL_AudioDevice *_this, const char *devname)
}
#endif
ALSA_snd_pcm_start(pcm_handle);
/* We're ready to rock and roll. :-) */
return 0;
}
@ -703,7 +691,7 @@ typedef struct ALSA_Device
struct ALSA_Device *next;
} 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));
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 void ALSA_HotplugIteration(void)
static void ALSA_HotplugIteration(SDL_bool *has_default_output, SDL_bool *has_default_capture)
{
void **hints = NULL;
ALSA_Device *dev;
ALSA_Device *unseen;
ALSA_Device *seen;
ALSA_Device *next;
ALSA_Device *prev;
ALSA_Device *unseen = NULL;
ALSA_Device *seen = NULL;
if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) {
int i, j;
const char *match = NULL;
int bestmatch = 0xFFFF;
int has_default = -1;
size_t match_len = 0;
int defaultdev = -1;
static const char *const prefixes[] = {
"hw:", "sysdefault:", "default:", NULL
};
@ -791,18 +775,18 @@ static void ALSA_HotplugIteration(void)
actual hardware. It could be prefixed with "hw:" or "default:"
or "sysdefault:" and maybe others. Go through the list and see
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");
if (name == NULL) {
continue;
}
/* full name, not a prefix */
if ((defaultdev == -1) && (SDL_strcmp(name, "default") == 0)) {
defaultdev = i;
if (SDL_strcmp(name, "default") == 0) {
if (has_default < 0) {
has_default = i;
}
for (j = 0; prefixes[j]; j++) {
} else {
for (int j = 0; prefixes[j]; j++) {
const char *prefix = prefixes[j];
const size_t prefixlen = SDL_strlen(prefix);
if (SDL_strncmp(name, prefix, prefixlen) == 0) {
@ -816,38 +800,45 @@ static void ALSA_HotplugIteration(void)
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) {
continue;
}
/* only want physical hardware interfaces */
if (match == NULL || (SDL_strncmp(name, match, match_len) == 0)) {
// only want physical hardware interfaces
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");
const SDL_bool isoutput = (ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0);
const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0);
SDL_bool have_output = SDL_FALSE;
SDL_bool have_input = SDL_FALSE;
free(ioid); /* This should NOT be SDL_free() */
free(ioid);
if (!isoutput && !isinput) {
free(name); /* This should NOT be SDL_free() */
free(name);
continue;
}
prev = NULL;
for (dev = unseen; dev; dev = next) {
if (is_default) {
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;
if ((SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture))) {
if (prev) {
@ -878,16 +869,18 @@ static void ALSA_HotplugIteration(void)
free(name); /* This should NOT be SDL_free() */
}
}
ALSA_snd_device_name_free_hint(hints);
hotplug_devices = seen; /* now we have a known-good list of attached devices. */
/* 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);*/
next = dev->next;
SDL_RemoveAudioDevice(dev->iscapture, dev->name);
SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle(dev->name));
SDL_free(dev->name);
SDL_free(dev);
}
@ -909,16 +902,25 @@ static int SDLCALL ALSA_HotplugThread(void *arg)
SDL_Delay(100);
}
ALSA_HotplugIteration(); /* run the check. */
ALSA_HotplugIteration(NULL, NULL); /* run the check. */
}
return 0;
}
#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
SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
@ -966,11 +968,11 @@ static SDL_bool ALSA_Init(SDL_AudioDriverImpl *impl)
impl->PlayDevice = ALSA_PlayDevice;
impl->CloseDevice = ALSA_CloseDevice;
impl->Deinitialize = ALSA_Deinitialize;
impl->WaitCaptureDevice = ALSA_WaitDevice;
impl->CaptureFromDevice = ALSA_CaptureFromDevice;
impl->FlushCapture = ALSA_FlushCapture;
impl->HasCaptureSupport = SDL_TRUE;
impl->SupportsNonPow2Samples = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */
}

View File

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

View File

@ -22,7 +22,7 @@
#ifdef SDL_AUDIO_DRIVER_ANDROID
/* Output audio to Android */
// Output audio to Android (legacy interface)
#include "../SDL_sysaudio.h"
#include "../SDL_audio_c.h"
@ -34,185 +34,158 @@
struct SDL_PrivateAudioData
{
/* Resume device if it was paused automatically */
int resume;
int resume; // Resume device if it was paused automatically
};
static SDL_AudioDevice *audioDevice = NULL;
static SDL_AudioDevice *captureDevice = NULL;
static int ANDROIDAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
static int ANDROIDAUDIO_OpenDevice(SDL_AudioDevice *device)
{
SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts;
SDL_bool iscapture = _this->iscapture;
device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
if (device->hidden == NULL) {
return SDL_OutOfMemory();
}
const SDL_bool iscapture = device->iscapture;
if (iscapture) {
if (captureDevice) {
return SDL_SetError("An audio capture device is already opened");
}
}
if (!iscapture) {
captureDevice = device;
} else {
if (audioDevice) {
return SDL_SetError("An audio playback device is already opened");
}
audioDevice = device;
}
if (iscapture) {
captureDevice = _this;
} 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);
SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
while ((test_format = *(closefmts++)) != 0) {
if ((test_format == SDL_AUDIO_U8) ||
(test_format == SDL_AUDIO_S16) ||
(test_format == SDL_AUDIO_F32)) {
_this->spec.format = test_format;
device->spec.format = test_format;
break;
}
}
if (!test_format) {
/* Didn't find a compatible format :( */
return SDL_SetError("%s: Unsupported audio format", "android");
return SDL_SetError("android: Unsupported audio format");
}
{
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) {
if (Android_JNI_OpenAudioDevice(device) < 0) {
return -1;
}
}
SDL_CalculateAudioSpec(&_this->spec);
SDL_UpdatedAudioDeviceFormat(device);
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();
}
static Uint8 *ANDROIDAUDIO_GetDeviceBuf(SDL_AudioDevice *_this)
static Uint8 *ANDROIDAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{
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);
}
static void ANDROIDAUDIO_FlushCapture(SDL_AudioDevice *_this)
static void ANDROIDAUDIO_FlushCapture(SDL_AudioDevice *device)
{
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
so it's safe to terminate the Java side buffer and AudioTrack
*/
Android_JNI_CloseAudioDevice(_this->iscapture);
if (_this->iscapture) {
SDL_assert(captureDevice == _this);
if (device->hidden) {
Android_JNI_CloseAudioDevice(device->iscapture);
if (device->iscapture) {
SDL_assert(captureDevice == device);
captureDevice = NULL;
} else {
SDL_assert(audioDevice == _this);
SDL_assert(audioDevice == device);
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)
{
/* Set the function pointers */
impl->DetectDevices = Android_DetectDevices;
// !!! 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->ThreadInit = Android_AudioThreadInit;
impl->DetectDevices = Android_StartAudioHotplug;
impl->Deinitialize = Android_StopAudioHotplug;
impl->OpenDevice = ANDROIDAUDIO_OpenDevice;
impl->PlayDevice = ANDROIDAUDIO_PlayDevice;
impl->GetDeviceBuf = ANDROIDAUDIO_GetDeviceBuf;
impl->CloseDevice = ANDROIDAUDIO_CloseDevice;
impl->CaptureFromDevice = ANDROIDAUDIO_CaptureFromDevice;
impl->FlushCapture = ANDROIDAUDIO_FlushCapture;
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
/* and the capabilities */
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 = {
"android", "SDL Android audio driver", ANDROIDAUDIO_Init, SDL_FALSE
};
/* Pause (block) all non already paused audio devices by taking their mixer lock */
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 */
#endif // SDL_AUDIO_DRIVER_ANDROID

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -22,34 +22,34 @@
#ifdef SDL_AUDIO_DRIVER_DSOUND
/* Allow access to a raw mixing buffer */
#include "../SDL_audio_c.h"
#include "SDL_directsound.h"
#include <mmreg.h>
#ifdef HAVE_MMDEVICEAPI_H
#include "../../core/windows/SDL_immdevice.h"
#endif /* HAVE_MMDEVICEAPI_H */
#endif
#ifndef WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif
/* For Vista+, we can enumerate DSound devices with IMMDevice */
// For Vista+, we can enumerate DSound devices with IMMDevice
#ifdef HAVE_MMDEVICEAPI_H
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;
typedef HRESULT(WINAPI *fnDirectSoundCreate8)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
typedef HRESULT(WINAPI *fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
typedef HRESULT(WINAPI *fnDirectSoundCaptureCreate8)(LPCGUID, LPDIRECTSOUNDCAPTURE8 *, LPUNKNOWN);
typedef HRESULT(WINAPI *fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
typedef HRESULT(WINAPI *fnGetDeviceID)(LPCGUID, LPGUID);
static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL;
static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL;
static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = 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_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
@ -60,6 +60,7 @@ static void DSOUND_Unload(void)
pDirectSoundEnumerateW = NULL;
pDirectSoundCaptureCreate8 = NULL;
pDirectSoundCaptureEnumerateW = NULL;
pGetDeviceID = NULL;
if (DSoundDLL != NULL) {
SDL_UnloadObject(DSoundDLL);
@ -77,18 +78,19 @@ static int DSOUND_Load(void)
if (DSoundDLL == NULL) {
SDL_SetError("DirectSound: failed to load DSOUND.DLL");
} else {
/* Now make sure we have DirectX 8 or better... */
// Now make sure we have DirectX 8 or better...
#define DSOUNDLOAD(f) \
{ \
p##f = (fn##f)SDL_LoadFunction(DSoundDLL, #f); \
if (!p##f) \
loaded = 0; \
}
loaded = 1; /* will reset if necessary. */
loaded = 1; // will reset if necessary.
DSOUNDLOAD(DirectSoundCreate8);
DSOUNDLOAD(DirectSoundEnumerateW);
DSOUNDLOAD(DirectSoundCaptureCreate8);
DSOUNDLOAD(DirectSoundCaptureEnumerateW);
DSOUNDLOAD(GetDeviceID);
#undef DSOUNDLOAD
if (!loaded) {
@ -149,56 +151,81 @@ static int SetDSerror(const char *function, int code)
return SDL_SetError("%s: %s (0x%x)", function, error, code);
}
static void DSOUND_FreeDeviceHandle(void *handle)
{
SDL_free(handle);
}
static int DSOUND_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
static void DSOUND_FreeDeviceHandle(SDL_AudioDevice *device)
{
#ifdef HAVE_MMDEVICEAPI_H
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);
if (guid != NULL) { /* skip default device */
SDL_bool iscapture;
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);
if (str != NULL) {
LPGUID cpyguid = (LPGUID)SDL_malloc(sizeof(GUID));
if (cpyguid) {
SDL_memcpy(cpyguid, guid, sizeof(GUID));
/* Note that spec is NULL, because we are required to connect to the
* device before getting the channel mask and output format, making
* this information inaccessible at enumeration time
*/
SDL_AddAudioDevice(iscapture, str, NULL, cpyguid);
SDL_free(str); /* addfn() makes a copy of this string. */
SDL_AudioDevice *device = SDL_AddAudioDevice(data->iscapture, str, NULL, cpyguid);
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
if (SupportsIMMDevice) {
SDL_IMMDevice_EnumerateEndpoints(SDL_TRUE);
} else {
#endif /* HAVE_MMDEVICEAPI_H */
pDirectSoundCaptureEnumerateW(FindAllDevs, (void *)((size_t)1));
pDirectSoundEnumerateW(FindAllDevs, (void *)((size_t)0));
#ifdef HAVE_MMDEVICEAPI_H
}
#endif /* HAVE_MMDEVICEAPI_H*/
SDL_IMMDevice_EnumerateEndpoints(default_output, default_capture);
} else
#endif
{
// Without IMMDevice, you can enumerate devices and figure out the default devices,
// 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.
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 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
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);
if (result != DS_OK) {
if (result == DSERR_BUFFERLOST) {
IDirectSoundBuffer_Restore(_this->hidden->mixbuf);
IDirectSoundBuffer_Restore(device->hidden->mixbuf);
}
#ifdef DEBUG_SOUND
SetDSerror("DirectSound GetCurrentPosition", result);
@ -220,21 +247,24 @@ static void DSOUND_WaitDevice(SDL_AudioDevice *_this)
return;
}
while ((cursor / _this->spec.size) == _this->hidden->lastchunk) {
/* FIXME: find out how much time is left and sleep that long */
while ((cursor / device->buffer_size) == device->hidden->lastchunk) {
if (SDL_AtomicGet(&device->shutdown)) {
return;
}
SDL_Delay(1);
/* Try to restore a lost sound buffer */
IDirectSoundBuffer_GetStatus(_this->hidden->mixbuf, &status);
// Try to restore a lost sound buffer
IDirectSoundBuffer_GetStatus(device->hidden->mixbuf, &status);
if (status & DSBSTATUS_BUFFERLOST) {
IDirectSoundBuffer_Restore(_this->hidden->mixbuf);
IDirectSoundBuffer_GetStatus(_this->hidden->mixbuf, &status);
IDirectSoundBuffer_Restore(device->hidden->mixbuf);
IDirectSoundBuffer_GetStatus(device->hidden->mixbuf, &status);
if (status & DSBSTATUS_BUFFERLOST) {
break;
}
}
if (!(status & DSBSTATUS_PLAYING)) {
result = IDirectSoundBuffer_Play(_this->hidden->mixbuf, 0, 0,
result = IDirectSoundBuffer_Play(device->hidden->mixbuf, 0, 0,
DSBPLAY_LOOPING);
if (result == DS_OK) {
continue;
@ -245,8 +275,8 @@ static void DSOUND_WaitDevice(SDL_AudioDevice *_this)
return;
}
/* Find out where we are playing */
result = IDirectSoundBuffer_GetCurrentPosition(_this->hidden->mixbuf,
// Find out where we are playing
result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf,
&junk, &cursor);
if (result != DS_OK) {
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 */
if (_this->hidden->locked_buf) {
IDirectSoundBuffer_Unlock(_this->hidden->mixbuf,
_this->hidden->locked_buf,
_this->spec.size, NULL, 0);
}
// Unlock the buffer, allowing it to play
SDL_assert(buflen == device->buffer_size);
IDirectSoundBuffer_Unlock(device->hidden->mixbuf, (LPVOID) buffer, buflen, NULL, 0);
}
static Uint8 *DSOUND_GetDeviceBuf(SDL_AudioDevice *_this)
static Uint8 *DSOUND_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{
DWORD cursor = 0;
DWORD junk = 0;
HRESULT result = DS_OK;
DWORD rawlen = 0;
/* Figure out which blocks to fill next */
_this->hidden->locked_buf = NULL;
result = IDirectSoundBuffer_GetCurrentPosition(_this->hidden->mixbuf,
SDL_assert(*buffer_size == device->buffer_size);
// Figure out which blocks to fill next
device->hidden->locked_buf = NULL;
result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf,
&junk, &cursor);
if (result == DSERR_BUFFERLOST) {
IDirectSoundBuffer_Restore(_this->hidden->mixbuf);
result = IDirectSoundBuffer_GetCurrentPosition(_this->hidden->mixbuf,
IDirectSoundBuffer_Restore(device->hidden->mixbuf);
result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf,
&junk, &cursor);
}
if (result != DS_OK) {
SetDSerror("DirectSound GetCurrentPosition", result);
return NULL;
}
cursor /= _this->spec.size;
cursor /= device->buffer_size;
#ifdef DEBUG_SOUND
/* Detect audio dropouts */
// Detect audio dropouts
{
DWORD spot = cursor;
if (spot < _this->hidden->lastchunk) {
spot += _this->hidden->num_buffers;
if (spot < device->hidden->lastchunk) {
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",
(spot - (_this->hidden->lastchunk + 1)));
(spot - (device->hidden->lastchunk + 1)));
}
}
#endif
_this->hidden->lastchunk = cursor;
cursor = (cursor + 1) % _this->hidden->num_buffers;
cursor *= _this->spec.size;
device->hidden->lastchunk = cursor;
cursor = (cursor + 1) % device->hidden->num_buffers;
cursor *= device->buffer_size;
/* Lock the audio buffer */
result = IDirectSoundBuffer_Lock(_this->hidden->mixbuf, cursor,
_this->spec.size,
(LPVOID *)&_this->hidden->locked_buf,
// Lock the audio buffer
DWORD rawlen = 0;
result = IDirectSoundBuffer_Lock(device->hidden->mixbuf, cursor,
device->buffer_size,
(LPVOID *)&device->hidden->locked_buf,
&rawlen, NULL, &junk, 0);
if (result == DSERR_BUFFERLOST) {
IDirectSoundBuffer_Restore(_this->hidden->mixbuf);
result = IDirectSoundBuffer_Lock(_this->hidden->mixbuf, cursor,
_this->spec.size,
(LPVOID *)&_this->hidden->locked_buf, &rawlen, NULL,
IDirectSoundBuffer_Restore(device->hidden->mixbuf);
result = IDirectSoundBuffer_Lock(device->hidden->mixbuf, cursor,
device->buffer_size,
(LPVOID *)&device->hidden->locked_buf, &rawlen, NULL,
&junk, 0);
}
if (result != DS_OK) {
SetDSerror("DirectSound Lock", result);
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;
DWORD junk, cursor, ptr1len, ptr2len;
struct SDL_PrivateAudioData *h = device->hidden;
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;
SDL_assert((Uint32)buflen == _this->spec.size);
SDL_assert(buflen == device->buffer_size);
while (SDL_TRUE) {
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) {
if (IDirectSoundCaptureBuffer_Lock(h->capturebuf, h->lastchunk * buflen, buflen, &ptr1, &ptr1len, &ptr2, &ptr2len, 0) != DS_OK) {
return -1;
}
SDL_assert(ptr1len == _this->spec.size);
SDL_assert(ptr1len == buflen);
SDL_assert(ptr2 == NULL);
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;
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;
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) {
IDirectSoundBuffer_Stop(_this->hidden->mixbuf);
IDirectSoundBuffer_Release(_this->hidden->mixbuf);
if (device->hidden) {
if (device->hidden->mixbuf != NULL) {
IDirectSoundBuffer_Stop(device->hidden->mixbuf);
IDirectSoundBuffer_Release(device->hidden->mixbuf);
}
if (_this->hidden->sound != NULL) {
IDirectSound_Release(_this->hidden->sound);
if (device->hidden->sound != NULL) {
IDirectSound_Release(device->hidden->sound);
}
if (_this->hidden->capturebuf != NULL) {
IDirectSoundCaptureBuffer_Stop(_this->hidden->capturebuf);
IDirectSoundCaptureBuffer_Release(_this->hidden->capturebuf);
if (device->hidden->capturebuf != NULL) {
IDirectSoundCaptureBuffer_Stop(device->hidden->capturebuf);
IDirectSoundCaptureBuffer_Release(device->hidden->capturebuf);
}
if (_this->hidden->capture != NULL) {
IDirectSoundCapture_Release(_this->hidden->capture);
if (device->hidden->capture != NULL) {
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
number of audio chunks available in the created buffer. This is for
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;
LPDIRECTSOUNDBUFFER *sndbuf = &_this->hidden->mixbuf;
LPDIRECTSOUND sndObj = device->hidden->sound;
LPDIRECTSOUNDBUFFER *sndbuf = &device->hidden->mixbuf;
HRESULT result = DS_OK;
DSBUFFERDESC format;
LPVOID pvAudioPtr1, pvAudioPtr2;
DWORD dwAudioBytes1, dwAudioBytes2;
/* Try to create the secondary buffer */
// Try to create the secondary buffer
SDL_zero(format);
format.dwSize = sizeof(format);
format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
@ -419,30 +450,29 @@ static int CreateSecondary(SDL_AudioDevice *_this, const DWORD bufsize, WAVEFORM
}
IDirectSoundBuffer_SetFormat(*sndbuf, wfmt);
/* Silence the initial audio buffer */
// Silence the initial audio buffer
result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes,
(LPVOID *)&pvAudioPtr1, &dwAudioBytes1,
(LPVOID *)&pvAudioPtr2, &dwAudioBytes2,
DSBLOCK_ENTIREBUFFER);
if (result == DS_OK) {
SDL_memset(pvAudioPtr1, _this->spec.silence, dwAudioBytes1);
SDL_memset(pvAudioPtr1, device->silence_value, dwAudioBytes1);
IDirectSoundBuffer_Unlock(*sndbuf,
(LPVOID)pvAudioPtr1, dwAudioBytes1,
(LPVOID)pvAudioPtr2, dwAudioBytes2);
}
/* We're ready to go */
return 0;
return 0; // We're ready to go
}
/* This function tries to create a capture buffer, and returns the
number of audio chunks available in the created buffer. This is for
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;
LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &_this->hidden->capturebuf;
LPDIRECTSOUNDCAPTURE capture = device->hidden->capture;
LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &device->hidden->capturebuf;
DSCBUFFERDESC format;
HRESULT result;
@ -464,7 +494,7 @@ static int CreateCaptureBuffer(SDL_AudioDevice *_this, const DWORD bufsize, WAVE
}
#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);
if (result != DS_OK) {
IDirectSoundCaptureBuffer_Stop(*capturebuf);
@ -472,42 +502,45 @@ static int CreateCaptureBuffer(SDL_AudioDevice *_this, const DWORD bufsize, WAVE
return SetDSerror("DirectSound GetCurrentPosition", result);
}
_this->hidden->lastchunk = cursor / _this->spec.size;
device->hidden->lastchunk = cursor / device->buffer_size;
#endif
return 0;
}
static int DSOUND_OpenDevice(SDL_AudioDevice *_this, const char *devname)
static int DSOUND_OpenDevice(SDL_AudioDevice *device)
{
const DWORD numchunks = 8;
HRESULT result;
SDL_bool tried_format = SDL_FALSE;
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) {
// Initialize all variables that we clean on shutdown
device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
if (device->hidden == NULL) {
return SDL_OutOfMemory();
}
SDL_zerop(_this->hidden);
/* Open the audio device */
if (iscapture) {
result = pDirectSoundCaptureCreate8(guid, &_this->hidden->capture, NULL);
// Open the audio device
LPGUID guid;
#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) {
return SetDSerror("DirectSoundCaptureCreate8", result);
}
} else {
result = pDirectSoundCreate8(guid, &_this->hidden->sound, NULL);
result = pDirectSoundCreate8(guid, &device->hidden->sound, NULL);
if (result != DS_OK) {
return SetDSerror("DirectSoundCreate8", result);
}
result = IDirectSound_SetCooperativeLevel(_this->hidden->sound,
result = IDirectSound_SetCooperativeLevel(device->hidden->sound,
GetDesktopWindow(),
DSSCL_NORMAL);
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) {
switch (test_format) {
case SDL_AUDIO_U8:
@ -524,69 +560,68 @@ static int DSOUND_OpenDevice(SDL_AudioDevice *_this, const char *devname)
case SDL_AUDIO_F32:
tried_format = SDL_TRUE;
_this->spec.format = test_format;
device->spec.format = test_format;
/* Update the fragment size as size in bytes */
SDL_CalculateAudioSpec(&_this->spec);
// Update the fragment size as size in bytes
SDL_UpdatedAudioDeviceFormat(device);
bufsize = numchunks * _this->spec.size;
const DWORD bufsize = numchunks * device->buffer_size;
if ((bufsize < DSBSIZE_MIN) || (bufsize > DSBSIZE_MAX)) {
SDL_SetError("Sound buffer size must be between %d and %d",
(int)((DSBSIZE_MIN < numchunks) ? 1 : DSBSIZE_MIN / numchunks),
(int)(DSBSIZE_MAX / numchunks));
} else {
int rc;
WAVEFORMATEXTENSIBLE wfmt;
SDL_zero(wfmt);
if (_this->spec.channels > 2) {
if (device->spec.channels > 2) {
wfmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
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));
} else {
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) {
case 3: /* 3.0 (or 2.1) */
switch (device->spec.channels) {
case 3: // 3.0 (or 2.1)
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER;
break;
case 4: /* 4.0 */
case 4: // 4.0
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
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;
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;
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;
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;
break;
default:
SDL_assert(0 && "Unsupported channel count!");
break;
}
} else if (SDL_AUDIO_ISFLOAT(_this->spec.format)) {
} else if (SDL_AUDIO_ISFLOAT(device->spec.format)) {
wfmt.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
} else {
wfmt.Format.wFormatTag = WAVE_FORMAT_PCM;
}
wfmt.Format.wBitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format);
wfmt.Format.nChannels = _this->spec.channels;
wfmt.Format.nSamplesPerSec = _this->spec.freq;
wfmt.Format.wBitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
wfmt.Format.nChannels = device->spec.channels;
wfmt.Format.nSamplesPerSec = device->spec.freq;
wfmt.Format.nBlockAlign = wfmt.Format.nChannels * (wfmt.Format.wBitsPerSample / 8);
wfmt.Format.nAvgBytesPerSec = wfmt.Format.nSamplesPerSec * wfmt.Format.nBlockAlign;
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) {
_this->hidden->num_buffers = numchunks;
device->hidden->num_buffers = numchunks;
break;
}
}
@ -599,14 +634,14 @@ static int DSOUND_OpenDevice(SDL_AudioDevice *_this, const char *devname)
if (!test_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");
}
/* 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)
@ -616,7 +651,7 @@ static void DSOUND_Deinitialize(void)
SDL_IMMDevice_Quit();
SupportsIMMDevice = SDL_FALSE;
}
#endif /* HAVE_MMDEVICEAPI_H */
#endif
DSOUND_Unload();
}
@ -628,29 +663,27 @@ static SDL_bool DSOUND_Init(SDL_AudioDriverImpl *impl)
#ifdef HAVE_MMDEVICEAPI_H
SupportsIMMDevice = !(SDL_IMMDevice_Init() < 0);
#endif /* HAVE_MMDEVICEAPI_H */
#endif
/* Set the function pointers */
impl->DetectDevices = DSOUND_DetectDevices;
impl->OpenDevice = DSOUND_OpenDevice;
impl->PlayDevice = DSOUND_PlayDevice;
impl->WaitDevice = DSOUND_WaitDevice;
impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
impl->WaitCaptureDevice = DSOUND_WaitCaptureDevice;
impl->CaptureFromDevice = DSOUND_CaptureFromDevice;
impl->FlushCapture = DSOUND_FlushCapture;
impl->CloseDevice = DSOUND_CloseDevice;
impl->FreeDeviceHandle = DSOUND_FreeDeviceHandle;
impl->Deinitialize = DSOUND_Deinitialize;
impl->GetDefaultAudioInfo = DSOUND_GetDefaultAudioInfo;
impl->HasCaptureSupport = SDL_TRUE;
impl->SupportsNonPow2Samples = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */
return SDL_TRUE;
}
AudioBootStrap DSOUND_bootstrap = {
"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"
/* The DirectSound objects */
// The DirectSound objects
struct SDL_PrivateAudioData
{
// !!! FIXME: make this a union with capture/playback sections?
LPDIRECTSOUND sound;
LPDIRECTSOUNDBUFFER mixbuf;
LPDIRECTSOUNDCAPTURE capture;
@ -39,4 +40,4 @@ struct SDL_PrivateAudioData
Uint8 *locked_buf;
};
#endif /* SDL_directsound_h_ */
#endif // SDL_directsound_h_

View File

@ -22,171 +22,151 @@
#ifdef SDL_AUDIO_DRIVER_DISK
/* Output raw audio data to a file. */
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
// Output raw audio data to a file.
#include "../SDL_audio_c.h"
#include "SDL_diskaudio.h"
/* !!! FIXME: these should be SDL hints, not environment variables. */
/* environment variables and defaults. */
// !!! FIXME: these should be SDL hints, not environment variables.
// environment variables and defaults.
#define DISKENVR_OUTFILE "SDL_DISKAUDIOFILE"
#define DISKDEFAULT_OUTFILE "sdlaudio.raw"
#define DISKENVR_INFILE "SDL_DISKAUDIOFILEIN"
#define DISKDEFAULT_INFILE "sdlaudio-in.raw"
#define DISKENVR_IODELAY "SDL_DISKAUDIODELAY"
/* This function waits until it is possible to write a full sound buffer */
static void DISKAUDIO_WaitDevice(SDL_AudioDevice *_this)
static void DISKAUDIO_WaitDevice(SDL_AudioDevice *device)
{
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,
_this->hidden->mixbuf,
_this->spec.size);
/* If we couldn't write, assume fatal error for now */
if (written != _this->spec.size) {
SDL_OpenedAudioDeviceDisconnected(_this);
const int written = (int)SDL_RWwrite(device->hidden->io, buffer, (size_t)buffer_size);
if (written != buffer_size) { // If we couldn't write, assume fatal error for now
SDL_AudioDeviceDisconnected(device);
}
#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
}
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;
SDL_Delay(h->io_delay);
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;
buffer = ((Uint8 *)buffer) + br;
if (buflen > 0) { /* EOF (or error, but whatever). */
if (buflen > 0) { // EOF (or error, but whatever).
SDL_RWclose(h->io);
h->io = NULL;
}
}
/* if we ran out of file, just write silence. */
SDL_memset(buffer, _this->spec.silence, buflen);
// if we ran out of file, just write silence.
SDL_memset(buffer, device->silence_value, buflen);
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) {
SDL_RWclose(_this->hidden->io);
if (device->hidden) {
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) {
devname = SDL_getenv(iscapture ? DISKENVR_INFILE : DISKENVR_OUTFILE);
const char *devname = SDL_getenv(iscapture ? DISKENVR_INFILE : DISKENVR_OUTFILE);
if (devname == NULL) {
devname = iscapture ? DISKDEFAULT_INFILE : DISKDEFAULT_OUTFILE;
}
}
return devname;
}
static int DISKAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
static int DISKAUDIO_OpenDevice(SDL_AudioDevice *device)
{
void *handle = _this->handle;
/* handle != NULL means "user specified the placeholder name on the fake detected device list" */
SDL_bool iscapture = _this->iscapture;
const char *fname = get_filename(iscapture, handle ? NULL : devname);
SDL_bool iscapture = device->iscapture;
const char *fname = get_filename(iscapture);
const char *envr = SDL_getenv(DISKENVR_IODELAY);
_this->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc(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();
}
SDL_zerop(_this->hidden);
if (envr != NULL) {
_this->hidden->io_delay = SDL_atoi(envr);
device->hidden->io_delay = SDL_atoi(envr);
} 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 */
_this->hidden->io = SDL_RWFromFile(fname, iscapture ? "rb" : "wb");
if (_this->hidden->io == NULL) {
// Open the "audio device"
device->hidden->io = SDL_RWFromFile(fname, iscapture ? "rb" : "wb");
if (device->hidden->io == NULL) {
return -1;
}
/* Allocate mixing buffer */
// Allocate mixing buffer
if (!iscapture) {
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->spec.size);
if (_this->hidden->mixbuf == NULL) {
device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
if (device->hidden->mixbuf == NULL) {
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,
"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, "You are using the SDL disk i/o audio driver!");
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;
return 0; // We're ready to rock and roll. :-)
}
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);
SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *)0x2);
*default_output = SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *)0x1);
*default_capture = SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *)0x2);
}
static SDL_bool DISKAUDIO_Init(SDL_AudioDriverImpl *impl)
{
/* Set the function pointers */
impl->OpenDevice = DISKAUDIO_OpenDevice;
impl->WaitDevice = DISKAUDIO_WaitDevice;
impl->WaitCaptureDevice = DISKAUDIO_WaitDevice;
impl->PlayDevice = DISKAUDIO_PlayDevice;
impl->GetDeviceBuf = DISKAUDIO_GetDeviceBuf;
impl->CaptureFromDevice = DISKAUDIO_CaptureFromDevice;
impl->FlushCapture = DISKAUDIO_FlushCapture;
impl->CloseDevice = DISKAUDIO_CloseDevice;
impl->DetectDevices = DISKAUDIO_DetectDevices;
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
impl->HasCaptureSupport = SDL_TRUE;
impl->SupportsNonPow2Samples = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */
return SDL_TRUE;
}
AudioBootStrap DISKAUDIO_bootstrap = {
"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"
#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 <string.h> /* For strerror() */
@ -38,82 +38,69 @@
#include "../SDL_audio_c.h"
#include "../SDL_audiodev_c.h"
#include "../../SDL_utils_c.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) {
close(_this->hidden->audio_fd);
if (device->hidden) {
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;
const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
int format = 0;
int value;
int frag_spec;
SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts;
/* 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.
// (I don't know which of these are actually legal values, though...)
if (device->spec.channels > 8) {
device->spec.channels = 8;
} else if (device->spec.channels > 4) {
device->spec.channels = 4;
} else if (device->spec.channels > 2) {
device->spec.channels = 2;
}
/* Make sure fragment size stays a power of 2, or OSS fails. */
/* I don't know which of these are actually legal values, though... */
if (_this->spec.channels > 8) {
_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) {
// Initialize all variables that we clean on shutdown
device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
if (device->hidden == NULL) {
return SDL_OutOfMemory();
}
SDL_zerop(_this->hidden);
/* Open the audio device */
_this->hidden->audio_fd = open(devname, flags | O_CLOEXEC, 0);
if (_this->hidden->audio_fd < 0) {
return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
// Open the audio device; we hardcode the device path in `device->name` for lack of better info, so use that.
const int flags = ((device->iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
device->hidden->audio_fd = open(device->name, flags | O_CLOEXEC, 0);
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;
ctlflags = fcntl(_this->hidden->audio_fd, F_GETFL);
ctlflags &= ~O_NONBLOCK;
if (fcntl(_this->hidden->audio_fd, F_SETFL, ctlflags) < 0) {
const long ctlflags = fcntl(device->hidden->audio_fd, F_GETFL) & ~O_NONBLOCK;
if (fcntl(device->hidden->audio_fd, F_SETFL, ctlflags) < 0) {
return SDL_SetError("Couldn't set audio blocking mode");
}
}
/* Get a list of supported hardware formats */
if (ioctl(_this->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
// Get a list of supported hardware formats
int value;
if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
perror("SNDCTL_DSP_GETFMTS");
return SDL_SetError("Couldn't get audio format list");
}
/* 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) {
#ifdef DEBUG_AUDIO
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;
}
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:
continue;
}
@ -153,40 +130,43 @@ static int DSP_OpenDevice(SDL_AudioDevice *_this, const char *devname)
if (format == 0) {
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;
if ((ioctl(_this->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
if ((ioctl(device->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
(value != format)) {
perror("SNDCTL_DSP_SETFMT");
return SDL_SetError("Couldn't set audio format");
}
/* Set the number of channels of output */
value = _this->spec.channels;
if (ioctl(_this->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
// Set the number of channels of output
value = device->spec.channels;
if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
perror("SNDCTL_DSP_CHANNELS");
return SDL_SetError("Cannot set the number of channels");
}
_this->spec.channels = value;
device->spec.channels = value;
/* Set the DSP frequency */
value = _this->spec.freq;
if (ioctl(_this->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
// Set the DSP frequency
value = device->spec.freq;
if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
perror("SNDCTL_DSP_SPEED");
return SDL_SetError("Couldn't set audio frequency");
}
_this->spec.freq = value;
device->spec.freq = value;
/* Calculate the final parameters for this audio specification */
SDL_CalculateAudioSpec(&_this->spec);
SDL_UpdatedAudioDeviceFormat(device);
/* Determine the power of two of the fragment size */
for (frag_spec = 0; (0x01U << frag_spec) < _this->spec.size; ++frag_spec) {
}
if ((0x01U << frag_spec) != _this->spec.size) {
return SDL_SetError("Fragment size must be a power of two");
/* Determine the power of two of the fragment size
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. */
SDL_assert(SDL_powerof2(device->buffer_size) == device->buffer_size);
int frag_spec = 0;
while ((0x01U << frag_spec) < device->buffer_size) {
frag_spec++;
}
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",
(frag_spec >> 16), 1 << (frag_spec & 0xFFFF));
#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");
}
#ifdef DEBUG_AUDIO
{
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, "fragstotal = %d\n", info.fragstotal);
fprintf(stderr, "fragsize = %d\n", info.fragsize);
@ -210,44 +190,67 @@ static int DSP_OpenDevice(SDL_AudioDevice *_this, const char *devname)
#endif
/* Allocate mixing buffer */
if (!iscapture) {
_this->hidden->mixlen = _this->spec.size;
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->hidden->mixlen);
if (_this->hidden->mixbuf == NULL) {
if (!device->iscapture) {
device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
if (device->hidden->mixbuf == NULL) {
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;
return 0; // We're ready to rock and roll. :-)
}
static void DSP_PlayDevice(SDL_AudioDevice *_this)
static void DSP_WaitDevice(SDL_AudioDevice *device)
{
struct SDL_PrivateAudioData *h = _this->hidden;
if (write(h->audio_fd, h->mixbuf, h->mixlen) == -1) {
const unsigned long ioctlreq = device->iscapture ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE;
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");
SDL_OpenedAudioDeviceDisconnected(_this);
SDL_AudioDeviceDisconnected(device);
return;
}
#ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", h->mixlen);
#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;
if (ioctl(h->audio_fd, SNDCTL_DSP_GETISPACE, &info) == 0) {
while (info.bytes > 0) {
@ -263,17 +266,17 @@ static void DSP_FlushCapture(SDL_AudioDevice *_this)
}
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. */
/* 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)
{
InitTimeDevicesExist = SDL_FALSE;
SDL_EnumUnixAudioDevices(0, look_for_devices_test);
SDL_EnumUnixAudioDevices(SDL_FALSE, look_for_devices_test);
if (!InitTimeDevicesExist) {
SDL_SetError("dsp: No such audio device");
return SDL_FALSE; /* maybe try a different backend. */
@ -282,9 +285,11 @@ static SDL_bool DSP_Init(SDL_AudioDriverImpl *impl)
/* Set the function pointers */
impl->DetectDevices = DSP_DetectDevices;
impl->OpenDevice = DSP_OpenDevice;
impl->WaitDevice = DSP_WaitDevice;
impl->PlayDevice = DSP_PlayDevice;
impl->GetDeviceBuf = DSP_GetDeviceBuf;
impl->CloseDevice = DSP_CloseDevice;
impl->WaitCaptureDevice = DSP_WaitDevice;
impl->CaptureFromDevice = DSP_CaptureFromDevice;
impl->FlushCapture = DSP_FlushCapture;
@ -295,7 +300,7 @@ static SDL_bool DSP_Init(SDL_AudioDriverImpl *impl)
}
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 */

View File

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

View File

@ -20,39 +20,75 @@
*/
#include "SDL_internal.h"
/* Output audio to nowhere... */
// Output audio to nowhere...
#include "../SDL_audio_c.h"
#include "SDL_dummyaudio.h"
static int DUMMYAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
{
_this->hidden = (void *)0x1; /* just something non-NULL */
// !!! FIXME: this should be an SDL hint, not an environment variable.
#define DUMMYENVR_IODELAY "SDL_DUMMYAUDIODELAY"
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. */
SDL_Delay((_this->spec.samples * 1000) / _this->spec.freq);
const char *envr = SDL_getenv(DUMMYENVR_IODELAY);
/* always return a full buffer of silence. */
SDL_memset(buffer, _this->spec.silence, buflen);
device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
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;
}
static SDL_bool DUMMYAUDIO_Init(SDL_AudioDriverImpl *impl)
{
/* Set the function pointers */
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->OnlyHasDefaultOutputDevice = SDL_TRUE;
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
impl->HasCaptureSupport = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */
return SDL_TRUE;
}
AudioBootStrap DUMMYAUDIO_bootstrap = {

View File

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

View File

@ -27,15 +27,18 @@
#include <emscripten/emscripten.h>
/* !!! FIXME: this currently expects that the audio callback runs in the main thread,
!!! FIXME: in intervals when the application isn't running, but that may not be
!!! 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;
// just turn off clang-format for this whole file, this INDENT_OFF stuff on
// each EM_ASM section is ugly.
/* *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({
var SDL3 = Module['SDL3'];
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) {
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);
/* *INDENT-ON* */ /* clang-format on */
}, buffer, buffer_size / framelen);
}
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;
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_OutputAudioThreadIterate(device);
}
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. */
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)
static void EMSCRIPTENAUDIO_FlushCapture(SDL_AudioDevice *device)
{
SDL_AudioCallback callback = _this->callbackspec.callback;
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;
// Do nothing, the new data will just be dropped.
}
/* *INDENT-OFF* */ /* clang-format off */
static int EMSCRIPTENAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{
MAIN_THREAD_EM_ASM({
var SDL3 = Module['SDL3'];
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!';
}
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) {
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);
/* *INDENT-ON* */ /* clang-format on */
}, buffer, (buflen / sizeof(float)) / device->spec.channels);
/* okay, we've got an interleaved float32 array in C now. */
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);
return buflen;
}
while (SDL_GetAudioStreamAvailable(_this->stream) >= stream_len) {
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)
static void HandleCaptureProcess(SDL_AudioDevice *device) // this fires when the main thread is idle.
{
/* *INDENT-OFF* */ /* clang-format off */
SDL_CaptureAudioThreadIterate(device);
}
static void EMSCRIPTENAUDIO_CloseDevice(SDL_AudioDevice *device)
{
if (!device->hidden) {
return;
}
MAIN_THREAD_EM_ASM({
var SDL3 = Module['SDL3'];
if ($0) {
@ -188,29 +140,23 @@ static void EMSCRIPTENAUDIO_CloseDevice(SDL_AudioDevice *_this)
SDL3.audioContext.close();
SDL3.audioContext = undefined;
}
}, _this->iscapture);
/* *INDENT-ON* */ /* clang-format on */
}, device->iscapture);
#if 0 /* !!! FIXME: currently not used. Can we move some stuff off the SDL3 namespace? --ryan. */
SDL_free(_this->hidden);
#endif
SDL_free(device->hidden->mixbuf);
SDL_free(device->hidden);
device->hidden = NULL;
SDL_AudioThreadFinalize(device);
}
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;
const SDL_AudioFormat *closefmts;
SDL_bool iscapture = _this->iscapture;
int result;
// based on parts of library_sdl.js
/* based on parts of library_sdl.js */
/* *INDENT-OFF* */ /* clang-format off */
/* create context */
result = MAIN_THREAD_EM_ASM_INT({
// create context
const int result = MAIN_THREAD_EM_ASM_INT({
if (typeof(Module['SDL3']) === 'undefined') {
Module['SDL3'] = {};
}
@ -232,57 +178,41 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devnam
}
}
return SDL3.audioContext === undefined ? -1 : 0;
}, iscapture);
/* *INDENT-ON* */ /* clang-format on */
}, device->iscapture);
if (result < 0) {
return SDL_SetError("Web Audio API is not available!");
}
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
while ((test_format = *(closefmts++)) != 0) {
switch (test_format) {
case SDL_AUDIO_F32: /* web audio only supports floats */
break;
default:
continue;
}
break;
}
device->spec.format = SDL_AUDIO_F32; // web audio only supports floats
if (!test_format) {
/* Didn't find a compatible format :( */
return SDL_SetError("%s: Unsupported audio format", "emscripten");
}
_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) {
// Initialize all variables that we clean on shutdown
device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
if (device->hidden == NULL) {
return SDL_OutOfMemory();
}
SDL_zerop(_this->hidden);
#endif
_this->hidden = (struct SDL_PrivateAudioData *)0x1;
/* limit to native freq */
_this->spec.freq = EM_ASM_INT({
var SDL3 = Module['SDL3'];
return SDL3.audioContext.sampleRate;
});
// limit to native freq
device->spec.freq = EM_ASM_INT({ return Module['SDL3'].audioContext.sampleRate; });
SDL_CalculateAudioSpec(&_this->spec);
SDL_UpdatedAudioDeviceFormat(device);
/* *INDENT-OFF* */ /* clang-format off */
if (iscapture) {
if (!device->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
audio graph where we can pass it through a ScriptProcessorNode
to access the raw PCM samples and push them to the SDL app's
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
callback whenever there's enough data to fire down into the app.
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.');
};
/* 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.getChannelData(0).fill(0.0);
var silence_callback = function() {
@ -332,9 +262,9 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devnam
} else if (navigator.webkitGetUserMedia !== undefined) {
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 {
/* setup a ScriptProcessorNode */
// setup a ScriptProcessorNode
MAIN_THREAD_EM_ASM({
var SDL3 = Module['SDL3'];
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]);
};
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;
}
static void EMSCRIPTENAUDIO_LockOrUnlockDeviceWithNoMixerLock(SDL_AudioDevice *device)
{
}
static SDL_bool EMSCRIPTENAUDIO_Init(SDL_AudioDriverImpl *impl)
{
SDL_bool available, capture_available;
/* Set the function pointers */
impl->OpenDevice = EMSCRIPTENAUDIO_OpenDevice;
impl->CloseDevice = EMSCRIPTENAUDIO_CloseDevice;
impl->GetDeviceBuf = EMSCRIPTENAUDIO_GetDeviceBuf;
impl->PlayDevice = EMSCRIPTENAUDIO_PlayDevice;
impl->FlushCapture = EMSCRIPTENAUDIO_FlushCapture;
impl->CaptureFromDevice = EMSCRIPTENAUDIO_CaptureFromDevice;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
/* no threads here */
impl->LockDevice = impl->UnlockDevice = EMSCRIPTENAUDIO_LockOrUnlockDeviceWithNoMixerLock;
// technically, this is just runs in idle time in the main thread, but it's close enough to a "thread" for our purposes.
impl->ProvidesOwnCallbackThread = SDL_TRUE;
/* *INDENT-OFF* */ /* clang-format off */
/* check availability */
// check availability
available = MAIN_THREAD_EM_ASM_INT({
if (typeof(AudioContext) !== 'undefined') {
return true;
@ -378,14 +304,12 @@ static SDL_bool EMSCRIPTENAUDIO_Init(SDL_AudioDriverImpl *impl)
return true;
}
return false;
});
/* *INDENT-ON* */ /* clang-format on */
}) ? SDL_TRUE : SDL_FALSE;
if (!available) {
SDL_SetError("No audio context available");
}
/* *INDENT-OFF* */ /* clang-format off */
capture_available = available && MAIN_THREAD_EM_ASM_INT({
if ((typeof(navigator.mediaDevices) !== 'undefined') && (typeof(navigator.mediaDevices.getUserMedia) !== 'undefined')) {
return true;
@ -393,8 +317,7 @@ static SDL_bool EMSCRIPTENAUDIO_Init(SDL_AudioDriverImpl *impl)
return true;
}
return false;
});
/* *INDENT-ON* */ /* clang-format on */
}) ? SDL_TRUE : SDL_FALSE;
impl->HasCaptureSupport = 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
};
#endif /* SDL_AUDIO_DRIVER_EMSCRIPTEN */
/* *INDENT-ON* */ /* clang-format on */
#endif // SDL_AUDIO_DRIVER_EMSCRIPTEN

View File

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

View File

@ -22,7 +22,7 @@
#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 <signal.h>
@ -38,58 +38,45 @@ extern "C"
}
/* !!! 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)
static Uint8 *HAIKUAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{
SDL_AudioDevice *audio = (SDL_AudioDevice *) device;
SDL_AudioCallback callback = audio->callbackspec.callback;
SDL_LockMutex(audio->mixer_lock);
/* 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;
}
SDL_assert(device->hidden->current_buffer != NULL);
SDL_assert(device->hidden->current_buffer_len > 0);
*buffer_size = device->hidden->current_buffer_len;
return device->hidden->current_buffer;
}
const int got = SDL_GetAudioStreamData(audio->stream, stream, ilen);
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)
static void HAIKUAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
{
if (_this->hidden->audio_obj) {
_this->hidden->audio_obj->Stop();
delete _this->hidden->audio_obj;
// We already wrote our output right into the BSoundPlayer's callback's stream. Just clean up our stuff.
SDL_assert(device->hidden->current_buffer != NULL);
SDL_assert(device->hidden->current_buffer_len > 0);
device->hidden->current_buffer = NULL;
device->hidden->current_buffer_len = 0;
}
// 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;
SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts;
/* Initialize all variables that we clean on shutdown */
_this->hidden = new SDL_PrivateAudioData;
if (_this->hidden == NULL) {
// Initialize all variables that we clean on shutdown
device->hidden = new SDL_PrivateAudioData;
if (device->hidden == NULL) {
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);
format.byte_order = B_MEDIA_LITTLE_ENDIAN;
format.frame_rate = (float) _this->spec.freq;
format.channel_count = _this->spec.channels; /* !!! FIXME: support > 2? */
format.frame_rate = (float) device->spec.freq;
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) {
switch (test_format) {
case SDL_AUDIO_S8:
@ -178,31 +163,30 @@ static int HAIKUAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
break;
}
if (!test_format) { /* shouldn't happen, but just in case... */
return SDL_SetError("%s: Unsupported audio format", "haiku");
if (!test_format) { // shouldn't happen, but just in case...
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 */
SDL_CalculateAudioSpec(&_this->spec);
// Calculate the final parameters for this audio specification
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;
MaskSignals(&omask);
_this->hidden->audio_obj = new BSoundPlayer(&format, "SDL Audio",
FillSound, NULL, _this);
device->hidden->audio_obj = new BSoundPlayer(&format, "SDL Audio",
FillSound, NULL, device);
UnmaskSignals(&omask);
if (_this->hidden->audio_obj->Start() == B_NO_ERROR) {
_this->hidden->audio_obj->SetHasData(true);
if (device->hidden->audio_obj->Start() == B_NO_ERROR) {
device->hidden->audio_obj->SetHasData(true);
} else {
return SDL_SetError("Unable to start Be audio");
return SDL_SetError("Unable to start Haiku audio");
}
/* We're running! */
return 0;
return 0; // We're running!
}
static void HAIKUAUDIO_Deinitialize(void)
@ -212,27 +196,27 @@ static void HAIKUAUDIO_Deinitialize(void)
static SDL_bool HAIKUAUDIO_Init(SDL_AudioDriverImpl *impl)
{
/* Initialize the Be Application, if it's not already started */
if (SDL_InitBeApp() < 0) {
return SDL_FALSE;
}
/* Set the function pointers */
// Set the function pointers
impl->OpenDevice = HAIKUAUDIO_OpenDevice;
impl->GetDeviceBuf = HAIKUAUDIO_GetDeviceBuf;
impl->PlayDevice = HAIKUAUDIO_PlayDevice;
impl->CloseDevice = HAIKUAUDIO_CloseDevice;
impl->Deinitialize = HAIKUAUDIO_Deinitialize;
impl->ProvidesOwnCallbackThread = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */
return SDL_TRUE;
}
extern "C"
{
extern AudioBootStrap HAIKUAUDIO_bootstrap;
}
extern "C" { extern AudioBootStrap HAIKUAUDIO_bootstrap; }
AudioBootStrap HAIKUAUDIO_bootstrap = {
"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
{
BSoundPlayer *audio_obj;
Uint8 *current_buffer;
int current_buffer_len;
};
#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. */
{
SDL_AudioDevice *_this = (SDL_AudioDevice *)arg;
SDL_OpenedAudioDeviceDisconnected(_this);
SDL_PostSemaphore(_this->hidden->iosem); /* unblock the SDL thread. */
SDL_AudioDeviceDisconnected((SDL_AudioDevice *)arg);
}
// !!! 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)
{
SDL_AudioDevice *_this = (SDL_AudioDevice *)arg;
jack_port_t **ports = _this->hidden->sdlports;
const int total_channels = _this->spec.channels;
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);
SDL_assert(nframes == ((SDL_AudioDevice *)arg)->sample_frames);
SDL_OutputAudioThreadIterate((SDL_AudioDevice *)arg);
return 0;
}
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);
if (dst) {
const float *src = _this->hidden->iobuffer + channelsi;
int framesi;
for (framesi = 0; framesi < total_frames; framesi++) {
const float *src = buffer + channelsi;
for (int framesi = 0; framesi < total_frames; framesi++) {
*(dst++) = *src;
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 void JACK_WaitDevice(SDL_AudioDevice *_this)
static Uint8 *JACK_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{
if (SDL_AtomicGet(&_this->enabled)) {
if (SDL_WaitSemaphore(_this->hidden->iosem) == -1) {
SDL_OpenedAudioDeviceDisconnected(_this);
}
}
}
static Uint8 *JACK_GetDeviceBuf(SDL_AudioDevice *_this)
{
return (Uint8 *)_this->hidden->iobuffer;
return (Uint8 *)device->hidden->iobuffer;
}
static int jackProcessCaptureCallback(jack_nframes_t nframes, void *arg)
{
SDL_AudioDevice *_this = (SDL_AudioDevice *)arg;
if (SDL_AtomicGet(&_this->enabled)) {
jack_port_t **ports = _this->hidden->sdlports;
const int total_channels = _this->spec.channels;
const int total_frames = _this->spec.samples;
int channelsi;
SDL_assert(nframes == ((SDL_AudioDevice *)arg)->sample_frames);
SDL_CaptureAudioThreadIterate((SDL_AudioDevice *)arg);
return 0;
}
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);
if (src) {
float *dst = _this->hidden->iobuffer + channelsi;
int framesi;
for (framesi = 0; framesi < total_frames; framesi++) {
float *dst = buffer + channelsi;
for (int framesi = 0; framesi < total_frames; framesi++) {
*dst = *(src++);
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;
}
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) {
JACK_jack_deactivate(_this->hidden->client);
if (device->hidden) {
if (device->hidden->client) {
JACK_jack_deactivate(device->hidden->client);
if (_this->hidden->sdlports) {
const int channels = _this->spec.channels;
if (device->hidden->sdlports) {
const int channels = device->spec.channels;
int 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_DestroySemaphore(_this->hidden->iosem);
SDL_free(device->hidden->iobuffer);
SDL_free(device->hidden);
device->hidden = NULL;
SDL_AudioThreadFinalize(device);
}
}
SDL_free(_this->hidden->iobuffer);
SDL_free(_this->hidden);
// !!! FIXME: unify this (PulseAudio has a getAppName, Pipewire has a thing, etc
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
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)
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 sdlportflags = iscapture ? JackPortIsInput : JackPortIsOutput;
const JackProcessCallback callback = iscapture ? jackProcessCaptureCallback : jackProcessPlaybackCallback;
@ -277,14 +268,13 @@ static int JACK_OpenDevice(SDL_AudioDevice *_this, const char *devname)
int i;
/* Initialize all variables that we clean on shutdown */
_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();
}
/* !!! FIXME: we _still_ need an API to specify an app name */
client = JACK_jack_client_open("SDL", JackNoStartServer, &status, NULL);
_this->hidden->client = client;
client = JACK_jack_client_open(GetJackAppName(), JackNoStartServer, &status, NULL);
device->hidden->client = client;
if (client == NULL) {
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." */
/* Jack pretty much demands what it wants. */
_this->spec.format = SDL_AUDIO_F32SYS;
_this->spec.freq = JACK_jack_get_sample_rate(client);
_this->spec.channels = channels;
_this->spec.samples = JACK_jack_get_buffer_size(client);
device->spec.format = SDL_AUDIO_F32SYS;
device->spec.freq = JACK_jack_get_sample_rate(client);
device->spec.channels = channels;
device->sample_frames = JACK_jack_get_buffer_size(client);
SDL_CalculateAudioSpec(&_this->spec);
SDL_UpdatedAudioDeviceFormat(device);
_this->hidden->iosem = SDL_CreateSemaphore(0);
if (!_this->hidden->iosem) {
SDL_free(audio_ports);
return -1; /* error was set by SDL_CreateSemaphore */
}
_this->hidden->iobuffer = (float *)SDL_calloc(1, _this->spec.size);
if (!_this->hidden->iobuffer) {
if (!device->iscapture) {
device->hidden->iobuffer = (float *)SDL_calloc(1, device->buffer_size);
if (!device->hidden->iobuffer) {
SDL_free(audio_ports);
return SDL_OutOfMemory();
}
}
/* 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 *));
if (_this->hidden->sdlports == NULL) {
device->hidden->sdlports = (jack_port_t **)SDL_calloc(channels, sizeof(jack_port_t *));
if (device->hidden->sdlports == NULL) {
SDL_free(audio_ports);
return SDL_OutOfMemory();
}
@ -346,19 +332,19 @@ static int JACK_OpenDevice(SDL_AudioDevice *_this, const char *devname)
for (i = 0; i < channels; i++) {
char portname[32];
(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);
if (_this->hidden->sdlports[i] == NULL) {
device->hidden->sdlports[i] = JACK_jack_port_register(client, portname, JACK_DEFAULT_AUDIO_TYPE, sdlportflags, 0);
if (device->hidden->sdlports[i] == NULL) {
SDL_free(audio_ports);
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);
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) {
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. */
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 *dstport = iscapture ? sdlport : devports[audio_ports[i]];
if (JACK_jack_connect(client, srcport, dstport) != 0) {
@ -406,8 +392,8 @@ static SDL_bool JACK_Init(SDL_AudioDriverImpl *impl)
/* Set the function pointers */
impl->OpenDevice = JACK_OpenDevice;
impl->WaitDevice = JACK_WaitDevice;
impl->GetDeviceBuf = JACK_GetDeviceBuf;
impl->PlayDevice = JACK_PlayDevice;
impl->CloseDevice = JACK_CloseDevice;
impl->Deinitialize = JACK_Deinitialize;
impl->CaptureFromDevice = JACK_CaptureFromDevice;
@ -415,6 +401,7 @@ static SDL_bool JACK_Init(SDL_AudioDriverImpl *impl)
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
impl->HasCaptureSupport = SDL_TRUE;
impl->ProvidesOwnCallbackThread = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */
}

View File

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

View File

@ -22,7 +22,7 @@
#ifdef SDL_AUDIO_DRIVER_N3DS
/* N3DS Audio driver */
// N3DS Audio driver
#include "../SDL_sysaudio.h"
#include "SDL_n3dsaudio.h"
@ -32,27 +32,14 @@
static dspHookCookie dsp_hook;
static SDL_AudioDevice *audio_device;
static void FreePrivateData(SDL_AudioDevice *_this);
static int FindAudioFormat(SDL_AudioDevice *_this);
static SDL_INLINE void contextLock(SDL_AudioDevice *_this)
static SDL_INLINE void contextLock(SDL_AudioDevice *device)
{
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);
}
static void N3DSAUD_LockAudio(SDL_AudioDevice *_this)
{
contextLock(_this);
}
static void N3DSAUD_UnlockAudio(SDL_AudioDevice *_this)
{
contextUnlock(_this);
LightLock_Unlock(&device->hidden->lock);
}
static void N3DSAUD_DspHook(DSP_HookType hook)
@ -60,46 +47,46 @@ static void N3DSAUD_DspHook(DSP_HookType hook)
if (hook == DSPHOOK_ONCANCEL) {
contextLock(audio_device);
audio_device->hidden->isCancelled = SDL_TRUE;
SDL_AtomicSet(&audio_device->enabled, SDL_FALSE);
SDL_AudioDeviceDisconnected(audio_device);
CondVar_Broadcast(&audio_device->hidden->cv);
contextUnlock(audio_device);
}
}
static void AudioFrameFinished(void *device)
static void AudioFrameFinished(void *vdevice)
{
bool shouldBroadcast = false;
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++) {
if (_this->hidden->waveBuf[i].status == NDSP_WBUF_DONE) {
_this->hidden->waveBuf[i].status = NDSP_WBUF_FREE;
if (device->hidden->waveBuf[i].status == NDSP_WBUF_DONE) {
device->hidden->waveBuf[i].status = NDSP_WBUF_FREE;
shouldBroadcast = SDL_TRUE;
}
}
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;
Uint8 *data_vaddr;
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();
}
/* Initialise the DSP service */
// Initialise the DSP service
ndsp_init_res = ndspInit();
if (R_FAILED(ndsp_init_res)) {
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;
}
/* Initialise internal state */
LightLock_Init(&_this->hidden->lock);
CondVar_Init(&_this->hidden->cv);
// Initialise internal state
LightLock_Init(&device->hidden->lock);
CondVar_Init(&device->hidden->cv);
if (_this->spec.channels > 2) {
_this->spec.channels = 2;
if (device->spec.channels > 2) {
device->spec.channels = 2;
}
/* Should not happen but better be safe. */
if (FindAudioFormat(_this) < 0) {
Uint32 format = 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.");
}
/* Update the fragment size as size in bytes */
SDL_CalculateAudioSpec(&_this->spec);
device->spec.format = test_format;
/* Allocate mixing buffer */
if (_this->spec.size >= SDL_MAX_UINT32 / 2) {
// Update the fragment size as size in bytes
SDL_UpdatedAudioDeviceFormat(device);
// Allocate mixing buffer
if (device->buffer_size >= SDL_MAX_UINT32 / 2) {
return SDL_SetError("Mixing buffer is too large.");
}
_this->hidden->mixlen = _this->spec.size;
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->spec.size);
if (_this->hidden->mixbuf == NULL) {
device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
if (device->hidden->mixbuf == NULL) {
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) {
return SDL_OutOfMemory();
}
SDL_memset(data_vaddr, 0, _this->hidden->mixlen * NUM_BUFFERS);
DSP_FlushDataCache(data_vaddr, _this->hidden->mixlen * NUM_BUFFERS);
SDL_memset(data_vaddr, 0, device->buffer_size * NUM_BUFFERS);
DSP_FlushDataCache(data_vaddr, device->buffer_size * NUM_BUFFERS);
_this->hidden->nextbuf = 0;
_this->hidden->channels = _this->spec.channels;
_this->hidden->samplerate = _this->spec.freq;
device->hidden->nextbuf = 0;
ndspChnReset(0);
ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
ndspChnSetRate(0, _this->spec.freq);
ndspChnSetFormat(0, _this->hidden->format);
ndspChnSetRate(0, device->spec.freq);
ndspChnSetFormat(0, format);
SDL_memset(mix, 0, sizeof(mix));
mix[0] = 1.0;
mix[1] = 1.0;
SDL_zeroa(mix);
mix[0] = mix[1] = 1.0f;
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++) {
_this->hidden->waveBuf[i].data_vaddr = data_vaddr;
_this->hidden->waveBuf[i].nsamples = _this->hidden->mixlen / _this->hidden->bytePerSample;
data_vaddr += _this->hidden->mixlen;
device->hidden->waveBuf[i].data_vaddr = data_vaddr;
device->hidden->waveBuf[i].nsamples = device->buffer_size / sample_frame_size;
data_vaddr += device->buffer_size;
}
/* Setup callback */
audio_device = _this;
ndspSetCallback(AudioFrameFinished, _this);
// Setup callback
audio_device = device;
ndspSetCallback(AudioFrameFinished, device);
dspHook(&dsp_hook, N3DSAUD_DspHook);
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. */
SDL_Delay((_this->spec.samples * 1000) / _this->spec.freq);
contextLock(device);
/* always return a full buffer of silence. */
SDL_memset(buffer, _this->spec.silence, buflen);
return buflen;
}
const size_t nextbuf = device->hidden->nextbuf;
static void N3DSAUDIO_PlayDevice(SDL_AudioDevice *_this)
{
size_t nextbuf;
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);
if (device->hidden->isCancelled ||
device->hidden->waveBuf[nextbuf].status != NDSP_WBUF_FREE) {
contextUnlock(device);
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,
_this->hidden->mixbuf, sampleLen);
DSP_FlushDataCache(_this->hidden->waveBuf[nextbuf].data_vaddr, sampleLen);
SDL_memcpy((void *)device->hidden->waveBuf[nextbuf].data_vaddr, buffer, buflen);
DSP_FlushDataCache(device->hidden->waveBuf[nextbuf].data_vaddr, buflen);
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);
while (!_this->hidden->isCancelled &&
_this->hidden->waveBuf[_this->hidden->nextbuf].status != NDSP_WBUF_FREE) {
CondVar_Wait(&_this->hidden->cv, &_this->hidden->lock);
contextLock(device);
while (!device->hidden->isCancelled && !SDL_AtomicGet(&device->shutdown) &&
device->hidden->waveBuf[device->hidden->nextbuf].status != NDSP_WBUF_FREE) {
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);
ndspSetCallback(NULL, NULL);
if (!_this->hidden->isCancelled) {
if (!device->hidden->isCancelled) {
ndspChnReset(0);
SDL_memset(_this->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS);
CondVar_Broadcast(&_this->hidden->cv);
SDL_memset(device->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS);
CondVar_Broadcast(&device->hidden->cv);
}
contextUnlock(_this);
contextUnlock(device);
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;
svcGetThreadPriority(&current_priority, CUR_THREAD_HANDLE);
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);
svcSetThreadPriority(CUR_THREAD_HANDLE, current_priority);
}
static SDL_bool N3DSAUDIO_Init(SDL_AudioDriverImpl *impl)
{
/* Set the function pointers */
impl->OpenDevice = N3DSAUDIO_OpenDevice;
impl->PlayDevice = N3DSAUDIO_PlayDevice;
impl->WaitDevice = N3DSAUDIO_WaitDevice;
impl->GetDeviceBuf = N3DSAUDIO_GetDeviceBuf;
impl->CloseDevice = N3DSAUDIO_CloseDevice;
impl->ThreadInit = N3DSAUDIO_ThreadInit;
impl->LockDevice = N3DSAUD_LockAudio;
impl->UnlockDevice = N3DSAUD_UnlockAudio;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
/* Should be possible, but micInit would fail */
// Should be possible, but micInit would fail
impl->HasCaptureSupport = SDL_FALSE;
impl->CaptureFromDevice = N3DSAUDIO_CaptureFromDevice;
return SDL_TRUE; /* this audio target is available. */
return SDL_TRUE;
}
AudioBootStrap N3DSAUDIO_bootstrap = {
@ -286,51 +280,4 @@ AudioBootStrap N3DSAUDIO_bootstrap = {
0
};
/**
* 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 */
#endif // SDL_AUDIO_DRIVER_N3DS

View File

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

View File

@ -22,10 +22,7 @@
#ifdef SDL_AUDIO_DRIVER_NETBSD
/*
* Driver for native NetBSD audio(4).
* nia@NetBSD.org
*/
// Driver for native NetBSD audio(4).
#include <errno.h>
#include <unistd.h>
@ -41,26 +38,26 @@
#include "../SDL_audiodev_c.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
/* *INDENT-OFF* */ /* clang-format off */
audio_info_t info;
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");
return;
}
prinfo = _this->iscapture ? &info.record : &info.play;
prinfo = device->iscapture ? &info.record : &info.play;
fprintf(stderr, "\n"
"[%s info]\n"
@ -77,7 +74,7 @@ static void NETBSDAUDIO_Status(SDL_AudioDevice *_this)
"waiting : %s\n"
"active : %s\n"
"",
_this->iscapture ? "record" : "play",
device->iscapture ? "record" : "play",
prinfo->buffer_size,
prinfo->sample_rate,
prinfo->channels,
@ -103,7 +100,7 @@ static void NETBSDAUDIO_Status(SDL_AudioDevice *_this)
info.blocksize,
info.hiwat, info.lowat,
(info.mode == AUMODE_PLAY) ? "PLAY"
: (info.mode = AUMODE_RECORD) ? "RECORD"
: (info.mode == AUMODE_RECORD) ? "RECORD"
: (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?"));
fprintf(stderr, "\n"
@ -111,23 +108,46 @@ static void NETBSDAUDIO_Status(SDL_AudioDevice *_this)
"format : 0x%x\n"
"size : %u\n"
"",
_this->spec.format,
_this->spec.size);
device->spec.format,
device->buffer_size);
/* *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;
int written;
const SDL_bool iscapture = device->iscapture;
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 */
written = write(h->audio_fd, h->mixbuf, h->mixlen);
static void NETBSDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{
struct SDL_PrivateAudioData *h = device->hidden;
const int written = write(h->audio_fd, buffer, buflen);
if (written == -1) {
/* Non recoverable error has occurred. It should be reported!!! */
SDL_OpenedAudioDeviceDisconnected(_this);
// Non recoverable error has occurred. It should be reported!!!
SDL_AudioDeviceDisconnected(device);
perror("audio");
return;
}
@ -137,19 +157,17 @@ static void NETBSDAUDIO_PlayDevice(SDL_AudioDevice *_this)
#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;
int br;
br = read(_this->hidden->audio_fd, buffer, buflen);
Uint8 *buffer = (Uint8 *)vbuffer;
const int br = read(device->hidden->audio_fd, buffer, buflen);
if (br == -1) {
/* Non recoverable error has occurred. It should be reported!!! */
// Non recoverable error has occurred. It should be reported!!!
perror("audio");
return -1;
}
@ -157,86 +175,73 @@ static int NETBSDAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *_buffer,
#ifdef DEBUG_AUDIO
fprintf(stderr, "Captured %d bytes of audio data\n", br);
#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;
size_t remain;
Uint8 buf[512];
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));
if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) == 0) {
size_t remain = (size_t)(info.record.seek * (SDL_AUDIO_BITSIZE(device->spec.format) / 8));
while (remain > 0) {
char buf[512];
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) {
return; /* oh well. */
break;
}
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;
SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts;
if (device->hidden) {
if (device->hidden->audio_fd >= 0) {
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;
audio_info_t info, hwinfo;
struct audio_prinfo *prinfo = iscapture ? &info.record : &info.play;
/* 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");
}
}
/* Initialize all variables that we clean on shutdown */
_this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc(sizeof(*_this->hidden));
if (_this->hidden == NULL) {
// Initialize all variables that we clean on shutdown
device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
if (device->hidden == NULL) {
return SDL_OutOfMemory();
}
SDL_zerop(_this->hidden);
/* Open the audio device */
_this->hidden->audio_fd = open(devname, (iscapture ? O_RDONLY : O_WRONLY) | O_CLOEXEC);
if (_this->hidden->audio_fd < 0) {
return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
// Open the audio device; we hardcode the device path in `device->name` for lack of better info, so use that.
const int flags = ((device->iscapture) ? O_RDONLY : O_WRONLY);
device->hidden->audio_fd = open(device->name, flags | O_CLOEXEC);
if (device->hidden->audio_fd < 0) {
return SDL_SetError("Couldn't open %s: %s", device->name, strerror(errno));
}
AUDIO_INITINFO(&info);
#ifdef AUDIO_GETFORMAT /* Introduced in NetBSD 9.0 */
if (ioctl(_this->hidden->audio_fd, AUDIO_GETFORMAT, &hwinfo) != -1) {
/*
* Use the device's native sample rate so the kernel doesn't have to
* resample.
*/
_this->spec.freq = iscapture ? hwinfo.record.sample_rate : hwinfo.play.sample_rate;
#ifdef AUDIO_GETFORMAT // Introduced in NetBSD 9.0
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.
device->spec.freq = iscapture ? hwinfo.record.sample_rate : hwinfo.play.sample_rate;
}
#endif
prinfo->sample_rate = _this->spec.freq;
prinfo->channels = _this->spec.channels;
prinfo->sample_rate = device->spec.freq;
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) {
switch (test_format) {
case SDL_AUDIO_U8:
@ -271,56 +276,56 @@ static int NETBSDAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
info.hiwat = 5;
info.lowat = 3;
if (ioctl(_this->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) {
return SDL_SetError("AUDIO_SETINFO failed for %s: %s", devname, strerror(errno));
if (ioctl(device->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) {
return SDL_SetError("AUDIO_SETINFO failed for %s: %s", device->name, strerror(errno));
}
if (ioctl(_this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
return SDL_SetError("AUDIO_GETINFO failed for %s: %s", devname, strerror(errno));
if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
return SDL_SetError("AUDIO_GETINFO failed for %s: %s", device->name, strerror(errno));
}
/* Final spec used for the device. */
_this->spec.format = test_format;
_this->spec.freq = prinfo->sample_rate;
_this->spec.channels = prinfo->channels;
// Final spec used for the device.
device->spec.format = test_format;
device->spec.freq = prinfo->sample_rate;
device->spec.channels = prinfo->channels;
SDL_CalculateAudioSpec(&_this->spec);
SDL_UpdatedAudioDeviceFormat(device);
if (!iscapture) {
/* Allocate mixing buffer */
_this->hidden->mixlen = _this->spec.size;
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->hidden->mixlen);
if (_this->hidden->mixbuf == NULL) {
// Allocate mixing buffer
device->hidden->mixlen = device->buffer_size;
device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->hidden->mixlen);
if (device->hidden->mixbuf == NULL) {
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;
return 0; // We're ready to rock and roll. :-)
}
static SDL_bool NETBSDAUDIO_Init(SDL_AudioDriverImpl *impl)
{
/* Set the function pointers */
impl->DetectDevices = NETBSDAUDIO_DetectDevices;
impl->OpenDevice = NETBSDAUDIO_OpenDevice;
impl->WaitDevice = NETBSDAUDIO_WaitDevice;
impl->PlayDevice = NETBSDAUDIO_PlayDevice;
impl->GetDeviceBuf = NETBSDAUDIO_GetDeviceBuf;
impl->CloseDevice = NETBSDAUDIO_CloseDevice;
impl->WaitCaptureDevice = NETBSDAUDIO_WaitDevice;
impl->CaptureFromDevice = NETBSDAUDIO_CaptureFromDevice;
impl->FlushCapture = NETBSDAUDIO_FlushCapture;
impl->HasCaptureSupport = SDL_TRUE;
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */
return SDL_TRUE;
}
AudioBootStrap NETBSDAUDIO_bootstrap = {
"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
/* For more discussion of low latency audio on Android, see this:
https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html
*/
// For more discussion of low latency audio on Android, see this:
// https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html
#include "../SDL_sysaudio.h"
#include "../SDL_audio_c.h"
@ -36,7 +35,7 @@
#include <android/log.h>
#define NUM_BUFFERS 2 /* -- Don't lower this! */
#define NUM_BUFFERS 2 // -- Don't lower this!
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_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT)
/* engine interfaces */
// engine interfaces
static SLObjectItf engineObject = NULL;
static SLEngineItf engineEngine = NULL;
/* output mix interfaces */
// output mix interfaces
static SLObjectItf outputMixObject = NULL;
/* buffer queue player interfaces */
// buffer queue player interfaces
static SLObjectItf bqPlayerObject = NULL;
static SLPlayItf bqPlayerPlay = NULL;
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
@ -98,7 +97,7 @@ static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
static SLVolumeItf bqPlayerVolume;
#endif
/* recorder interfaces */
// recorder interfaces
static SLObjectItf recorderObject = NULL;
static SLRecordItf recorderRecord = NULL;
static SLAndroidSimpleBufferQueueItf recorderBufferQueue = NULL;
@ -123,13 +122,13 @@ static void openslES_DestroyEngine(void)
{
LOGI("openslES_DestroyEngine()");
/* destroy output mix object, and invalidate all associated interfaces */
// destroy output mix object, and invalidate all associated interfaces
if (outputMixObject != NULL) {
(*outputMixObject)->Destroy(outputMixObject);
outputMixObject = NULL;
}
/* destroy engine object, and invalidate all associated interfaces */
// destroy engine object, and invalidate all associated interfaces
if (engineObject != NULL) {
(*engineObject)->Destroy(engineObject);
engineObject = NULL;
@ -145,7 +144,7 @@ static int openslES_CreateEngine(void)
LOGI("openSLES_CreateEngine()");
/* create engine */
// create engine
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
if (SL_RESULT_SUCCESS != result) {
LOGE("slCreateEngine failed: %d", result);
@ -153,7 +152,7 @@ static int openslES_CreateEngine(void)
}
LOGI("slCreateEngine OK");
/* realize the engine */
// realize the engine
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeEngine failed: %d", result);
@ -161,7 +160,7 @@ static int openslES_CreateEngine(void)
}
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);
if (SL_RESULT_SUCCESS != result) {
LOGE("EngineGetInterface failed: %d", result);
@ -169,7 +168,7 @@ static int openslES_CreateEngine(void)
}
LOGI("EngineGetInterface OK");
/* create output mix */
// create output mix
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
if (SL_RESULT_SUCCESS != result) {
LOGE("CreateOutputMix failed: %d", result);
@ -177,7 +176,7 @@ static int openslES_CreateEngine(void)
}
LOGI("CreateOutputMix OK");
/* realize the output mix */
// realize the output mix
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeOutputMix failed: %d", result);
@ -190,7 +189,7 @@ error:
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)
{
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context;
@ -199,12 +198,12 @@ static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
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;
/* stop recording */
// stop recording
if (recorderRecord != NULL) {
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
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) {
(*recorderObject)->Destroy(recorderObject);
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;
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
SLDataSink audioSnk;
@ -248,19 +247,19 @@ static int openslES_CreatePCMRecorder(SDL_AudioDevice *_this)
return SDL_SetError("This app doesn't have RECORD_AUDIO permission");
}
/* Just go with signed 16-bit audio as it's the most compatible */
_this->spec.format = SDL_AUDIO_S16SYS;
_this->spec.channels = 1;
/*_this->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/
// Just go with signed 16-bit audio as it's the most compatible
device->spec.format = SDL_AUDIO_S16SYS;
device->spec.channels = 1;
//device->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/
/* Update the fragment size as size in bytes */
SDL_CalculateAudioSpec(&_this->spec);
// Update the fragment size as size in bytes
SDL_UpdatedAudioDeviceFormat(device);
LOGI("Try to open %u hz %u bit chan %u %s samples %u",
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format),
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples);
device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
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.deviceType = SL_IODEVICE_AUDIOINPUT;
loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
@ -268,93 +267,93 @@ static int openslES_CreatePCMRecorder(SDL_AudioDevice *_this)
audioSrc.pLocator = &loc_dev;
audioSrc.pFormat = NULL;
/* configure audio sink */
// configure audio sink
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
loc_bufq.numBuffers = NUM_BUFFERS;
format_pcm.formatType = SL_DATAFORMAT_PCM;
format_pcm.numChannels = _this->spec.channels;
format_pcm.samplesPerSec = _this->spec.freq * 1000; /* / kilo Hz to milli Hz */
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format);
format_pcm.containerSize = SDL_AUDIO_BITSIZE(_this->spec.format);
format_pcm.numChannels = device->spec.channels;
format_pcm.samplesPerSec = device->spec.freq * 1000; // / kilo Hz to milli Hz
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
format_pcm.containerSize = SDL_AUDIO_BITSIZE(device->spec.format);
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER;
audioSnk.pLocator = &loc_bufq;
audioSnk.pFormat = &format_pcm;
/* create audio recorder */
/* (requires the RECORD_AUDIO permission) */
// create audio recorder
// (requires the RECORD_AUDIO permission)
result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk, 1, ids, req);
if (SL_RESULT_SUCCESS != result) {
LOGE("CreateAudioRecorder failed: %d", result);
goto failed;
}
/* realize the recorder */
// realize the recorder
result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeAudioPlayer failed: %d", result);
goto failed;
}
/* get the record interface */
// get the record interface
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_RECORD interface get failed: %d", result);
goto failed;
}
/* get the buffer queue interface */
// get the buffer queue interface
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue);
if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
goto failed;
}
/* register callback on the buffer queue */
/* context is '(SDL_PrivateAudioData *)_this->hidden' */
result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback, _this->hidden);
// register callback on the buffer queue
// context is '(SDL_PrivateAudioData *)device->hidden'
result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback, device->hidden);
if (SL_RESULT_SUCCESS != result) {
LOGE("RegisterCallback failed: %d", result);
goto failed;
}
/* Create the audio buffer semaphore */
// Create the audio buffer semaphore
audiodata->playsem = SDL_CreateSemaphore(0);
if (!audiodata->playsem) {
LOGE("cannot create Semaphore!");
goto failed;
}
/* Create the sound buffers */
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * _this->spec.size);
// Create the sound buffers
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * device->buffer_size);
if (audiodata->mixbuff == NULL) {
LOGE("mixbuffer allocate - out of memory");
goto failed;
}
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);
if (SL_RESULT_SUCCESS != result) {
LOGE("Record set state failed: %d", result);
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++) {
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) {
LOGE("Record enqueue buffers failed: %d", result);
goto failed;
}
}
/* start recording */
// start recording
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
if (SL_RESULT_SUCCESS != result) {
LOGE("Record set state failed: %d", result);
@ -367,7 +366,7 @@ 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)
{
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context;
@ -376,20 +375,19 @@ static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
SDL_PostSemaphore(audiodata->playsem);
}
static void openslES_DestroyPCMPlayer(SDL_AudioDevice *_this)
static void openslES_DestroyPCMPlayer(SDL_AudioDevice *device)
{
struct SDL_PrivateAudioData *audiodata = _this->hidden;
SLresult result;
struct SDL_PrivateAudioData *audiodata = device->hidden;
/* set the player's state to 'stopped' */
// set the player's state to 'stopped'
if (bqPlayerPlay != NULL) {
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
const SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
if (SL_RESULT_SUCCESS != 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) {
(*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)
it can be done as described here:
https://developer.android.com/ndk/guides/audio/opensl/android-extensions.html#floating-point
*/
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;
while ((test_format = *(closefmts++)) != 0) {
if (SDL_AUDIO_ISSIGNED(test_format)) {
@ -436,40 +422,42 @@ static int openslES_CreatePCMPlayer(SDL_AudioDevice *_this)
}
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");
test_format = SDL_AUDIO_S16SYS;
}
_this->spec.format = test_format;
device->spec.format = test_format;
} else {
/* Just go with signed 16-bit audio as it's the most compatible */
_this->spec.format = SDL_AUDIO_S16SYS;
// Just go with signed 16-bit audio as it's the most compatible
device->spec.format = SDL_AUDIO_S16SYS;
}
/* Update the fragment size as size in bytes */
SDL_CalculateAudioSpec(&_this->spec);
// Update the fragment size as size in bytes
SDL_UpdatedAudioDeviceFormat(device);
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),
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples);
device->spec.freq, SDL_AUDIO_ISFLOAT(device->spec.format) ? "float" : "pcm", SDL_AUDIO_BITSIZE(device->spec.format),
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.numBuffers = NUM_BUFFERS;
SLDataFormat_PCM format_pcm;
format_pcm.formatType = SL_DATAFORMAT_PCM;
format_pcm.numChannels = _this->spec.channels;
format_pcm.samplesPerSec = _this->spec.freq * 1000; /* / kilo Hz to milli Hz */
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format);
format_pcm.containerSize = SDL_AUDIO_BITSIZE(_this->spec.format);
format_pcm.numChannels = device->spec.channels;
format_pcm.samplesPerSec = device->spec.freq * 1000; // / kilo Hz to milli Hz
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(device->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;
} else {
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
}
switch (_this->spec.channels) {
switch (device->spec.channels) {
case 1:
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT;
break;
@ -495,14 +483,19 @@ static int openslES_CreatePCMPlayer(SDL_AudioDevice *_this)
format_pcm.channelMask = SL_ANDROID_SPEAKER_7DOT1;
break;
default:
/* Unknown number of channels, fall back to stereo */
_this->spec.channels = 2;
// Unknown number of channels, fall back to stereo
device->spec.channels = 2;
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
break;
}
if (SDL_AUDIO_ISFLOAT(_this->spec.format)) {
/* Copy all setup into PCM EX structure */
SLDataSink audioSnk;
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.endianness = format_pcm.endianness;
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.containerSize = format_pcm.containerSize;
format_pcm_ex.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
audioSrc.pFormat = (void *)&format_pcm_ex;
}
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.outputMix = outputMixObject;
audioSnk.pLocator = &loc_outmix;
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);
if (SL_RESULT_SUCCESS != result) {
LOGE("CreateAudioPlayer failed: %d", result);
goto failed;
}
/* realize the player */
// realize the player
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeAudioPlayer failed: %d", result);
goto failed;
}
/* get the play interface */
// get the play interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_PLAY interface get failed: %d", result);
goto failed;
}
/* get the buffer queue interface */
// get the buffer queue interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue);
if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
goto failed;
}
/* register callback on the buffer queue */
/* context is '(SDL_PrivateAudioData *)_this->hidden' */
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, _this->hidden);
// register callback on the buffer queue
// context is '(SDL_PrivateAudioData *)device->hidden'
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, device->hidden);
if (SL_RESULT_SUCCESS != result) {
LOGE("RegisterCallback failed: %d", result);
goto failed;
}
#if 0
/* get the volume interface */
// get the volume interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_VOLUME interface get failed: %d", result);
/* goto failed; */
// goto failed;
}
#endif
/* Create the audio buffer semaphore */
struct SDL_PrivateAudioData *audiodata = device->hidden;
// Create the audio buffer semaphore
audiodata->playsem = SDL_CreateSemaphore(NUM_BUFFERS - 1);
if (!audiodata->playsem) {
LOGE("cannot create Semaphore!");
goto failed;
}
/* Create the sound buffers */
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * _this->spec.size);
// Create the sound buffers
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * device->buffer_size);
if (audiodata->mixbuff == NULL) {
LOGE("mixbuffer allocate - out of memory");
goto failed;
}
for (i = 0; i < NUM_BUFFERS; i++) {
audiodata->pmixbuff[i] = audiodata->mixbuff + i * _this->spec.size;
for (int i = 0; i < NUM_BUFFERS; i++) {
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);
if (SL_RESULT_SUCCESS != result) {
LOGE("Play set state failed: %d", result);
@ -598,103 +597,98 @@ failed:
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));
if (_this->hidden == NULL) {
device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
if (device->hidden == NULL) {
return SDL_OutOfMemory();
}
if (_this->iscapture) {
LOGI("openslES_OpenDevice() %s for capture", devname);
return openslES_CreatePCMRecorder(_this);
if (device->iscapture) {
LOGI("openslES_OpenDevice() for capture");
return openslES_CreatePCMRecorder(device);
} else {
int ret;
LOGI("openslES_OpenDevice() %s for playing", devname);
ret = openslES_CreatePCMPlayer(_this);
LOGI("openslES_OpenDevice() for playing");
ret = openslES_CreatePCMPlayer(device);
if (ret < 0) {
/* Another attempt to open the device with a lower frequency */
if (_this->spec.freq > 48000) {
openslES_DestroyPCMPlayer(_this);
_this->spec.freq = 48000;
ret = openslES_CreatePCMPlayer(_this);
// Another attempt to open the device with a lower frequency
if (device->spec.freq > 48000) {
openslES_DestroyPCMPlayer(device);
device->spec.freq = 48000;
ret = openslES_CreatePCMPlayer(device);
}
}
if (ret == 0) {
return 0;
} else {
if (ret != 0) {
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()");
/* Wait for an audio chunk to finish */
// Wait for an audio chunk to finish
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;
SLresult result;
struct SDL_PrivateAudioData *audiodata = device->hidden;
LOGV("======openslES_PlayDevice()======");
/* Queue it up */
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], _this->spec.size);
// Queue it up
const SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer, buflen);
audiodata->next_buffer++;
if (audiodata->next_buffer >= NUM_BUFFERS) {
audiodata->next_buffer = 0;
}
/* If Enqueue fails, callback won't be called.
* Post the semaphore, not to run out of buffer */
// If Enqueue fails, callback won't be called.
// Post the semaphore, not to run out of buffer
if (SL_RESULT_SUCCESS != result) {
SDL_PostSemaphore(audiodata->playsem);
}
}
/*/ n playn sem */
/* getbuf 0 - 1 */
/* fill buff 0 - 1 */
/* play 0 - 0 1 */
/* wait 1 0 0 */
/* getbuf 1 0 0 */
/* fill buff 1 0 0 */
/* play 0 0 0 */
/* wait */
/* */
/* okay.. */
/// n playn sem
// getbuf 0 - 1
// fill buff 0 - 1
// play 0 - 0 1
// wait 1 0 0
// getbuf 1 0 0
// fill buff 1 0 0
// play 0 0 0
// wait
//
// 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()");
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;
SLresult result;
struct SDL_PrivateAudioData *audiodata = device->hidden;
/* Wait for new recorded data */
SDL_WaitSemaphore(audiodata->playsem);
// Copy it to the output buffer
SDL_assert(buflen == device->buffer_size);
SDL_memcpy(buffer, audiodata->pmixbuff[audiodata->next_buffer], device->buffer_size);
/* Copy it to the output buffer */
SDL_assert(buflen == _this->spec.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);
// Re-enqueue the buffer
const SLresult result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], device->buffer_size);
if (SL_RESULT_SUCCESS != result) {
LOGE("Record enqueue buffers failed: %d", result);
return -1;
@ -705,22 +699,24 @@ static int openslES_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int
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; */
if (_this->iscapture) {
// struct SDL_PrivateAudioData *audiodata = device->hidden;
if (device->hidden) {
if (device->iscapture) {
LOGI("openslES_CloseDevice() for capture");
openslES_DestroyPCMRecorder(_this);
openslES_DestroyPCMRecorder(device);
} else {
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)
@ -733,24 +729,26 @@ static SDL_bool openslES_Init(SDL_AudioDriverImpl *impl)
LOGI("openslES_Init() - set pointers");
/* Set the function pointers */
/* impl->DetectDevices = openslES_DetectDevices; */
// Set the function pointers
// impl->DetectDevices = openslES_DetectDevices;
impl->ThreadInit = Android_AudioThreadInit;
impl->OpenDevice = openslES_OpenDevice;
impl->WaitDevice = openslES_WaitDevice;
impl->PlayDevice = openslES_PlayDevice;
impl->GetDeviceBuf = openslES_GetDeviceBuf;
impl->WaitCaptureDevice = openslES_WaitDevice;
impl->CaptureFromDevice = openslES_CaptureFromDevice;
impl->CloseDevice = openslES_CloseDevice;
impl->Deinitialize = openslES_DestroyEngine;
/* and the capabilities */
// and the capabilities
impl->HasCaptureSupport = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
LOGI("openslES_Init() - success");
/* this audio target is available. */
// this audio target is available.
return SDL_TRUE;
}
@ -761,7 +759,7 @@ AudioBootStrap openslES_bootstrap = {
void openslES_ResumeDevices(void)
{
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);
if (SL_RESULT_SUCCESS != result) {
LOGE("openslES_ResumeDevices failed: %d", result);
@ -772,7 +770,7 @@ void openslES_ResumeDevices(void)
void openslES_PauseDevices(void)
{
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);
if (SL_RESULT_SUCCESS != 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);
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);
@ -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)
{
struct io_node *n, *temp;
@ -383,17 +358,6 @@ static struct io_node *io_list_get_by_id(Uint32 id)
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)
{
SDL_assert(node);
@ -640,6 +604,19 @@ static char *get_name_from_json(const char *json)
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 */
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);
node->persist = SDL_TRUE;
change_default_device(pipewire_default_sink_id);
} else if (!SDL_strcmp(key, "default.audio.source")) {
if (pipewire_default_source_id != NULL) {
SDL_free(pipewire_default_source_id);
}
pipewire_default_source_id = get_name_from_json(value);
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;
@ -844,11 +823,15 @@ static void PIPEWIRE_DetectDevices(void)
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) {
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;
@ -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;
struct spa_buffer *spa_buf;
Uint8 *dst;
// 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.
// !!! FIXME: It's not clear to me if this ever returns NULL or if this was just defensive coding.
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;
}
/* See if a buffer is available */
pw_buf = PIPEWIRE_pw_stream_dequeue_buffer(stream);
struct pw_stream *stream = device->hidden->stream;
struct pw_buffer *pw_buf = PIPEWIRE_pw_stream_dequeue_buffer(stream);
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) {
return;
PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf);
return NULL;
}
/*
* If the device is disabled, write silence to the stream buffer
* 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);
device->hidden->pw_buf = pw_buf;
return (Uint8 *) spa_buf->datas[0].data;
}
if (!_this->stream) {
_this->callbackspec.callback(_this->callbackspec.userdata, dst, _this->callbackspec.size);
} else {
int got;
/* 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);
static void PIPEWIRE_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
{
struct pw_stream *stream = device->hidden->stream;
struct pw_buffer *pw_buf = device->hidden->pw_buf;
struct spa_buffer *spa_buf = pw_buf->buffer;
spa_buf->datas[0].chunk->offset = 0;
spa_buf->datas[0].chunk->stride = _this->hidden->stride;
spa_buf->datas[0].chunk->size = _this->spec.size;
spa_buf->datas[0].chunk->stride = device->hidden->stride;
spa_buf->datas[0].chunk->size = buffer_size;
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)
{
struct pw_buffer *pw_buf;
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);
SDL_CaptureAudioThreadIterate((SDL_AudioDevice *)data);
}
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) {
/*
* 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 (_this->spec.size > buffer->buffer->datas[0].maxsize) {
_this->spec.samples = buffer->buffer->datas[0].maxsize / _this->hidden->stride;
_this->spec.size = buffer->buffer->datas[0].maxsize;
if (device->iscapture == SDL_FALSE) {
/* 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 (device->buffer_size > buffer->buffer->datas[0].maxsize) {
SDL_LockMutex(device->lock);
device->sample_frames = buffer->buffer->datas[0].maxsize / device->hidden->stride;
device->buffer_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;
PIPEWIRE_pw_thread_loop_signal(_this->hidden->loop, false);
device->hidden->stream_init_status |= PW_READY_FLAG_BUFFER_ADDED;
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)
{
SDL_AudioDevice *_this = data;
SDL_AudioDevice *device = (SDL_AudioDevice *) data;
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) {
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,
.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
@ -1128,12 +1059,12 @@ static int PIPEWIRE_OpenDevice(SDL_AudioDevice *_this, const char *devname)
struct SDL_PrivateAudioData *priv;
struct pw_properties *props;
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);
SDL_bool iscapture = _this->iscapture;
Uint32 node_id = device->handle == NULL ? PW_ID_ANY : PW_HANDLE_TO_ID(device->handle);
const SDL_bool iscapture = device->iscapture;
int res;
/* 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 */
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_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);
if (params == NULL) {
return SDL_SetError("Pipewire: Failed to set audio format parameters");
}
priv = SDL_calloc(1, sizeof(struct SDL_PrivateAudioData));
_this->hidden = priv;
device->hidden = priv;
if (priv == NULL) {
return SDL_OutOfMemory();
}
/* 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) {
_this->spec.samples = min_period;
_this->spec.size = _this->spec.samples * priv->stride;
if (device->sample_frames < min_period) {
device->sample_frames = min_period;
}
(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);
if (priv->loop == NULL) {
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_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_RATE, "1/%u", _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", device->spec.freq);
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
@ -1240,7 +1173,7 @@ static int PIPEWIRE_OpenDevice(SDL_AudioDevice *_this, const char *devname)
/* Create the new stream */
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) {
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);
}
/* 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;
}
static void PIPEWIRE_CloseDevice(SDL_AudioDevice *_this)
static void PIPEWIRE_CloseDevice(SDL_AudioDevice *device)
{
if (_this->hidden->loop) {
PIPEWIRE_pw_thread_loop_stop(_this->hidden->loop);
if (!device->hidden) {
return;
}
if (_this->hidden->stream) {
PIPEWIRE_pw_stream_destroy(_this->hidden->stream);
if (device->hidden->loop) {
PIPEWIRE_pw_thread_loop_stop(device->hidden->loop);
}
if (_this->hidden->context) {
PIPEWIRE_pw_context_destroy(_this->hidden->context);
if (device->hidden->stream) {
PIPEWIRE_pw_stream_destroy(device->hidden->stream);
}
if (_this->hidden->loop) {
PIPEWIRE_pw_thread_loop_destroy(_this->hidden->loop);
if (device->hidden->context) {
PIPEWIRE_pw_context_destroy(device->hidden->context);
}
if (_this->hidden->buffer) {
SDL_DestroyDataQueue(_this->hidden->buffer);
if (device->hidden->loop) {
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)
{
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;
SDL_AudioThreadFinalize(device);
}
static void PIPEWIRE_Deinitialize(void)
@ -1366,13 +1259,15 @@ static SDL_bool PIPEWIRE_Init(SDL_AudioDriverImpl *impl)
/* Set the function pointers */
impl->DetectDevices = PIPEWIRE_DetectDevices;
impl->OpenDevice = PIPEWIRE_OpenDevice;
impl->CloseDevice = PIPEWIRE_CloseDevice;
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->ProvidesOwnCallbackThread = SDL_TRUE;
impl->SupportsNonPow2Samples = SDL_TRUE;
return SDL_TRUE;
}

View File

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

View File

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

View File

@ -34,41 +34,34 @@
#include <pspaudio.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)
{
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;
_this->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc(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();
}
SDL_zerop(_this->hidden);
/* device only natively supports S16LSB */
_this->spec.format = SDL_AUDIO_S16LSB;
// device only natively supports S16LSB
device->spec.format = SDL_AUDIO_S16LSB;
/* 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,
so a resampler must be done for these scenarios */
if (isBasicAudioConfig(&_this->spec)) {
/* The sample count must be a multiple of 64. */
_this->spec.samples = PSP_AUDIO_SAMPLE_ALIGN(_this->spec.samples);
/* The number of channels (1 or 2). */
_this->spec.channels = _this->spec.channels == 1 ? 1 : 2;
format = _this->spec.channels == 1 ? PSP_AUDIO_FORMAT_MONO : PSP_AUDIO_FORMAT_STEREO;
_this->hidden->channel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, _this->spec.samples, format);
if (isBasicAudioConfig(&device->spec)) {
// The sample count must be a multiple of 64.
device->sample_frames = PSP_AUDIO_SAMPLE_ALIGN(device->sample_frames);
// The number of channels (1 or 2).
device->spec.channels = device->spec.channels == 1 ? 1 : 2;
const int format = (device->spec.channels == 1) ? PSP_AUDIO_FORMAT_MONO : PSP_AUDIO_FORMAT_STEREO;
device->hidden->channel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, device->sample_frames, format);
} else {
/* 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11050, 8000 */
switch (_this->spec.freq) {
// 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11050, 8000
switch (device->spec.freq) {
case 8000:
case 11025:
case 12000:
@ -78,93 +71,90 @@ static int PSPAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
case 32000:
case 44100:
case 48000:
_this->spec.freq = _this->spec.freq;
break;
break; // acceptable, keep it
default:
_this->spec.freq = 48000;
device->spec.freq = 48000;
break;
}
/* 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);
_this->spec.channels = 2; /* we're forcing the hardware to stereo. */
_this->hidden->channel = sceAudioSRCChReserve(_this->spec.samples, _this->spec.freq, 2);
// The number of samples to output in one output call (min 17, max 4111).
device->sample_frames = device->sample_frames < 17 ? 17 : (device->sample_frames > 4111 ? 4111 : device->sample_frames);
device->spec.channels = 2; // we're forcing the hardware to stereo.
device->hidden->channel = sceAudioSRCChReserve(device->sample_frames, device->spec.freq, 2);
}
if (_this->hidden->channel < 0) {
SDL_aligned_free(_this->hidden->rawbuf);
_this->hidden->rawbuf = NULL;
if (device->hidden->channel < 0) {
return SDL_SetError("Couldn't reserve hardware channel");
}
/* Update the fragment size as size in bytes. */
SDL_CalculateAudioSpec(&_this->spec);
// Update the fragment size as size in bytes.
SDL_UpdatedAudioDeviceFormat(device);
/* Allocate the mixing buffer. Its size and starting address must
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. */
mixlen = _this->spec.size * NUM_BUFFERS;
_this->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen);
if (_this->hidden->rawbuf == NULL) {
const int mixlen = device->buffer_size * NUM_BUFFERS;
device->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen);
if (device->hidden->rawbuf == NULL) {
return SDL_SetError("Couldn't allocate mixing buffer");
}
SDL_memset(_this->hidden->rawbuf, 0, mixlen);
for (i = 0; i < NUM_BUFFERS; i++) {
_this->hidden->mixbufs[i] = &_this->hidden->rawbuf[i * _this->spec.size];
SDL_memset(device->hidden->rawbuf, device->silence_value, mixlen);
for (int i = 0; i < NUM_BUFFERS; i++) {
device->hidden->mixbufs[i] = &device->hidden->rawbuf[i * device->buffer_size];
}
_this->hidden->next_buffer = 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(&_this->spec)) {
SDL_assert(_this->spec.channels == 2);
sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, mixbuf);
if (!isBasicAudioConfig(&device->spec)) {
SDL_assert(device->spec.channels == 2);
sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, (void *) buffer);
} 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;
}
/* This function waits until it is possible to write a full sound buffer */
static void PSPAUDIO_WaitDevice(SDL_AudioDevice *_this)
static void PSPAUDIO_WaitDevice(SDL_AudioDevice *device)
{
/* 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 (!isBasicAudioConfig(&_this->spec)) {
if (device->hidden) {
if (device->hidden->channel >= 0) {
if (!isBasicAudioConfig(&device->spec)) {
sceAudioSRCChRelease();
} else {
sceAudioChRelease(_this->hidden->channel);
sceAudioChRelease(device->hidden->channel);
}
_this->hidden->channel = -1;
device->hidden->channel = -1;
}
if (_this->hidden->rawbuf != NULL) {
SDL_aligned_free(_this->hidden->rawbuf);
_this->hidden->rawbuf = NULL;
if (device->hidden->rawbuf != NULL) {
SDL_aligned_free(device->hidden->rawbuf);
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
ahead of other SDL threads. */
SceUID thid;
const SceUID thid = sceKernelGetThreadId();
SceKernelThreadInfo status;
thid = sceKernelGetThreadId();
status.size = sizeof(SceKernelThreadInfo);
if (sceKernelReferThreadStatus(thid, &status) == 0) {
sceKernelChangeThreadPriority(thid, status.currentPriority - 1);
@ -173,25 +163,20 @@ static void PSPAUDIO_ThreadInit(SDL_AudioDevice *_this)
static SDL_bool PSPAUDIO_Init(SDL_AudioDriverImpl *impl)
{
/* Set the function pointers */
impl->OpenDevice = PSPAUDIO_OpenDevice;
impl->PlayDevice = PSPAUDIO_PlayDevice;
impl->WaitDevice = PSPAUDIO_WaitDevice;
impl->GetDeviceBuf = PSPAUDIO_GetDeviceBuf;
impl->CloseDevice = PSPAUDIO_CloseDevice;
impl->ThreadInit = PSPAUDIO_ThreadInit;
/* PSP audio device */
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
/*
impl->HasCaptureSupport = SDL_TRUE;
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
*/
return SDL_TRUE; /* this audio target is available. */
//impl->HasCaptureSupport = SDL_TRUE;
//impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
return SDL_TRUE;
}
AudioBootStrap PSPAUDIO_bootstrap = {
"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_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_source_path = NULL;
/* ... and these are the descriptions we use in GetDefaultAudioInfo. */
static char *default_sink_name = NULL;
static char *default_source_name = NULL;
// ... and these are the PulseAudio device indices of the default devices.
static uint32_t default_sink_index = 0;
static uint32_t default_source_index = 0;
static const char *(*PULSEAUDIO_pa_get_library_version)(void);
static pa_channel_map *(*PULSEAUDIO_pa_channel_map_init_auto)(
@ -359,12 +358,6 @@ failed:
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)
{
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);
}
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;
int available = h->mixlen;
int written = 0;
int cpy;
struct SDL_PrivateAudioData *h = device->hidden;
/*printf("PULSEAUDIO PLAYDEVICE START! mixlen=%d\n", available);*/
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
while (SDL_AtomicGet(&_this->enabled) && (available > 0)) {
cpy = SDL_min(h->bytes_requested, available);
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");*/
while (!SDL_AtomicGet(&device->shutdown) && (h->bytes_requested < (device->buffer_size / 2))) {
/*printf("PULSEAUDIO WAIT IN WAITDEVICE!\n");*/
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)) {
/*printf("PULSEAUDIO DEVICE FAILURE IN PLAYDEVICE!\n");*/
SDL_OpenedAudioDeviceDisconnected(_this);
/*printf("PULSEAUDIO DEVICE FAILURE IN WAITDEVICE!\n");*/
SDL_AudioDeviceDisconnected(device);
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);
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);*/
}
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)
@ -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 */
}
static int PULSEAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
static void PULSEAUDIO_WaitCaptureDevice(SDL_AudioDevice *device)
{
struct SDL_PrivateAudioData *h = _this->hidden;
const void *data = NULL;
size_t nbytes = 0;
int retval = 0;
struct SDL_PrivateAudioData *h = device->hidden;
if (h->capturebuf != NULL) {
return; // there's still data available to read.
}
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
while (SDL_AtomicGet(&_this->enabled)) {
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)) {
while (!SDL_AtomicGet(&device->shutdown)) {
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)) {
/*printf("PULSEAUDIO DEVICE FAILURE IN CAPTUREFROMDEVICE!\n");*/
SDL_OpenedAudioDeviceDisconnected(_this);
retval = -1;
//printf("PULSEAUDIO DEVICE FAILURE IN WAITCAPTUREDEVICE!\n");
SDL_AudioDeviceDisconnected(device);
break;
}
}
if ((retval == -1) || !SDL_AtomicGet(&_this->enabled)) { /* in case this happened while we were blocking. */
retval = -1;
break;
}
/* a new fragment is available! */
} else if (PULSEAUDIO_pa_stream_readable_size(h->stream) > 0) {
// a new fragment is available!
const void *data = NULL;
size_t nbytes = 0;
PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes);
SDL_assert(nbytes > 0);
/* If data == NULL, then the buffer had a hole, ignore that */
if (data == NULL) {
PULSEAUDIO_pa_stream_drop(h->stream); /* drop this fragment. */
if (data == NULL) { // If NULL, then the buffer had a hole, ignore that
PULSEAUDIO_pa_stream_drop(h->stream); // drop this fragment.
} else {
/* store this fragment's data, start feeding it to SDL. */
/*printf("PULSEAUDIO: captured %d new bytes\n", (int) nbytes);*/
// store this fragment's data for use with CaptureFromDevice
//printf("PULSEAUDIO: captured %d new bytes\n", (int) nbytes);
h->capturebuf = (const Uint8 *)data;
h->capturelen = nbytes;
break;
}
}
}
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;
size_t nbytes = 0;
@ -497,11 +499,11 @@ static void PULSEAUDIO_FlushCapture(SDL_AudioDevice *_this)
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);
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");*/
SDL_OpenedAudioDeviceDisconnected(_this);
SDL_AudioDeviceDisconnected(device);
break;
}
@ -515,22 +517,23 @@ static void PULSEAUDIO_FlushCapture(SDL_AudioDevice *_this)
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);
if (_this->hidden->stream) {
if (_this->hidden->capturebuf != NULL) {
PULSEAUDIO_pa_stream_drop(_this->hidden->stream);
if (device->hidden->stream) {
if (device->hidden->capturebuf != NULL) {
PULSEAUDIO_pa_stream_drop(device->hidden->stream);
}
PULSEAUDIO_pa_stream_disconnect(_this->hidden->stream);
PULSEAUDIO_pa_stream_unref(_this->hidden->stream);
PULSEAUDIO_pa_stream_disconnect(device->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);
SDL_free(_this->hidden->mixbuf);
SDL_free(_this->hidden->device_name);
SDL_free(_this->hidden);
SDL_free(device->hidden->mixbuf);
SDL_free(device->hidden->device_name);
SDL_free(device->hidden);
}
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);
}
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. */
return SDL_TRUE;
}
if (iscapture) {
if (device->iscapture) {
WaitForPulseOperation(PULSEAUDIO_pa_context_get_source_info_by_index(pulseaudio_context, idx, SourceDeviceNameCallback, &h->device_name));
} else {
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. */
}
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;
SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts;
@ -582,7 +584,6 @@ static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
pa_buffer_attr paattr;
pa_channel_map pacmap;
pa_stream_flags_t flags = 0;
SDL_bool iscapture = _this->iscapture;
int format = PA_SAMPLE_INVALID;
int retval = 0;
@ -590,14 +591,13 @@ static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
SDL_assert(pulseaudio_context != NULL);
/* Initialize all variables that we clean on shutdown */
h = _this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*_this->hidden));
if (_this->hidden == NULL) {
h = device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
if (device->hidden == NULL) {
return SDL_OutOfMemory();
}
SDL_zerop(_this->hidden);
/* 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) {
#ifdef DEBUG_AUDIO
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) {
return SDL_SetError("pulseaudio: Unsupported audio format");
}
_this->spec.format = test_format;
device->spec.format = test_format;
paspec.format = format;
/* Calculate the final parameters for this audio specification */
SDL_CalculateAudioSpec(&_this->spec);
SDL_UpdatedAudioDeviceFormat(device);
/* Allocate mixing buffer */
if (!iscapture) {
h->mixlen = _this->spec.size;
h->mixbuf = (Uint8 *)SDL_malloc(h->mixlen);
h->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
if (h->mixbuf == NULL) {
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.rate = _this->spec.freq;
paspec.channels = device->spec.channels;
paspec.rate = device->spec.freq;
/* Reduced prebuffering compared to the defaults. */
paattr.fragsize = _this->spec.size;
paattr.tlength = h->mixlen;
paattr.fragsize = device->buffer_size;
paattr.tlength = device->buffer_size;
paattr.prebuf = -1;
paattr.maxlength = -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);
if (!FindDeviceName(h, iscapture, _this->handle)) {
if (!FindDeviceName(device)) {
retval = SDL_SetError("Requested PulseAudio sink/source missing?");
} else {
const char *name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME);
/* The SDL ALSA output hints us that we use Windows' channel mapping */
/* https://bugzilla.libsdl.org/show_bug.cgi?id=110 */
PULSEAUDIO_pa_channel_map_init_auto(&pacmap, _this->spec.channels,
PA_CHANNEL_MAP_WAVEEX);
PULSEAUDIO_pa_channel_map_init_auto(&pacmap, device->spec.channels, PA_CHANNEL_MAP_WAVEEX);
h->stream = PULSEAUDIO_pa_stream_new(
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);
/* now that we have multi-device support, don't move a stream from
a device that was unplugged to something else, unless we're default. */
if (h->device_name != NULL) {
// SDL manages device moves if the default changes, so don't ever let Pulse automatically migrate this stream.
flags |= PA_STREAM_DONT_MOVE;
}
if (iscapture) {
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)
{
SDL_AudioSpec spec;
SDL_bool add = (SDL_bool)((intptr_t)data);
if (i) {
spec.freq = i->sample_spec.rate;
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;
const SDL_bool add = (SDL_bool) ((intptr_t)data);
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));
}
if (default_sink_path != NULL && SDL_strcmp(i->name, default_sink_path) == 0) {
if (default_sink_name != NULL) {
SDL_free(default_sink_name);
}
default_sink_name = SDL_strdup(i->description);
default_sink_index = i->index;
}
}
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
}
/* 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)
{
SDL_AudioSpec spec;
SDL_bool add = (SDL_bool)((intptr_t)data);
if (i) {
/* Maybe skip "monitor" sources. These are just output from other sinks. */
if (include_monitors || (i->monitor_of_sink == PA_INVALID_INDEX)) {
spec.freq = i->sample_spec.rate;
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 (i && (include_monitors || (i->monitor_of_sink == PA_INVALID_INDEX))) {
const SDL_bool add = (SDL_bool) ((intptr_t)data);
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));
}
if (default_source_path != NULL && SDL_strcmp(i->name, default_source_path) == 0) {
if (default_source_name != NULL) {
SDL_free(default_source_name);
}
default_source_name = SDL_strdup(i->description);
}
default_source_index = i->index;
}
}
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)
{
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_source_path);
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);
}
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)
{
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 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) {
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)));
} 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)));
} else if (removed && (sink || source)) {
/* removes we can handle just with the device index. */
SDL_RemoveAudioDevice(source != 0, (void *)((intptr_t)idx + 1));
// removes we can handle just with the device index.
SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle((void *)((intptr_t)idx + 1)));
}
}
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)
{
uint32_t prev_default_sink_index = default_sink_index;
uint32_t prev_default_source_index = default_source_index;
pa_operation *op;
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);
/* 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);
@ -865,6 +864,13 @@ static int SDLCALL HotplugThread(void *data)
PULSEAUDIO_pa_operation_unref(op);
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) {
@ -874,9 +880,11 @@ static int SDLCALL HotplugThread(void *data)
PULSEAUDIO_pa_context_set_subscribe_callback(pulseaudio_context, NULL, NULL);
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
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);
@ -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)));
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... */
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_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)
{
if (pulseaudio_hotplug_thread) {
@ -941,10 +929,9 @@ static void PULSEAUDIO_Deinitialize(void)
default_sink_path = NULL;
SDL_free(default_source_path);
default_source_path = NULL;
SDL_free(default_sink_name);
default_sink_name = NULL;
SDL_free(default_source_name);
default_source_name = NULL;
default_source_index = 0;
default_sink_index = 0;
UnloadPulseAudioLibrary();
}
@ -968,12 +955,11 @@ static SDL_bool PULSEAUDIO_Init(SDL_AudioDriverImpl *impl)
impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
impl->CloseDevice = PULSEAUDIO_CloseDevice;
impl->Deinitialize = PULSEAUDIO_Deinitialize;
impl->WaitCaptureDevice = PULSEAUDIO_WaitCaptureDevice;
impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice;
impl->FlushCapture = PULSEAUDIO_FlushCapture;
impl->GetDefaultAudioInfo = PULSEAUDIO_GetDefaultAudioInfo;
impl->HasCaptureSupport = SDL_TRUE;
impl->SupportsNonPow2Samples = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */
}

View File

@ -36,7 +36,6 @@ struct SDL_PrivateAudioData
/* Raw mixing buffer */
Uint8 *mixbuf;
int mixlen;
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.
*/
/*
* !!! 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? */
// !!! FIXME: can this target support hotplugging?
#include "../../SDL_internal.h"
@ -48,7 +42,7 @@
#include "../SDL_audio_c.h"
#include "SDL_qsa_audio.h"
/* default channel communication parameters */
// default channel communication parameters
#define DEFAULT_CPARAMS_RATE 44100
#define DEFAULT_CPARAMS_VOICES 1
@ -56,32 +50,17 @@
#define DEFAULT_CPARAMS_FRAGS_MIN 1
#define DEFAULT_CPARAMS_FRAGS_MAX 1
/* List of found devices */
#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;
#define QSA_MAX_NAME_LENGTH 81+16 // Hardcoded in QSA, can't be changed
static int QSA_SetError(const char *fn, int 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? */
static void QSA_ThreadInit(SDL_AudioDevice *_this)
// !!! FIXME: does this need to be here? Does the SDL version not work?
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;
if (SchedGet(0, 0, &param) != -1) {
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)
{
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;
}
/* This function waits until it is possible to write a full sound buffer */
static void QSA_WaitDevice(SDL_AudioDevice *_this)
// This function waits until it is possible to write a full sound buffer
static void QSA_WaitDevice(SDL_AudioDevice *device)
{
int result;
/* Setup timeout for playing one fragment equal to 2 seconds */
/* If timeout occurred than something wrong with hardware or driver */
/* For example, Vortex 8820 audio driver stucks on second DAC because */
/* it doesn't exist ! */
result = SDL_IOReady(_this->hidden->audio_fd,
_this->hidden->iscapture ? SDL_IOR_READ : SDL_IOR_WRITE,
// Setup timeout for playing one fragment equal to 2 seconds
// If timeout occurred than something wrong with hardware or driver
// For example, Vortex 8820 audio driver stucks on second DAC because
// it doesn't exist !
result = SDL_IOReady(device->hidden->audio_fd,
device->iscapture ? SDL_IOR_READ : SDL_IOR_WRITE,
2 * 1000);
switch (result) {
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;
case 0:
SDL_SetError("QSA: timeout on buffer waiting occurred");
_this->hidden->timeout_on_wait = 1;
device->hidden->timeout_on_wait = SDL_TRUE; // !!! FIXME: Should we just disconnect the device in this case?
break;
default:
_this->hidden->timeout_on_wait = 0;
device->hidden->timeout_on_wait = SDL_FALSE;
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;
int written;
int status;
int towrite;
void *pcmbuffer;
if (!SDL_AtomicGet(&_this->enabled) || !_this->hidden) {
if (SDL_AtomicGet(&device->shutdown) || !device->hidden) {
return;
}
towrite = _this->spec.size;
pcmbuffer = _this->hidden->pcm_buf;
int towrite = buflen;
/* Write the audio data, checking for EAGAIN (buffer full) and underrun */
do {
written =
snd_pcm_plugin_write(_this->hidden->audio_handle, pcmbuffer,
towrite);
if (written != towrite) {
/* Check if samples playback got stuck somewhere in hardware or in */
/* the audio device driver */
if ((errno == EAGAIN) && (written == 0)) {
if (_this->hidden->timeout_on_wait != 0) {
SDL_SetError("QSA: buffer playback timeout");
return;
// Write the audio data, checking for EAGAIN (buffer full) and underrun
while ((towrite > 0) && !SDL_AtomicGet(&device->shutdown));
const int bw = snd_pcm_plugin_write(device->hidden->audio_handle, buffer, towrite);
if (bw != towrite) {
// Check if samples playback got stuck somewhere in hardware or in the audio device driver
if ((errno == EAGAIN) && (bw == 0)) {
if (device->hidden->timeout_on_wait) {
return; // oh well, try again next time. !!! FIXME: Should we just disconnect the device in this case?
}
}
/* Check for errors or conditions */
// Check for errors or conditions
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
/* Let a little CPU time go by and try to write again */
SDL_Delay(1);
SDL_Delay(1); // Let a little CPU time go by and try to write again
/* if we wrote some data */
towrite -= written;
pcmbuffer += written * _this->spec.channels;
// if we wrote some data
towrite -= bw;
buffer += bw * device->spec.channels;
continue;
} else {
if ((errno == EINVAL) || (errno == EIO)) {
} else if ((errno == EINVAL) || (errno == EIO)) {
snd_pcm_channel_status_t cstatus;
SDL_zero(cstatus);
if (!_this->hidden->iscapture) {
cstatus.channel = SND_PCM_CHANNEL_PLAYBACK;
} else {
cstatus.channel = SND_PCM_CHANNEL_CAPTURE;
}
cstatus.channel = device->iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK;
status =
snd_pcm_plugin_status(_this->hidden->audio_handle,
&cstatus);
int status = snd_pcm_plugin_status(device->hidden->audio_handle, &cstatus);
if (status < 0) {
QSA_SetError("snd_pcm_plugin_status", status);
return;
}
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);
}
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 (status < 0) {
QSA_SetError("snd_pcm_plugin_prepare", status);
return;
return; // !!! FIXME: disconnect the device?
}
}
continue;
} else {
return;
}
return; // !!! FIXME: disconnect the device?
}
} else {
/* we wrote all remaining data */
towrite -= written;
pcmbuffer += written * _this->spec.channels;
// we wrote all remaining data
towrite -= bw;
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) {
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 (!_this->hidden->iscapture) {
/* Finish playing available samples */
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);
}
// Finish playing available samples or cancel unread samples during capture
snd_pcm_plugin_flush(device->hidden->audio_handle, device->iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK);
#endif
snd_pcm_close(_this->hidden->audio_handle);
snd_pcm_close(device->hidden->audio_handle);
}
SDL_free(_this->hidden->pcm_buf);
SDL_free(_this->hidden);
SDL_free(device->hidden->pcm_buf);
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
/* !!! FIXME: SDL2 used to pass this handle. What's the alternative? */
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;
if (device->iscapture) {
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!
}
/* Initialize all variables that we clean on shutdown */
_this->hidden =
(struct SDL_PrivateAudioData *) SDL_calloc(1,
(sizeof
(struct
SDL_PrivateAudioData)));
if (_this->hidden == NULL) {
SDL_assert(device->handle != NULL); // NULL used to mean "system default device" in SDL2; it does not mean that in SDL3.
const Uint32 sdlhandle = (Uint32) ((size_t) device->handle);
const uint32_t cardno = (uint32_t) (sdlhandle & 0xFFFF);
const uint32_t deviceno = (uint32_t) ((sdlhandle >> 16) & 0xFFFF);
const SDL_bool iscapture = device->iscapture;
int status = 0;
// 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();
}
/* Initialize channel transfer parameters to default */
// Initialize channel transfer parameters to default
snd_pcm_channel_params_t cparams;
QSA_InitAudioParams(&cparams);
/* Initialize channel direction: capture or playback */
_this->hidden->iscapture = iscapture ? SDL_TRUE : SDL_FALSE;
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 */
// Open requested audio device
status = snd_pcm_open(&device->hidden->audio_handle, cardno, deviceno, iscapture ? SND_PCM_OPEN_CAPTURE : SND_PCM_OPEN_PLAYBACK);
if (status < 0) {
_this->hidden->audio_handle = NULL;
device->hidden->audio_handle = NULL;
return QSA_SetError("snd_pcm_open", status);
}
/* Try for a closest match on audio format */
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
// Try for a closest match on audio format
SDL_AudioFormat test_format = 0;
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
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) {
#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(S8, S8);
CHECKFMT(S16LSB, S16_LE);
@ -323,161 +243,151 @@ static int QSA_OpenDevice(SDL_AudioDevice *_this, const char *devname)
break;
}
/* assumes test_format not 0 on success */
// assumes test_format not 0 on success
if (test_format == 0) {
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 */
cparams.format.format = format;
// Set mono/stereo/4ch/6ch/8ch audio
cparams.format.voices = device->spec.channels;
/* Set mono/stereo/4ch/6ch/8ch audio */
cparams.format.voices = _this->spec.channels;
// Set rate
cparams.format.rate = device->spec.freq;
/* Set rate */
cparams.format.rate = _this->spec.freq;
/* Setup the transfer parameters according to cparams */
status = snd_pcm_plugin_params(_this->hidden->audio_handle, &cparams);
// Setup the transfer parameters according to cparams
status = snd_pcm_plugin_params(device->hidden->audio_handle, &cparams);
if (status < 0) {
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);
if (!_this->hidden->iscapture) {
csetup.channel = SND_PCM_CHANNEL_PLAYBACK;
} else {
csetup.channel = SND_PCM_CHANNEL_CAPTURE;
}
/* Setup an audio channel */
if (snd_pcm_plugin_setup(_this->hidden->audio_handle, &csetup) < 0) {
csetup.channel = iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK;
if (snd_pcm_plugin_setup(device->hidden->audio_handle, &csetup) < 0) {
return SDL_SetError("QSA: Unable to setup channel");
}
/* Calculate the final parameters for this audio specification */
SDL_CalculateAudioSpec(&_this->spec);
device->sample_frames = csetup.buf.block.frag_size;
_this->hidden->pcm_len = _this->spec.size;
// Calculate the final parameters for this audio specification
SDL_UpdatedAudioDeviceFormat(device);
if (_this->hidden->pcm_len == 0) {
_this->hidden->pcm_len =
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) {
device->hidden->pcm_buf = (Uint8 *) SDL_malloc(device->buffer_size);
if (device->hidden->pcm_buf == NULL) {
return SDL_OutOfMemory();
}
SDL_memset(_this->hidden->pcm_buf, _this->spec.silence,
_this->hidden->pcm_len);
SDL_memset(device->hidden->pcm_buf, device->silence_value, device->buffer_size);
/* get the file descriptor */
if (!_this->hidden->iscapture) {
_this->hidden->audio_fd =
snd_pcm_file_descriptor(_this->hidden->audio_handle,
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);
// get the file descriptor
device->hidden->audio_fd = snd_pcm_file_descriptor(device->hidden->audio_handle, csetup.channel);
if (device->hidden->audio_fd < 0) {
return QSA_SetError("snd_pcm_file_descriptor", device->hidden->audio_fd);
}
// Prepare an audio channel
status = snd_pcm_plugin_prepare(device->hidden->audio_handle, csetup.channel)
if (status < 0) {
return QSA_SetError("snd_pcm_plugin_prepare", status);
}
/* We're really ready to rock and roll. :-) */
return 0;
return 0; // We're really ready to rock and roll. :-)
}
static void QSA_DetectDevices(void)
static SDL_AudioFormat QnxFormatToSDLFormat(const int32_t qnxfmt)
{
uint32_t it;
uint32_t cards;
uint32_t devices;
int32_t status;
switch (qnxfmt) {
#define CHECKFMT(sdlfmt, qsafmt) case SND_PCM_SFMT_##qsafmt: return SDL_AUDIO_##sdlfmt
CHECKFMT(U8, U8);
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 */
/* this value can be changed in the runtime */
cards = snd_cards();
static void QSA_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{
// 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 */
/* of available audio devices */
if (cards == 0) {
/* We have no any available audio devices */
// If io-audio manager is not running we will get 0 as number of available audio devices
if (num_cards == 0) { // not any available audio devices?
SDL_small_free(cards, isstack);
return;
}
/* !!! FIXME: code duplication */
/* Find requested devices by type */
{ /* output devices */
/* Playback devices enumeration requested */
for (it = 0; it < cards; it++) {
devices = 0;
do {
status =
snd_card_get_longname(it,
qsa_playback_device
[qsa_playback_devices].name,
QSA_MAX_NAME_LENGTH);
// Find requested devices by type
for (int it = 0; it < num_cards; it++) {
const int card = cards[it];
for (uint32_t deviceno = 0; ; deviceno++) {
int32_t status;
char name[QSA_MAX_NAME_LENGTH];
status = snd_card_get_longname(card, name, sizeof (name));
if (status == EOK) {
snd_pcm_t *handle;
/* Add device number to device name */
sprintf(qsa_playback_device[qsa_playback_devices].name +
SDL_strlen(qsa_playback_device
[qsa_playback_devices].name), " d%d",
devices);
// Add device number to device name
char fullname[QSA_MAX_NAME_LENGTH + 32];
SDL_snprintf(fullname, sizeof (fullname), "%s d%d", name, (int) deviceno);
/* Store associated card number id */
qsa_playback_device[qsa_playback_devices].cardno = it;
/* Check if this device id could play anything */
status =
snd_pcm_open(&handle, it, devices,
SND_PCM_OPEN_PLAYBACK);
// Check if this device id could play anything
SDL_bool iscapture = SDL_FALSE;
status = snd_pcm_open(&handle, card, deviceno, SND_PCM_OPEN_PLAYBACK);
if (status != EOK) { // no? See if it's a capture device instead.
#if 0 // !!! FIXME: most of this code has support for capture devices, but there's no CaptureFromDevice, etc functions. Fill them in!
status = snd_pcm_open(&handle, card, deviceno, SND_PCM_OPEN_CAPTURE);
if (status == EOK) {
qsa_playback_device[qsa_playback_devices].deviceno =
devices;
iscapture = SDL_TRUE;
}
#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);
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_FALSE, qsa_playback_device[qsa_playback_devices].name, NULL, &qsa_playback_device[qsa_playback_devices]);
qsa_playback_devices++;
// !!! FIXME: I'm assuming each of these values are way less than 0xFFFF. Fix this if not.
SDL_assert(card <= 0xFFFF);
SDL_assert(deviceno <= 0xFFFF);
const Uint32 sdlhandle = ((Uint32) card) | (((Uint32) deviceno) << 16);
SDL_AddAudioDevice(iscapture, fullname, pspec, (void *) ((size_t) sdlhandle));
}
} else {
/* Check if we got end of devices list */
// Check if we got end of devices list
if (status == -ENOENT) {
break;
}
@ -485,105 +395,40 @@ static void QSA_DetectDevices(void)
} else {
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 */
if (qsa_capture_devices >= QSA_MAX_DEVICES) {
break;
}
} else {
break;
}
devices++;
} while (1);
SDL_small_free(cards, isstack);
/* Check if we reached maximum devices count */
if (qsa_capture_devices >= QSA_MAX_DEVICES) {
break;
}
// Try to open the "preferred" devices, which will tell us the card/device pairs for the default devices.
snd_pcm_t handle;
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)
{
/* Clear devices array on shutdown */
/* !!! 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;
// nothing to do here atm.
}
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->OpenDevice = QSA_OpenDevice;
impl->ThreadInit = QSA_ThreadInit;
@ -592,21 +437,16 @@ static SDL_bool QSA_Init(SDL_AudioDriverImpl * impl)
impl->GetDeviceBuf = QSA_GetDeviceBuf;
impl->CloseDevice = QSA_CloseDevice;
impl->Deinitialize = QSA_Deinitialize;
impl->LockDevice = NULL;
impl->UnlockDevice = NULL;
impl->ProvidesOwnCallbackThread = 0;
impl->HasCaptureSupport = 1;
impl->OnlyHasDefaultOutputDevice = 0;
impl->OnlyHasDefaultCaptureDevice = 0;
// !!! FIXME: most of this code has support for capture devices, but there's no CaptureFromDevice, etc functions. Fill them in!
//impl->HasCaptureSupport = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */
return SDL_TRUE;
}
AudioBootStrap QSAAUDIO_bootstrap = {
"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
{
/* SDL capture state */
SDL_bool iscapture;
/* The audio device handle */
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;
snd_pcm_t *audio_handle; // The audio device handle
int audio_fd; // The audio file descriptor, for selecting on
SDL_bool timeout_on_wait; // Select timeout status
Uint8 *pcm_buf; // Raw mixing buffer
};
#endif /* __SDL_QSA_AUDIO_H__ */

View File

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

View File

@ -38,41 +38,41 @@
#define SCE_AUDIO_SAMPLE_ALIGN(s) (((s) + 63) & ~63)
#define SCE_AUDIO_MAX_VOLUME 0x8000
static int VITAAUD_OpenCaptureDevice(SDL_AudioDevice *_this)
static int VITAAUD_OpenCaptureDevice(SDL_AudioDevice *device)
{
_this->spec.freq = 16000;
_this->spec.samples = 512;
_this->spec.channels = 1;
device->spec.freq = 16000;
device->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) {
return SDL_SetError("Couldn't open audio in port: %x", _this->hidden->port);
if (device->hidden->port < 0) {
return SDL_SetError("Couldn't open audio in port: %x", device->hidden->port);
}
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 vols[2] = { SCE_AUDIO_MAX_VOLUME, SCE_AUDIO_MAX_VOLUME };
SDL_AudioFormat test_format;
const SDL_AudioFormat *closefmts;
_this->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc(sizeof(*_this->hidden));
if (_this->hidden == NULL) {
device->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc(sizeof(*device->hidden));
if (device->hidden == NULL) {
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) {
if (test_format == SDL_AUDIO_S16LSB) {
_this->spec.format = test_format;
device->spec.format = test_format;
break;
}
}
@ -81,106 +81,127 @@ static int VITAAUD_OpenDevice(SDL_AudioDevice *_this, const char *devname)
return SDL_SetError("Unsupported audio format");
}
if (_this->iscapture) {
return VITAAUD_OpenCaptureDevice(_this);
if (device->iscapture) {
return VITAAUD_OpenCaptureDevice(device);
}
/* The sample count must be a multiple of 64. */
_this->spec.samples = SCE_AUDIO_SAMPLE_ALIGN(_this->spec.samples);
// The sample count must be a multiple of 64.
device->sample_frames = SCE_AUDIO_SAMPLE_ALIGN(device->sample_frames);
/* Update the fragment size as size in bytes. */
SDL_CalculateAudioSpec(&_this->spec);
// Update the fragment size as size in bytes.
SDL_UpdatedAudioDeviceFormat(device);
/* Allocate the mixing buffer. Its size and starting address must
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. */
mixlen = _this->spec.size * NUM_BUFFERS;
_this->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen);
if (_this->hidden->rawbuf == NULL) {
mixlen = device->buffer_size * NUM_BUFFERS;
device->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen);
if (device->hidden->rawbuf == NULL) {
return SDL_SetError("Couldn't allocate mixing buffer");
}
/* Setup the hardware channel. */
if (_this->spec.channels == 1) {
// Setup the hardware channel.
if (device->spec.channels == 1) {
format = SCE_AUDIO_OUT_MODE_MONO;
} else {
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;
}
_this->hidden->port = sceAudioOutOpenPort(port, _this->spec.samples, _this->spec.freq, format);
if (_this->hidden->port < 0) {
SDL_aligned_free(_this->hidden->rawbuf);
_this->hidden->rawbuf = NULL;
return SDL_SetError("Couldn't open audio out port: %x", _this->hidden->port);
device->hidden->port = sceAudioOutOpenPort(port, device->sample_frames, device->spec.freq, format);
if (device->hidden->port < 0) {
SDL_aligned_free(device->hidden->rawbuf);
device->hidden->rawbuf = NULL;
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++) {
_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;
}
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(_this->hidden->port, mixbuf);
_this->hidden->next_buffer = (_this->hidden->next_buffer + 1) % NUM_BUFFERS;
sceAudioOutOutput(device->hidden->port, buffer);
}
/* This function waits until it is possible to write a full sound buffer */
static void VITAAUD_WaitDevice(SDL_AudioDevice *_this)
// This function waits until it is possible to write a full sound buffer
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 (_this->iscapture) {
sceAudioInReleasePort(_this->hidden->port);
if (device->hidden) {
if (device->hidden->port >= 0) {
if (device->iscapture) {
sceAudioInReleasePort(device->hidden->port);
} else {
sceAudioOutReleasePort(_this->hidden->port);
sceAudioOutReleasePort(device->hidden->port);
}
_this->hidden->port = -1;
device->hidden->port = -1;
}
if (!_this->iscapture && _this->hidden->rawbuf != NULL) {
SDL_aligned_free(_this->hidden->rawbuf);
_this->hidden->rawbuf = NULL;
if (!device->iscapture && device->hidden->rawbuf != NULL) {
SDL_aligned_free(device->hidden->rawbuf); // this uses memalign(), not SDL_malloc().
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;
SDL_assert(buflen == _this->spec.size);
ret = sceAudioInInput(_this->hidden->port, buffer);
SDL_assert(buflen == device->buffer_size);
ret = sceAudioInInput(device->hidden->port, buffer);
if (ret < 0) {
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
ahead of other SDL threads. */
// just grab the latest and dump it.
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;
SceKernelThreadInfo info;
thid = sceKernelGetThreadId();
@ -192,26 +213,25 @@ static void VITAAUD_ThreadInit(SDL_AudioDevice *_this)
static SDL_bool VITAAUD_Init(SDL_AudioDriverImpl *impl)
{
/* Set the function pointers */
impl->OpenDevice = VITAAUD_OpenDevice;
impl->PlayDevice = VITAAUD_PlayDevice;
impl->WaitDevice = VITAAUD_WaitDevice;
impl->GetDeviceBuf = VITAAUD_GetDeviceBuf;
impl->CloseDevice = VITAAUD_CloseDevice;
impl->ThreadInit = VITAAUD_ThreadInit;
impl->WaitCaptureDevice = VITAAUD_WaitCaptureDevice;
impl->FlushCapture = VITAAUD_FlushCapture;
impl->CaptureFromDevice = VITAAUD_CaptureFromDevice;
/* and the capabilities */
impl->HasCaptureSupport = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */
return SDL_TRUE;
}
AudioBootStrap VITAAUD_bootstrap = {
"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
{
SDL_AtomicInt refcount;
WCHAR *devid;
WAVEFORMATEX *waveformat;
IAudioClient *client;
IAudioRenderClient *render;
IAudioCaptureClient *capture;
SDL_AudioStream *capturestream;
HANDLE event;
HANDLE task;
SDL_bool coinitialized;
int framesize;
int default_device_generation;
SDL_bool device_lost;
void *activation_handler;
SDL_AtomicInt just_activated;
};
/* win32 and winrt implementations call into these. */
int WASAPI_PrepDevice(SDL_AudioDevice *_this, const SDL_bool updatestream);
void WASAPI_RefDevice(SDL_AudioDevice *_this);
void WASAPI_UnrefDevice(SDL_AudioDevice *_this);
int WASAPI_PrepDevice(SDL_AudioDevice *device);
void WASAPI_DisconnectDevice(SDL_AudioDevice *device);
// 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. */
// UNLESS OTHERWISE NOTED THESE ALL HAPPEN ON THE MANAGEMENT THREAD.
int WASAPI_PlatformInit(void);
void WASAPI_PlatformDeinit(void);
void WASAPI_EnumerateEndpoints(void);
int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture);
int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery);
void WASAPI_PlatformThreadInit(SDL_AudioDevice *_this);
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *_this);
void WASAPI_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
int WASAPI_ActivateDevice(SDL_AudioDevice *device);
void WASAPI_PlatformThreadInit(SDL_AudioDevice *device); // this happens on the audio device thread, not the management thread.
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device); // this happens on the audio device thread, not the management thread.
void WASAPI_PlatformDeleteActivationHandler(void *handler);
void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device);
#ifdef __cplusplus
}

View File

@ -49,7 +49,7 @@ static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x
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 */
}
@ -72,72 +72,68 @@ void WASAPI_PlatformDeinit(void)
pAvSetMmThreadCharacteristicsW = 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. */
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. */
if (pAvSetMmThreadCharacteristicsW) {
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. */
if (_this->hidden->task && pAvRevertMmThreadCharacteristics) {
pAvRevertMmThreadCharacteristics(_this->hidden->task);
_this->hidden->task = NULL;
if (device->hidden->task && pAvRevertMmThreadCharacteristics) {
pAvRevertMmThreadCharacteristics(device->hidden->task);
device->hidden->task = NULL;
}
if (_this->hidden->coinitialized) {
if (device->hidden->coinitialized) {
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;
HRESULT ret;
if (SDL_IMMDevice_Get(_this->hidden->devid, &device, _this->iscapture) < 0) {
_this->hidden->client = NULL;
IMMDevice *immdevice = NULL;
if (SDL_IMMDevice_Get(device, &immdevice, device->iscapture) < 0) {
device->hidden->client = NULL;
return -1; /* This is already set by SDL_IMMDevice_Get */
}
/* this is not async in standard win32, yay! */
ret = IMMDevice_Activate(device, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&_this->hidden->client);
IMMDevice_Release(device);
/* this is _not_ async in standard win32, yay! */
HRESULT ret = IMMDevice_Activate(immdevice, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&device->hidden->client);
IMMDevice_Release(immdevice);
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);
}
SDL_assert(_this->hidden->client != NULL);
if (WASAPI_PrepDevice(_this, isrecovery) == -1) { /* not async, fire it right away. */
SDL_assert(device->hidden->client != NULL);
if (WASAPI_PrepDevice(device) == -1) { /* not async, fire it right away. */
return -1;
}
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);
}
int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
{
return SDL_IMMDevice_GetDefaultAudioInfo(name, spec, iscapture);
SDL_IMMDevice_EnumerateEndpoints(default_output, default_capture);
}
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.");
}
void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device)
{
SDL_IMMDevice_FreeDeviceHandle(device);
}
#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 void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid);
static void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid);
extern "C" {
SDL_AtomicInt SDL_IMMDevice_DefaultPlaybackGeneration;
SDL_AtomicInt SDL_IMMDevice_DefaultCaptureGeneration;
static SDL_bool FindWinRTAudioDeviceCallback(SDL_AudioDevice *device, void *userdata)
{
return (SDL_wcscmp((LPCWSTR) device->handle, (LPCWSTR) userdata) == 0) ? SDL_TRUE : SDL_FALSE;
}
/* This is a list of device id strings we have inflight, so we have consistent pointers to the same device. */
typedef struct DevIdList
static SDL_AudioDevice *FindWinRTAudioDevice(LPCWSTR devid)
{
WCHAR *str;
struct DevIdList *next;
} DevIdList;
static DevIdList *deviceid_list = NULL;
return SDL_FindPhysicalAudioDeviceByCallback(FindWinRTAudioDeviceCallback, (void *) devid);
}
class SDL_WasapiDeviceEventHandler
{
@ -80,9 +75,10 @@ class SDL_WasapiDeviceEventHandler
void OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args);
void OnDefaultRenderDeviceChanged(Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args);
void OnDefaultCaptureDeviceChanged(Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args);
SDL_Semaphore *completed;
void WaitForCompletion();
private:
SDL_Semaphore *completed_semaphore;
const SDL_bool iscapture;
DeviceWatcher ^ watcher;
Windows::Foundation::EventRegistrationToken added_handler;
@ -93,10 +89,11 @@ class SDL_WasapiDeviceEventHandler
};
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.
}
Platform::String ^ selector = _iscapture ? MediaDevice::GetAudioCaptureSelector() : MediaDevice::GetAudioRenderSelector();
Platform::Collections::Vector<Platform::String ^> properties;
@ -128,9 +125,10 @@ SDL_WasapiDeviceEventHandler::~SDL_WasapiDeviceEventHandler()
watcher->Stop();
watcher = nullptr;
}
if (completed) {
SDL_DestroySemaphore(completed);
completed = nullptr;
if (completed_semaphore) {
SDL_DestroySemaphore(completed_semaphore);
completed_semaphore = nullptr;
}
if (iscapture) {
@ -142,21 +140,34 @@ SDL_WasapiDeviceEventHandler::~SDL_WasapiDeviceEventHandler()
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);
char *utf8dev = WIN_StringToUTF8(info->Name->Data());
if (utf8dev) {
WAVEFORMATEXTENSIBLE fmt;
SDL_AudioSpec spec;
SDL_zero(spec);
Platform::Object ^ obj = info->Properties->Lookup(SDL_PKEY_AudioEngine_DeviceFormat);
if (obj) {
IPropertyValue ^ property = (IPropertyValue ^) obj;
Platform::Array<unsigned char> ^ data;
property->GetUInt8Array(&data);
SDL_memcpy(&fmt, data->Data, SDL_min(data->Length, sizeof(WAVEFORMATEXTENSIBLE)));
} else {
WAVEFORMATEXTENSIBLE 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);
}
}
@ -164,7 +175,7 @@ void SDL_WasapiDeviceEventHandler::OnDeviceAdded(DeviceWatcher ^ sender, DeviceI
void SDL_WasapiDeviceEventHandler::OnDeviceRemoved(DeviceWatcher ^ sender, DeviceInformationUpdate ^ info)
{
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)
@ -175,19 +186,30 @@ void SDL_WasapiDeviceEventHandler::OnDeviceUpdated(DeviceWatcher ^ sender, Devic
void SDL_WasapiDeviceEventHandler::OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args)
{
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)
{
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)
{
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;
@ -195,54 +217,63 @@ static SDL_WasapiDeviceEventHandler *capture_device_event_handler;
int WASAPI_PlatformInit(void)
{
SDL_AtomicSet(&SDL_IMMDevice_DefaultPlaybackGeneration, 1);
SDL_AtomicSet(&SDL_IMMDevice_DefaultCaptureGeneration, 1);
return 0;
}
void WASAPI_PlatformDeinit(void)
{
DevIdList *devidlist;
DevIdList *next;
delete playback_device_event_handler;
playback_device_event_handler = nullptr;
delete capture_device_event_handler;
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
// startup, so we don't need to enumerate them separately before
// listening for updates.
playback_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_FALSE);
capture_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_TRUE);
SDL_WaitSemaphore(playback_device_event_handler->completed);
SDL_WaitSemaphore(capture_device_event_handler->completed);
playback_device_event_handler->WaitForCompletion();
defdevid = MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default);
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) {}
STDMETHOD(ActivateCompleted)
(IActivateAudioInterfaceAsyncOperation *operation);
SDL_AudioDevice *device;
public:
SDL_WasapiActivationHandler() : completion_semaphore(SDL_CreateSemaphore(0)) { SDL_assert(completion_semaphore != NULL); }
STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation *operation);
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
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.
SDL_AtomicSet(&device->hidden->just_activated, 1);
WASAPI_UnrefDevice(device);
SDL_PostSemaphore(completion_semaphore);
return S_OK;
}
@ -251,24 +282,10 @@ void WASAPI_PlatformDeleteActivationHandler(void *handler)
((SDL_WasapiActivationHandler *)handler)->Release();
}
int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
int WASAPI_ActivateDevice(SDL_AudioDevice *device)
{
return SDL_Unsupported();
}
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);
LPCWSTR devid = (LPCWSTR) device->handle;
SDL_assert(devid != NULL);
ComPtr<SDL_WasapiActivationHandler> handler = Make<SDL_WasapiActivationHandler>();
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()->device = _this;
_this->hidden->activation_handler = handler.Get();
device->hidden->activation_handler = handler.Get();
WASAPI_RefDevice(_this); /* completion handler will unref it. */
IActivateAudioInterfaceAsyncOperation *async = nullptr;
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();
}
handler.Get()->Release();
WASAPI_UnrefDevice(_this);
return WIN_SetErrorFromHRESULT("WASAPI can't activate requested audio endpoint", ret);
}
/* Spin until the async operation is complete.
* If we don't PrepDevice before leaving this function, the bug list gets LONG:
* - 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);
}
// !!! 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.
handler.Get()->WaitForCompletion(); // block here until we have an answer, so this is synchronous to us after all.
HRESULT activateRes = S_OK;
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);
}
iunknown->QueryInterface(IID_PPV_ARGS(&_this->hidden->client));
if (!_this->hidden->client) {
iunknown->QueryInterface(IID_PPV_ARGS(&device->hidden->client));
if (!device->hidden->client) {
return SDL_SetError("Failed to query WASAPI client interface");
}
if (WASAPI_PrepDevice(_this, isrecovery) == -1) {
if (WASAPI_PrepDevice(device) == -1) {
return -1;
}
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.
}
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *_this)
void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device)
{
// !!! FIXME: set this thread to "Pro Audio" priority.
}
/* 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);
SDL_free(device->handle);
}
#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);
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);
JNIEXPORT void JNICALL
@ -242,7 +242,7 @@ JNIEXPORT void JNICALL
static JNINativeMethod SDLAudioManager_tab[] = {
{ "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) }
};
@ -350,8 +350,8 @@ static jmethodID midSupportsRelativeMouse;
static jclass mAudioManagerClass;
/* method signatures */
static jmethodID midGetAudioOutputDevices;
static jmethodID midGetAudioInputDevices;
static jmethodID midRegisterAudioDeviceCallback;
static jmethodID midUnregisterAudioDeviceCallback;
static jmethodID midAudioOpen;
static jmethodID midAudioWriteByteBuffer;
static jmethodID midAudioWriteShortBuffer;
@ -681,12 +681,12 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jcl
mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
midGetAudioOutputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"getAudioOutputDevices",
"()[I");
midGetAudioInputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"getAudioInputDevices",
"()[I");
midRegisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"registerAudioDeviceCallback",
"()V");
midUnregisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"unregisterAudioDeviceCallback",
"()V");
midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"audioOpen", "(IIIII)[I");
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,
"audioSetThreadPriority", "(ZI)V");
if (!midGetAudioOutputDevices || !midGetAudioInputDevices || !midAudioOpen ||
if (!midRegisterAudioDeviceCallback || !midUnregisterAudioDeviceCallback || !midAudioOpen ||
!midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer ||
!midAudioClose ||
!midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer ||
@ -1002,18 +1002,17 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)(
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
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture,
jint device_id)
jstring name, jint device_id)
{
if (SDL_GetCurrentAudioDriver() != NULL) {
char device_name[64];
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id);
SDL_Log("Adding device with name %s, capture %d", device_name, is_capture);
SDL_AddAudioDevice(is_capture, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1));
void *handle = (void *)((size_t)device_id);
if (!SDL_FindPhysicalAudioDeviceByHandle(handle)) {
const char *utf8name = (*env)->GetStringUTFChars(env, name, NULL);
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)
{
if (SDL_GetCurrentAudioDriver() != NULL) {
SDL_Log("Removing device with handle %d, capture %d", device_id + 1, is_capture);
SDL_RemoveAudioDevice(is_capture, (void *)((size_t)device_id + 1));
SDL_Log("Removing device with handle %d, capture %d", device_id, is_capture);
SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle((void *)((size_t)device_id)));
}
}
@ -1152,7 +1151,6 @@ retry:
SDL_LockMutex(Android_ActivityMutex);
if (Android_Window) {
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_WindowData *data = Android_Window->driverdata;
/* Wait for Main thread being paused and context un-activated to release 'egl_surface' */
@ -1169,7 +1167,7 @@ retry:
#ifdef SDL_VIDEO_OPENGL_EGL
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;
}
#endif
@ -1564,58 +1562,25 @@ static void *audioBufferPinned = NULL;
static int captureBufferFormat = 0;
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();
jintArray result;
if (is_input) {
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioInputDevices);
} else {
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioOutputDevices);
// 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);
*default_output = *default_capture = NULL; // !!! FIXME: how do you decide the default device id?
}
*length = (*env)->GetArrayLength(env, result);
*length = SDL_min(*length, max_len);
(*env)->GetIntArrayRegion(env, result, 0, *length, devices);
}
void Android_DetectDevices(void)
void Android_StopAudioHotplug(void)
{
int inputs[100];
int outputs[100];
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));
JNIEnv *env = Android_JNI_GetEnv();
(*env)->CallStaticVoidMethod(env, mAudioManagerClass, midUnregisterAudioDeviceCallback);
}
SDL_zeroa(outputs);
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)
int Android_JNI_OpenAudioDevice(SDL_AudioDevice *device)
{
const SDL_bool iscapture = device->iscapture;
SDL_AudioSpec *spec = &device->spec;
const int device_id = (int) ((size_t) device->handle);
int audioformat;
jobject jbufobj = NULL;
jobject result;
@ -1640,10 +1605,10 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
if (iscapture) {
__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 {
__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) {
/* 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;
break;
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->samples = resultElements[3];
device->sample_frames = resultElements[3];
(*env)->ReleaseIntArrayElements(env, (jintArray)result, resultElements, JNI_ABORT);
(*env)->DeleteLocalRef(env, result);
@ -1680,7 +1645,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
switch (audioformat) {
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) {
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
(*env)->DeleteLocalRef(env, audioBufferLocal);
@ -1688,7 +1653,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
} break;
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) {
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
(*env)->DeleteLocalRef(env, audioBufferLocal);
@ -1696,7 +1661,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
} break;
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) {
jbufobj = (*env)->NewGlobalRef(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();
(*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 */
/* If the parameter silent is truthy then SDL_SetError() will not be called. */
static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)
@ -2013,13 +1983,18 @@ int Android_JNI_FileOpen(SDL_RWops *ctx,
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;
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");
}

View File

@ -30,6 +30,8 @@ extern "C" {
#include <EGL/eglplatform.h>
#include <android/native_window_jni.h>
#include "../../audio/SDL_sysaudio.h"
/* Interface from the SDL library into the Android Java activity */
extern void Android_JNI_SetActivityTitle(const char *title);
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);
/* Audio support */
extern void Android_DetectDevices(void);
extern int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spec);
void Android_StartAudioHotplug(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
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_WriteAudioBuffer(void);
extern int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen);
extern void Android_JNI_FlushCapturedAudio(void);
extern void Android_JNI_CloseAudioDevice(const int iscapture);
extern void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id);
/* Detecting device type */
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);
Sint64 Android_JNI_FileSize(SDL_RWops *ctx);
Sint64 Android_JNI_FileSeek(SDL_RWops *ctx, Sint64 offset, int whence);
Sint64 Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, Sint64 size);
Sint64 Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer, Sint64 size);
size_t Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, size_t size);
size_t Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer, size_t size);
int Android_JNI_FileClose(SDL_RWops *ctx);
/* Environment support */

View File

@ -27,6 +27,12 @@
#include "../../audio/SDL_sysaudio.h"
#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? */
/* 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_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 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 */
/* these increment as default devices change. Opened default devices pick up changes in their threads. */
SDL_AtomicInt SDL_IMMDevice_DefaultPlaybackGeneration;
SDL_AtomicInt SDL_IMMDevice_DefaultCaptureGeneration;
static SDL_bool FindByDevIDCallback(SDL_AudioDevice *device, void *userdata)
{
const SDL_IMMDevice_HandleData *handle = (const SDL_IMMDevice_HandleData *) device->handle;
return (SDL_wcscmp(handle->immdevice_id, (LPCWSTR) userdata) == 0) ? SDL_TRUE : SDL_FALSE;
}
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)
{
@ -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. */
typedef struct DevIdList
void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device)
{
LPWSTR str;
LPGUID guid;
struct DevIdList *next;
} DevIdList;
if (device && device->handle) {
SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *) device->handle;
SDL_free(handle->immdevice_id);
SDL_free(handle);
device->handle = NULL;
}
}
static DevIdList *deviceid_list = NULL;
static void SDL_IMMDevice_Remove(const SDL_bool iscapture, LPCWSTR devid, SDL_bool useguid)
static SDL_AudioDevice *SDL_IMMDevice_Add(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid, GUID *dsoundguid)
{
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).
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. */
}
if (!devname) {
return NULL;
}
devidlist = (DevIdList *)SDL_malloc(sizeof(*devidlist));
if (devidlist == NULL) {
return; /* oh well. */
// see if we already have this one first.
SDL_AudioDevice *device = SDL_IMMDevice_FindByDevID(devid);
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;
}
devidcopy = SDL_wcsdup(devid);
if (!devidcopy) {
SDL_free(devidlist);
return; /* oh well. */
handle->immdevice_id = SDL_wcsdup(devid);
if (!handle->immdevice_id) {
SDL_OutOfMemory();
SDL_free(handle);
return NULL;
}
SDL_memcpy(&handle->directsound_guid, dsoundguid, sizeof(GUID));
if (useguid) {
/* 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_AudioSpec spec;
SDL_zero(spec);
spec.channels = (Uint8)fmt->Format.nChannels;
spec.freq = fmt->Format.nSamplesPerSec;
spec.format = WaveFormatToSDLFormat((WAVEFORMATEX *)fmt);
SDL_AddAudioDevice(iscapture, devname, &spec, driverdata);
spec.format = SDL_WaveFormatExToSDLFormat((WAVEFORMATEX *)fmt);
device = SDL_AddAudioDevice(iscapture, devname, &spec, handle);
}
return device;
}
/* We need a COM subclass of IMMNotificationClient for hotplug support, which is
@ -181,14 +162,13 @@ typedef struct SDLMMNotificationClient
{
const IMMNotificationClientVtbl *lpVtbl;
SDL_AtomicInt refcount;
SDL_bool useguid;
} 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))) {
*ppv = this;
this->lpVtbl->AddRef(this);
*ppv = client;
client->lpVtbl->AddRef(client);
return S_OK;
}
@ -196,55 +176,34 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotif
return E_NOINTERFACE;
}
static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_AddRef(IMMNotificationClient *ithis)
static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_AddRef(IMMNotificationClient *iclient)
{
SDLMMNotificationClient *this = (SDLMMNotificationClient *)ithis;
return (ULONG)(SDL_AtomicIncRef(&this->refcount) + 1);
SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient;
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. */
SDLMMNotificationClient *this = (SDLMMNotificationClient *)ithis;
const ULONG retval = SDL_AtomicDecRef(&this->refcount);
/* client is a static object; we don't ever free it. */
SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient;
const ULONG retval = SDL_AtomicDecRef(&client->refcount);
if (retval == 0) {
SDL_AtomicSet(&this->refcount, 0); /* uhh... */
SDL_AtomicSet(&client->refcount, 0); /* uhh... */
return 0;
}
return retval - 1;
}
/* These are the entry points called when WASAPI device endpoints change. */
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *ithis, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
// These are the entry points called when WASAPI device endpoints change.
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *iclient, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
{
if (role != SDL_IMMDevice_role) {
return S_OK; /* ignore it. */
if (role == SDL_IMMDevice_role) {
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;
}
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
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;
}
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;
return S_OK; // See notes in OnDeviceAdded handler about why we ignore this.
}
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;
@ -270,18 +228,17 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IM
EDataFlow flow;
if (SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) {
const SDL_bool iscapture = (flow == eCapture);
const SDLMMNotificationClient *client = (SDLMMNotificationClient *)ithis;
if (dwNewState == DEVICE_STATE_ACTIVE) {
char *utf8dev;
WAVEFORMATEXTENSIBLE fmt;
GUID dsoundguid;
GetMMDeviceInfo(device, &utf8dev, &fmt, &dsoundguid);
if (utf8dev) {
SDL_IMMDevice_Add(iscapture, utf8dev, &fmt, pwstrDeviceId, &dsoundguid, client->useguid);
SDL_IMMDevice_Add(iscapture, utf8dev, &fmt, pwstrDeviceId, &dsoundguid);
SDL_free(utf8dev);
}
} else {
SDL_IMMDevice_Remove(iscapture, pwstrDeviceId, client->useguid);
SDL_AudioDeviceDisconnected(SDL_IMMDevice_FindByDevID(pwstrDeviceId));
}
}
IMMEndpoint_Release(endpoint);
@ -292,9 +249,9 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IM
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 = {
@ -314,31 +271,25 @@ int SDL_IMMDevice_Init(void)
{
HRESULT ret;
SDL_AtomicSet(&SDL_IMMDevice_DefaultPlaybackGeneration, 1);
SDL_AtomicSet(&SDL_IMMDevice_DefaultCaptureGeneration, 1);
/* just skip the discussion with COM here. */
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())) {
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);
if (FAILED(ret)) {
WIN_CoUninitialize();
return WIN_SetErrorFromHRESULT("WASAPI CoCreateInstance(MMDeviceEnumerator)", ret);
return WIN_SetErrorFromHRESULT("IMMDevice CoCreateInstance(MMDeviceEnumerator)", ret);
}
return 0;
}
void SDL_IMMDevice_Quit(void)
{
DevIdList *devidlist;
DevIdList *next;
if (enumerator) {
IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)&notification_client);
IMMDeviceEnumerator_Release(enumerator);
@ -346,197 +297,99 @@ void SDL_IMMDevice_Quit(void)
}
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. */
HRESULT ret;
SDL_assert(device != NULL);
SDL_assert(immdevice != NULL);
while (SDL_TRUE) {
if (devid == NULL) {
const EDataFlow dataflow = iscapture ? eCapture : eRender;
ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_IMMDevice_role, device);
} else {
ret = IMMDeviceEnumerator_GetDevice(enumerator, devid, device);
}
LPCWSTR devid = SDL_IMMDevice_GetDevID(device);
SDL_assert(devid != NULL);
if (SUCCEEDED(ret)) {
break;
}
if (ret == E_NOTFOUND) {
HRESULT ret;
while ((ret = IMMDeviceEnumerator_GetDevice(enumerator, devid, immdevice)) == E_NOTFOUND) {
const Uint64 now = SDL_GetTicks();
if (timeout > now) {
const Uint64 ticksleft = timeout - now;
SDL_Delay((Uint32)SDL_min(ticksleft, 300)); /* wait awhile and try again. */
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;
}
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)
static void EnumerateEndpointsForFlow(const SDL_bool iscapture, SDL_AudioDevice **default_device)
{
IMMDeviceCollection *collection = NULL;
EndpointItem *items;
UINT i, total;
/* Note that WASAPI separates "adapter devices" from "audio endpoint devices"
...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))) {
return;
}
UINT total = 0;
if (FAILED(IMMDeviceCollection_GetCount(collection, &total))) {
IMMDeviceCollection_Release(collection);
return;
}
items = (EndpointItem *)SDL_calloc(total, sizeof(EndpointItem));
if (items == NULL) {
return; /* oh well. */
LPWSTR default_devid = NULL;
if (default_device) {
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);
}
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);
IMMDevice_Release(default_immdevice);
}
}
/* sort the list of devices by their guid so list is consistent between runs */
SDL_qsort(items, total, sizeof(*items), sort_endpoints);
/* Send the sorted list on to the SDL's higher level. */
for (i = 0; i < total; i++) {
EndpointItem *item = items + i;
if ((item->devid) && (item->devname)) {
SDL_IMMDevice_Add(iscapture, item->devname, &item->fmt, item->devid, &item->dsoundguid, notification_client.useguid);
for (UINT i = 0; i < total; i++) {
IMMDevice *immdevice = NULL;
if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &immdevice))) {
LPWSTR devid = NULL;
if (SUCCEEDED(IMMDevice_GetId(immdevice, &devid))) {
char *devname = NULL;
WAVEFORMATEXTENSIBLE fmt;
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);
}
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); /* playback */
EnumerateEndpointsForFlow(SDL_TRUE); /* capture */
EnumerateEndpointsForFlow(SDL_FALSE, default_output); /* playback */
EnumerateEndpointsForFlow(SDL_TRUE, default_capture); /* capture */
/* if this fails, we just won't get hotplug events. Carry on anyhow. */
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) */

View File

@ -26,16 +26,14 @@
#include <mmdeviceapi.h>
#include <mmreg.h>
typedef struct SDL_AudioDevice SDL_AudioDevice; // this is defined in src/audio/SDL_sysaudio.h
int SDL_IMMDevice_Init(void);
void SDL_IMMDevice_Quit(void);
int SDL_IMMDevice_Get(LPCWSTR devid, IMMDevice **device, SDL_bool iscapture);
void SDL_IMMDevice_EnumerateEndpoints(SDL_bool useguid);
int SDL_IMMDevice_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture);
SDL_AudioFormat WaveFormatToSDLFormat(WAVEFORMATEX *waveformat);
/* 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;
int SDL_IMMDevice_Get(SDL_AudioDevice *device, IMMDevice **immdevice, SDL_bool iscapture);
void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
LPGUID SDL_IMMDevice_GetDirectSoundGUID(SDL_AudioDevice *device);
LPCWSTR SDL_IMMDevice_GetDevID(SDL_AudioDevice *device);
void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device);
#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);
}
/* 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,
based on SDL_windows_main.c, placed in the public domain by Sam Lantinga 4/13/98 */
#ifdef __WIN32__
@ -348,7 +375,7 @@ static int OutOfMemory(void)
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

View File

@ -76,8 +76,22 @@
#define WINVER _WIN32_WINNT
#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 <basetyps.h> /* for REFIID with broken mingw.org headers */
#include <mmreg.h>
/* Older Visual C++ headers don't have the Win64-compatible typedefs... */
#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 */
extern BOOL WIN_IsRectEmpty(const RECT *rect);
extern SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}

View File

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

View File

@ -59,22 +59,17 @@
#define SDL_BroadcastCondition SDL_BroadcastCondition_REAL
#define SDL_CaptureMouse SDL_CaptureMouse_REAL
#define SDL_CleanupTLS SDL_CleanupTLS_REAL
#define SDL_ClearAudioStream SDL_ClearAudioStream_REAL
#define SDL_ClearComposition SDL_ClearComposition_REAL
#define SDL_ClearError SDL_ClearError_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_CloseJoystick SDL_CloseJoystick_REAL
#define SDL_CloseSensor SDL_CloseSensor_REAL
#define SDL_ComposeCustomBlendMode SDL_ComposeCustomBlendMode_REAL
#define SDL_ConvertAudioSamples SDL_ConvertAudioSamples_REAL
#define SDL_ConvertEventToRenderCoordinates SDL_ConvertEventToRenderCoordinates_REAL
#define SDL_ConvertPixels SDL_ConvertPixels_REAL
#define SDL_ConvertSurface SDL_ConvertSurface_REAL
#define SDL_ConvertSurfaceFormat SDL_ConvertSurfaceFormat_REAL
#define SDL_CreateAudioStream SDL_CreateAudioStream_REAL
#define SDL_CreateColorCursor SDL_CreateColorCursor_REAL
#define SDL_CreateCondition SDL_CreateCondition_REAL
#define SDL_CreateCursor SDL_CreateCursor_REAL
@ -106,8 +101,6 @@
#define SDL_DelHintCallback SDL_DelHintCallback_REAL
#define SDL_Delay SDL_Delay_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_DestroyCursor SDL_DestroyCursor_REAL
#define SDL_DestroyMutex SDL_DestroyMutex_REAL
@ -138,7 +131,6 @@
#define SDL_FillSurfaceRects SDL_FillSurfaceRects_REAL
#define SDL_FilterEvents SDL_FilterEvents_REAL
#define SDL_FlashWindow SDL_FlashWindow_REAL
#define SDL_FlushAudioStream SDL_FlushAudioStream_REAL
#define SDL_FlushEvent SDL_FlushEvent_REAL
#define SDL_FlushEvents SDL_FlushEvents_REAL
#define SDL_GDKGetTaskQueue SDL_GDKGetTaskQueue_REAL
@ -174,27 +166,18 @@
#define SDL_GetAndroidSDKVersion SDL_GetAndroidSDKVersion_REAL
#define SDL_GetAssertionHandler SDL_GetAssertionHandler_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_GetCPUCacheLineSize SDL_GetCPUCacheLineSize_REAL
#define SDL_GetCPUCount SDL_GetCPUCount_REAL
#define SDL_GetClipboardData SDL_GetClipboardData_REAL
#define SDL_GetClipboardText SDL_GetClipboardText_REAL
#define SDL_GetClosestFullscreenDisplayMode SDL_GetClosestFullscreenDisplayMode_REAL
#define SDL_GetCurrentAudioDriver SDL_GetCurrentAudioDriver_REAL
#define SDL_GetCurrentDisplayMode SDL_GetCurrentDisplayMode_REAL
#define SDL_GetCurrentDisplayOrientation SDL_GetCurrentDisplayOrientation_REAL
#define SDL_GetCurrentRenderOutputSize SDL_GetCurrentRenderOutputSize_REAL
#define SDL_GetCurrentVideoDriver SDL_GetCurrentVideoDriver_REAL
#define SDL_GetCursor SDL_GetCursor_REAL
#define SDL_GetDefaultAssertionHandler SDL_GetDefaultAssertionHandler_REAL
#define SDL_GetDefaultAudioInfo SDL_GetDefaultAudioInfo_REAL
#define SDL_GetDefaultCursor SDL_GetDefaultCursor_REAL
#define SDL_GetDesktopDisplayMode SDL_GetDesktopDisplayMode_REAL
#define SDL_GetDisplayBounds SDL_GetDisplayBounds_REAL
@ -291,8 +274,6 @@
#define SDL_GetMouseState SDL_GetMouseState_REAL
#define SDL_GetNaturalDisplayOrientation SDL_GetNaturalDisplayOrientation_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_GetNumGamepadTouchpadFingers SDL_GetNumGamepadTouchpadFingers_REAL
#define SDL_GetNumGamepadTouchpads SDL_GetNumGamepadTouchpads_REAL
@ -304,7 +285,7 @@
#define SDL_GetNumTouchFingers SDL_GetNumTouchFingers_REAL
#define SDL_GetNumVideoDrivers SDL_GetNumVideoDrivers_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_GetPerformanceFrequency SDL_GetPerformanceFrequency_REAL
#define SDL_GetPixelFormatEnumForMasks SDL_GetPixelFormatEnumForMasks_REAL
@ -315,7 +296,6 @@
#define SDL_GetPreferredLocales SDL_GetPreferredLocales_REAL
#define SDL_GetPrimaryDisplay SDL_GetPrimaryDisplay_REAL
#define SDL_GetPrimarySelectionText SDL_GetPrimarySelectionText_REAL
#define SDL_GetQueuedAudioSize SDL_GetQueuedAudioSize_REAL
#define SDL_GetRGB SDL_GetRGB_REAL
#define SDL_GetRGBA SDL_GetRGBA_REAL
#define SDL_GetRectAndLineIntersection SDL_GetRectAndLineIntersection_REAL
@ -485,8 +465,6 @@
#define SDL_LoadFile_RW SDL_LoadFile_RW_REAL
#define SDL_LoadFunction SDL_LoadFunction_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_LockMutex SDL_LockMutex_REAL
#define SDL_LockRWLockForReading SDL_LockRWLockForReading_REAL
@ -518,7 +496,6 @@
#define SDL_Metal_DestroyView SDL_Metal_DestroyView_REAL
#define SDL_Metal_GetLayer SDL_Metal_GetLayer_REAL
#define SDL_MinimizeWindow SDL_MinimizeWindow_REAL
#define SDL_MixAudioFormat SDL_MixAudioFormat_REAL
#define SDL_MouseIsHaptic SDL_MouseIsHaptic_REAL
#define SDL_NumHaptics SDL_NumHaptics_REAL
#define SDL_OnApplicationDidBecomeActive SDL_OnApplicationDidBecomeActive_REAL
@ -528,22 +505,17 @@
#define SDL_OnApplicationWillEnterForeground SDL_OnApplicationWillEnterForeground_REAL
#define SDL_OnApplicationWillResignActive SDL_OnApplicationWillResignActive_REAL
#define SDL_OnApplicationWillTerminate SDL_OnApplicationWillTerminate_REAL
#define SDL_OpenAudioDevice SDL_OpenAudioDevice_REAL
#define SDL_OpenGamepad SDL_OpenGamepad_REAL
#define SDL_OpenJoystick SDL_OpenJoystick_REAL
#define SDL_OpenSensor SDL_OpenSensor_REAL
#define SDL_OpenURL SDL_OpenURL_REAL
#define SDL_PauseAudioDevice SDL_PauseAudioDevice_REAL
#define SDL_PeepEvents SDL_PeepEvents_REAL
#define SDL_PlayAudioDevice SDL_PlayAudioDevice_REAL
#define SDL_PollEvent SDL_PollEvent_REAL
#define SDL_PostSemaphore SDL_PostSemaphore_REAL
#define SDL_PremultiplyAlpha SDL_PremultiplyAlpha_REAL
#define SDL_PumpEvents SDL_PumpEvents_REAL
#define SDL_PushEvent SDL_PushEvent_REAL
#define SDL_PutAudioStreamData SDL_PutAudioStreamData_REAL
#define SDL_QueryTexture SDL_QueryTexture_REAL
#define SDL_QueueAudio SDL_QueueAudio_REAL
#define SDL_Quit SDL_Quit_REAL
#define SDL_QuitSubSystem SDL_QuitSubSystem_REAL
#define SDL_RWFromConstMem SDL_RWFromConstMem_REAL
@ -556,12 +528,12 @@
#define SDL_RWtell SDL_RWtell_REAL
#define SDL_RWwrite SDL_RWwrite_REAL
#define SDL_RaiseWindow SDL_RaiseWindow_REAL
#define SDL_ReadBE16 SDL_ReadBE16_REAL
#define SDL_ReadBE32 SDL_ReadBE32_REAL
#define SDL_ReadBE64 SDL_ReadBE64_REAL
#define SDL_ReadLE16 SDL_ReadLE16_REAL
#define SDL_ReadLE32 SDL_ReadLE32_REAL
#define SDL_ReadLE64 SDL_ReadLE64_REAL
#define SDL_ReadU16BE SDL_ReadU16BE_REAL
#define SDL_ReadU32BE SDL_ReadU32BE_REAL
#define SDL_ReadU64BE SDL_ReadU64BE_REAL
#define SDL_ReadU16LE SDL_ReadU16LE_REAL
#define SDL_ReadU32LE SDL_ReadU32LE_REAL
#define SDL_ReadU64LE SDL_ReadU64LE_REAL
#define SDL_ReadU8 SDL_ReadU8_REAL
#define SDL_RegisterApp SDL_RegisterApp_REAL
#define SDL_RegisterEvents SDL_RegisterEvents_REAL
@ -605,7 +577,6 @@
#define SDL_SendGamepadEffect SDL_SendGamepadEffect_REAL
#define SDL_SendJoystickEffect SDL_SendJoystickEffect_REAL
#define SDL_SetAssertionHandler SDL_SetAssertionHandler_REAL
#define SDL_SetAudioStreamFormat SDL_SetAudioStreamFormat_REAL
#define SDL_SetClipboardData SDL_SetClipboardData_REAL
#define SDL_SetClipboardText SDL_SetClipboardText_REAL
#define SDL_SetCursor SDL_SetCursor_REAL
@ -696,7 +667,6 @@
#define SDL_TryLockRWLockForWriting SDL_TryLockRWLockForWriting_REAL
#define SDL_TryWaitSemaphore SDL_TryWaitSemaphore_REAL
#define SDL_UnloadObject SDL_UnloadObject_REAL
#define SDL_UnlockAudioDevice SDL_UnlockAudioDevice_REAL
#define SDL_UnlockJoysticks SDL_UnlockJoysticks_REAL
#define SDL_UnlockMutex SDL_UnlockMutex_REAL
#define SDL_UnlockRWLock SDL_UnlockRWLock_REAL
@ -729,12 +699,12 @@
#define SDL_WinRTGetDeviceFamily SDL_WinRTGetDeviceFamily_REAL
#define SDL_WinRTGetFSPathUNICODE SDL_WinRTGetFSPathUNICODE_REAL
#define SDL_WinRTGetFSPathUTF8 SDL_WinRTGetFSPathUTF8_REAL
#define SDL_WriteBE16 SDL_WriteBE16_REAL
#define SDL_WriteBE32 SDL_WriteBE32_REAL
#define SDL_WriteBE64 SDL_WriteBE64_REAL
#define SDL_WriteLE16 SDL_WriteLE16_REAL
#define SDL_WriteLE32 SDL_WriteLE32_REAL
#define SDL_WriteLE64 SDL_WriteLE64_REAL
#define SDL_WriteU16BE SDL_WriteU16BE_REAL
#define SDL_WriteU32BE SDL_WriteU32BE_REAL
#define SDL_WriteU64BE SDL_WriteU64BE_REAL
#define SDL_WriteU16LE SDL_WriteU16LE_REAL
#define SDL_WriteU32LE SDL_WriteU32LE_REAL
#define SDL_WriteU64LE SDL_WriteU64LE_REAL
#define SDL_WriteU8 SDL_WriteU8_REAL
#define SDL_abs SDL_abs_REAL
#define SDL_acos SDL_acos_REAL
@ -905,3 +875,52 @@
#define SDL_strnlen SDL_strnlen_REAL
#define SDL_AddGamepadMappingsFromFile SDL_AddGamepadMappingsFromFile_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(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(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)
@ -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_CaptureMouse,(SDL_bool a),(a),return)
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_ClearError,(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_CloseJoystick,(SDL_Joystick *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(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_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_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_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)
@ -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_Delay,(Uint32 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_DestroyCursor,(SDL_Cursor *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(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_FlushAudioStream,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(void,SDL_FlushEvent,(Uint32 a),(a),)
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)
@ -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_AssertionHandler,SDL_GetAssertionHandler,(void **a),(a),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(int,SDL_GetCPUCacheLineSize,(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(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 char*,SDL_GetCurrentAudioDriver,(void),(),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(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(SDL_Cursor*,SDL_GetCursor,(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(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)
@ -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(SDL_DisplayOrientation,SDL_GetNaturalDisplayOrientation,(SDL_DisplayID a),(a),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_GetNumGamepadTouchpadFingers,(SDL_Gamepad *a, int b),(a,b),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_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(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_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)
@ -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_DisplayID,SDL_GetPrimaryDisplay,(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_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)
@ -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(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(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(int,SDL_LockMutex,(SDL_Mutex *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_GetLayer,(SDL_MetalView 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_NumHaptics,(void),(),return)
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_OnApplicationWillResignActive,(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_Joystick*,SDL_OpenJoystick,(SDL_JoystickID 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_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_PlayAudioDevice,(SDL_AudioDeviceID 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_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(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_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_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_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(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_RWsize,(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(Uint16,SDL_ReadBE16,(SDL_RWops *a),(a),return)
SDL_DYNAPI_PROC(Uint32,SDL_ReadBE32,(SDL_RWops *a),(a),return)
SDL_DYNAPI_PROC(Uint64,SDL_ReadBE64,(SDL_RWops *a),(a),return)
SDL_DYNAPI_PROC(Uint16,SDL_ReadLE16,(SDL_RWops *a),(a),return)
SDL_DYNAPI_PROC(Uint32,SDL_ReadLE32,(SDL_RWops *a),(a),return)
SDL_DYNAPI_PROC(Uint64,SDL_ReadLE64,(SDL_RWops *a),(a),return)
SDL_DYNAPI_PROC(Uint8,SDL_ReadU8,(SDL_RWops *a),(a),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadU16BE,(SDL_RWops *a, Uint16 *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadU32BE,(SDL_RWops *a, Uint32 *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadU64BE,(SDL_RWops *a, Uint64 *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadU16LE,(SDL_RWops *a, Uint16 *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadU32LE,(SDL_RWops *a, Uint32 *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadU64LE,(SDL_RWops *a, Uint64 *b),(a,b),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(SDL_bool,SDL_RemoveTimer,(SDL_TimerID 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(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_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_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_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(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_SetClipboardText,(const char *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_TryWaitSemaphore,(SDL_Semaphore *a),(a),return)
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(int,SDL_UnlockMutex,(SDL_Mutex *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(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(size_t,SDL_WriteBE16,(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(size_t,SDL_WriteBE64,(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(size_t,SDL_WriteLE32,(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(size_t,SDL_WriteU8,(SDL_RWops *a, Uint8 b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU16BE,(SDL_RWops *a, Uint16 b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU32BE,(SDL_RWops *a, Uint32 b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU64BE,(SDL_RWops *a, Uint64 b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU16LE,(SDL_RWops *a, Uint16 b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU32LE,(SDL_RWops *a, Uint32 b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_WriteU64LE,(SDL_RWops *a, Uint64 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(double,SDL_acos,(double 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(int,SDL_AddGamepadMappingsFromFile,(const char *a),(a),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_TEXT, SDL_FALSE);
#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_UnlockMutex(SDL_EventQ.lock);

View File

@ -33,8 +33,9 @@
typedef enum
{
KEYBOARD_HARDWARE = 0x01,
KEYBOARD_AUTORELEASE = 0x02,
KEYBOARD_IGNOREMODIFIERS = 0x04
KEYBOARD_VIRTUAL = 0x02,
KEYBOARD_AUTORELEASE = 0x04,
KEYBOARD_IGNOREMODIFIERS = 0x08
} SDL_KeyboardFlags;
#define KEYBOARD_SOURCE_MASK (KEYBOARD_HARDWARE | KEYBOARD_AUTORELEASE)
@ -50,6 +51,7 @@ struct SDL_Keyboard
Uint8 keystate[SDL_NUM_SCANCODES];
SDL_Keycode keymap[SDL_NUM_SCANCODES];
SDL_bool autorelease_pending;
Uint64 hardware_timestamp;
};
static SDL_Keyboard SDL_keyboard;
@ -875,7 +877,9 @@ static int SDL_SendKeyboardKeyInternal(Uint64 timestamp, SDL_KeyboardFlags flags
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;
}
@ -978,20 +982,25 @@ int SDL_SendKeyboardUnicodeKey(Uint64 timestamp, Uint32 ch)
if (mod & SDL_KMOD_SHIFT) {
/* 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 */
SDL_SendKeyboardKey(timestamp, SDL_PRESSED, code);
SDL_SendKeyboardKey(timestamp, SDL_RELEASED, code);
SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_PRESSED, code, SDLK_UNKNOWN);
SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_RELEASED, code, SDLK_UNKNOWN);
if (mod & SDL_KMOD_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;
}
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)
{
return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, state, scancode, SDLK_UNKNOWN);
@ -1025,6 +1034,13 @@ void SDL_ReleaseAutoReleaseKeys(void)
}
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)
@ -1037,7 +1053,8 @@ SDL_bool SDL_HardwareKeyboardKeyPressed(void)
return SDL_TRUE;
}
}
return SDL_FALSE;
return keyboard->hardware_timestamp ? SDL_TRUE : SDL_FALSE;
}
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);
/* 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 */
extern int SDL_SendKeyboardKey(Uint64 timestamp, Uint8 state, 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 */
int SDL_InitMouse(void)
int SDL_PreInitMouse(void)
{
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->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);
if (surface) {
SDL_memset(surface->pixels, 0, (size_t)surface->h * surface->pitch);
@ -212,14 +221,29 @@ int SDL_InitMouse(void)
SDL_DestroySurface(surface);
}
}
return 0;
}
void SDL_SetDefaultCursor(SDL_Cursor *cursor)
{
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;
if (!mouse->cur_cursor) {
SDL_SetCursor(cursor);
}
@ -845,6 +869,10 @@ void SDL_QuitMouse(void)
SDL_SetRelativeMouseMode(SDL_FALSE);
SDL_ShowCursor();
if (mouse->def_cursor) {
SDL_SetDefaultCursor(NULL);
}
cursor = mouse->cursors;
while (cursor) {
next = cursor->next;
@ -854,15 +882,6 @@ void SDL_QuitMouse(void)
mouse->cursors = 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) {
SDL_free(mouse->sources);
mouse->sources = NULL;
@ -1377,7 +1396,7 @@ void SDL_DestroyCursor(SDL_Cursor *cursor)
mouse->cursors = curr->next;
}
if (mouse->FreeCursor) {
if (mouse->FreeCursor && curr->driverdata) {
mouse->FreeCursor(curr);
} else {
SDL_free(curr);

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