forked from Green-Sky/tomato
Squashed 'external/sdl/SDL/' changes from ec0042081..399bc709b
399bc709b build-scripts.pl: Added add-source-to-projects.pl ac6827187 Visual-WinRT: dos2unix the project files to match other Visual Studio targets. 34719cba9 Fixed crash in hid_init() if the HIDDeviceManager isn't available 2e92e94eb Make sure we update device->sample_frames in SDL_AudioDeviceFormatChangedAlreadyLocked() 9964e5c5b wayland: Don't retrieve the drag offer strings with every pointer motion event bac7eeaaa Added missing include a541e2ac1 audio: Change a few SDL_memcpy calls to SDL_copyp. 54125c140 audio: Only update bound audiostreams' formats when necessary. e0b0f9a36 testaudio: Fix mouseover testing. 2f3deec24 wayland: Don't process drag & drop events from surfaces not owned by SDL 42bdced05 events: Log file drop position events and print the pointer coordinates c10d93d3a wayland: Replace magic constant with define 500852153 emscripten: Restore compatibility with existing emsdk releases. 953b55dd6 Use EM_ASM_PTR when the return value is a pointer a4541a255 audio: SDL_GetAudioStreamQueued now returns bytes, not frames. 703aefbce Sync SDL3 wiki -> header 99421b64d linux: Add portal drag and drop 952c5059b Remove stray  eebd5d18a linux: Handle upower's UP_DEVICE_STATE_PENDING_CHARGE, PENDING_DISCHARGE f8fdb20d8 audio: Destroy all existing SDL_AudioStreams on shutdown. 62d445997 audio: Removed declarations of functions that don't exist anymore. b656720bc loopwave: Use SDL_GetAudioStreamQueued() for more accurate results. 34b931f7e audio: Added SDL_GetAudioStreamQueued 23206b9e3 audio: Added SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED c7e6d7a1f audio: Changed debug logging output. 87ec6acf2 audio: Added a FIXME ac88ffb7e audio: don't allocate buffer in SDL_SetAudioPostmixCallback for NULL callback. 2a950f6ae audio: Replace some SDL_memcpy calls with SDL_copyp. 0dc0434a3 audio: Fixed race condition in subsystem shutdown. 23f60203a audio: precalculate if we can use simple copies instead of the full mixer. 36b0f1141 audio: Optimize setting device formats during audio thread iteration. 4c3e84897 testspriteminimal: make standalone by embedding icon.bmp 2a01f9dcb tests: plug leaks when running with --trackmem f42bbeca2 SDL_test: track stack frames of allocations on Windows 12c0be028 SDL_test: clear text cache on exit event b4bfb1831 SDL_test: free state before logging allocations 248b1edd3 SDL_test: destroy windows in SDL_CommonQuit 98da2dd30 SDL_test: don't warn about expected allocations when running with --trackmem 6a381567b Support audio rate conversion up to 384KHz b2b548a1f Don't hang if IAudioRenderClient_GetBuffer() fails indefinitely a3a5e1728 Fixed build warning '=': conversion from 'Uint32' to 'Uint16', possible loss of data 6d3e21c27 Fixed android build warnings fca2f5318 Fixed warning: this function declaration is not a prototype a72dfa6a5 Fixed sensor timestamp units for third-party PS5 controllers f6756047a Fixed error: array subscript 2 is above array bounds of ‘const Uint8[2]’ 7059a55cc Fixed sensor timestamp calculation for third-party PS5 controllers c0443e5d1 Fixed crash in SDL_IMMDevice_FindByDevID() fde8499f6 Use around 20ms for the audio buffer size e5739d7d1 video: Remove SDL_GetFocusWindow() 39c2f9737 Fix NULL dereference in SDL_OpenAudio 9a23d0e3f Added new audio files to the Xcode project a62e62f97 Refactored SDL_audiocvt.c 31229fd47 include: Added a note about SDL's iOS app delegate functions. 65aaf3a9a x11: Always update clipboard owner f622f21e6 Fixed build 5774c9638 Prefer hidraw over libusb when libusb whitelisting is not enabled 9301f7ace hidapi/libusb: only enumerate each interface once 859dc14ad Replaced SDL_GetGamepadBindForAxis() and SDL_GetGamepadBindForButton() with SDL_GetGamepadBindings() 9e50048ab Revert "Removed SDL_GamepadBinding from the API" 9f17d1a9d Don't reference the same function in "see also" 86505ea63 fix SDL_AudioStreamCallback documentation d885d5c31 Sync SDL3 wiki -> header 2f43f7bc5 audio: Allow querying of device buffer size. cf9572113 audio: Added a hint to let apps force device buffer size. 47d8c77c6 audio: Choose better default sample frame counts. 8b26e95f9 audio: Change SDL_AudioStreamCallback 9da34e8fb docs: Updated README-emscripten.md. fd1c54a00 detect fanatec steering wheels cb4414608 docs: Whoops, this got added by the wiki bridge by accident! cd633b9a8 Renamed SDL_IsAudioDevicePaused() to SDL_AudioDevicePaused() c6cad07fa Sync SDL3 wiki -> header a6e52f9e4 Sync SDL3 wiki -> header 2de2e9d03 Fix flickering of window when using desktop-fullscreen and borderless window on multiple monitors on Linux. Closes #8186. 723835d16 Windows: fix for client rect resizing larger each time we came from exclusive fullscreen -> windowed on a monitor with HiDPI set. The problem was we were using the monitor DPI rather than the window DPI so AdjustWindowRectExForDpi was giving us an incorrect size which would be too large for the client rect. Closes #8237. ce27363df wikiheaders: Sort undocumented functions. e22282b09 Added README about transparent windows in Win32 1d1c6e630 Turn off COREAUDIO debug logging by default 52efefca0 wayland: Fix drag offer leak 3a992af44 audio: Added a postmix callback to logical devices. 7207bdce5 render: Enable clipping for zero-sized rectangles 22d81fb3e cmake: use MSVC_RUNTIME_LIBRARY to force MT a2e17852d cmake: make sure SDL_GetPrefPath is run before testfilesystem 2fb266e0a ci: run tests in parallel ad1313e75 testaudio: Patched to compile. 5747ddc01 testaudio: Clean up some messy memory management. fafbea1ce audio: Move internal float32 mixing to a simplified function 116b0ec97 include: minor tweak to audio API documentation fb1377035 include: Replaced old Bugzilla URL. 38c8fc05c audio: Remove ChooseMixStrategy. b00cbd76a wikiheaders.pl: create Unsupported.md file with list of functions undocumented in either the headers or the wiki 37e1fc3b5 wayland: Ensure that the toplevel window is recreated when switching decoration modes f2ca9a615 Added SDL_AUDIO_FRAMESIZE 53122593f Added SDL_AUDIO_BYTESIZE 544351c98 Sync SDL3 wiki -> header 2e7d2b94e Clarify that SDL_BlitSurface() ignores the width and height in dstrect a2c1984d3 Detect Simagic wheel bases as wheels (#8198) 1d8dfbb22 avoid type redefinition errors after PR/8181 266b91d2f Detect Logitech G923 Playstation as wheel G923 have two different versions - Xbox version is already present in the wheel list, but not the PS version. cde67ea49 Detect Logitech PRO Racing Wheel for Xbox (PC mode) as wheel Logitech PRO Racing Wheel have two different versions - for Playstation and Xbox. Vendor + Product ID for Playstation version already present in SDL sources, but not an Xbox version 3a932141e Restore audio format binary compatibility with SDL 2.0 e85206ffd wikiheaders.pl: add --rev= option to pass revision string 233789b0d Audio types have the same naming convention as other SDL endian types, e.g. [S|U][BITS][LE|BE] 36b5f3e35 Sync SDL3 wiki -> header 0e552761b Renamed AudioStreamSpeed to AudioStreamFrequencyRatio 47bcb078f Fixed some incorrect SDL_AUDIO_F32 uses 2833f2e7b Fixed OOB access in audio_convertAccuracy test 8387fae69 Sync SDL3 wiki -> header 832181345 docs: Add note about Wayland application icons 825d34475 Make sure that the same timestamp is used for all PS5 events from the same packet 9c1430324 Removed SDL_dataqueue 28b28bd8f Added audio_formatChange test a59152688 Try and avoid overflow when handling very large audio streams 5394a805f Improved testaudiostreamdynamicresample e55844274 Added SDL_(Get|Set)AudioStreamSpeed 43c3c5736 Track the formats of data in an SDL_AudioStream 337fed3df Tweaked ResampleFrame_SSE Use _mm_unpack(lo|hi)_ps instead of _mm_shuffle_ps fd7cd91dc audio: Mix multiple streams in float32 to prevent clipping. 9097573e3 audio: Choose a mixing strategy on each iteration. bbe2e012a Don't provide the SDL3 header path c17a35f09 Fixed typo 4f72255eb Fixed README.md link e0ab59754 Simplified SDL_main.h migration notes d44bde61e Added SDL migration information to the top level README.md 6ff31e10c metal: Add hint to select low power device instead of the default one (#8182) 8a8aed477 Make sure that we process touch events that position the mouse f84c87f20 Sync SDL3 wiki -> header a7eea9997 macOS: Don't raise the parent top-level window when raising a child window, only raise the child window to the top of the parent a5e721479 Add SDL_WINDOW_NOT_FOCUSABLE flag to set that the window should not be able to gain key focus b385dc3b6 n3dsaudio: Patched to compile. 4e0c7c91f audio: PlayDevice() should return an error code. a94d724f1 wayland: add SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL da5d93d3d wayland: don't define SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_* macro's f002f7d12 ci: build emscripten with Debug buid type 3699b12ed audio: Fixed some "is_*" variables to be cleaner and/or more specific. 2471d8cc2 audio: Fixed logic error in SDL_OpenAudioDeviceStream. 1b03a2430 testsurround: fix order of arguments of callback 82db2b58f Renamed audio stream callback and moved the userdata parameter first 5bdad5210 Sync SDL3 wiki -> header 58c859f64 audio: Rename SDL_GetAudioStreamBinding to SDL_GetAudioStreamDevice. efd2023a7 audio: Fixed documentation. 1e775e0ee audio: Replace SDL_CreateAndBindAudioStream with SDL_OpenAudioDeviceStream. bd088c2f9 Revert "Clarify whether an audio function expects a physical or logical device ID" 82e481b52 Added --randmem test parameter ea68bb802 Add some additional checks to audio_convertAudio f8286df16 Fixed ResampleFrame_SSE doing unnecessary work b1d63be53 Fixed audio_resampleLoss test c191d6c30 Better Win32 transparent window support 923d612ca hidapi: sync macOS code with mainstream. 363f4fa9c avoid type redefinition errors after commit ee806597b9. 615824a80 Updated documentation now that SDL_GetAudioDevices() has been split into separate functions for output and capture devices 506a133d8 Clarify whether an audio function expects a physical or logical device ID 3b1d1e4e3 hidapi: sync the hidraw changes with mainstream f617918e0 cmake: check linkage to libusb too, instead of libusb.h presence only. 041dbd6b5 Fixed GetResamplerAvailableOutputFrames Non-euclidean division is a pain b49d0a607 x11: Avoid including full Vulkan headers. 4d2f9f3a3 yuv_rgb: Comment out unused code. 3c3486e2a wayland: Don't include full Vulkan headers when not necessary. f066bbe98 x11: Don't include system headers twice. d86d02bbb updated dynapi after SDL_GDKGetDefaultUser addition 4355f9cec Fixed warning C4389: '!=': signed/unsigned mismatch 5755de07a Fixed build warnings 0f80d47bb Fixed thread-safety warning ee806597b Removed SDL_vulkan_internal.h from SDL_sysvideo.h 34860b932 Fixed testautomation --filter pixels_allocFreeFormat 6f8a6a31c gdk: GetBasePath should be a UTF8 version of Win32 GetBasePath e30e5c77e Sync SDL3 wiki -> header c0cd8c814 gdk: Add SDL_GDKGetDefaultUser, SDL_GetPrefPath implementation 106abce69 Refactored GetAudioStreamDataInternal buffer handling The final conversion step should now always go straight into the output buffer. e44f54ec5 Avoid using hex-floats 5b696996c Added ResampleFrame_SSE 958b3cfae Tweaked and enabled audio_convertAudio test 7dbb9b65b audio_convertAccuracy: Shuffle the data in case of a bad SIMD implementation f6a4080ff audio_resampleLoss: Add support for multiple channels 4f894e748 audio_resampleLoss: SDL_GetAudioStreamData now returns the correct length ab83f75bb Make sure GetAudioStreamDataInternal is called with a valid length 6a73f74b6 Rebuild full ResamplerFilter (left wing + right wing) at runtime 0c15ce006 Add a missing int cast b74ee86b1 Optimized ResampleAudio, with special cases for 1 and 2 channels This would also benefit from some SIMD, since it's just a bunch of multiply-adds fba6e1e3d Removed ResamplerFilterDifference It takes 1 extra multiply to calculate the correct interpolation, but I think the improvement in cache locality (and binary size) outweighs that. 9f7a22fa4 Removed 64-bit handling from AudioConvertByteswap 1f5327a9f Removed future_buffer, left_padding, and right_padding from SDL_AudioStream 71ad52d6d Lowered SDL_GetAudioStreamData to 32 KB No particular reason for this number, but 1 MB was a bit silly 69aec8c91 Fixed the report format for the Razer Wolverine V2 Pro 7c2669c9d Accept key events from any source 1e9d31448 Updated to Android minSdkVersion 19 and targetSdkVersion 34 8924d0d92 Added missing function prototype for SDL_WriteS64BE() 845f3c745 Fixed mismatch between stdlib calloc() and SDL free() fb7921173 emscriptenaudio: Fire the capture silence_callback at an interval. 5191b2054 emscriptenaudio: Don't bother undefining things about to be unreachable. fd75a4ca0 emscriptenaudio: Deal with blocked audio devices better. 981b8a337 emscriptenaudio: Remove unnecessary functions. c7588e426 Transparent window for Win32 + OpenGL (#8143) f9581178d cmake: fixed a typo. e6c878824 Fixed ResampleAudio interpolation factor calculation 498363863 Misc audio tweaks/cleanup 72d9d53de Invert the inner ResampleAudio loops to avoid doing unnecessary work 88123a510 The history buffer should always have the maximum possible padding frames 96e47f165 Clamp results of GetResampler(AvailableOutput|NeededInput)Frames d2b9c8b80 Fixed maths in testaudiostreamdynamicresample (and just show the actual scale) 14e38b17d Removed assertions from inner ResampleAudio loop 9d413dfdc The history buffer doesn't need to be so large 2788e848f Allow resampling less than 1 frame of input 383084e0a Pre-calculate resampling rate, and use it instead of .freq in most places 40a6a445c Update resample_offset inside ResampleAudio 47fea7f06 Used fixed-point arithmetic in ResampleAudio 7bb4e806e Clear resample_offset in SDL_ClearAudioStream, not SetAudioStreamFormat Not entirely sure if ClearAudioStream is the right place, but SetAudioStreamFormat was the wrong place b9541b9ea Improved ResampleAudio * filterindex2 was off-by-one * Generate ResamplerFilter using doubles * Transpose ResamplerFilter to improve access patterns cdaa19869 Track offset within the current sample when resampling d60ebb06d mouse: Ensure that the dummy default cursor is removed from the cursor list e58c2731f mouse: Free the default cursor when destroyed 789ce17e1 audio: Don't resample in chunks for now. cbab33482 audio: Don't call SDL_AudioStream callbacks for empty data sets. 3e1ae0c86 Clearified the libusb whitelist default logic f4520821e Removed some unnecessary integer casts 0989b7e86 Avoid using designated initializers c6c1e673c Optimized SDL_Convert_*_to_*_Scalar f97b920b3 Optimized SDL_Convert_*_to_*_SSE2 Some of the SDL_Convert_F32_to_*_SSE2 do not explicitly clamp the input, but instead rely on saturating casts. Inputs very far outside the valid [-1.0, 1.0] range may produce an incorrect result, but I believe that is an acceptable trade-off. 300d1ec3e Added audio_convertAccuracy test 32cecc2ea Fixed assertion in audio_convertAudio 33f11e21e Removed assertions in AudioConvert(To|From)Float c2f388fd8 cmake: add SDL_HIDAPI_LIBUSB_SHARED option + test on ci 371cc2d17 wayland: Remove unnecessary flag and state settings fe85e6e75 cocoa: Send a maximized event instead of restored if a deminiaturized window is zoomed ddddcb78c cocoa: Use the close method to hide a miniaturized window be8c42cfd Clarify that a window being 'hidden' means that it is unmapped/ordered out a44338cbc Fix typo in SDL_audiocvt.c f464eb2c5 SDL_hidapi.c: change 'use_libusb_whitelist_default' into a macro. 6607a3cfa Disable cache in python http server 181d5d285 hidapi: Enable libusb support by default. f0f15e365 hidapi: Use a whitelist for libusb when other backends are available c3f7a7dc4 Convert audio using SDL_AUDIO_F32SYS format instead of SDL_AUDIO_F32 796713b9d xxd.py: always write \n line endings 723bcd0a8 SDL_TriggerBreakppoint for riscv arch (both 32/64) version. git-subtree-dir: external/sdl/SDL git-subtree-split: 399bc709b7485bab57880f8261f826f29dc0d7b2
This commit is contained in:
@ -34,7 +34,15 @@
|
||||
#endif
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#include <emscripten.h>
|
||||
/* older Emscriptens don't have this, but we need to for wasm64 compatibility. */
|
||||
#ifndef MAIN_THREAD_EM_ASM_PTR
|
||||
#ifdef __wasm64__
|
||||
#error You need to upgrade your Emscripten compiler to support wasm64
|
||||
#else
|
||||
#define MAIN_THREAD_EM_ASM_PTR MAIN_THREAD_EM_ASM_INT
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* The size of the stack buffer to use for rendering assert messages. */
|
||||
@ -211,7 +219,7 @@ static SDL_AssertState SDLCALL SDL_PromptAssertion(const SDL_AssertData *data, v
|
||||
}
|
||||
|
||||
/* Leave fullscreen mode, if possible (scary!) */
|
||||
window = SDL_GetFocusWindow();
|
||||
window = SDL_GetToplevelForKeyboardFocus();
|
||||
if (window) {
|
||||
if (window->fullscreen_exclusive) {
|
||||
SDL_MinimizeWindow(window);
|
||||
@ -243,7 +251,7 @@ static SDL_AssertState SDLCALL SDL_PromptAssertion(const SDL_AssertData *data, v
|
||||
for (;;) {
|
||||
SDL_bool okay = SDL_TRUE;
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
char *buf = (char *) EM_ASM_INT({
|
||||
char *buf = (char *) MAIN_THREAD_EM_ASM_PTR({
|
||||
var str =
|
||||
UTF8ToString($0) + '\n\n' +
|
||||
'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :';
|
||||
|
@ -1,318 +0,0 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "./SDL_dataqueue.h"
|
||||
|
||||
typedef struct SDL_DataQueuePacket
|
||||
{
|
||||
size_t datalen; /* bytes currently in use in this packet. */
|
||||
size_t startpos; /* bytes currently consumed in this packet. */
|
||||
struct SDL_DataQueuePacket *next; /* next item in linked list. */
|
||||
Uint8 data[SDL_VARIABLE_LENGTH_ARRAY]; /* packet data */
|
||||
} SDL_DataQueuePacket;
|
||||
|
||||
struct SDL_DataQueue
|
||||
{
|
||||
SDL_Mutex *lock;
|
||||
SDL_DataQueuePacket *head; /* device fed from here. */
|
||||
SDL_DataQueuePacket *tail; /* queue fills to here. */
|
||||
SDL_DataQueuePacket *pool; /* these are unused packets. */
|
||||
size_t packet_size; /* size of new packets */
|
||||
size_t queued_bytes; /* number of bytes of data in the queue. */
|
||||
};
|
||||
|
||||
static void SDL_FreeDataQueueList(SDL_DataQueuePacket *packet)
|
||||
{
|
||||
while (packet) {
|
||||
SDL_DataQueuePacket *next = packet->next;
|
||||
SDL_free(packet);
|
||||
packet = next;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_DataQueue *SDL_CreateDataQueue(const size_t _packetlen, const size_t initialslack)
|
||||
{
|
||||
SDL_DataQueue *queue = (SDL_DataQueue *)SDL_calloc(1, sizeof(SDL_DataQueue));
|
||||
|
||||
if (queue == NULL) {
|
||||
SDL_OutOfMemory();
|
||||
} else {
|
||||
const size_t packetlen = _packetlen ? _packetlen : 1024;
|
||||
const size_t wantpackets = (initialslack + (packetlen - 1)) / packetlen;
|
||||
size_t i;
|
||||
|
||||
queue->packet_size = packetlen;
|
||||
|
||||
queue->lock = SDL_CreateMutex();
|
||||
if (!queue->lock) {
|
||||
SDL_free(queue);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < wantpackets; i++) {
|
||||
SDL_DataQueuePacket *packet = (SDL_DataQueuePacket *)SDL_malloc(sizeof(SDL_DataQueuePacket) + packetlen);
|
||||
if (packet) { /* don't care if this fails, we'll deal later. */
|
||||
packet->datalen = 0;
|
||||
packet->startpos = 0;
|
||||
packet->next = queue->pool;
|
||||
queue->pool = packet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
void SDL_DestroyDataQueue(SDL_DataQueue *queue)
|
||||
{
|
||||
if (queue) {
|
||||
SDL_FreeDataQueueList(queue->head);
|
||||
SDL_FreeDataQueueList(queue->pool);
|
||||
SDL_DestroyMutex(queue->lock);
|
||||
SDL_free(queue);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_ClearDataQueue(SDL_DataQueue *queue, const size_t slack)
|
||||
{
|
||||
const size_t packet_size = queue ? queue->packet_size : 1;
|
||||
const size_t slackpackets = (slack + (packet_size - 1)) / packet_size;
|
||||
SDL_DataQueuePacket *packet;
|
||||
SDL_DataQueuePacket *prev = NULL;
|
||||
size_t i;
|
||||
|
||||
if (queue == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_LockMutex(queue->lock);
|
||||
|
||||
packet = queue->head;
|
||||
|
||||
/* merge the available pool and the current queue into one list. */
|
||||
if (packet) {
|
||||
queue->tail->next = queue->pool;
|
||||
} else {
|
||||
packet = queue->pool;
|
||||
}
|
||||
|
||||
/* Remove the queued packets from the device. */
|
||||
queue->tail = NULL;
|
||||
queue->head = NULL;
|
||||
queue->queued_bytes = 0;
|
||||
queue->pool = packet;
|
||||
|
||||
/* Optionally keep some slack in the pool to reduce memory allocation pressure. */
|
||||
for (i = 0; packet && (i < slackpackets); i++) {
|
||||
prev = packet;
|
||||
packet = packet->next;
|
||||
}
|
||||
|
||||
if (prev) {
|
||||
prev->next = NULL;
|
||||
} else {
|
||||
queue->pool = NULL;
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
|
||||
SDL_FreeDataQueueList(packet); /* free extra packets */
|
||||
}
|
||||
|
||||
/* You must hold queue->lock before calling this! */
|
||||
static SDL_DataQueuePacket *AllocateDataQueuePacket(SDL_DataQueue *queue)
|
||||
{
|
||||
SDL_DataQueuePacket *packet;
|
||||
|
||||
SDL_assert(queue != NULL);
|
||||
|
||||
packet = queue->pool;
|
||||
if (packet != NULL) {
|
||||
/* we have one available in the pool. */
|
||||
queue->pool = packet->next;
|
||||
} else {
|
||||
/* Have to allocate a new one! */
|
||||
packet = (SDL_DataQueuePacket *)SDL_malloc(sizeof(SDL_DataQueuePacket) + queue->packet_size);
|
||||
if (packet == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
packet->datalen = 0;
|
||||
packet->startpos = 0;
|
||||
packet->next = NULL;
|
||||
|
||||
SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
|
||||
if (queue->tail == NULL) {
|
||||
queue->head = packet;
|
||||
} else {
|
||||
queue->tail->next = packet;
|
||||
}
|
||||
queue->tail = packet;
|
||||
return packet;
|
||||
}
|
||||
|
||||
int SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *_data, const size_t _len)
|
||||
{
|
||||
size_t len = _len;
|
||||
const Uint8 *data = (const Uint8 *)_data;
|
||||
const size_t packet_size = queue ? queue->packet_size : 0;
|
||||
SDL_DataQueuePacket *orighead;
|
||||
SDL_DataQueuePacket *origtail;
|
||||
size_t origlen;
|
||||
size_t datalen;
|
||||
|
||||
if (queue == NULL) {
|
||||
return SDL_InvalidParamError("queue");
|
||||
}
|
||||
|
||||
SDL_LockMutex(queue->lock);
|
||||
|
||||
orighead = queue->head;
|
||||
origtail = queue->tail;
|
||||
origlen = origtail ? origtail->datalen : 0;
|
||||
|
||||
while (len > 0) {
|
||||
SDL_DataQueuePacket *packet = queue->tail;
|
||||
SDL_assert(packet == NULL || (packet->datalen <= packet_size));
|
||||
if (packet == NULL || (packet->datalen >= packet_size)) {
|
||||
/* tail packet missing or completely full; we need a new packet. */
|
||||
packet = AllocateDataQueuePacket(queue);
|
||||
if (packet == NULL) {
|
||||
/* uhoh, reset so we've queued nothing new, free what we can. */
|
||||
if (origtail == NULL) {
|
||||
packet = queue->head; /* whole queue. */
|
||||
} else {
|
||||
packet = origtail->next; /* what we added to existing queue. */
|
||||
origtail->next = NULL;
|
||||
origtail->datalen = origlen;
|
||||
}
|
||||
queue->head = orighead;
|
||||
queue->tail = origtail;
|
||||
queue->pool = NULL;
|
||||
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
SDL_FreeDataQueueList(packet); /* give back what we can. */
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
}
|
||||
|
||||
datalen = SDL_min(len, packet_size - packet->datalen);
|
||||
SDL_memcpy(packet->data + packet->datalen, data, datalen);
|
||||
data += datalen;
|
||||
len -= datalen;
|
||||
packet->datalen += datalen;
|
||||
queue->queued_bytes += datalen;
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t SDL_PeekIntoDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len)
|
||||
{
|
||||
size_t len = _len;
|
||||
Uint8 *buf = (Uint8 *)_buf;
|
||||
Uint8 *ptr = buf;
|
||||
SDL_DataQueuePacket *packet;
|
||||
|
||||
if (queue == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_LockMutex(queue->lock);
|
||||
|
||||
for (packet = queue->head; len && packet; packet = packet->next) {
|
||||
const size_t avail = packet->datalen - packet->startpos;
|
||||
const size_t cpy = SDL_min(len, avail);
|
||||
SDL_assert(queue->queued_bytes >= avail);
|
||||
|
||||
SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
|
||||
ptr += cpy;
|
||||
len -= cpy;
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
|
||||
return (size_t)(ptr - buf);
|
||||
}
|
||||
|
||||
size_t SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len)
|
||||
{
|
||||
size_t len = _len;
|
||||
Uint8 *buf = (Uint8 *)_buf;
|
||||
Uint8 *ptr = buf;
|
||||
SDL_DataQueuePacket *packet;
|
||||
|
||||
if (queue == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_LockMutex(queue->lock);
|
||||
|
||||
while ((len > 0) && ((packet = queue->head) != NULL)) {
|
||||
const size_t avail = packet->datalen - packet->startpos;
|
||||
const size_t cpy = SDL_min(len, avail);
|
||||
SDL_assert(queue->queued_bytes >= avail);
|
||||
|
||||
SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
|
||||
packet->startpos += cpy;
|
||||
ptr += cpy;
|
||||
queue->queued_bytes -= cpy;
|
||||
len -= cpy;
|
||||
|
||||
if (packet->startpos == packet->datalen) { /* packet is done, put it in the pool. */
|
||||
queue->head = packet->next;
|
||||
SDL_assert((packet->next != NULL) || (packet == queue->tail));
|
||||
packet->next = queue->pool;
|
||||
queue->pool = packet;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
|
||||
|
||||
if (queue->head == NULL) {
|
||||
queue->tail = NULL; /* in case we drained the queue entirely. */
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
|
||||
return (size_t)(ptr - buf);
|
||||
}
|
||||
|
||||
size_t SDL_GetDataQueueSize(SDL_DataQueue *queue)
|
||||
{
|
||||
size_t retval = 0;
|
||||
if (queue) {
|
||||
SDL_LockMutex(queue->lock);
|
||||
retval = queue->queued_bytes;
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
SDL_Mutex *SDL_GetDataQueueMutex(SDL_DataQueue *queue)
|
||||
{
|
||||
return queue ? queue->lock : NULL;
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#ifndef SDL_dataqueue_h_
|
||||
#define SDL_dataqueue_h_
|
||||
|
||||
/* this is not (currently) a public API. But maybe it should be! */
|
||||
|
||||
struct SDL_DataQueue;
|
||||
typedef struct SDL_DataQueue SDL_DataQueue;
|
||||
|
||||
SDL_DataQueue *SDL_CreateDataQueue(const size_t packetlen, const size_t initialslack);
|
||||
void SDL_DestroyDataQueue(SDL_DataQueue *queue);
|
||||
void SDL_ClearDataQueue(SDL_DataQueue *queue, const size_t slack);
|
||||
int SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *data, const size_t len);
|
||||
size_t SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *buf, const size_t len);
|
||||
size_t SDL_PeekIntoDataQueue(SDL_DataQueue *queue, void *buf, const size_t len);
|
||||
size_t SDL_GetDataQueueSize(SDL_DataQueue *queue);
|
||||
SDL_Mutex *SDL_GetDataQueueMutex(SDL_DataQueue *queue); /* don't destroy this, obviously. */
|
||||
|
||||
#endif /* SDL_dataqueue_h_ */
|
@ -116,6 +116,106 @@ const char *SDL_GetCurrentAudioDriver(void)
|
||||
return current_audio.name;
|
||||
}
|
||||
|
||||
static int GetDefaultSampleFramesFromFreq(const int freq)
|
||||
{
|
||||
const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES);
|
||||
if (hint) {
|
||||
const int val = SDL_atoi(hint);
|
||||
if (val > 0) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
if (freq <= 22050) {
|
||||
return 512;
|
||||
} else if (freq <= 48000) {
|
||||
return 1024;
|
||||
} else if (freq <= 96000) {
|
||||
return 2048;
|
||||
} else {
|
||||
return 4096;
|
||||
}
|
||||
}
|
||||
|
||||
void OnAudioStreamCreated(SDL_AudioStream *stream)
|
||||
{
|
||||
SDL_assert(SDL_GetCurrentAudioDriver() != NULL);
|
||||
SDL_assert(stream != NULL);
|
||||
|
||||
// this isn't really part of the "device list" but it's a convenient lock to use here.
|
||||
SDL_LockRWLockForWriting(current_audio.device_list_lock);
|
||||
if (current_audio.existing_streams) {
|
||||
current_audio.existing_streams->prev = stream;
|
||||
}
|
||||
stream->prev = NULL;
|
||||
stream->next = current_audio.existing_streams;
|
||||
current_audio.existing_streams = stream;
|
||||
SDL_UnlockRWLock(current_audio.device_list_lock);
|
||||
}
|
||||
|
||||
void OnAudioStreamDestroy(SDL_AudioStream *stream)
|
||||
{
|
||||
SDL_assert(SDL_GetCurrentAudioDriver() != NULL);
|
||||
SDL_assert(stream != NULL);
|
||||
|
||||
// this isn't really part of the "device list" but it's a convenient lock to use here.
|
||||
SDL_LockRWLockForWriting(current_audio.device_list_lock);
|
||||
if (stream->prev) {
|
||||
stream->prev->next = stream->next;
|
||||
}
|
||||
if (stream->next) {
|
||||
stream->next->prev = stream->prev;
|
||||
}
|
||||
if (stream == current_audio.existing_streams) {
|
||||
current_audio.existing_streams = stream->next;
|
||||
}
|
||||
SDL_UnlockRWLock(current_audio.device_list_lock);
|
||||
}
|
||||
|
||||
|
||||
// should hold logdev's physical device's lock before calling.
|
||||
static void UpdateAudioStreamFormatsLogical(SDL_LogicalAudioDevice *logdev)
|
||||
{
|
||||
const SDL_bool iscapture = logdev->physical_device->iscapture;
|
||||
SDL_AudioSpec spec;
|
||||
SDL_copyp(&spec, &logdev->physical_device->spec);
|
||||
if (logdev->postmix != NULL) {
|
||||
spec.format = SDL_AUDIO_F32;
|
||||
}
|
||||
|
||||
for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) {
|
||||
// set the proper end of the stream to the device's format.
|
||||
// SDL_SetAudioStreamFormat does a ton of validation just to memcpy an audiospec.
|
||||
SDL_LockMutex(stream->lock);
|
||||
SDL_copyp(iscapture ? &stream->src_spec : &stream->dst_spec, &spec);
|
||||
SDL_UnlockMutex(stream->lock);
|
||||
}
|
||||
}
|
||||
|
||||
// should hold device->lock before calling.
|
||||
static void UpdateAudioStreamFormatsPhysical(SDL_AudioDevice *device)
|
||||
{
|
||||
for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) {
|
||||
UpdateAudioStreamFormatsLogical(logdev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// device should be locked when calling this.
|
||||
static SDL_bool AudioDeviceCanUseSimpleCopy(SDL_AudioDevice *device)
|
||||
{
|
||||
SDL_assert(device != NULL);
|
||||
return (
|
||||
device->logical_devices && // there's a logical device
|
||||
!device->logical_devices->next && // there's only _ONE_ logical device
|
||||
!device->logical_devices->postmix && // there isn't a postmix callback
|
||||
!SDL_AtomicGet(&device->logical_devices->paused) && // it isn't paused
|
||||
device->logical_devices->bound_streams && // there's a bound stream
|
||||
!device->logical_devices->bound_streams->next_binding // there's only _ONE_ bound stream.
|
||||
) ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
|
||||
|
||||
// device management and hotplug...
|
||||
|
||||
|
||||
@ -176,6 +276,8 @@ static void DestroyLogicalAudioDevice(SDL_LogicalAudioDevice *logdev)
|
||||
SDL_UnlockMutex(stream->lock);
|
||||
}
|
||||
|
||||
logdev->physical_device->simple_copy = AudioDeviceCanUseSimpleCopy(logdev->physical_device);
|
||||
|
||||
SDL_free(logdev);
|
||||
}
|
||||
|
||||
@ -208,7 +310,10 @@ static SDL_AudioDevice *CreatePhysicalAudioDevice(const char *name, SDL_bool isc
|
||||
{
|
||||
SDL_assert(name != NULL);
|
||||
|
||||
if (SDL_AtomicGet(¤t_audio.shutting_down)) {
|
||||
SDL_LockRWLockForReading(current_audio.device_list_lock);
|
||||
const int shutting_down = SDL_AtomicGet(¤t_audio.shutting_down);
|
||||
SDL_UnlockRWLock(current_audio.device_list_lock);
|
||||
if (shutting_down) {
|
||||
return NULL; // we're shutting down, don't add any devices that are hotplugged at the last possible moment.
|
||||
}
|
||||
|
||||
@ -236,8 +341,9 @@ static SDL_AudioDevice *CreatePhysicalAudioDevice(const char *name, SDL_bool isc
|
||||
SDL_AtomicSet(&device->condemned, 0);
|
||||
SDL_AtomicSet(&device->zombie, 0);
|
||||
device->iscapture = iscapture;
|
||||
SDL_memcpy(&device->spec, spec, sizeof (SDL_AudioSpec));
|
||||
SDL_memcpy(&device->default_spec, spec, sizeof (SDL_AudioSpec));
|
||||
SDL_copyp(&device->spec, spec);
|
||||
SDL_copyp(&device->default_spec, spec);
|
||||
device->sample_frames = GetDefaultSampleFramesFromFreq(device->spec.freq);
|
||||
device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format);
|
||||
device->handle = handle;
|
||||
device->prev = NULL;
|
||||
@ -336,7 +442,7 @@ void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device)
|
||||
SDL_LogicalAudioDevice *next = NULL;
|
||||
for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = next) {
|
||||
next = logdev->next;
|
||||
if (!logdev->is_default) { // if opened as a default, leave it on the zombie device for later migration.
|
||||
if (!logdev->opened_as_default) { // if opened as a default, leave it on the zombie device for later migration.
|
||||
DisconnectLogicalAudioDevice(logdev);
|
||||
}
|
||||
}
|
||||
@ -412,7 +518,7 @@ void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device)
|
||||
|
||||
static void SDL_AudioThreadDeinit_Default(SDL_AudioDevice *device) { /* no-op. */ }
|
||||
static void SDL_AudioWaitDevice_Default(SDL_AudioDevice *device) { /* no-op. */ }
|
||||
static void SDL_AudioPlayDevice_Default(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { /* no-op. */ }
|
||||
static int SDL_AudioPlayDevice_Default(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { return 0; /* no-op. */ }
|
||||
static void SDL_AudioWaitCaptureDevice_Default(SDL_AudioDevice *device) { /* no-op. */ }
|
||||
static void SDL_AudioFlushCapture_Default(SDL_AudioDevice *device) { /* no-op. */ }
|
||||
static void SDL_AudioCloseDevice_Default(SDL_AudioDevice *device) { /* no-op. */ }
|
||||
@ -493,6 +599,7 @@ int SDL_InitAudio(const char *driver_name)
|
||||
}
|
||||
|
||||
SDL_ChooseAudioConverters();
|
||||
SDL_SetupAudioResampler();
|
||||
|
||||
SDL_RWLock *device_list_lock = SDL_CreateRWLock(); // create this early, so if it fails we don't have to tear down the whole audio subsystem.
|
||||
if (!device_list_lock) {
|
||||
@ -614,6 +721,11 @@ void SDL_QuitAudio(void)
|
||||
return;
|
||||
}
|
||||
|
||||
// Destroy any audio streams that still exist...
|
||||
while (current_audio.existing_streams != NULL) {
|
||||
SDL_DestroyAudioStream(current_audio.existing_streams);
|
||||
}
|
||||
|
||||
// merge device lists so we don't have to duplicate work below.
|
||||
SDL_LockRWLockForWriting(current_audio.device_list_lock);
|
||||
SDL_AtomicSet(¤t_audio.shutting_down, 1);
|
||||
@ -633,7 +745,7 @@ void SDL_QuitAudio(void)
|
||||
SDL_AtomicSet(¤t_audio.output_device_count, 0);
|
||||
SDL_AtomicSet(¤t_audio.capture_device_count, 0);
|
||||
SDL_UnlockRWLock(current_audio.device_list_lock);
|
||||
|
||||
|
||||
// mark all devices for shutdown so all threads can begin to terminate.
|
||||
for (SDL_AudioDevice *i = devices; i != NULL; i = i->next) {
|
||||
SDL_AtomicSet(&i->shutdown, 1);
|
||||
@ -676,6 +788,14 @@ void SDL_AudioThreadFinalize(SDL_AudioDevice *device)
|
||||
SDL_AtomicSet(&device->thread_alive, 0);
|
||||
}
|
||||
|
||||
static void MixFloat32Audio(float *dst, const float *src, const int buffer_size)
|
||||
{
|
||||
if (SDL_MixAudioFormat((Uint8 *) dst, (const Uint8 *) src, SDL_AUDIO_F32, buffer_size, SDL_MIX_MAXVOLUME) < 0) {
|
||||
SDL_assert(!"This shouldn't happen.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Output device thread. This is split into chunks, so backends that need to control this directly can use the pieces they need without duplicating effort.
|
||||
|
||||
void SDL_OutputAudioThreadSetup(SDL_AudioDevice *device)
|
||||
@ -697,41 +817,90 @@ SDL_bool SDL_OutputAudioThreadIterate(SDL_AudioDevice *device)
|
||||
|
||||
SDL_bool retval = SDL_TRUE;
|
||||
int buffer_size = device->buffer_size;
|
||||
Uint8 *mix_buffer = current_audio.impl.GetDeviceBuf(device, &buffer_size);
|
||||
if (!mix_buffer) {
|
||||
Uint8 *device_buffer = current_audio.impl.GetDeviceBuf(device, &buffer_size);
|
||||
if (!device_buffer) {
|
||||
retval = SDL_FALSE;
|
||||
} else {
|
||||
SDL_assert(buffer_size <= device->buffer_size); // you can ask for less, but not more.
|
||||
SDL_memset(mix_buffer, device->silence_value, buffer_size); // start with silence.
|
||||
SDL_assert(AudioDeviceCanUseSimpleCopy(device) == device->simple_copy); // make sure this hasn't gotten out of sync.
|
||||
|
||||
for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) {
|
||||
if (SDL_AtomicGet(&logdev->paused)) {
|
||||
continue; // paused? Skip this logical device.
|
||||
// can we do a basic copy without silencing/mixing the buffer? This is an extremely likely scenario, so we special-case it.
|
||||
if (device->simple_copy) {
|
||||
SDL_LogicalAudioDevice *logdev = device->logical_devices;
|
||||
SDL_AudioStream *stream = logdev->bound_streams;
|
||||
|
||||
// We should have updated this elsewhere if the format changed!
|
||||
SDL_assert(AUDIO_SPECS_EQUAL(stream->dst_spec, device->spec));
|
||||
|
||||
const int br = SDL_GetAudioStreamData(stream, device_buffer, buffer_size);
|
||||
if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow.
|
||||
retval = SDL_FALSE;
|
||||
SDL_memset(device_buffer, device->silence_value, buffer_size); // just supply silence to the device before we die.
|
||||
} else if (br < buffer_size) {
|
||||
SDL_memset(device_buffer + br, device->silence_value, buffer_size - br); // silence whatever we didn't write to.
|
||||
}
|
||||
} else { // need to actually mix (or silence the buffer)
|
||||
float *final_mix_buffer = (float *) ((device->spec.format == SDL_AUDIO_F32) ? device_buffer : device->mix_buffer);
|
||||
const int needed_samples = buffer_size / SDL_AUDIO_BYTESIZE(device->spec.format);
|
||||
const int work_buffer_size = needed_samples * sizeof (float);
|
||||
SDL_AudioSpec outspec;
|
||||
|
||||
for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) {
|
||||
/* this will hold a lock on `stream` while getting. We don't explicitly lock the streams
|
||||
for iterating here because the binding linked list can only change while the device lock is held.
|
||||
(we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind
|
||||
the same stream to different devices at the same time, though.) */
|
||||
const int br = SDL_GetAudioStreamData(stream, device->work_buffer, buffer_size);
|
||||
if (br < 0) {
|
||||
// oh crud, we probably ran out of memory. This is possibly an overreaction to kill the audio device, but it's likely the whole thing is going down in a moment anyhow.
|
||||
retval = SDL_FALSE;
|
||||
break;
|
||||
} else if (br > 0) { // it's okay if we get less than requested, we mix what we have.
|
||||
// !!! FIXME: this needs to mix to float32 or int32, so we don't clip.
|
||||
if (SDL_MixAudioFormat(mix_buffer, device->work_buffer, device->spec.format, br, SDL_MIX_MAXVOLUME) < 0) { // !!! FIXME: allow streams to specify gain?
|
||||
SDL_assert(!"We probably ended up with some totally unexpected audio format here");
|
||||
retval = SDL_FALSE; // uh...?
|
||||
SDL_assert(work_buffer_size <= device->work_buffer_size);
|
||||
|
||||
outspec.format = SDL_AUDIO_F32;
|
||||
outspec.channels = device->spec.channels;
|
||||
outspec.freq = device->spec.freq;
|
||||
|
||||
SDL_memset(final_mix_buffer, '\0', work_buffer_size); // start with silence.
|
||||
|
||||
for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) {
|
||||
if (SDL_AtomicGet(&logdev->paused)) {
|
||||
continue; // paused? Skip this logical device.
|
||||
}
|
||||
|
||||
const SDL_AudioPostmixCallback postmix = logdev->postmix;
|
||||
float *mix_buffer = final_mix_buffer;
|
||||
if (postmix) {
|
||||
mix_buffer = device->postmix_buffer;
|
||||
SDL_memset(mix_buffer, '\0', work_buffer_size); // start with silence.
|
||||
}
|
||||
|
||||
for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) {
|
||||
// We should have updated this elsewhere if the format changed!
|
||||
SDL_assert(AUDIO_SPECS_EQUAL(stream->dst_spec, outspec));
|
||||
|
||||
/* this will hold a lock on `stream` while getting. We don't explicitly lock the streams
|
||||
for iterating here because the binding linked list can only change while the device lock is held.
|
||||
(we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind
|
||||
the same stream to different devices at the same time, though.) */
|
||||
const int br = SDL_GetAudioStreamData(stream, device->work_buffer, work_buffer_size);
|
||||
if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow.
|
||||
retval = SDL_FALSE;
|
||||
break;
|
||||
} else if (br > 0) { // it's okay if we get less than requested, we mix what we have.
|
||||
MixFloat32Audio(mix_buffer, (float *) device->work_buffer, br);
|
||||
}
|
||||
}
|
||||
|
||||
if (postmix) {
|
||||
SDL_assert(mix_buffer == device->postmix_buffer);
|
||||
postmix(logdev->postmix_userdata, &outspec, mix_buffer, work_buffer_size);
|
||||
MixFloat32Audio(final_mix_buffer, mix_buffer, work_buffer_size);
|
||||
}
|
||||
}
|
||||
|
||||
if (((Uint8 *) final_mix_buffer) != device_buffer) {
|
||||
// !!! FIXME: we can't promise the device buf is aligned/padded for SIMD.
|
||||
//ConvertAudio(needed_samples * device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, device_buffer, device->spec.format, device->spec.channels, device->work_buffer);
|
||||
ConvertAudio(needed_samples / device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, device->work_buffer, device->spec.format, device->spec.channels, NULL);
|
||||
SDL_memcpy(device_buffer, device->work_buffer, buffer_size);
|
||||
}
|
||||
}
|
||||
|
||||
// !!! FIXME: have PlayDevice return a value and do disconnects in here with it.
|
||||
current_audio.impl.PlayDevice(device, mix_buffer, buffer_size); // this SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitDevice!
|
||||
// PlayDevice SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitDevice instead!
|
||||
if (current_audio.impl.PlayDevice(device, device_buffer, buffer_size) < 0) {
|
||||
retval = SDL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(device->lock);
|
||||
@ -746,9 +915,9 @@ SDL_bool SDL_OutputAudioThreadIterate(SDL_AudioDevice *device)
|
||||
void SDL_OutputAudioThreadShutdown(SDL_AudioDevice *device)
|
||||
{
|
||||
SDL_assert(!device->iscapture);
|
||||
const int samples = (device->buffer_size / (SDL_AUDIO_BITSIZE(device->spec.format) / 8)) / device->spec.channels;
|
||||
const int frames = device->buffer_size / SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
// Wait for the audio to drain. !!! FIXME: don't bother waiting if device is lost.
|
||||
SDL_Delay(((samples * 1000) / device->spec.freq) * 2);
|
||||
SDL_Delay(((frames * 1000) / device->spec.freq) * 2);
|
||||
current_audio.impl.ThreadDeinit(device);
|
||||
SDL_AudioThreadFinalize(device);
|
||||
}
|
||||
@ -791,21 +960,42 @@ SDL_bool SDL_CaptureAudioThreadIterate(SDL_AudioDevice *device)
|
||||
current_audio.impl.FlushCapture(device); // nothing wants data, dump anything pending.
|
||||
} else {
|
||||
// this SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitCaptureDevice!
|
||||
const int rc = current_audio.impl.CaptureFromDevice(device, device->work_buffer, device->buffer_size);
|
||||
if (rc < 0) { // uhoh, device failed for some reason!
|
||||
int br = current_audio.impl.CaptureFromDevice(device, device->work_buffer, device->buffer_size);
|
||||
if (br < 0) { // uhoh, device failed for some reason!
|
||||
retval = SDL_FALSE;
|
||||
} else if (rc > 0) { // queue the new data to each bound stream.
|
||||
} else if (br > 0) { // queue the new data to each bound stream.
|
||||
for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) {
|
||||
if (SDL_AtomicGet(&logdev->paused)) {
|
||||
continue; // paused? Skip this logical device.
|
||||
}
|
||||
|
||||
void *output_buffer = device->work_buffer;
|
||||
|
||||
// I don't know why someone would want a postmix on a capture device, but we offer it for API consistency.
|
||||
if (logdev->postmix) {
|
||||
// move to float format.
|
||||
SDL_AudioSpec outspec;
|
||||
outspec.format = SDL_AUDIO_F32;
|
||||
outspec.channels = device->spec.channels;
|
||||
outspec.freq = device->spec.freq;
|
||||
output_buffer = device->postmix_buffer;
|
||||
const int frames = br / SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
br = frames * SDL_AUDIO_FRAMESIZE(outspec);
|
||||
ConvertAudio(frames, device->work_buffer, device->spec.format, outspec.channels, device->postmix_buffer, SDL_AUDIO_F32, outspec.channels, NULL);
|
||||
logdev->postmix(logdev->postmix_userdata, &outspec, device->postmix_buffer, br);
|
||||
}
|
||||
|
||||
for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) {
|
||||
// We should have updated this elsewhere if the format changed!
|
||||
SDL_assert(stream->src_spec.format == (logdev->postmix ? SDL_AUDIO_F32 : device->spec.format));
|
||||
SDL_assert(stream->src_spec.channels == device->spec.channels);
|
||||
SDL_assert(stream->src_spec.freq == device->spec.freq);
|
||||
|
||||
/* this will hold a lock on `stream` while putting. We don't explicitly lock the streams
|
||||
for iterating here because the binding linked list can only change while the device lock is held.
|
||||
(we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind
|
||||
the same stream to different devices at the same time, though.) */
|
||||
if (SDL_PutAudioStreamData(stream, device->work_buffer, rc) < 0) {
|
||||
if (SDL_PutAudioStreamData(stream, output_buffer, br) < 0) {
|
||||
// oh crud, we probably ran out of memory. This is possibly an overreaction to kill the audio device, but it's likely the whole thing is going down in a moment anyhow.
|
||||
retval = SDL_FALSE;
|
||||
break;
|
||||
@ -1036,22 +1226,22 @@ char *SDL_GetAudioDeviceName(SDL_AudioDeviceID devid)
|
||||
return retval;
|
||||
}
|
||||
|
||||
int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec)
|
||||
int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec, int *sample_frames)
|
||||
{
|
||||
if (!spec) {
|
||||
return SDL_InvalidParamError("spec");
|
||||
}
|
||||
|
||||
SDL_bool is_default = SDL_FALSE;
|
||||
SDL_bool wants_default = SDL_FALSE;
|
||||
if (devid == SDL_AUDIO_DEVICE_DEFAULT_OUTPUT) {
|
||||
devid = current_audio.default_output_device_id;
|
||||
is_default = SDL_TRUE;
|
||||
wants_default = SDL_TRUE;
|
||||
} else if (devid == SDL_AUDIO_DEVICE_DEFAULT_CAPTURE) {
|
||||
devid = current_audio.default_capture_device_id;
|
||||
is_default = SDL_TRUE;
|
||||
wants_default = SDL_TRUE;
|
||||
}
|
||||
|
||||
if ((devid == 0) && is_default) {
|
||||
if ((devid == 0) && wants_default) {
|
||||
return SDL_SetError("No default audio device available");
|
||||
}
|
||||
|
||||
@ -1060,7 +1250,10 @@ int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec)
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_memcpy(spec, &device->spec, sizeof (SDL_AudioSpec));
|
||||
SDL_copyp(spec, &device->spec);
|
||||
if (sample_frames) {
|
||||
*sample_frames = device->sample_frames;
|
||||
}
|
||||
SDL_UnlockMutex(device->lock);
|
||||
|
||||
return 0;
|
||||
@ -1080,18 +1273,22 @@ static void ClosePhysicalAudioDevice(SDL_AudioDevice *device)
|
||||
SDL_AtomicSet(&device->thread_alive, 0);
|
||||
}
|
||||
|
||||
if (device->is_opened) {
|
||||
if (device->currently_opened) {
|
||||
current_audio.impl.CloseDevice(device); // if ProvidesOwnCallbackThread, this must join on any existing device thread before returning!
|
||||
device->is_opened = SDL_FALSE;
|
||||
device->currently_opened = SDL_FALSE;
|
||||
device->hidden = NULL; // just in case.
|
||||
}
|
||||
|
||||
if (device->work_buffer) {
|
||||
SDL_aligned_free(device->work_buffer);
|
||||
device->work_buffer = NULL;
|
||||
}
|
||||
SDL_aligned_free(device->work_buffer);
|
||||
device->work_buffer = NULL;
|
||||
|
||||
SDL_memcpy(&device->spec, &device->default_spec, sizeof (SDL_AudioSpec));
|
||||
SDL_aligned_free(device->mix_buffer);
|
||||
device->mix_buffer = NULL;
|
||||
|
||||
SDL_aligned_free(device->postmix_buffer);
|
||||
device->postmix_buffer = NULL;
|
||||
|
||||
SDL_copyp(&device->spec, &device->default_spec);
|
||||
device->sample_frames = 0;
|
||||
device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format);
|
||||
SDL_AtomicSet(&device->shutdown, 0); // ready to go again.
|
||||
@ -1121,16 +1318,14 @@ static SDL_AudioFormat ParseAudioFormatString(const char *string)
|
||||
#define CHECK_FMT_STRING(x) if (SDL_strcmp(string, #x) == 0) { return SDL_AUDIO_##x; }
|
||||
CHECK_FMT_STRING(U8);
|
||||
CHECK_FMT_STRING(S8);
|
||||
CHECK_FMT_STRING(S16LSB);
|
||||
CHECK_FMT_STRING(S16MSB);
|
||||
CHECK_FMT_STRING(S16LE);
|
||||
CHECK_FMT_STRING(S16BE);
|
||||
CHECK_FMT_STRING(S16);
|
||||
CHECK_FMT_STRING(S32LSB);
|
||||
CHECK_FMT_STRING(S32MSB);
|
||||
CHECK_FMT_STRING(S32SYS);
|
||||
CHECK_FMT_STRING(S32LE);
|
||||
CHECK_FMT_STRING(S32BE);
|
||||
CHECK_FMT_STRING(S32);
|
||||
CHECK_FMT_STRING(F32LSB);
|
||||
CHECK_FMT_STRING(F32MSB);
|
||||
CHECK_FMT_STRING(F32SYS);
|
||||
CHECK_FMT_STRING(F32LE);
|
||||
CHECK_FMT_STRING(F32BE);
|
||||
CHECK_FMT_STRING(F32);
|
||||
#undef CHECK_FMT_STRING
|
||||
}
|
||||
@ -1168,15 +1363,12 @@ static void PrepareAudioFormat(SDL_bool iscapture, SDL_AudioSpec *spec)
|
||||
}
|
||||
}
|
||||
|
||||
static int GetDefaultSampleFramesFromFreq(int freq)
|
||||
{
|
||||
return SDL_powerof2((freq / 1000) * 46); // Pick the closest power-of-two to ~46 ms at desired frequency
|
||||
}
|
||||
|
||||
void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device)
|
||||
{
|
||||
device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format);
|
||||
device->buffer_size = device->sample_frames * (SDL_AUDIO_BITSIZE(device->spec.format) / 8) * device->spec.channels;
|
||||
device->buffer_size = device->sample_frames * SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
device->work_buffer_size = device->sample_frames * sizeof (float) * device->spec.channels;
|
||||
device->work_buffer_size = SDL_max(device->buffer_size, device->work_buffer_size); // just in case we end up with a 64-bit audio format at some point.
|
||||
}
|
||||
|
||||
char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen)
|
||||
@ -1189,7 +1381,7 @@ char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen)
|
||||
// this expects the device lock to be held.
|
||||
static int OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec *inspec)
|
||||
{
|
||||
SDL_assert(!device->is_opened);
|
||||
SDL_assert(!device->currently_opened);
|
||||
SDL_assert(device->logical_devices == NULL);
|
||||
|
||||
// Just pretend to open a zombie device. It can still collect logical devices on the assumption they will all migrate when the default device is officially changed.
|
||||
@ -1198,7 +1390,7 @@ static int OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec
|
||||
}
|
||||
|
||||
SDL_AudioSpec spec;
|
||||
SDL_memcpy(&spec, inspec ? inspec : &device->default_spec, sizeof (SDL_AudioSpec));
|
||||
SDL_copyp(&spec, inspec ? inspec : &device->default_spec);
|
||||
PrepareAudioFormat(device->iscapture, &spec);
|
||||
|
||||
/* We allow the device format to change if it's better than the current settings (by various definitions of "better"). This prevents
|
||||
@ -1211,7 +1403,7 @@ static int OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec
|
||||
device->sample_frames = GetDefaultSampleFramesFromFreq(device->spec.freq);
|
||||
SDL_UpdatedAudioDeviceFormat(device); // start this off sane.
|
||||
|
||||
device->is_opened = SDL_TRUE; // mark this true even if impl.OpenDevice fails, so we know to clean up.
|
||||
device->currently_opened = SDL_TRUE; // mark this true even if impl.OpenDevice fails, so we know to clean up.
|
||||
if (current_audio.impl.OpenDevice(device) < 0) {
|
||||
ClosePhysicalAudioDevice(device); // clean up anything the backend left half-initialized.
|
||||
return -1;
|
||||
@ -1220,12 +1412,20 @@ static int OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec
|
||||
SDL_UpdatedAudioDeviceFormat(device); // in case the backend changed things and forgot to call this.
|
||||
|
||||
// Allocate a scratch audio buffer
|
||||
device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->buffer_size);
|
||||
device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size);
|
||||
if (device->work_buffer == NULL) {
|
||||
ClosePhysicalAudioDevice(device);
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
if (device->spec.format != SDL_AUDIO_F32) {
|
||||
device->mix_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size);
|
||||
if (device->mix_buffer == NULL) {
|
||||
ClosePhysicalAudioDevice(device);
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
}
|
||||
|
||||
// Start the audio thread if necessary
|
||||
SDL_AtomicSet(&device->thread_alive, 1);
|
||||
if (!current_audio.impl.ProvidesOwnCallbackThread) {
|
||||
@ -1251,16 +1451,16 @@ SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSp
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_bool is_default = SDL_FALSE;
|
||||
SDL_bool wants_default = SDL_FALSE;
|
||||
if (devid == SDL_AUDIO_DEVICE_DEFAULT_OUTPUT) {
|
||||
devid = current_audio.default_output_device_id;
|
||||
is_default = SDL_TRUE;
|
||||
wants_default = SDL_TRUE;
|
||||
} else if (devid == SDL_AUDIO_DEVICE_DEFAULT_CAPTURE) {
|
||||
devid = current_audio.default_capture_device_id;
|
||||
is_default = SDL_TRUE;
|
||||
wants_default = SDL_TRUE;
|
||||
}
|
||||
|
||||
if ((devid == 0) && is_default) {
|
||||
if ((devid == 0) && wants_default) {
|
||||
SDL_SetError("No default audio device available");
|
||||
return 0;
|
||||
}
|
||||
@ -1273,7 +1473,7 @@ SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSp
|
||||
} else {
|
||||
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid); // this locks the physical device, too.
|
||||
if (logdev) {
|
||||
is_default = logdev->is_default; // was the original logical device meant to be a default? Make this one, too.
|
||||
wants_default = logdev->opened_as_default; // was the original logical device meant to be a default? Make this one, too.
|
||||
device = logdev->physical_device;
|
||||
}
|
||||
}
|
||||
@ -1282,23 +1482,24 @@ SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSp
|
||||
|
||||
if (device) {
|
||||
SDL_LogicalAudioDevice *logdev = NULL;
|
||||
if (!is_default && SDL_AtomicGet(&device->zombie)) {
|
||||
if (!wants_default && SDL_AtomicGet(&device->zombie)) {
|
||||
// uhoh, this device is undead, and just waiting for a new default device to be declared so it can hand off to it. Refuse explicit opens.
|
||||
SDL_SetError("Device was already lost and can't accept new opens");
|
||||
} else if ((logdev = (SDL_LogicalAudioDevice *) SDL_calloc(1, sizeof (SDL_LogicalAudioDevice))) == NULL) {
|
||||
SDL_OutOfMemory();
|
||||
} else if (!device->is_opened && OpenPhysicalAudioDevice(device, spec) == -1) { // first thing using this physical device? Open at the OS level...
|
||||
} else if (!device->currently_opened && OpenPhysicalAudioDevice(device, spec) == -1) { // first thing using this physical device? Open at the OS level...
|
||||
SDL_free(logdev);
|
||||
} else {
|
||||
SDL_AtomicSet(&logdev->paused, 0);
|
||||
retval = logdev->instance_id = assign_audio_device_instance_id(device->iscapture, /*islogical=*/SDL_TRUE);
|
||||
logdev->physical_device = device;
|
||||
logdev->is_default = is_default;
|
||||
logdev->opened_as_default = wants_default;
|
||||
logdev->next = device->logical_devices;
|
||||
if (device->logical_devices) {
|
||||
device->logical_devices->prev = logdev;
|
||||
}
|
||||
device->logical_devices = logdev;
|
||||
device->simple_copy = AudioDeviceCanUseSimpleCopy(device);
|
||||
}
|
||||
SDL_UnlockMutex(device->lock);
|
||||
}
|
||||
@ -1313,6 +1514,7 @@ static int SetLogicalAudioDevicePauseState(SDL_AudioDeviceID devid, int value)
|
||||
return -1; // ObtainLogicalAudioDevice will have set an error.
|
||||
}
|
||||
SDL_AtomicSet(&logdev->paused, value);
|
||||
logdev->physical_device->simple_copy = AudioDeviceCanUseSimpleCopy(logdev->physical_device);
|
||||
SDL_UnlockMutex(logdev->physical_device->lock);
|
||||
return 0;
|
||||
}
|
||||
@ -1327,7 +1529,7 @@ int SDLCALL SDL_ResumeAudioDevice(SDL_AudioDeviceID devid)
|
||||
return SetLogicalAudioDevicePauseState(devid, 0);
|
||||
}
|
||||
|
||||
SDL_bool SDL_IsAudioDevicePaused(SDL_AudioDeviceID devid)
|
||||
SDL_bool SDL_AudioDevicePaused(SDL_AudioDeviceID devid)
|
||||
{
|
||||
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid);
|
||||
SDL_bool retval = SDL_FALSE;
|
||||
@ -1340,6 +1542,31 @@ SDL_bool SDL_IsAudioDevicePaused(SDL_AudioDeviceID devid)
|
||||
return retval;
|
||||
}
|
||||
|
||||
int SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallback callback, void *userdata)
|
||||
{
|
||||
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid);
|
||||
int retval = 0;
|
||||
if (logdev) {
|
||||
SDL_AudioDevice *device = logdev->physical_device;
|
||||
if (callback && !device->postmix_buffer) {
|
||||
device->postmix_buffer = (float *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size);
|
||||
if (device->mix_buffer == NULL) {
|
||||
retval = SDL_OutOfMemory();
|
||||
}
|
||||
}
|
||||
|
||||
if (retval == 0) {
|
||||
logdev->postmix = callback;
|
||||
logdev->postmix_userdata = userdata;
|
||||
}
|
||||
|
||||
UpdateAudioStreamFormatsLogical(logdev);
|
||||
device->simple_copy = AudioDeviceCanUseSimpleCopy(device);
|
||||
|
||||
SDL_UnlockMutex(device->lock);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int num_streams)
|
||||
{
|
||||
@ -1356,8 +1583,14 @@ int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int
|
||||
return SDL_SetError("Audio streams are bound to device ids from SDL_OpenAudioDevice, not raw physical devices");
|
||||
} else if ((logdev = ObtainLogicalAudioDevice(devid)) == NULL) {
|
||||
return -1; // ObtainLogicalAudioDevice set the error message.
|
||||
} else if (logdev->simplified) {
|
||||
SDL_UnlockMutex(logdev->physical_device->lock);
|
||||
return SDL_SetError("Cannot change stream bindings on device opened with SDL_OpenAudioDeviceStream");
|
||||
}
|
||||
|
||||
// !!! FIXME: We'll set the device's side's format below, but maybe we should refuse to bind a stream if the app's side doesn't have a format set yet.
|
||||
// !!! FIXME: Actually, why do we allow there to be an invalid format, again?
|
||||
|
||||
// make sure start of list is sane.
|
||||
SDL_assert(!logdev->bound_streams || (logdev->bound_streams->prev_binding == NULL));
|
||||
|
||||
@ -1388,18 +1621,8 @@ int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int
|
||||
|
||||
if (retval == 0) {
|
||||
// Now that everything is verified, chain everything together.
|
||||
const SDL_bool iscapture = device->iscapture;
|
||||
for (int i = 0; i < num_streams; i++) {
|
||||
SDL_AudioStream *stream = streams[i];
|
||||
SDL_AudioSpec src_spec, dst_spec;
|
||||
|
||||
// set the proper end of the stream to the device's format.
|
||||
SDL_GetAudioStreamFormat(stream, &src_spec, &dst_spec);
|
||||
if (iscapture) {
|
||||
SDL_SetAudioStreamFormat(stream, &device->spec, &dst_spec);
|
||||
} else {
|
||||
SDL_SetAudioStreamFormat(stream, &src_spec, &device->spec);
|
||||
}
|
||||
|
||||
stream->bound_device = logdev;
|
||||
stream->prev_binding = NULL;
|
||||
@ -1411,8 +1634,12 @@ int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int
|
||||
|
||||
SDL_UnlockMutex(stream->lock);
|
||||
}
|
||||
|
||||
UpdateAudioStreamFormatsLogical(logdev);
|
||||
}
|
||||
|
||||
device->simple_copy = AudioDeviceCanUseSimpleCopy(device);
|
||||
|
||||
SDL_UnlockMutex(device->lock);
|
||||
|
||||
return retval;
|
||||
@ -1459,7 +1686,8 @@ void SDL_UnbindAudioStreams(SDL_AudioStream **streams, int num_streams)
|
||||
// everything is locked, start unbinding streams.
|
||||
for (int i = 0; i < num_streams; i++) {
|
||||
SDL_AudioStream *stream = streams[i];
|
||||
if (stream && stream->bound_device) {
|
||||
// don't allow unbinding from "simplified" devices (opened with SDL_OpenAudioDeviceStream). Just ignore them.
|
||||
if (stream && stream->bound_device && !stream->bound_device->simplified) {
|
||||
if (stream->bound_device->bound_streams == stream) {
|
||||
SDL_assert(stream->prev_binding == NULL);
|
||||
stream->bound_device->bound_streams = stream->next_binding;
|
||||
@ -1482,6 +1710,7 @@ void SDL_UnbindAudioStreams(SDL_AudioStream **streams, int num_streams)
|
||||
stream->bound_device = NULL;
|
||||
SDL_UnlockMutex(stream->lock);
|
||||
if (logdev) {
|
||||
logdev->physical_device->simple_copy = AudioDeviceCanUseSimpleCopy(logdev->physical_device);
|
||||
SDL_UnlockMutex(logdev->physical_device->lock);
|
||||
}
|
||||
}
|
||||
@ -1493,7 +1722,7 @@ void SDL_UnbindAudioStream(SDL_AudioStream *stream)
|
||||
SDL_UnbindAudioStreams(&stream, 1);
|
||||
}
|
||||
|
||||
SDL_AudioDeviceID SDL_GetAudioStreamBinding(SDL_AudioStream *stream)
|
||||
SDL_AudioDeviceID SDL_GetAudioStreamDevice(SDL_AudioStream *stream)
|
||||
{
|
||||
SDL_AudioDeviceID retval = 0;
|
||||
if (stream) {
|
||||
@ -1506,45 +1735,71 @@ SDL_AudioDeviceID SDL_GetAudioStreamBinding(SDL_AudioStream *stream)
|
||||
return retval;
|
||||
}
|
||||
|
||||
SDL_AudioStream *SDL_CreateAndBindAudioStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec)
|
||||
SDL_AudioStream *SDL_OpenAudioDeviceStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec, SDL_AudioStreamCallback callback, void *userdata)
|
||||
{
|
||||
const SDL_bool islogical = (devid & (1<<1)) ? SDL_FALSE : SDL_TRUE;
|
||||
if (!islogical) {
|
||||
SDL_SetError("Audio streams are bound to device ids from SDL_OpenAudioDevice, not raw physical devices");
|
||||
return NULL;
|
||||
SDL_AudioDeviceID logdevid = SDL_OpenAudioDevice(devid, spec);
|
||||
if (!logdevid) {
|
||||
return NULL; // error string should already be set.
|
||||
}
|
||||
|
||||
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(logdevid);
|
||||
if (logdev == NULL) { // this shouldn't happen, but just in case.
|
||||
SDL_CloseAudioDevice(logdevid);
|
||||
return NULL; // error string should already be set.
|
||||
}
|
||||
|
||||
SDL_AudioDevice *physdevice = logdev->physical_device;
|
||||
SDL_assert(physdevice != NULL);
|
||||
|
||||
SDL_AtomicSet(&logdev->paused, 1); // start the device paused, to match SDL2.
|
||||
physdevice->simple_copy = AudioDeviceCanUseSimpleCopy(physdevice);
|
||||
|
||||
SDL_UnlockMutex(physdevice->lock); // we don't need to hold the lock for any of this.
|
||||
const SDL_bool iscapture = physdevice->iscapture;
|
||||
|
||||
SDL_AudioStream *stream = NULL;
|
||||
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid);
|
||||
if (logdev) {
|
||||
SDL_AudioDevice *device = logdev->physical_device;
|
||||
if (device->iscapture) {
|
||||
stream = SDL_CreateAudioStream(&device->spec, spec);
|
||||
} else {
|
||||
stream = SDL_CreateAudioStream(spec, &device->spec);
|
||||
}
|
||||
|
||||
if (stream) {
|
||||
if (SDL_BindAudioStream(devid, stream) == -1) {
|
||||
SDL_DestroyAudioStream(stream);
|
||||
stream = NULL;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(device->lock);
|
||||
if (iscapture) {
|
||||
stream = SDL_CreateAudioStream(&physdevice->spec, spec);
|
||||
} else {
|
||||
stream = SDL_CreateAudioStream(spec, &physdevice->spec);
|
||||
}
|
||||
return stream;
|
||||
|
||||
if (!stream) {
|
||||
SDL_CloseAudioDevice(logdevid);
|
||||
return NULL; // error string should already be set.
|
||||
}
|
||||
if (SDL_BindAudioStream(logdevid, stream) == -1) {
|
||||
SDL_DestroyAudioStream(stream);
|
||||
SDL_CloseAudioDevice(logdevid);
|
||||
return NULL; // error string should already be set.
|
||||
}
|
||||
|
||||
logdev->simplified = SDL_TRUE; // forbid further binding changes on this logical device.
|
||||
stream->simplified = SDL_TRUE; // so we know to close the audio device when this is destroyed.
|
||||
|
||||
if (callback) {
|
||||
int rc;
|
||||
if (iscapture) {
|
||||
rc = SDL_SetAudioStreamPutCallback(stream, callback, userdata);
|
||||
} else {
|
||||
rc = SDL_SetAudioStreamGetCallback(stream, callback, userdata);
|
||||
}
|
||||
SDL_assert(rc == 0); // should only fail if stream==NULL atm.
|
||||
}
|
||||
|
||||
return stream; // ready to rock.
|
||||
}
|
||||
|
||||
#define NUM_FORMATS 8
|
||||
static const SDL_AudioFormat format_list[NUM_FORMATS][NUM_FORMATS + 1] = {
|
||||
{ SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, 0 },
|
||||
{ SDL_AUDIO_S8, SDL_AUDIO_U8, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, 0 },
|
||||
{ SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
|
||||
{ SDL_AUDIO_S16MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
|
||||
{ SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
|
||||
{ SDL_AUDIO_S32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
|
||||
{ SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
|
||||
{ SDL_AUDIO_F32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
|
||||
{ SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, SDL_AUDIO_F32LE, SDL_AUDIO_F32BE, 0 },
|
||||
{ SDL_AUDIO_S8, SDL_AUDIO_U8, SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, SDL_AUDIO_F32LE, SDL_AUDIO_F32BE, 0 },
|
||||
{ SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, SDL_AUDIO_F32LE, SDL_AUDIO_F32BE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
|
||||
{ SDL_AUDIO_S16BE, SDL_AUDIO_S16LE, SDL_AUDIO_S32BE, SDL_AUDIO_S32LE, SDL_AUDIO_F32BE, SDL_AUDIO_F32LE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
|
||||
{ SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, SDL_AUDIO_F32LE, SDL_AUDIO_F32BE, SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
|
||||
{ SDL_AUDIO_S32BE, SDL_AUDIO_S32LE, SDL_AUDIO_F32BE, SDL_AUDIO_F32LE, SDL_AUDIO_S16BE, SDL_AUDIO_S16LE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
|
||||
{ SDL_AUDIO_F32LE, SDL_AUDIO_F32BE, SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
|
||||
{ SDL_AUDIO_F32BE, SDL_AUDIO_F32LE, SDL_AUDIO_S32BE, SDL_AUDIO_S32LE, SDL_AUDIO_S16BE, SDL_AUDIO_S16LE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
|
||||
};
|
||||
|
||||
const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format)
|
||||
@ -1597,8 +1852,9 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device)
|
||||
SDL_AudioSpec spec;
|
||||
SDL_bool needs_migration = SDL_FALSE;
|
||||
SDL_zero(spec);
|
||||
|
||||
for (SDL_LogicalAudioDevice *logdev = current_default_device->logical_devices; logdev != NULL; logdev = logdev->next) {
|
||||
if (logdev->is_default) {
|
||||
if (logdev->opened_as_default) {
|
||||
needs_migration = SDL_TRUE;
|
||||
for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) {
|
||||
const SDL_AudioSpec *streamspec = iscapture ? &stream->dst_spec : &stream->src_spec;
|
||||
@ -1624,19 +1880,16 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device)
|
||||
}
|
||||
|
||||
if (needs_migration) {
|
||||
const SDL_bool spec_changed = !AUDIO_SPECS_EQUAL(current_default_device->spec, new_default_device->spec);
|
||||
const SDL_bool post_fmt_event = (spec_changed && SDL_EventEnabled(SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED)) ? SDL_TRUE : SDL_FALSE;
|
||||
SDL_LogicalAudioDevice *next = NULL;
|
||||
for (SDL_LogicalAudioDevice *logdev = current_default_device->logical_devices; logdev != NULL; logdev = next) {
|
||||
next = logdev->next;
|
||||
|
||||
if (!logdev->is_default) {
|
||||
if (!logdev->opened_as_default) {
|
||||
continue; // not opened as a default, leave it on the current physical device.
|
||||
}
|
||||
|
||||
// make sure all our streams are targeting the new device's format.
|
||||
for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) {
|
||||
SDL_SetAudioStreamFormat(stream, iscapture ? &new_default_device->spec : NULL, iscapture ? NULL : &new_default_device->spec);
|
||||
}
|
||||
|
||||
// now migrate the logical device.
|
||||
if (logdev->next) {
|
||||
logdev->next->prev = logdev->prev;
|
||||
@ -1652,8 +1905,25 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device)
|
||||
logdev->prev = NULL;
|
||||
logdev->next = new_default_device->logical_devices;
|
||||
new_default_device->logical_devices = logdev;
|
||||
|
||||
// make sure all our streams are targeting the new device's format.
|
||||
UpdateAudioStreamFormatsLogical(logdev);
|
||||
|
||||
// Post an event for each logical device we moved.
|
||||
if (post_fmt_event) {
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED;
|
||||
event.common.timestamp = 0;
|
||||
event.adevice.iscapture = iscapture ? 1 : 0;
|
||||
event.adevice.which = logdev->instance_id;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
}
|
||||
|
||||
current_default_device->simple_copy = AudioDeviceCanUseSimpleCopy(current_default_device);
|
||||
new_default_device->simple_copy = AudioDeviceCanUseSimpleCopy(new_default_device);
|
||||
|
||||
if (current_default_device->logical_devices == NULL) { // nothing left on the current physical device, close it.
|
||||
// !!! FIXME: we _need_ to release this lock, but doing so can cause a race condition if someone opens a device while we're closing it.
|
||||
SDL_UnlockMutex(current_default_device->lock); // can't hold the lock or the audio thread will deadlock while we WaitThread it.
|
||||
@ -1675,31 +1945,56 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device)
|
||||
|
||||
int SDL_AudioDeviceFormatChangedAlreadyLocked(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames)
|
||||
{
|
||||
const int orig_work_buffer_size = device->work_buffer_size;
|
||||
|
||||
if (AUDIO_SPECS_EQUAL(device->spec, *newspec) && new_sample_frames == device->sample_frames) {
|
||||
return 0; // we're already in that format.
|
||||
}
|
||||
|
||||
SDL_copyp(&device->spec, newspec);
|
||||
UpdateAudioStreamFormatsPhysical(device);
|
||||
|
||||
SDL_bool kill_device = SDL_FALSE;
|
||||
|
||||
const int orig_buffer_size = device->buffer_size;
|
||||
const SDL_bool iscapture = device->iscapture;
|
||||
device->sample_frames = new_sample_frames;
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
if (device->work_buffer && (device->work_buffer_size > orig_work_buffer_size)) {
|
||||
SDL_aligned_free(device->work_buffer);
|
||||
device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size);
|
||||
if (!device->work_buffer) {
|
||||
kill_device = SDL_TRUE;
|
||||
}
|
||||
|
||||
if ((device->spec.format != newspec->format) || (device->spec.channels != newspec->channels) || (device->spec.freq != newspec->freq)) {
|
||||
SDL_memcpy(&device->spec, newspec, sizeof (*newspec));
|
||||
for (SDL_LogicalAudioDevice *logdev = device->logical_devices; !kill_device && (logdev != NULL); logdev = logdev->next) {
|
||||
for (SDL_AudioStream *stream = logdev->bound_streams; !kill_device && (stream != NULL); stream = stream->next_binding) {
|
||||
if (SDL_SetAudioStreamFormat(stream, iscapture ? &device->spec : NULL, iscapture ? NULL : &device->spec) == -1) {
|
||||
kill_device = SDL_TRUE;
|
||||
}
|
||||
if (device->postmix_buffer) {
|
||||
SDL_aligned_free(device->postmix_buffer);
|
||||
device->postmix_buffer = (float *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size);
|
||||
if (!device->postmix_buffer) {
|
||||
kill_device = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_aligned_free(device->mix_buffer);
|
||||
device->mix_buffer = NULL;
|
||||
if (device->spec.format != SDL_AUDIO_F32) {
|
||||
device->mix_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size);
|
||||
if (!device->mix_buffer) {
|
||||
kill_device = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!kill_device) {
|
||||
device->sample_frames = new_sample_frames;
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
if (device->work_buffer && (device->buffer_size > orig_buffer_size)) {
|
||||
SDL_aligned_free(device->work_buffer);
|
||||
device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->buffer_size);
|
||||
if (!device->work_buffer) {
|
||||
kill_device = SDL_TRUE;
|
||||
}
|
||||
// Post an event for the physical device, and each logical device on this physical device.
|
||||
if (!kill_device && SDL_EventEnabled(SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED)) {
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED;
|
||||
event.common.timestamp = 0;
|
||||
event.adevice.iscapture = device->iscapture ? 1 : 0;
|
||||
event.adevice.which = device->instance_id;
|
||||
SDL_PushEvent(&event);
|
||||
for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) {
|
||||
event.adevice.which = logdev->instance_id;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
516
src/audio/SDL_audioqueue.c
Normal file
516
src/audio/SDL_audioqueue.c
Normal file
@ -0,0 +1,516 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_audioqueue.h"
|
||||
|
||||
#define AUDIO_SPECS_EQUAL(x, y) (((x).format == (y).format) && ((x).channels == (y).channels) && ((x).freq == (y).freq))
|
||||
|
||||
struct SDL_AudioTrack
|
||||
{
|
||||
SDL_AudioSpec spec;
|
||||
SDL_bool flushed;
|
||||
SDL_AudioTrack *next;
|
||||
|
||||
size_t (*avail)(void *ctx);
|
||||
int (*write)(void *ctx, const Uint8 *buf, size_t len);
|
||||
size_t (*read)(void *ctx, Uint8 *buf, size_t len, SDL_bool advance);
|
||||
void (*destroy)(void *ctx);
|
||||
};
|
||||
|
||||
struct SDL_AudioQueue
|
||||
{
|
||||
SDL_AudioTrack *head;
|
||||
SDL_AudioTrack *tail;
|
||||
size_t chunk_size;
|
||||
};
|
||||
|
||||
typedef struct SDL_AudioChunk SDL_AudioChunk;
|
||||
|
||||
struct SDL_AudioChunk
|
||||
{
|
||||
SDL_AudioChunk *next;
|
||||
size_t head;
|
||||
size_t tail;
|
||||
Uint8 data[SDL_VARIABLE_LENGTH_ARRAY];
|
||||
};
|
||||
|
||||
typedef struct SDL_ChunkedAudioTrack
|
||||
{
|
||||
SDL_AudioTrack track;
|
||||
|
||||
size_t chunk_size;
|
||||
|
||||
SDL_AudioChunk *head;
|
||||
SDL_AudioChunk *tail;
|
||||
size_t queued_bytes;
|
||||
|
||||
SDL_AudioChunk *free_chunks;
|
||||
size_t num_free_chunks;
|
||||
} SDL_ChunkedAudioTrack;
|
||||
|
||||
static void DestroyAudioChunk(SDL_AudioChunk *chunk)
|
||||
{
|
||||
SDL_free(chunk);
|
||||
}
|
||||
|
||||
static void DestroyAudioChunks(SDL_AudioChunk *chunk)
|
||||
{
|
||||
while (chunk) {
|
||||
SDL_AudioChunk *next = chunk->next;
|
||||
DestroyAudioChunk(chunk);
|
||||
chunk = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void ResetAudioChunk(SDL_AudioChunk *chunk)
|
||||
{
|
||||
chunk->next = NULL;
|
||||
chunk->head = 0;
|
||||
chunk->tail = 0;
|
||||
}
|
||||
|
||||
static SDL_AudioChunk *CreateAudioChunk(size_t chunk_size)
|
||||
{
|
||||
SDL_AudioChunk *chunk = (SDL_AudioChunk *)SDL_malloc(sizeof(*chunk) + chunk_size);
|
||||
|
||||
if (chunk == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ResetAudioChunk(chunk);
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
static void DestroyAudioTrackChunk(SDL_ChunkedAudioTrack *track, SDL_AudioChunk *chunk)
|
||||
{
|
||||
// Keeping a list of free chunks reduces memory allocations,
|
||||
// But also increases the amount of work to perform when freeing the track.
|
||||
const size_t max_free_bytes = 64 * 1024;
|
||||
|
||||
if (track->chunk_size * track->num_free_chunks < max_free_bytes) {
|
||||
chunk->next = track->free_chunks;
|
||||
track->free_chunks = chunk;
|
||||
++track->num_free_chunks;
|
||||
} else {
|
||||
DestroyAudioChunk(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_AudioChunk *CreateAudioTrackChunk(SDL_ChunkedAudioTrack *track)
|
||||
{
|
||||
if (track->num_free_chunks > 0) {
|
||||
SDL_AudioChunk *chunk = track->free_chunks;
|
||||
|
||||
track->free_chunks = chunk->next;
|
||||
--track->num_free_chunks;
|
||||
|
||||
ResetAudioChunk(chunk);
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
return CreateAudioChunk(track->chunk_size);
|
||||
}
|
||||
|
||||
static size_t AvailChunkedAudioTrack(void *ctx)
|
||||
{
|
||||
SDL_ChunkedAudioTrack *track = ctx;
|
||||
|
||||
return track->queued_bytes;
|
||||
}
|
||||
|
||||
static int WriteToChunkedAudioTrack(void *ctx, const Uint8 *data, size_t len)
|
||||
{
|
||||
SDL_ChunkedAudioTrack *track = ctx;
|
||||
|
||||
SDL_AudioChunk *chunk = track->tail;
|
||||
|
||||
// Handle the first chunk
|
||||
if (chunk == NULL) {
|
||||
chunk = CreateAudioTrackChunk(track);
|
||||
|
||||
if (chunk == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
SDL_assert((track->head == NULL) && (track->tail == NULL) && (track->queued_bytes == 0));
|
||||
track->head = chunk;
|
||||
track->tail = chunk;
|
||||
}
|
||||
|
||||
size_t total = 0;
|
||||
size_t old_tail = chunk->tail;
|
||||
size_t chunk_size = track->chunk_size;
|
||||
|
||||
while (chunk) {
|
||||
size_t to_write = chunk_size - chunk->tail;
|
||||
to_write = SDL_min(to_write, len - total);
|
||||
SDL_memcpy(&chunk->data[chunk->tail], &data[total], to_write);
|
||||
total += to_write;
|
||||
|
||||
chunk->tail += to_write;
|
||||
|
||||
if (total == len) {
|
||||
break;
|
||||
}
|
||||
|
||||
SDL_AudioChunk *next = CreateAudioTrackChunk(track);
|
||||
chunk->next = next;
|
||||
chunk = next;
|
||||
}
|
||||
|
||||
// Roll back the changes if we couldn't write all the data
|
||||
if (chunk == NULL) {
|
||||
chunk = track->tail;
|
||||
|
||||
SDL_AudioChunk *next = chunk->next;
|
||||
chunk->next = NULL;
|
||||
chunk->tail = old_tail;
|
||||
|
||||
DestroyAudioChunks(next);
|
||||
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
track->tail = chunk;
|
||||
track->queued_bytes += total;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t ReadFromChunkedAudioTrack(void *ctx, Uint8 *data, size_t len, SDL_bool advance)
|
||||
{
|
||||
SDL_ChunkedAudioTrack *track = ctx;
|
||||
SDL_AudioChunk *chunk = track->head;
|
||||
|
||||
size_t total = 0;
|
||||
size_t head = 0;
|
||||
|
||||
while (chunk) {
|
||||
head = chunk->head;
|
||||
|
||||
size_t to_read = chunk->tail - head;
|
||||
to_read = SDL_min(to_read, len - total);
|
||||
SDL_memcpy(&data[total], &chunk->data[head], to_read);
|
||||
total += to_read;
|
||||
|
||||
SDL_AudioChunk *next = chunk->next;
|
||||
|
||||
if (total == len) {
|
||||
head += to_read;
|
||||
break;
|
||||
}
|
||||
|
||||
if (advance) {
|
||||
DestroyAudioTrackChunk(track, chunk);
|
||||
}
|
||||
|
||||
chunk = next;
|
||||
}
|
||||
|
||||
if (advance) {
|
||||
if (chunk) {
|
||||
chunk->head = head;
|
||||
track->head = chunk;
|
||||
} else {
|
||||
track->head = NULL;
|
||||
track->tail = NULL;
|
||||
}
|
||||
|
||||
track->queued_bytes -= total;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static void DestroyChunkedAudioTrack(void *ctx)
|
||||
{
|
||||
SDL_ChunkedAudioTrack *track = ctx;
|
||||
DestroyAudioChunks(track->head);
|
||||
DestroyAudioChunks(track->free_chunks);
|
||||
SDL_free(track);
|
||||
}
|
||||
|
||||
static SDL_AudioTrack *CreateChunkedAudioTrack(const SDL_AudioSpec *spec, size_t chunk_size)
|
||||
{
|
||||
SDL_ChunkedAudioTrack *track = (SDL_ChunkedAudioTrack *)SDL_calloc(1, sizeof(*track));
|
||||
|
||||
if (track == NULL) {
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_copyp(&track->track.spec, spec);
|
||||
track->track.avail = AvailChunkedAudioTrack;
|
||||
track->track.write = WriteToChunkedAudioTrack;
|
||||
track->track.read = ReadFromChunkedAudioTrack;
|
||||
track->track.destroy = DestroyChunkedAudioTrack;
|
||||
|
||||
track->chunk_size = chunk_size;
|
||||
|
||||
return &track->track;
|
||||
}
|
||||
|
||||
SDL_AudioQueue *SDL_CreateAudioQueue(size_t chunk_size)
|
||||
{
|
||||
SDL_AudioQueue *queue = (SDL_AudioQueue *)SDL_calloc(1, sizeof(*queue));
|
||||
|
||||
if (queue == NULL) {
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
queue->chunk_size = chunk_size;
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
void SDL_DestroyAudioQueue(SDL_AudioQueue *queue)
|
||||
{
|
||||
SDL_ClearAudioQueue(queue);
|
||||
|
||||
SDL_free(queue);
|
||||
}
|
||||
|
||||
void SDL_ClearAudioQueue(SDL_AudioQueue *queue)
|
||||
{
|
||||
SDL_AudioTrack *track = queue->head;
|
||||
queue->head = NULL;
|
||||
queue->tail = NULL;
|
||||
|
||||
while (track) {
|
||||
SDL_AudioTrack *next = track->next;
|
||||
track->destroy(track);
|
||||
track = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_FlushAudioTrack(SDL_AudioTrack *track)
|
||||
{
|
||||
track->flushed = SDL_TRUE;
|
||||
track->write = NULL;
|
||||
}
|
||||
|
||||
void SDL_FlushAudioQueue(SDL_AudioQueue *queue)
|
||||
{
|
||||
SDL_AudioTrack *track = queue->tail;
|
||||
|
||||
if (track) {
|
||||
SDL_FlushAudioTrack(track);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_PopAudioQueueHead(SDL_AudioQueue *queue)
|
||||
{
|
||||
SDL_AudioTrack *track = queue->head;
|
||||
|
||||
for (;;) {
|
||||
SDL_bool flushed = track->flushed;
|
||||
|
||||
SDL_AudioTrack *next = track->next;
|
||||
track->destroy(track);
|
||||
track = next;
|
||||
|
||||
if (flushed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
queue->head = track;
|
||||
|
||||
if (track == NULL) {
|
||||
queue->tail = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
size_t SDL_GetAudioQueueChunkSize(SDL_AudioQueue *queue)
|
||||
{
|
||||
return queue->chunk_size;
|
||||
}
|
||||
|
||||
SDL_AudioTrack *SDL_CreateChunkedAudioTrack(const SDL_AudioSpec *spec, const Uint8 *data, size_t len, size_t chunk_size)
|
||||
{
|
||||
SDL_AudioTrack *track = CreateChunkedAudioTrack(spec, chunk_size);
|
||||
|
||||
if (track == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (track->write(track, data, len) != 0) {
|
||||
track->destroy(track);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
void SDL_AddTrackToAudioQueue(SDL_AudioQueue *queue, SDL_AudioTrack *track)
|
||||
{
|
||||
SDL_AudioTrack *tail = queue->tail;
|
||||
|
||||
if (tail) {
|
||||
// If the spec has changed, make sure to flush the previous track
|
||||
if (!AUDIO_SPECS_EQUAL(tail->spec, track->spec)) {
|
||||
SDL_FlushAudioTrack(tail);
|
||||
}
|
||||
|
||||
tail->next = track;
|
||||
} else {
|
||||
queue->head = track;
|
||||
}
|
||||
|
||||
queue->tail = track;
|
||||
}
|
||||
|
||||
int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const Uint8 *data, size_t len)
|
||||
{
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_AudioTrack *track = queue->tail;
|
||||
|
||||
if ((track != NULL) && !AUDIO_SPECS_EQUAL(track->spec, *spec)) {
|
||||
SDL_FlushAudioTrack(track);
|
||||
}
|
||||
|
||||
if ((track == NULL) || (track->write == NULL)) {
|
||||
SDL_AudioTrack *new_track = CreateChunkedAudioTrack(spec, queue->chunk_size);
|
||||
|
||||
if (new_track == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
if (track) {
|
||||
track->next = new_track;
|
||||
} else {
|
||||
queue->head = new_track;
|
||||
}
|
||||
|
||||
queue->tail = new_track;
|
||||
|
||||
track = new_track;
|
||||
}
|
||||
|
||||
return track->write(track, data, len);
|
||||
}
|
||||
|
||||
void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue)
|
||||
{
|
||||
return queue->head;
|
||||
}
|
||||
|
||||
size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, SDL_bool *out_flushed)
|
||||
{
|
||||
SDL_AudioTrack *iter = *inout_iter;
|
||||
SDL_assert(iter != NULL);
|
||||
|
||||
SDL_copyp(out_spec, &iter->spec);
|
||||
|
||||
SDL_bool flushed = SDL_FALSE;
|
||||
size_t queued_bytes = 0;
|
||||
|
||||
while (iter) {
|
||||
SDL_AudioTrack *track = iter;
|
||||
iter = iter->next;
|
||||
|
||||
size_t avail = track->avail(track);
|
||||
|
||||
if (avail >= SDL_SIZE_MAX - queued_bytes) {
|
||||
queued_bytes = SDL_SIZE_MAX;
|
||||
flushed = SDL_FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
queued_bytes += avail;
|
||||
flushed = track->flushed;
|
||||
|
||||
if (flushed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*inout_iter = iter;
|
||||
*out_flushed = flushed;
|
||||
|
||||
return queued_bytes;
|
||||
}
|
||||
|
||||
int SDL_ReadFromAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len)
|
||||
{
|
||||
size_t total = 0;
|
||||
SDL_AudioTrack *track = queue->head;
|
||||
|
||||
for (;;) {
|
||||
if (track == NULL) {
|
||||
return SDL_SetError("Reading past end of queue");
|
||||
}
|
||||
|
||||
total += track->read(track, &data[total], len - total, SDL_TRUE);
|
||||
|
||||
if (total == len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (track->flushed) {
|
||||
return SDL_SetError("Reading past end of flushed track");
|
||||
}
|
||||
|
||||
SDL_AudioTrack *next = track->next;
|
||||
|
||||
if (next == NULL) {
|
||||
return SDL_SetError("Reading past end of incomplete track");
|
||||
}
|
||||
|
||||
queue->head = next;
|
||||
|
||||
track->destroy(track);
|
||||
track = next;
|
||||
}
|
||||
}
|
||||
|
||||
int SDL_PeekIntoAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len)
|
||||
{
|
||||
size_t total = 0;
|
||||
SDL_AudioTrack *track = queue->head;
|
||||
|
||||
for (;;) {
|
||||
if (track == NULL) {
|
||||
return SDL_SetError("Peeking past end of queue");
|
||||
}
|
||||
|
||||
total += track->read(track, &data[total], len - total, SDL_FALSE);
|
||||
|
||||
if (total == len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (track->flushed) {
|
||||
// If we have run out of data, fill the rest with silence.
|
||||
SDL_memset(&data[total], SDL_GetSilenceValueForFormat(track->spec.format), len - total);
|
||||
return 0;
|
||||
}
|
||||
|
||||
track = track->next;
|
||||
}
|
||||
}
|
77
src/audio/SDL_audioqueue.h
Normal file
77
src/audio/SDL_audioqueue.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_audioqueue_h_
|
||||
#define SDL_audioqueue_h_
|
||||
|
||||
// Internal functions used by SDL_AudioStream for queueing audio.
|
||||
|
||||
typedef struct SDL_AudioQueue SDL_AudioQueue;
|
||||
typedef struct SDL_AudioTrack SDL_AudioTrack;
|
||||
|
||||
// Create a new audio queue
|
||||
SDL_AudioQueue *SDL_CreateAudioQueue(size_t chunk_size);
|
||||
|
||||
// Destroy an audio queue
|
||||
void SDL_DestroyAudioQueue(SDL_AudioQueue *queue);
|
||||
|
||||
// Completely clear the queue
|
||||
void SDL_ClearAudioQueue(SDL_AudioQueue *queue);
|
||||
|
||||
// Mark the last track as flushed
|
||||
void SDL_FlushAudioQueue(SDL_AudioQueue *queue);
|
||||
|
||||
// Pop the current head track
|
||||
// REQUIRES: The head track must exist, and must have been flushed
|
||||
void SDL_PopAudioQueueHead(SDL_AudioQueue *queue);
|
||||
|
||||
// Get the chunk size, mostly for use with SDL_CreateChunkedAudioTrack
|
||||
// This can be called from any thread
|
||||
size_t SDL_GetAudioQueueChunkSize(SDL_AudioQueue *queue);
|
||||
|
||||
// Write data to the end of queue
|
||||
// REQUIRES: If the spec has changed, the last track must have been flushed
|
||||
int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const Uint8 *data, size_t len);
|
||||
|
||||
// Create a track without needing to hold any locks
|
||||
SDL_AudioTrack *SDL_CreateChunkedAudioTrack(const SDL_AudioSpec *spec, const Uint8 *data, size_t len, size_t chunk_size);
|
||||
|
||||
// Add a track to the end of the queue
|
||||
// REQUIRES: `track != NULL`
|
||||
void SDL_AddTrackToAudioQueue(SDL_AudioQueue *queue, SDL_AudioTrack *track);
|
||||
|
||||
// Iterate over the tracks in the queue
|
||||
void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue);
|
||||
|
||||
// Query and update the track iterator
|
||||
// REQUIRES: `*inout_iter != NULL` (a valid iterator)
|
||||
size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, SDL_bool *out_flushed);
|
||||
|
||||
// Read data from the start of the queue
|
||||
// REQUIRES: There must be enough data in the queue
|
||||
int SDL_ReadFromAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len);
|
||||
|
||||
// Peek into the start of the queue
|
||||
// REQUIRES: There must be enough data in the queue, unless it has been flushed, in which case missing data is filled with silence.
|
||||
int SDL_PeekIntoAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len);
|
||||
|
||||
#endif // SDL_audioqueue_h_
|
335
src/audio/SDL_audioresample.c
Normal file
335
src/audio/SDL_audioresample.c
Normal file
@ -0,0 +1,335 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_sysaudio.h"
|
||||
#include "SDL_audioresample.h"
|
||||
|
||||
/* SDL's resampler uses a "bandlimited interpolation" algorithm:
|
||||
https://ccrma.stanford.edu/~jos/resample/ */
|
||||
|
||||
#include "SDL_audio_resampler_filter.h"
|
||||
|
||||
/* For a given srcpos, `srcpos + frame` are sampled, where `-RESAMPLER_ZERO_CROSSINGS < frame <= RESAMPLER_ZERO_CROSSINGS`.
|
||||
* Note, when upsampling, it is also possible to start sampling from `srcpos = -1`. */
|
||||
#define RESAMPLER_MAX_PADDING_FRAMES (RESAMPLER_ZERO_CROSSINGS + 1)
|
||||
|
||||
#define RESAMPLER_FILTER_INTERP_BITS (32 - RESAMPLER_BITS_PER_ZERO_CROSSING)
|
||||
#define RESAMPLER_FILTER_INTERP_RANGE (1 << RESAMPLER_FILTER_INTERP_BITS)
|
||||
|
||||
#define RESAMPLER_SAMPLES_PER_FRAME (RESAMPLER_ZERO_CROSSINGS * 2)
|
||||
|
||||
#define RESAMPLER_FULL_FILTER_SIZE (RESAMPLER_SAMPLES_PER_FRAME * (RESAMPLER_SAMPLES_PER_ZERO_CROSSING + 1))
|
||||
|
||||
static void ResampleFrame_Scalar(const float *src, float *dst, const float *raw_filter, float interp, int chans)
|
||||
{
|
||||
int i, chan;
|
||||
|
||||
float filter[RESAMPLER_SAMPLES_PER_FRAME];
|
||||
|
||||
// Interpolate between the nearest two filters
|
||||
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
|
||||
filter[i] = (raw_filter[i] * (1.0f - interp)) + (raw_filter[i + RESAMPLER_SAMPLES_PER_FRAME] * interp);
|
||||
}
|
||||
|
||||
if (chans == 2) {
|
||||
float out[2];
|
||||
out[0] = 0.0f;
|
||||
out[1] = 0.0f;
|
||||
|
||||
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
|
||||
const float scale = filter[i];
|
||||
out[0] += src[i * 2 + 0] * scale;
|
||||
out[1] += src[i * 2 + 1] * scale;
|
||||
}
|
||||
|
||||
dst[0] = out[0];
|
||||
dst[1] = out[1];
|
||||
return;
|
||||
}
|
||||
|
||||
if (chans == 1) {
|
||||
float out = 0.0f;
|
||||
|
||||
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
|
||||
out += src[i] * filter[i];
|
||||
}
|
||||
|
||||
dst[0] = out;
|
||||
return;
|
||||
}
|
||||
|
||||
for (chan = 0; chan < chans; chan++) {
|
||||
float f = 0.0f;
|
||||
|
||||
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
|
||||
f += src[i * chans + chan] * filter[i];
|
||||
}
|
||||
|
||||
dst[chan] = f;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SDL_SSE_INTRINSICS
|
||||
static void SDL_TARGETING("sse") ResampleFrame_SSE(const float *src, float *dst, const float *raw_filter, float interp, int chans)
|
||||
{
|
||||
#if RESAMPLER_SAMPLES_PER_FRAME != 10
|
||||
#error Invalid samples per frame
|
||||
#endif
|
||||
|
||||
// Load the filter
|
||||
__m128 f0 = _mm_loadu_ps(raw_filter + 0);
|
||||
__m128 f1 = _mm_loadu_ps(raw_filter + 4);
|
||||
__m128 f2 = _mm_loadl_pi(_mm_setzero_ps(), (const __m64 *)(raw_filter + 8));
|
||||
|
||||
__m128 g0 = _mm_loadu_ps(raw_filter + 10);
|
||||
__m128 g1 = _mm_loadu_ps(raw_filter + 14);
|
||||
__m128 g2 = _mm_loadl_pi(_mm_setzero_ps(), (const __m64 *)(raw_filter + 18));
|
||||
|
||||
__m128 interp1 = _mm_set1_ps(interp);
|
||||
__m128 interp2 = _mm_sub_ps(_mm_set1_ps(1.0f), _mm_set1_ps(interp));
|
||||
|
||||
// Linear interpolate the filter
|
||||
f0 = _mm_add_ps(_mm_mul_ps(f0, interp2), _mm_mul_ps(g0, interp1));
|
||||
f1 = _mm_add_ps(_mm_mul_ps(f1, interp2), _mm_mul_ps(g1, interp1));
|
||||
f2 = _mm_add_ps(_mm_mul_ps(f2, interp2), _mm_mul_ps(g2, interp1));
|
||||
|
||||
if (chans == 2) {
|
||||
// Duplicate each of the filter elements
|
||||
g0 = _mm_unpackhi_ps(f0, f0);
|
||||
f0 = _mm_unpacklo_ps(f0, f0);
|
||||
g1 = _mm_unpackhi_ps(f1, f1);
|
||||
f1 = _mm_unpacklo_ps(f1, f1);
|
||||
f2 = _mm_unpacklo_ps(f2, f2);
|
||||
|
||||
// Multiply the filter by the input
|
||||
f0 = _mm_mul_ps(f0, _mm_loadu_ps(src + 0));
|
||||
g0 = _mm_mul_ps(g0, _mm_loadu_ps(src + 4));
|
||||
f1 = _mm_mul_ps(f1, _mm_loadu_ps(src + 8));
|
||||
g1 = _mm_mul_ps(g1, _mm_loadu_ps(src + 12));
|
||||
f2 = _mm_mul_ps(f2, _mm_loadu_ps(src + 16));
|
||||
|
||||
// Calculate the sum
|
||||
f0 = _mm_add_ps(_mm_add_ps(_mm_add_ps(f0, g0), _mm_add_ps(f1, g1)), f2);
|
||||
f0 = _mm_add_ps(f0, _mm_movehl_ps(f0, f0));
|
||||
|
||||
// Store the result
|
||||
_mm_storel_pi((__m64 *)dst, f0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (chans == 1) {
|
||||
// Multiply the filter by the input
|
||||
f0 = _mm_mul_ps(f0, _mm_loadu_ps(src + 0));
|
||||
f1 = _mm_mul_ps(f1, _mm_loadu_ps(src + 4));
|
||||
f2 = _mm_mul_ps(f2, _mm_loadl_pi(_mm_setzero_ps(), (const __m64 *)(src + 8)));
|
||||
|
||||
// Calculate the sum
|
||||
f0 = _mm_add_ps(f0, f1);
|
||||
f0 = _mm_add_ps(_mm_add_ps(f0, f2), _mm_movehl_ps(f0, f0));
|
||||
f0 = _mm_add_ss(f0, _mm_shuffle_ps(f0, f0, _MM_SHUFFLE(1, 1, 1, 1)));
|
||||
|
||||
// Store the result
|
||||
_mm_store_ss(dst, f0);
|
||||
return;
|
||||
}
|
||||
|
||||
float filter[RESAMPLER_SAMPLES_PER_FRAME];
|
||||
_mm_storeu_ps(filter + 0, f0);
|
||||
_mm_storeu_ps(filter + 4, f1);
|
||||
_mm_storel_pi((__m64 *)(filter + 8), f2);
|
||||
|
||||
int i, chan = 0;
|
||||
|
||||
for (; chan + 4 <= chans; chan += 4) {
|
||||
f0 = _mm_setzero_ps();
|
||||
|
||||
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
|
||||
f0 = _mm_add_ps(f0, _mm_mul_ps(_mm_loadu_ps(&src[i * chans + chan]), _mm_load1_ps(&filter[i])));
|
||||
}
|
||||
|
||||
_mm_storeu_ps(&dst[chan], f0);
|
||||
}
|
||||
|
||||
for (; chan < chans; chan++) {
|
||||
f0 = _mm_setzero_ps();
|
||||
|
||||
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
|
||||
f0 = _mm_add_ss(f0, _mm_mul_ss(_mm_load_ss(&src[i * chans + chan]), _mm_load_ss(&filter[i])));
|
||||
}
|
||||
|
||||
_mm_store_ss(&dst[chan], f0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void (*ResampleFrame)(const float *src, float *dst, const float *raw_filter, float interp, int chans);
|
||||
|
||||
static float FullResamplerFilter[RESAMPLER_FULL_FILTER_SIZE];
|
||||
|
||||
void SDL_SetupAudioResampler(void)
|
||||
{
|
||||
static SDL_bool setup = SDL_FALSE;
|
||||
if (setup) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build a table combining the left and right wings, for faster access
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < RESAMPLER_SAMPLES_PER_ZERO_CROSSING; ++i) {
|
||||
for (j = 0; j < RESAMPLER_ZERO_CROSSINGS; j++) {
|
||||
int lwing = (i * RESAMPLER_SAMPLES_PER_FRAME) + (RESAMPLER_ZERO_CROSSINGS - 1) - j;
|
||||
int rwing = (RESAMPLER_FULL_FILTER_SIZE - 1) - lwing;
|
||||
|
||||
float value = ResamplerFilter[(i * RESAMPLER_ZERO_CROSSINGS) + j];
|
||||
FullResamplerFilter[lwing] = value;
|
||||
FullResamplerFilter[rwing] = value;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < RESAMPLER_ZERO_CROSSINGS; ++i) {
|
||||
int rwing = i + RESAMPLER_ZERO_CROSSINGS;
|
||||
int lwing = (RESAMPLER_FULL_FILTER_SIZE - 1) - rwing;
|
||||
|
||||
FullResamplerFilter[lwing] = 0.0f;
|
||||
FullResamplerFilter[rwing] = 0.0f;
|
||||
}
|
||||
|
||||
ResampleFrame = ResampleFrame_Scalar;
|
||||
|
||||
#ifdef SDL_SSE_INTRINSICS
|
||||
if (SDL_HasSSE()) {
|
||||
ResampleFrame = ResampleFrame_SSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
setup = SDL_TRUE;
|
||||
}
|
||||
|
||||
Sint64 SDL_GetResampleRate(int src_rate, int dst_rate)
|
||||
{
|
||||
SDL_assert(src_rate > 0);
|
||||
SDL_assert(dst_rate > 0);
|
||||
|
||||
Sint64 sample_rate = ((Sint64)src_rate << 32) / (Sint64)dst_rate;
|
||||
SDL_assert(sample_rate > 0);
|
||||
|
||||
return sample_rate;
|
||||
}
|
||||
|
||||
int SDL_GetResamplerHistoryFrames(void)
|
||||
{
|
||||
// Even if we aren't currently resampling, make sure to keep enough history in case we need to later.
|
||||
|
||||
return RESAMPLER_MAX_PADDING_FRAMES;
|
||||
}
|
||||
|
||||
int SDL_GetResamplerPaddingFrames(Sint64 resample_rate)
|
||||
{
|
||||
// This must always be <= SDL_GetResamplerHistoryFrames()
|
||||
|
||||
return resample_rate ? RESAMPLER_MAX_PADDING_FRAMES : 0;
|
||||
}
|
||||
|
||||
// These are not general purpose. They do not check for all possible underflow/overflow
|
||||
SDL_FORCE_INLINE Sint64 ResamplerAdd(Sint64 a, Sint64 b, Sint64 *ret)
|
||||
{
|
||||
if ((b > 0) && (a > SDL_MAX_SINT64 - b)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*ret = a + b;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_FORCE_INLINE Sint64 ResamplerMul(Sint64 a, Sint64 b, Sint64 *ret)
|
||||
{
|
||||
if ((b > 0) && (a > SDL_MAX_SINT64 / b)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*ret = a * b;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Sint64 SDL_GetResamplerInputFrames(Sint64 output_frames, Sint64 resample_rate, Sint64 resample_offset)
|
||||
{
|
||||
// Calculate the index of the last input frame, then add 1.
|
||||
// ((((output_frames - 1) * resample_rate) + resample_offset) >> 32) + 1
|
||||
|
||||
Sint64 output_offset;
|
||||
if (ResamplerMul(output_frames, resample_rate, &output_offset) ||
|
||||
ResamplerAdd(output_offset, -resample_rate + resample_offset + 0x100000000, &output_offset)) {
|
||||
output_offset = SDL_MAX_SINT64;
|
||||
}
|
||||
|
||||
Sint64 input_frames = (Sint64)(Sint32)(output_offset >> 32);
|
||||
input_frames = SDL_max(input_frames, 0);
|
||||
|
||||
return input_frames;
|
||||
}
|
||||
|
||||
Sint64 SDL_GetResamplerOutputFrames(Sint64 input_frames, Sint64 resample_rate, Sint64 *inout_resample_offset)
|
||||
{
|
||||
Sint64 resample_offset = *inout_resample_offset;
|
||||
|
||||
// input_offset = (input_frames << 32) - resample_offset;
|
||||
Sint64 input_offset;
|
||||
if (ResamplerMul(input_frames, 0x100000000, &input_offset) ||
|
||||
ResamplerAdd(input_offset, -resample_offset, &input_offset)) {
|
||||
input_offset = SDL_MAX_SINT64;
|
||||
}
|
||||
|
||||
// output_frames = div_ceil(input_offset, resample_rate)
|
||||
Sint64 output_frames = (input_offset > 0) ? (((input_offset - 1) / resample_rate) + 1) : 0;
|
||||
|
||||
*inout_resample_offset = (output_frames * resample_rate) - input_offset;
|
||||
|
||||
return output_frames;
|
||||
}
|
||||
|
||||
void SDL_ResampleAudio(int chans, const float *src, int inframes, float *dst, int outframes,
|
||||
Sint64 resample_rate, Sint64 *inout_resample_offset)
|
||||
{
|
||||
int i;
|
||||
Sint64 srcpos = *inout_resample_offset;
|
||||
|
||||
SDL_assert(resample_rate > 0);
|
||||
|
||||
for (i = 0; i < outframes; i++) {
|
||||
int srcindex = (int)(Sint32)(srcpos >> 32);
|
||||
Uint32 srcfraction = (Uint32)(srcpos & 0xFFFFFFFF);
|
||||
srcpos += resample_rate;
|
||||
|
||||
SDL_assert(srcindex >= -1 && srcindex < inframes);
|
||||
|
||||
const float *filter = &FullResamplerFilter[(srcfraction >> RESAMPLER_FILTER_INTERP_BITS) * RESAMPLER_SAMPLES_PER_FRAME];
|
||||
const float interp = (float)(srcfraction & (RESAMPLER_FILTER_INTERP_RANGE - 1)) * (1.0f / RESAMPLER_FILTER_INTERP_RANGE);
|
||||
|
||||
const float *frame = &src[(srcindex - (RESAMPLER_ZERO_CROSSINGS - 1)) * chans];
|
||||
ResampleFrame(frame, dst, filter, interp, chans);
|
||||
|
||||
dst += chans;
|
||||
}
|
||||
|
||||
*inout_resample_offset = srcpos - ((Sint64)inframes << 32);
|
||||
}
|
43
src/audio/SDL_audioresample.h
Normal file
43
src/audio/SDL_audioresample.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_audioresample_h_
|
||||
#define SDL_audioresample_h_
|
||||
|
||||
// Internal functions used by SDL_AudioStream for resampling audio.
|
||||
// The resampler uses 32:32 fixed-point arithmetic to track its position.
|
||||
|
||||
Sint64 SDL_GetResampleRate(const int src_rate, const int dst_rate);
|
||||
|
||||
int SDL_GetResamplerHistoryFrames(void);
|
||||
int SDL_GetResamplerPaddingFrames(Sint64 resample_rate);
|
||||
|
||||
Sint64 SDL_GetResamplerInputFrames(Sint64 output_frames, Sint64 resample_rate, Sint64 resample_offset);
|
||||
Sint64 SDL_GetResamplerOutputFrames(Sint64 input_frames, Sint64 resample_rate, Sint64 *inout_resample_offset);
|
||||
|
||||
// Resample some audio.
|
||||
// REQUIRES: `inframes >= SDL_GetResamplerInputFrames(outframes)`
|
||||
// REQUIRES: At least `SDL_GetResamplerPaddingFrames(...)` extra frames to the left of src, and right of src+inframes
|
||||
void SDL_ResampleAudio(int chans, const float *src, int inframes, float *dst, int outframes,
|
||||
Sint64 resample_rate, Sint64 *inout_resample_offset);
|
||||
|
||||
#endif // SDL_audioresample_h_
|
@ -39,482 +39,510 @@
|
||||
#define NEED_SCALAR_CONVERTER_FALLBACKS 1
|
||||
#endif
|
||||
|
||||
#define DIVBY128 0.0078125f
|
||||
#define DIVBY32768 0.000030517578125f
|
||||
#define DIVBY8388607 0.00000011920930376163766f
|
||||
#define DIVBY2147483648 0.0000000004656612873077392578125f /* 0x1p-31f */
|
||||
|
||||
#if NEED_SCALAR_CONVERTER_FALLBACKS
|
||||
|
||||
/* these all convert backwards because (currently) float32 is >= to the size of anything it converts to, so it lets us safely convert in-place. */
|
||||
#define AUDIOCVT_TOFLOAT_SCALAR(from, fromtype, equation) \
|
||||
static void SDL_Convert_##from##_to_F32_Scalar(float *dst, const fromtype *src, int num_samples) { \
|
||||
int i; \
|
||||
LOG_DEBUG_AUDIO_CONVERT(#from, "F32"); \
|
||||
for (i = num_samples - 1; i >= 0; --i) { \
|
||||
dst[i] = equation; \
|
||||
} \
|
||||
/* This code requires that floats are in the IEEE-754 binary32 format */
|
||||
SDL_COMPILE_TIME_ASSERT(float_bits, sizeof(float) == sizeof(Uint32));
|
||||
|
||||
union float_bits {
|
||||
Uint32 u32;
|
||||
float f32;
|
||||
};
|
||||
|
||||
static void SDL_Convert_S8_to_F32_Scalar(float *dst, const Sint8 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("S8", "F32");
|
||||
|
||||
for (i = num_samples - 1; i >= 0; --i) {
|
||||
/* 1) Construct a float in the range [65536.0, 65538.0)
|
||||
* 2) Shift the float range to [-1.0, 1.0) */
|
||||
union float_bits x;
|
||||
x.u32 = (Uint8)src[i] ^ 0x47800080u;
|
||||
dst[i] = x.f32 - 65537.0f;
|
||||
}
|
||||
}
|
||||
|
||||
AUDIOCVT_TOFLOAT_SCALAR(S8, Sint8, ((float)src[i]) * DIVBY128)
|
||||
AUDIOCVT_TOFLOAT_SCALAR(U8, Uint8, (((float)src[i]) * DIVBY128) - 1.0f)
|
||||
AUDIOCVT_TOFLOAT_SCALAR(S16, Sint16, ((float)src[i]) * DIVBY32768)
|
||||
AUDIOCVT_TOFLOAT_SCALAR(S32, Sint32, ((float)(src[i] >> 8)) * DIVBY8388607)
|
||||
#undef AUDIOCVT_FROMFLOAT_SCALAR
|
||||
static void SDL_Convert_U8_to_F32_Scalar(float *dst, const Uint8 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* these all convert forwards because (currently) float32 is >= to the size of anything it converts from, so it lets us safely convert in-place. */
|
||||
#define AUDIOCVT_FROMFLOAT_SCALAR(to, totype, clampmin, clampmax, equation) \
|
||||
static void SDL_Convert_F32_to_##to##_Scalar(totype *dst, const float *src, int num_samples) { \
|
||||
int i; \
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", #to); \
|
||||
for (i = 0; i < num_samples; i++) { \
|
||||
const float sample = src[i]; \
|
||||
if (sample >= 1.0f) { \
|
||||
dst[i] = (totype) (clampmax); \
|
||||
} else if (sample <= -1.0f) { \
|
||||
dst[i] = (totype) (clampmin); \
|
||||
} else { \
|
||||
dst[i] = (totype) (equation); \
|
||||
} \
|
||||
} \
|
||||
LOG_DEBUG_AUDIO_CONVERT("U8", "F32");
|
||||
|
||||
for (i = num_samples - 1; i >= 0; --i) {
|
||||
/* 1) Construct a float in the range [65536.0, 65538.0)
|
||||
* 2) Shift the float range to [-1.0, 1.0) */
|
||||
union float_bits x;
|
||||
x.u32 = src[i] ^ 0x47800000u;
|
||||
dst[i] = x.f32 - 65537.0f;
|
||||
}
|
||||
}
|
||||
|
||||
AUDIOCVT_FROMFLOAT_SCALAR(S8, Sint8, -128, 127, sample * 127.0f);
|
||||
AUDIOCVT_FROMFLOAT_SCALAR(U8, Uint8, 0, 255, (sample + 1.0f) * 127.0f);
|
||||
AUDIOCVT_FROMFLOAT_SCALAR(S16, Sint16, -32768, 32767, sample * 32767.0f);
|
||||
AUDIOCVT_FROMFLOAT_SCALAR(S32, Sint32, -2147483648LL, 2147483647, ((Sint32)(sample * 8388607.0f)) << 8);
|
||||
#undef AUDIOCVT_FROMFLOAT_SCALAR
|
||||
static void SDL_Convert_S16_to_F32_Scalar(float *dst, const Sint16 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("S16", "F32");
|
||||
|
||||
for (i = num_samples - 1; i >= 0; --i) {
|
||||
/* 1) Construct a float in the range [256.0, 258.0)
|
||||
* 2) Shift the float range to [-1.0, 1.0) */
|
||||
union float_bits x;
|
||||
x.u32 = (Uint16)src[i] ^ 0x43808000u;
|
||||
dst[i] = x.f32 - 257.0f;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_Convert_S32_to_F32_Scalar(float *dst, const Sint32 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("S32", "F32");
|
||||
|
||||
for (i = num_samples - 1; i >= 0; --i) {
|
||||
dst[i] = (float)src[i] * DIVBY2147483648;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a bit-mask based on the sign-bit. Should optimize to a single arithmetic-shift-right */
|
||||
#define SIGNMASK(x) (Uint32)(0u - ((Uint32)(x) >> 31))
|
||||
|
||||
static void SDL_Convert_F32_to_S8_Scalar(Sint8 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "S8");
|
||||
|
||||
for (i = 0; i < num_samples; ++i) {
|
||||
/* 1) Shift the float range from [-1.0, 1.0] to [98303.0, 98305.0]
|
||||
* 2) Shift the integer range from [0x47BFFF80, 0x47C00080] to [-128, 128]
|
||||
* 3) Clamp the value to [-128, 127] */
|
||||
union float_bits x;
|
||||
x.f32 = src[i] + 98304.0f;
|
||||
|
||||
Uint32 y = x.u32 - 0x47C00000u;
|
||||
Uint32 z = 0x7Fu - (y ^ SIGNMASK(y));
|
||||
y = y ^ (z & SIGNMASK(z));
|
||||
|
||||
dst[i] = (Sint8)(y & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_Convert_F32_to_U8_Scalar(Uint8 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "U8");
|
||||
|
||||
for (i = 0; i < num_samples; ++i) {
|
||||
/* 1) Shift the float range from [-1.0, 1.0] to [98303.0, 98305.0]
|
||||
* 2) Shift the integer range from [0x47BFFF80, 0x47C00080] to [-128, 128]
|
||||
* 3) Clamp the value to [-128, 127]
|
||||
* 4) Shift the integer range from [-128, 127] to [0, 255] */
|
||||
union float_bits x;
|
||||
x.f32 = src[i] + 98304.0f;
|
||||
|
||||
Uint32 y = x.u32 - 0x47C00000u;
|
||||
Uint32 z = 0x7Fu - (y ^ SIGNMASK(y));
|
||||
y = (y ^ 0x80u) ^ (z & SIGNMASK(z));
|
||||
|
||||
dst[i] = (Uint8)(y & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_Convert_F32_to_S16_Scalar(Sint16 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "S16");
|
||||
|
||||
for (i = 0; i < num_samples; ++i) {
|
||||
/* 1) Shift the float range from [-1.0, 1.0] to [383.0, 385.0]
|
||||
* 2) Shift the integer range from [0x43BF8000, 0x43C08000] to [-32768, 32768]
|
||||
* 3) Clamp values outside the [-32768, 32767] range */
|
||||
union float_bits x;
|
||||
x.f32 = src[i] + 384.0f;
|
||||
|
||||
Uint32 y = x.u32 - 0x43C00000u;
|
||||
Uint32 z = 0x7FFFu - (y ^ SIGNMASK(y));
|
||||
y = y ^ (z & SIGNMASK(z));
|
||||
|
||||
dst[i] = (Sint16)(y & 0xFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_Convert_F32_to_S32_Scalar(Sint32 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "S32");
|
||||
|
||||
for (i = 0; i < num_samples; ++i) {
|
||||
/* 1) Shift the float range from [-1.0, 1.0] to [-2147483648.0, 2147483648.0]
|
||||
* 2) Set values outside the [-2147483648.0, 2147483647.0] range to -2147483648.0
|
||||
* 3) Convert the float to an integer, and fixup values outside the valid range */
|
||||
union float_bits x;
|
||||
x.f32 = src[i];
|
||||
|
||||
Uint32 y = x.u32 + 0x0F800000u;
|
||||
Uint32 z = y - 0xCF000000u;
|
||||
z &= SIGNMASK(y ^ z);
|
||||
x.u32 = y - z;
|
||||
|
||||
dst[i] = (Sint32)x.f32 ^ (Sint32)SIGNMASK(z);
|
||||
}
|
||||
}
|
||||
|
||||
#undef SIGNMASK
|
||||
|
||||
#endif /* NEED_SCALAR_CONVERTER_FALLBACKS */
|
||||
|
||||
#ifdef SDL_SSE2_INTRINSICS
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_S8_to_F32_SSE2(float *dst, const Sint8 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
int i = num_samples;
|
||||
|
||||
/* 1) Flip the sign bit to convert from S8 to U8 format
|
||||
* 2) Construct a float in the range [65536.0, 65538.0)
|
||||
* 3) Shift the float range to [-1.0, 1.0)
|
||||
* dst[i] = i2f((src[i] ^ 0x80) | 0x47800000) - 65537.0 */
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const __m128i flipper = _mm_set1_epi8(-0x80);
|
||||
const __m128i caster = _mm_set1_epi16(0x4780 /* 0x47800000 = f2i(65536.0) */);
|
||||
const __m128 offset = _mm_set1_ps(-65537.0);
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("S8", "F32 (using SSE2)");
|
||||
|
||||
src += num_samples - 1;
|
||||
dst += num_samples - 1;
|
||||
while (i >= 16) {
|
||||
i -= 16;
|
||||
|
||||
/* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
|
||||
for (i = num_samples; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
|
||||
*dst = ((float)*src) * DIVBY128;
|
||||
const __m128i bytes = _mm_xor_si128(_mm_loadu_si128((const __m128i *)&src[i]), flipper);
|
||||
|
||||
const __m128i shorts1 = _mm_unpacklo_epi8(bytes, zero);
|
||||
const __m128i shorts2 = _mm_unpackhi_epi8(bytes, zero);
|
||||
|
||||
const __m128 floats1 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts1, caster)), offset);
|
||||
const __m128 floats2 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts1, caster)), offset);
|
||||
const __m128 floats3 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts2, caster)), offset);
|
||||
const __m128 floats4 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts2, caster)), offset);
|
||||
|
||||
_mm_storeu_ps(&dst[i], floats1);
|
||||
_mm_storeu_ps(&dst[i + 4], floats2);
|
||||
_mm_storeu_ps(&dst[i + 8], floats3);
|
||||
_mm_storeu_ps(&dst[i + 12], floats4);
|
||||
}
|
||||
|
||||
src -= 15;
|
||||
dst -= 15; /* adjust to read SSE blocks from the start. */
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128i *mmsrc = (const __m128i *)src;
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const __m128 divby128 = _mm_set1_ps(DIVBY128);
|
||||
while (i >= 16) { /* 16 * 8-bit */
|
||||
const __m128i bytes = _mm_load_si128(mmsrc); /* get 16 sint8 into an XMM register. */
|
||||
/* treat as int16, shift left to clear every other sint16, then back right with sign-extend. Now sint16. */
|
||||
const __m128i shorts1 = _mm_srai_epi16(_mm_slli_epi16(bytes, 8), 8);
|
||||
/* right-shift-sign-extend gets us sint16 with the other set of values. */
|
||||
const __m128i shorts2 = _mm_srai_epi16(bytes, 8);
|
||||
/* unpack against zero to make these int32, shift to make them sign-extend, convert to float, multiply. Whew! */
|
||||
const __m128 floats1 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpacklo_epi16(shorts1, zero), 16), 16)), divby128);
|
||||
const __m128 floats2 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpacklo_epi16(shorts2, zero), 16), 16)), divby128);
|
||||
const __m128 floats3 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpackhi_epi16(shorts1, zero), 16), 16)), divby128);
|
||||
const __m128 floats4 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpackhi_epi16(shorts2, zero), 16), 16)), divby128);
|
||||
/* Interleave back into correct order, store. */
|
||||
_mm_store_ps(dst, _mm_unpacklo_ps(floats1, floats2));
|
||||
_mm_store_ps(dst + 4, _mm_unpackhi_ps(floats1, floats2));
|
||||
_mm_store_ps(dst + 8, _mm_unpacklo_ps(floats3, floats4));
|
||||
_mm_store_ps(dst + 12, _mm_unpackhi_ps(floats3, floats4));
|
||||
i -= 16;
|
||||
mmsrc--;
|
||||
dst -= 16;
|
||||
}
|
||||
|
||||
src = (const Sint8 *)mmsrc;
|
||||
}
|
||||
|
||||
src += 15;
|
||||
dst += 15; /* adjust for any scalar finishing. */
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
*dst = ((float)*src) * DIVBY128;
|
||||
i--;
|
||||
src--;
|
||||
dst--;
|
||||
--i;
|
||||
_mm_store_ss(&dst[i], _mm_add_ss(_mm_castsi128_ps(_mm_cvtsi32_si128((Uint8)src[i] ^ 0x47800080u)), offset));
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_U8_to_F32_SSE2(float *dst, const Uint8 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
int i = num_samples;
|
||||
|
||||
/* 1) Construct a float in the range [65536.0, 65538.0)
|
||||
* 2) Shift the float range to [-1.0, 1.0)
|
||||
* dst[i] = i2f(src[i] | 0x47800000) - 65537.0 */
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const __m128i caster = _mm_set1_epi16(0x4780 /* 0x47800000 = f2i(65536.0) */);
|
||||
const __m128 offset = _mm_set1_ps(-65537.0);
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("U8", "F32 (using SSE2)");
|
||||
|
||||
src += num_samples - 1;
|
||||
dst += num_samples - 1;
|
||||
while (i >= 16) {
|
||||
i -= 16;
|
||||
|
||||
/* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
|
||||
for (i = num_samples; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
|
||||
*dst = (((float)*src) * DIVBY128) - 1.0f;
|
||||
const __m128i bytes = _mm_loadu_si128((const __m128i *)&src[i]);
|
||||
|
||||
const __m128i shorts1 = _mm_unpacklo_epi8(bytes, zero);
|
||||
const __m128i shorts2 = _mm_unpackhi_epi8(bytes, zero);
|
||||
|
||||
const __m128 floats1 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts1, caster)), offset);
|
||||
const __m128 floats2 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts1, caster)), offset);
|
||||
const __m128 floats3 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts2, caster)), offset);
|
||||
const __m128 floats4 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts2, caster)), offset);
|
||||
|
||||
_mm_storeu_ps(&dst[i], floats1);
|
||||
_mm_storeu_ps(&dst[i + 4], floats2);
|
||||
_mm_storeu_ps(&dst[i + 8], floats3);
|
||||
_mm_storeu_ps(&dst[i + 12], floats4);
|
||||
}
|
||||
|
||||
src -= 15;
|
||||
dst -= 15; /* adjust to read SSE blocks from the start. */
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128i *mmsrc = (const __m128i *)src;
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const __m128 divby128 = _mm_set1_ps(DIVBY128);
|
||||
const __m128 minus1 = _mm_set1_ps(-1.0f);
|
||||
while (i >= 16) { /* 16 * 8-bit */
|
||||
const __m128i bytes = _mm_load_si128(mmsrc); /* get 16 uint8 into an XMM register. */
|
||||
/* treat as int16, shift left to clear every other sint16, then back right with zero-extend. Now uint16. */
|
||||
const __m128i shorts1 = _mm_srli_epi16(_mm_slli_epi16(bytes, 8), 8);
|
||||
/* right-shift-zero-extend gets us uint16 with the other set of values. */
|
||||
const __m128i shorts2 = _mm_srli_epi16(bytes, 8);
|
||||
/* unpack against zero to make these int32, convert to float, multiply, add. Whew! */
|
||||
/* Note that AVX2 can do floating point multiply+add in one instruction, fwiw. SSE2 cannot. */
|
||||
const __m128 floats1 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(shorts1, zero)), divby128), minus1);
|
||||
const __m128 floats2 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(shorts2, zero)), divby128), minus1);
|
||||
const __m128 floats3 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(shorts1, zero)), divby128), minus1);
|
||||
const __m128 floats4 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(shorts2, zero)), divby128), minus1);
|
||||
/* Interleave back into correct order, store. */
|
||||
_mm_store_ps(dst, _mm_unpacklo_ps(floats1, floats2));
|
||||
_mm_store_ps(dst + 4, _mm_unpackhi_ps(floats1, floats2));
|
||||
_mm_store_ps(dst + 8, _mm_unpacklo_ps(floats3, floats4));
|
||||
_mm_store_ps(dst + 12, _mm_unpackhi_ps(floats3, floats4));
|
||||
i -= 16;
|
||||
mmsrc--;
|
||||
dst -= 16;
|
||||
}
|
||||
|
||||
src = (const Uint8 *)mmsrc;
|
||||
}
|
||||
|
||||
src += 15;
|
||||
dst += 15; /* adjust for any scalar finishing. */
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
*dst = (((float)*src) * DIVBY128) - 1.0f;
|
||||
i--;
|
||||
src--;
|
||||
dst--;
|
||||
--i;
|
||||
_mm_store_ss(&dst[i], _mm_add_ss(_mm_castsi128_ps(_mm_cvtsi32_si128((Uint8)src[i] ^ 0x47800000u)), offset));
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_S16_to_F32_SSE2(float *dst, const Sint16 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
int i = num_samples;
|
||||
|
||||
/* 1) Flip the sign bit to convert from S16 to U16 format
|
||||
* 2) Construct a float in the range [256.0, 258.0)
|
||||
* 3) Shift the float range to [-1.0, 1.0)
|
||||
* dst[i] = i2f((src[i] ^ 0x8000) | 0x43800000) - 257.0 */
|
||||
const __m128i flipper = _mm_set1_epi16(-0x8000);
|
||||
const __m128i caster = _mm_set1_epi16(0x4380 /* 0x43800000 = f2i(256.0) */);
|
||||
const __m128 offset = _mm_set1_ps(-257.0f);
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("S16", "F32 (using SSE2)");
|
||||
|
||||
src += num_samples - 1;
|
||||
dst += num_samples - 1;
|
||||
while (i >= 16) {
|
||||
i -= 16;
|
||||
|
||||
/* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
|
||||
for (i = num_samples; i && (((size_t)(dst - 7)) & 15); --i, --src, --dst) {
|
||||
*dst = ((float)*src) * DIVBY32768;
|
||||
const __m128i shorts1 = _mm_xor_si128(_mm_loadu_si128((const __m128i *)&src[i]), flipper);
|
||||
const __m128i shorts2 = _mm_xor_si128(_mm_loadu_si128((const __m128i *)&src[i + 8]), flipper);
|
||||
|
||||
const __m128 floats1 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts1, caster)), offset);
|
||||
const __m128 floats2 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts1, caster)), offset);
|
||||
const __m128 floats3 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts2, caster)), offset);
|
||||
const __m128 floats4 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts2, caster)), offset);
|
||||
|
||||
_mm_storeu_ps(&dst[i], floats1);
|
||||
_mm_storeu_ps(&dst[i + 4], floats2);
|
||||
_mm_storeu_ps(&dst[i + 8], floats3);
|
||||
_mm_storeu_ps(&dst[i + 12], floats4);
|
||||
}
|
||||
|
||||
src -= 7;
|
||||
dst -= 7; /* adjust to read SSE blocks from the start. */
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128 divby32768 = _mm_set1_ps(DIVBY32768);
|
||||
while (i >= 8) { /* 8 * 16-bit */
|
||||
const __m128i ints = _mm_load_si128((__m128i const *)src); /* get 8 sint16 into an XMM register. */
|
||||
/* treat as int32, shift left to clear every other sint16, then back right with sign-extend. Now sint32. */
|
||||
const __m128i a = _mm_srai_epi32(_mm_slli_epi32(ints, 16), 16);
|
||||
/* right-shift-sign-extend gets us sint32 with the other set of values. */
|
||||
const __m128i b = _mm_srai_epi32(ints, 16);
|
||||
/* Interleave these back into the right order, convert to float, multiply, store. */
|
||||
_mm_store_ps(dst, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi32(a, b)), divby32768));
|
||||
_mm_store_ps(dst + 4, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi32(a, b)), divby32768));
|
||||
i -= 8;
|
||||
src -= 8;
|
||||
dst -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
src += 7;
|
||||
dst += 7; /* adjust for any scalar finishing. */
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
*dst = ((float)*src) * DIVBY32768;
|
||||
i--;
|
||||
src--;
|
||||
dst--;
|
||||
--i;
|
||||
_mm_store_ss(&dst[i], _mm_add_ss(_mm_castsi128_ps(_mm_cvtsi32_si128((Uint16)src[i] ^ 0x43808000u)), offset));
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_S32_to_F32_SSE2(float *dst, const Sint32 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
int i = num_samples;
|
||||
|
||||
/* dst[i] = f32(src[i]) / f32(0x80000000) */
|
||||
const __m128 scaler = _mm_set1_ps(DIVBY2147483648);
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("S32", "F32 (using SSE2)");
|
||||
|
||||
/* Get dst aligned to 16 bytes */
|
||||
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
|
||||
*dst = ((float)(*src >> 8)) * DIVBY8388607;
|
||||
while (i >= 16) {
|
||||
i -= 16;
|
||||
|
||||
const __m128i ints1 = _mm_loadu_si128((const __m128i *)&src[i]);
|
||||
const __m128i ints2 = _mm_loadu_si128((const __m128i *)&src[i + 4]);
|
||||
const __m128i ints3 = _mm_loadu_si128((const __m128i *)&src[i + 8]);
|
||||
const __m128i ints4 = _mm_loadu_si128((const __m128i *)&src[i + 12]);
|
||||
|
||||
const __m128 floats1 = _mm_mul_ps(_mm_cvtepi32_ps(ints1), scaler);
|
||||
const __m128 floats2 = _mm_mul_ps(_mm_cvtepi32_ps(ints2), scaler);
|
||||
const __m128 floats3 = _mm_mul_ps(_mm_cvtepi32_ps(ints3), scaler);
|
||||
const __m128 floats4 = _mm_mul_ps(_mm_cvtepi32_ps(ints4), scaler);
|
||||
|
||||
_mm_storeu_ps(&dst[i], floats1);
|
||||
_mm_storeu_ps(&dst[i + 4], floats2);
|
||||
_mm_storeu_ps(&dst[i + 8], floats3);
|
||||
_mm_storeu_ps(&dst[i + 12], floats4);
|
||||
}
|
||||
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128 divby8388607 = _mm_set1_ps(DIVBY8388607);
|
||||
const __m128i *mmsrc = (const __m128i *)src;
|
||||
while (i >= 4) { /* 4 * sint32 */
|
||||
/* shift out lowest bits so int fits in a float32. Small precision loss, but much faster. */
|
||||
_mm_store_ps(dst, _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_load_si128(mmsrc), 8)), divby8388607));
|
||||
i -= 4;
|
||||
mmsrc++;
|
||||
dst += 4;
|
||||
}
|
||||
src = (const Sint32 *)mmsrc;
|
||||
}
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
*dst = ((float)(*src >> 8)) * DIVBY8388607;
|
||||
i--;
|
||||
src++;
|
||||
dst++;
|
||||
--i;
|
||||
_mm_store_ss(&dst[i], _mm_mul_ss(_mm_cvt_si2ss(_mm_setzero_ps(), src[i]), scaler));
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S8_SSE2(Sint8 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
int i = num_samples;
|
||||
|
||||
/* 1) Shift the float range from [-1.0, 1.0] to [98303.0, 98305.0]
|
||||
* 2) Extract the lowest 16 bits and clamp to [-128, 127]
|
||||
* Overflow is correctly handled for inputs between roughly [-255.0, 255.0]
|
||||
* dst[i] = clamp(i16(f2i(src[i] + 98304.0) & 0xFFFF), -128, 127) */
|
||||
const __m128 offset = _mm_set1_ps(98304.0f);
|
||||
const __m128i mask = _mm_set1_epi16(0xFF);
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "S8 (using SSE2)");
|
||||
|
||||
/* Get dst aligned to 16 bytes */
|
||||
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 127;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = -128;
|
||||
} else {
|
||||
*dst = (Sint8)(sample * 127.0f);
|
||||
}
|
||||
while (i >= 16) {
|
||||
const __m128 floats1 = _mm_loadu_ps(&src[0]);
|
||||
const __m128 floats2 = _mm_loadu_ps(&src[4]);
|
||||
const __m128 floats3 = _mm_loadu_ps(&src[8]);
|
||||
const __m128 floats4 = _mm_loadu_ps(&src[12]);
|
||||
|
||||
const __m128i ints1 = _mm_castps_si128(_mm_add_ps(floats1, offset));
|
||||
const __m128i ints2 = _mm_castps_si128(_mm_add_ps(floats2, offset));
|
||||
const __m128i ints3 = _mm_castps_si128(_mm_add_ps(floats3, offset));
|
||||
const __m128i ints4 = _mm_castps_si128(_mm_add_ps(floats4, offset));
|
||||
|
||||
const __m128i shorts1 = _mm_and_si128(_mm_packs_epi16(ints1, ints2), mask);
|
||||
const __m128i shorts2 = _mm_and_si128(_mm_packs_epi16(ints3, ints4), mask);
|
||||
|
||||
const __m128i bytes = _mm_packus_epi16(shorts1, shorts2);
|
||||
|
||||
_mm_storeu_si128((__m128i*)dst, bytes);
|
||||
|
||||
i -= 16;
|
||||
src += 16;
|
||||
dst += 16;
|
||||
}
|
||||
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128 one = _mm_set1_ps(1.0f);
|
||||
const __m128 negone = _mm_set1_ps(-1.0f);
|
||||
const __m128 mulby127 = _mm_set1_ps(127.0f);
|
||||
__m128i *mmdst = (__m128i *)dst;
|
||||
while (i >= 16) { /* 16 * float32 */
|
||||
const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 4)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 8)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 12)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
_mm_store_si128(mmdst, _mm_packs_epi16(_mm_packs_epi32(ints1, ints2), _mm_packs_epi32(ints3, ints4))); /* pack down, store out. */
|
||||
i -= 16;
|
||||
src += 16;
|
||||
mmdst++;
|
||||
}
|
||||
dst = (Sint8 *)mmdst;
|
||||
}
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 127;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = -128;
|
||||
} else {
|
||||
*dst = (Sint8)(sample * 127.0f);
|
||||
}
|
||||
i--;
|
||||
src++;
|
||||
dst++;
|
||||
const __m128i ints = _mm_castps_si128(_mm_add_ss(_mm_load_ss(src), offset));
|
||||
*dst = (Sint8)(_mm_cvtsi128_si32(_mm_packs_epi16(ints, ints)) & 0xFF);
|
||||
|
||||
--i;
|
||||
++src;
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_F32_to_U8_SSE2(Uint8 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
int i = num_samples;
|
||||
|
||||
/* 1) Shift the float range from [-1.0, 1.0] to [98304.0, 98306.0]
|
||||
* 2) Extract the lowest 16 bits and clamp to [0, 255]
|
||||
* Overflow is correctly handled for inputs between roughly [-254.0, 254.0]
|
||||
* dst[i] = clamp(i16(f2i(src[i] + 98305.0) & 0xFFFF), 0, 255) */
|
||||
const __m128 offset = _mm_set1_ps(98305.0f);
|
||||
const __m128i mask = _mm_set1_epi16(0xFF);
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "U8 (using SSE2)");
|
||||
|
||||
/* Get dst aligned to 16 bytes */
|
||||
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 255;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = 0;
|
||||
} else {
|
||||
*dst = (Uint8)((sample + 1.0f) * 127.0f);
|
||||
}
|
||||
while (i >= 16) {
|
||||
const __m128 floats1 = _mm_loadu_ps(&src[0]);
|
||||
const __m128 floats2 = _mm_loadu_ps(&src[4]);
|
||||
const __m128 floats3 = _mm_loadu_ps(&src[8]);
|
||||
const __m128 floats4 = _mm_loadu_ps(&src[12]);
|
||||
|
||||
const __m128i ints1 = _mm_castps_si128(_mm_add_ps(floats1, offset));
|
||||
const __m128i ints2 = _mm_castps_si128(_mm_add_ps(floats2, offset));
|
||||
const __m128i ints3 = _mm_castps_si128(_mm_add_ps(floats3, offset));
|
||||
const __m128i ints4 = _mm_castps_si128(_mm_add_ps(floats4, offset));
|
||||
|
||||
const __m128i shorts1 = _mm_and_si128(_mm_packus_epi16(ints1, ints2), mask);
|
||||
const __m128i shorts2 = _mm_and_si128(_mm_packus_epi16(ints3, ints4), mask);
|
||||
|
||||
const __m128i bytes = _mm_packus_epi16(shorts1, shorts2);
|
||||
|
||||
_mm_storeu_si128((__m128i*)dst, bytes);
|
||||
|
||||
i -= 16;
|
||||
src += 16;
|
||||
dst += 16;
|
||||
}
|
||||
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128 one = _mm_set1_ps(1.0f);
|
||||
const __m128 negone = _mm_set1_ps(-1.0f);
|
||||
const __m128 mulby127 = _mm_set1_ps(127.0f);
|
||||
__m128i *mmdst = (__m128i *)dst;
|
||||
while (i >= 16) { /* 16 * float32 */
|
||||
const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 4)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 8)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 12)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
_mm_store_si128(mmdst, _mm_packus_epi16(_mm_packs_epi32(ints1, ints2), _mm_packs_epi32(ints3, ints4))); /* pack down, store out. */
|
||||
i -= 16;
|
||||
src += 16;
|
||||
mmdst++;
|
||||
}
|
||||
dst = (Uint8 *)mmdst;
|
||||
}
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 255;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = 0;
|
||||
} else {
|
||||
*dst = (Uint8)((sample + 1.0f) * 127.0f);
|
||||
}
|
||||
i--;
|
||||
src++;
|
||||
dst++;
|
||||
const __m128i ints = _mm_castps_si128(_mm_add_ss(_mm_load_ss(src), offset));
|
||||
*dst = (Uint8)(_mm_cvtsi128_si32(_mm_packus_epi16(ints, ints)) & 0xFF);
|
||||
|
||||
--i;
|
||||
++src;
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S16_SSE2(Sint16 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
int i = num_samples;
|
||||
|
||||
/* 1) Shift the float range from [-1.0, 1.0] to [256.0, 258.0]
|
||||
* 2) Shift the int range from [0x43800000, 0x43810000] to [-32768,32768]
|
||||
* 3) Clamp to range [-32768,32767]
|
||||
* Overflow is correctly handled for inputs between roughly [-257.0, +inf)
|
||||
* dst[i] = clamp(f2i(src[i] + 257.0) - 0x43808000, -32768, 32767) */
|
||||
const __m128 offset = _mm_set1_ps(257.0f);
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "S16 (using SSE2)");
|
||||
|
||||
/* Get dst aligned to 16 bytes */
|
||||
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 32767;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = -32768;
|
||||
} else {
|
||||
*dst = (Sint16)(sample * 32767.0f);
|
||||
}
|
||||
while (i >= 16) {
|
||||
const __m128 floats1 = _mm_loadu_ps(&src[0]);
|
||||
const __m128 floats2 = _mm_loadu_ps(&src[4]);
|
||||
const __m128 floats3 = _mm_loadu_ps(&src[8]);
|
||||
const __m128 floats4 = _mm_loadu_ps(&src[12]);
|
||||
|
||||
const __m128i ints1 = _mm_sub_epi32(_mm_castps_si128(_mm_add_ps(floats1, offset)), _mm_castps_si128(offset));
|
||||
const __m128i ints2 = _mm_sub_epi32(_mm_castps_si128(_mm_add_ps(floats2, offset)), _mm_castps_si128(offset));
|
||||
const __m128i ints3 = _mm_sub_epi32(_mm_castps_si128(_mm_add_ps(floats3, offset)), _mm_castps_si128(offset));
|
||||
const __m128i ints4 = _mm_sub_epi32(_mm_castps_si128(_mm_add_ps(floats4, offset)), _mm_castps_si128(offset));
|
||||
|
||||
const __m128i shorts1 = _mm_packs_epi32(ints1, ints2);
|
||||
const __m128i shorts2 = _mm_packs_epi32(ints3, ints4);
|
||||
|
||||
_mm_storeu_si128((__m128i*)&dst[0], shorts1);
|
||||
_mm_storeu_si128((__m128i*)&dst[8], shorts2);
|
||||
|
||||
i -= 16;
|
||||
src += 16;
|
||||
dst += 16;
|
||||
}
|
||||
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128 one = _mm_set1_ps(1.0f);
|
||||
const __m128 negone = _mm_set1_ps(-1.0f);
|
||||
const __m128 mulby32767 = _mm_set1_ps(32767.0f);
|
||||
__m128i *mmdst = (__m128i *)dst;
|
||||
while (i >= 8) { /* 8 * float32 */
|
||||
const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby32767)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 4)), one), mulby32767)); /* load 4 floats, clamp, convert to sint32 */
|
||||
_mm_store_si128(mmdst, _mm_packs_epi32(ints1, ints2)); /* pack to sint16, store out. */
|
||||
i -= 8;
|
||||
src += 8;
|
||||
mmdst++;
|
||||
}
|
||||
dst = (Sint16 *)mmdst;
|
||||
}
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 32767;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = -32768;
|
||||
} else {
|
||||
*dst = (Sint16)(sample * 32767.0f);
|
||||
}
|
||||
i--;
|
||||
src++;
|
||||
dst++;
|
||||
const __m128i ints = _mm_sub_epi32(_mm_castps_si128(_mm_add_ss(_mm_load_ss(src), offset)), _mm_castps_si128(offset));
|
||||
*dst = (Sint16)(_mm_cvtsi128_si32(_mm_packs_epi32(ints, ints)) & 0xFFFF);
|
||||
|
||||
--i;
|
||||
++src;
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S32_SSE2(Sint32 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
int i = num_samples;
|
||||
|
||||
/* 1) Scale the float range from [-1.0, 1.0] to [-2147483648.0, 2147483648.0]
|
||||
* 2) Convert to integer (values too small/large become 0x80000000 = -2147483648)
|
||||
* 3) Fixup values which were too large (0x80000000 ^ 0xFFFFFFFF = 2147483647)
|
||||
* dst[i] = i32(src[i] * 2147483648.0) ^ ((src[i] >= 2147483648.0) ? 0xFFFFFFFF : 0x00000000) */
|
||||
const __m128 limit = _mm_set1_ps(2147483648.0f);
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "S32 (using SSE2)");
|
||||
|
||||
/* Get dst aligned to 16 bytes */
|
||||
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 2147483647;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = (Sint32)-2147483648LL;
|
||||
} else {
|
||||
*dst = ((Sint32)(sample * 8388607.0f)) << 8;
|
||||
}
|
||||
while (i >= 16) {
|
||||
const __m128 floats1 = _mm_loadu_ps(&src[0]);
|
||||
const __m128 floats2 = _mm_loadu_ps(&src[4]);
|
||||
const __m128 floats3 = _mm_loadu_ps(&src[8]);
|
||||
const __m128 floats4 = _mm_loadu_ps(&src[12]);
|
||||
|
||||
const __m128 values1 = _mm_mul_ps(floats1, limit);
|
||||
const __m128 values2 = _mm_mul_ps(floats2, limit);
|
||||
const __m128 values3 = _mm_mul_ps(floats3, limit);
|
||||
const __m128 values4 = _mm_mul_ps(floats4, limit);
|
||||
|
||||
const __m128i ints1 = _mm_xor_si128(_mm_cvttps_epi32(values1), _mm_castps_si128(_mm_cmpge_ps(values1, limit)));
|
||||
const __m128i ints2 = _mm_xor_si128(_mm_cvttps_epi32(values2), _mm_castps_si128(_mm_cmpge_ps(values2, limit)));
|
||||
const __m128i ints3 = _mm_xor_si128(_mm_cvttps_epi32(values3), _mm_castps_si128(_mm_cmpge_ps(values3, limit)));
|
||||
const __m128i ints4 = _mm_xor_si128(_mm_cvttps_epi32(values4), _mm_castps_si128(_mm_cmpge_ps(values4, limit)));
|
||||
|
||||
_mm_storeu_si128((__m128i*)&dst[0], ints1);
|
||||
_mm_storeu_si128((__m128i*)&dst[4], ints2);
|
||||
_mm_storeu_si128((__m128i*)&dst[8], ints3);
|
||||
_mm_storeu_si128((__m128i*)&dst[12], ints4);
|
||||
|
||||
i -= 16;
|
||||
src += 16;
|
||||
dst += 16;
|
||||
}
|
||||
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
SDL_assert(!i || !(((size_t)src) & 15));
|
||||
|
||||
{
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128 one = _mm_set1_ps(1.0f);
|
||||
const __m128 negone = _mm_set1_ps(-1.0f);
|
||||
const __m128 mulby8388607 = _mm_set1_ps(8388607.0f);
|
||||
__m128i *mmdst = (__m128i *)dst;
|
||||
while (i >= 4) { /* 4 * float32 */
|
||||
_mm_store_si128(mmdst, _mm_slli_epi32(_mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby8388607)), 8)); /* load 4 floats, clamp, convert to sint32 */
|
||||
i -= 4;
|
||||
src += 4;
|
||||
mmdst++;
|
||||
}
|
||||
dst = (Sint32 *)mmdst;
|
||||
}
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 2147483647;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = (Sint32)-2147483648LL;
|
||||
} else {
|
||||
*dst = ((Sint32)(sample * 8388607.0f)) << 8;
|
||||
}
|
||||
i--;
|
||||
src++;
|
||||
dst++;
|
||||
const __m128 floats = _mm_load_ss(src);
|
||||
const __m128 values = _mm_mul_ss(floats, limit);
|
||||
const __m128i ints = _mm_xor_si128(_mm_cvttps_epi32(values), _mm_castps_si128(_mm_cmpge_ss(values, limit)));
|
||||
*dst = (Sint32)_mm_cvtsi128_si32(ints);
|
||||
|
||||
--i;
|
||||
++src;
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SDL_NEON_INTRINSICS
|
||||
#define DIVBY128 0.0078125f /* 0x1p-7f */
|
||||
#define DIVBY32768 0.000030517578125f /* 0x1p-15f */
|
||||
#define DIVBY8388607 0.00000011920930376163766f /* 0x1.000002p-23f */
|
||||
|
||||
static void SDL_Convert_S8_to_F32_NEON(float *dst, const Sint8 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
@ -131,7 +131,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_S16LSB:
|
||||
case SDL_AUDIO_S16LE:
|
||||
{
|
||||
Sint16 src1, src2;
|
||||
int dst_sample;
|
||||
@ -155,7 +155,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_S16MSB:
|
||||
case SDL_AUDIO_S16BE:
|
||||
{
|
||||
Sint16 src1, src2;
|
||||
int dst_sample;
|
||||
@ -179,7 +179,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_S32LSB:
|
||||
case SDL_AUDIO_S32LE:
|
||||
{
|
||||
const Uint32 *src32 = (Uint32 *)src;
|
||||
Uint32 *dst32 = (Uint32 *)dst;
|
||||
@ -204,7 +204,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_S32MSB:
|
||||
case SDL_AUDIO_S32BE:
|
||||
{
|
||||
const Uint32 *src32 = (Uint32 *)src;
|
||||
Uint32 *dst32 = (Uint32 *)dst;
|
||||
@ -229,7 +229,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_F32LSB:
|
||||
case SDL_AUDIO_F32LE:
|
||||
{
|
||||
const float fmaxvolume = 1.0f / ((float)SDL_MIX_MAXVOLUME);
|
||||
const float fvolume = (float)volume;
|
||||
@ -257,7 +257,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_F32MSB:
|
||||
case SDL_AUDIO_F32BE:
|
||||
{
|
||||
const float fmaxvolume = 1.0f / ((float)SDL_MIX_MAXVOLUME);
|
||||
const float fvolume = (float)volume;
|
||||
|
@ -24,8 +24,6 @@
|
||||
#ifndef SDL_sysaudio_h_
|
||||
#define SDL_sysaudio_h_
|
||||
|
||||
#include "../SDL_dataqueue.h"
|
||||
|
||||
#define DEBUG_AUDIOSTREAM 0
|
||||
#define DEBUG_AUDIO_CONVERT 0
|
||||
|
||||
@ -58,6 +56,8 @@ extern void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_sam
|
||||
#define DEFAULT_AUDIO_CAPTURE_CHANNELS 1
|
||||
#define DEFAULT_AUDIO_CAPTURE_FREQUENCY 44100
|
||||
|
||||
#define AUDIO_SPECS_EQUAL(x, y) (((x).format == (y).format) && ((x).channels == (y).channels) && ((x).freq == (y).freq))
|
||||
|
||||
typedef struct SDL_AudioDevice SDL_AudioDevice;
|
||||
typedef struct SDL_LogicalAudioDevice SDL_LogicalAudioDevice;
|
||||
|
||||
@ -70,8 +70,9 @@ extern void SDL_QuitAudio(void);
|
||||
// Function to get a list of audio formats, ordered most similar to `format` to least, 0-terminated. Don't free results.
|
||||
const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format);
|
||||
|
||||
// Must be called at least once before using converters (SDL_CreateAudioStream will call it !!! FIXME but probably shouldn't).
|
||||
// Must be called at least once before using converters.
|
||||
extern void SDL_ChooseAudioConverters(void);
|
||||
extern void SDL_SetupAudioResampler(void);
|
||||
|
||||
/* Backends should call this as devices are added to the system (such as
|
||||
a USB headset being plugged in), and should also be called for
|
||||
@ -101,7 +102,7 @@ extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByCallback(SDL_bool (*callbac
|
||||
extern void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device);
|
||||
|
||||
// Backends can call this to get a standardized name for a thread to power a specific audio device.
|
||||
char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen);
|
||||
extern char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen);
|
||||
|
||||
|
||||
// These functions are the heart of the audio threads. Backends can call them directly if they aren't using the SDL-provided thread.
|
||||
@ -113,6 +114,14 @@ extern SDL_bool SDL_CaptureAudioThreadIterate(SDL_AudioDevice *device);
|
||||
extern void SDL_CaptureAudioThreadShutdown(SDL_AudioDevice *device);
|
||||
extern void SDL_AudioThreadFinalize(SDL_AudioDevice *device);
|
||||
|
||||
// this gets used from the audio device threads. It has rules, don't use this if you don't know how to use it!
|
||||
extern void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_format, int src_channels,
|
||||
void *dst, SDL_AudioFormat dst_format, int dst_channels, void* scratch);
|
||||
|
||||
// Special case to let something in SDL_audiocvt.c access something in SDL_audio.c. Don't use this.
|
||||
extern void OnAudioStreamCreated(SDL_AudioStream *stream);
|
||||
extern void OnAudioStreamDestroy(SDL_AudioStream *stream);
|
||||
|
||||
typedef struct SDL_AudioDriverImpl
|
||||
{
|
||||
void (*DetectDevices)(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
|
||||
@ -120,7 +129,7 @@ typedef struct SDL_AudioDriverImpl
|
||||
void (*ThreadInit)(SDL_AudioDevice *device); // Called by audio thread at start
|
||||
void (*ThreadDeinit)(SDL_AudioDevice *device); // Called by audio thread at end
|
||||
void (*WaitDevice)(SDL_AudioDevice *device);
|
||||
void (*PlayDevice)(SDL_AudioDevice *device, const Uint8 *buffer, int buflen); // buffer and buflen are always from GetDeviceBuf, passed here for convenience.
|
||||
int (*PlayDevice)(SDL_AudioDevice *device, const Uint8 *buffer, int buflen); // buffer and buflen are always from GetDeviceBuf, passed here for convenience.
|
||||
Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *device, int *buffer_size);
|
||||
void (*WaitCaptureDevice)(SDL_AudioDevice *device);
|
||||
int (*CaptureFromDevice)(SDL_AudioDevice *device, void *buffer, int buflen);
|
||||
@ -145,6 +154,7 @@ typedef struct SDL_AudioDriver
|
||||
SDL_RWLock *device_list_lock; // A mutex for device detection
|
||||
SDL_AudioDevice *output_devices; // the list of currently-available audio output devices.
|
||||
SDL_AudioDevice *capture_devices; // the list of currently-available audio capture devices.
|
||||
SDL_AudioStream *existing_streams; // a list of all existing SDL_AudioStreams.
|
||||
SDL_AudioDeviceID default_output_device_id;
|
||||
SDL_AudioDeviceID default_capture_device_id;
|
||||
SDL_AtomicInt output_device_count;
|
||||
@ -153,46 +163,41 @@ typedef struct SDL_AudioDriver
|
||||
SDL_AtomicInt shutting_down; // non-zero during SDL_Quit, so we known not to accept any last-minute device hotplugs.
|
||||
} SDL_AudioDriver;
|
||||
|
||||
struct SDL_AudioQueue; // forward decl.
|
||||
|
||||
struct SDL_AudioStream
|
||||
{
|
||||
SDL_DataQueue *queue;
|
||||
SDL_Mutex *lock; // this is just a copy of `queue`'s mutex. We share a lock.
|
||||
SDL_Mutex* lock;
|
||||
|
||||
SDL_AudioStreamRequestCallback get_callback;
|
||||
SDL_AudioStreamCallback get_callback;
|
||||
void *get_callback_userdata;
|
||||
SDL_AudioStreamRequestCallback put_callback;
|
||||
SDL_AudioStreamCallback put_callback;
|
||||
void *put_callback_userdata;
|
||||
|
||||
Uint8 *work_buffer; // used for scratch space during data conversion/resampling.
|
||||
Uint8 *history_buffer; // history for left padding and future sample rate changes.
|
||||
Uint8 *future_buffer; // stuff that left the queue for the right padding and will be next read's data.
|
||||
float *left_padding; // left padding for resampling.
|
||||
float *right_padding; // right padding for resampling.
|
||||
|
||||
SDL_bool flushed;
|
||||
|
||||
size_t work_buffer_allocation;
|
||||
size_t history_buffer_allocation;
|
||||
size_t future_buffer_allocation;
|
||||
size_t resampler_padding_allocation;
|
||||
|
||||
int resampler_padding_frames;
|
||||
int history_buffer_frames;
|
||||
int future_buffer_filled_frames;
|
||||
|
||||
SDL_AudioSpec src_spec;
|
||||
SDL_AudioSpec dst_spec;
|
||||
float freq_ratio;
|
||||
|
||||
int src_sample_frame_size;
|
||||
int dst_sample_frame_size;
|
||||
int max_sample_frame_size;
|
||||
struct SDL_AudioQueue* queue;
|
||||
Uint64 total_bytes_queued;
|
||||
|
||||
int pre_resample_channels;
|
||||
int packetlen;
|
||||
SDL_AudioSpec input_spec; // The spec of input data currently being processed
|
||||
Sint64 resample_offset;
|
||||
|
||||
Uint8 *work_buffer; // used for scratch space during data conversion/resampling.
|
||||
size_t work_buffer_allocation;
|
||||
|
||||
Uint8 *history_buffer; // history for left padding and future sample rate changes.
|
||||
size_t history_buffer_allocation;
|
||||
|
||||
SDL_bool simplified; // SDL_TRUE if created via SDL_OpenAudioDeviceStream
|
||||
|
||||
SDL_LogicalAudioDevice *bound_device;
|
||||
SDL_AudioStream *next_binding;
|
||||
SDL_AudioStream *prev_binding;
|
||||
|
||||
SDL_AudioStream *prev; // linked list of all existing streams (so we can free them on shutdown).
|
||||
SDL_AudioStream *next; // linked list of all existing streams (so we can free them on shutdown).
|
||||
};
|
||||
|
||||
/* Logical devices are an abstraction in SDL3; you can open the same physical
|
||||
@ -214,7 +219,16 @@ struct SDL_LogicalAudioDevice
|
||||
SDL_AudioStream *bound_streams;
|
||||
|
||||
// SDL_TRUE if this was opened as a default device.
|
||||
SDL_bool is_default;
|
||||
SDL_bool opened_as_default;
|
||||
|
||||
// SDL_TRUE if device was opened with SDL_OpenAudioDeviceStream (so it forbids binding changes, etc).
|
||||
SDL_bool simplified;
|
||||
|
||||
// If non-NULL, callback into the app that lets them access the final postmix buffer.
|
||||
SDL_AudioPostmixCallback postmix;
|
||||
|
||||
// App-supplied pointer for postmix callback.
|
||||
void *postmix_userdata;
|
||||
|
||||
// double-linked list of opened devices on the same physical device.
|
||||
SDL_LogicalAudioDevice *next;
|
||||
@ -263,14 +277,22 @@ struct SDL_AudioDevice
|
||||
// SDL_TRUE if this is a capture device instead of an output device
|
||||
SDL_bool iscapture;
|
||||
|
||||
// Scratch buffer used for mixing.
|
||||
// SDL_TRUE if audio thread can skip silence/mix/convert stages and just do a basic memcpy.
|
||||
SDL_bool simple_copy;
|
||||
|
||||
// Scratch buffers used for mixing.
|
||||
Uint8 *work_buffer;
|
||||
Uint8 *mix_buffer;
|
||||
float *postmix_buffer;
|
||||
|
||||
// Size of work_buffer (and mix_buffer) in bytes.
|
||||
int work_buffer_size;
|
||||
|
||||
// A thread to feed the audio device
|
||||
SDL_Thread *thread;
|
||||
|
||||
// SDL_TRUE if this physical device is currently opened by the backend.
|
||||
SDL_bool is_opened;
|
||||
SDL_bool currently_opened;
|
||||
|
||||
// Data private to this driver
|
||||
struct SDL_PrivateAudioData *hidden;
|
||||
@ -316,7 +338,4 @@ extern AudioBootStrap N3DSAUDIO_bootstrap;
|
||||
extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap;
|
||||
extern AudioBootStrap QSAAUDIO_bootstrap;
|
||||
|
||||
extern SDL_AudioDevice *get_audio_dev(SDL_AudioDeviceID id);
|
||||
extern int get_max_num_audio_dev(void);
|
||||
|
||||
#endif // SDL_sysaudio_h_
|
||||
|
@ -2039,10 +2039,10 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 *
|
||||
case ALAW_CODE:
|
||||
case MULAW_CODE:
|
||||
/* These can be easily stored in the byte order of the system. */
|
||||
spec->format = SDL_AUDIO_S16SYS;
|
||||
spec->format = SDL_AUDIO_S16;
|
||||
break;
|
||||
case IEEE_FLOAT_CODE:
|
||||
spec->format = SDL_AUDIO_F32LSB;
|
||||
spec->format = SDL_AUDIO_F32LE;
|
||||
break;
|
||||
case PCM_CODE:
|
||||
switch (format->bitspersample) {
|
||||
@ -2050,11 +2050,11 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 *
|
||||
spec->format = SDL_AUDIO_U8;
|
||||
break;
|
||||
case 16:
|
||||
spec->format = SDL_AUDIO_S16LSB;
|
||||
spec->format = SDL_AUDIO_S16LE;
|
||||
break;
|
||||
case 24: /* Has been shifted to 32 bits. */
|
||||
case 32:
|
||||
spec->format = SDL_AUDIO_S32LSB;
|
||||
spec->format = SDL_AUDIO_S32LE;
|
||||
break;
|
||||
default:
|
||||
/* Just in case something unexpected happened in the checks. */
|
||||
|
@ -111,13 +111,14 @@ static void AAUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
SDL_WaitSemaphore(device->hidden->semaphore);
|
||||
}
|
||||
|
||||
static void AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
static int AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
// AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here.
|
||||
if (SDL_AtomicGet(&device->hidden->error_callback_triggered)) {
|
||||
SDL_AtomicSet(&device->hidden->error_callback_triggered, 0);
|
||||
SDL_AudioDeviceDisconnected(device);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// no need for a FlushCapture implementation, just don't read mixbuf until the next iteration.
|
||||
@ -197,9 +198,9 @@ static int AAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
const aaudio_direction_t direction = (iscapture ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT);
|
||||
ctx.AAudioStreamBuilder_setDirection(builder, direction);
|
||||
aaudio_format_t format;
|
||||
if ((device->spec.format == SDL_AUDIO_S32SYS) && (SDL_GetAndroidSDKVersion() >= 31)) {
|
||||
if ((device->spec.format == SDL_AUDIO_S32) && (SDL_GetAndroidSDKVersion() >= 31)) {
|
||||
format = AAUDIO_FORMAT_PCM_I32;
|
||||
} else if (device->spec.format == SDL_AUDIO_F32SYS) {
|
||||
} else if (device->spec.format == SDL_AUDIO_F32) {
|
||||
format = AAUDIO_FORMAT_PCM_FLOAT;
|
||||
} else {
|
||||
format = AAUDIO_FORMAT_PCM_I16; // sint16 is a safe bet for everything else.
|
||||
@ -244,11 +245,11 @@ static int AAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
|
||||
format = ctx.AAudioStream_getFormat(hidden->stream);
|
||||
if (format == AAUDIO_FORMAT_PCM_I16) {
|
||||
device->spec.format = SDL_AUDIO_S16SYS;
|
||||
device->spec.format = SDL_AUDIO_S16;
|
||||
} else if (format == AAUDIO_FORMAT_PCM_I32) {
|
||||
device->spec.format = SDL_AUDIO_S32SYS;
|
||||
device->spec.format = SDL_AUDIO_S32;
|
||||
} else if (format == AAUDIO_FORMAT_PCM_FLOAT) {
|
||||
device->spec.format = SDL_AUDIO_F32SYS;
|
||||
device->spec.format = SDL_AUDIO_F32;
|
||||
} else {
|
||||
return SDL_SetError("Got unexpected audio format %d from AAudioStream_getFormat", (int) format);
|
||||
}
|
||||
|
@ -351,12 +351,11 @@ static void ALSA_WaitDevice(SDL_AudioDevice *device)
|
||||
}
|
||||
}
|
||||
|
||||
static void ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
static int ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
SDL_assert(buffer == device->hidden->mixbuf);
|
||||
Uint8 *sample_buf = device->hidden->mixbuf;
|
||||
const int frame_size = ((SDL_AUDIO_BITSIZE(device->spec.format)) / 8) *
|
||||
device->spec.channels;
|
||||
const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
snd_pcm_uframes_t frames_left = (snd_pcm_uframes_t) (buflen / frame_size);
|
||||
|
||||
device->hidden->swizzle_func(device, sample_buf, frames_left);
|
||||
@ -378,8 +377,7 @@ static void ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int bu
|
||||
SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
|
||||
"ALSA write failed (unrecoverable): %s",
|
||||
ALSA_snd_strerror(status));
|
||||
SDL_AudioDeviceDisconnected(device);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
} else if (status == 0) {
|
||||
@ -391,6 +389,8 @@ static void ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int bu
|
||||
sample_buf += status * frame_size;
|
||||
frames_left -= status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
@ -401,8 +401,7 @@ static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
static int ALSA_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
Uint8 *sample_buf = (Uint8 *)buffer;
|
||||
const int frame_size = ((SDL_AUDIO_BITSIZE(device->spec.format)) / 8) *
|
||||
device->spec.channels;
|
||||
const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
const int total_frames = buflen / frame_size;
|
||||
snd_pcm_uframes_t frames_left = total_frames;
|
||||
|
||||
@ -564,22 +563,22 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device)
|
||||
case SDL_AUDIO_S8:
|
||||
format = SND_PCM_FORMAT_S8;
|
||||
break;
|
||||
case SDL_AUDIO_S16LSB:
|
||||
case SDL_AUDIO_S16LE:
|
||||
format = SND_PCM_FORMAT_S16_LE;
|
||||
break;
|
||||
case SDL_AUDIO_S16MSB:
|
||||
case SDL_AUDIO_S16BE:
|
||||
format = SND_PCM_FORMAT_S16_BE;
|
||||
break;
|
||||
case SDL_AUDIO_S32LSB:
|
||||
case SDL_AUDIO_S32LE:
|
||||
format = SND_PCM_FORMAT_S32_LE;
|
||||
break;
|
||||
case SDL_AUDIO_S32MSB:
|
||||
case SDL_AUDIO_S32BE:
|
||||
format = SND_PCM_FORMAT_S32_BE;
|
||||
break;
|
||||
case SDL_AUDIO_F32LSB:
|
||||
case SDL_AUDIO_F32LE:
|
||||
format = SND_PCM_FORMAT_FLOAT_LE;
|
||||
break;
|
||||
case SDL_AUDIO_F32MSB:
|
||||
case SDL_AUDIO_F32BE:
|
||||
format = SND_PCM_FORMAT_FLOAT_BE;
|
||||
break;
|
||||
default:
|
||||
|
@ -87,9 +87,10 @@ static int ANDROIDAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
|
||||
// !!! FIXME: this needs a WaitDevice implementation.
|
||||
|
||||
static void ANDROIDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
static int ANDROIDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
Android_JNI_WriteAudioBuffer();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *ANDROIDAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
|
@ -27,7 +27,7 @@
|
||||
#include "SDL_coreaudio.h"
|
||||
#include "../../thread/SDL_systhread.h"
|
||||
|
||||
#define DEBUG_COREAUDIO 1
|
||||
#define DEBUG_COREAUDIO 0
|
||||
|
||||
#if DEBUG_COREAUDIO
|
||||
#define CHECK_RESULT(msg) \
|
||||
@ -525,7 +525,7 @@ static SDL_bool UpdateAudioSession(SDL_AudioDevice *device, SDL_bool open, SDL_b
|
||||
#endif
|
||||
|
||||
|
||||
static void COREAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
static int COREAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
{
|
||||
AudioQueueBufferRef current_buffer = device->hidden->current_buffer;
|
||||
SDL_assert(current_buffer != NULL); // should have been called from OutputBufferReadyCallback
|
||||
@ -533,6 +533,7 @@ static void COREAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, i
|
||||
current_buffer->mAudioDataByteSize = current_buffer->mAudioDataBytesCapacity;
|
||||
device->hidden->current_buffer = NULL;
|
||||
AudioQueueEnqueueBuffer(device->hidden->audioQueue, current_buffer, 0, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *COREAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
@ -875,12 +876,12 @@ static int COREAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
switch (test_format) {
|
||||
case SDL_AUDIO_U8:
|
||||
case SDL_AUDIO_S8:
|
||||
case SDL_AUDIO_S16LSB:
|
||||
case SDL_AUDIO_S16MSB:
|
||||
case SDL_AUDIO_S32LSB:
|
||||
case SDL_AUDIO_S32MSB:
|
||||
case SDL_AUDIO_F32LSB:
|
||||
case SDL_AUDIO_F32MSB:
|
||||
case SDL_AUDIO_S16LE:
|
||||
case SDL_AUDIO_S16BE:
|
||||
case SDL_AUDIO_S32LE:
|
||||
case SDL_AUDIO_S32BE:
|
||||
case SDL_AUDIO_F32LE:
|
||||
case SDL_AUDIO_F32BE:
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -179,7 +179,7 @@ static BOOL CALLBACK FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVO
|
||||
if (str != NULL) {
|
||||
LPGUID cpyguid = (LPGUID)SDL_malloc(sizeof(GUID));
|
||||
if (cpyguid) {
|
||||
SDL_memcpy(cpyguid, guid, sizeof(GUID));
|
||||
SDL_copyp(cpyguid, guid);
|
||||
|
||||
/* Note that spec is NULL, because we are required to connect to the
|
||||
* device before getting the channel mask and output format, making
|
||||
@ -285,11 +285,14 @@ static void DSOUND_WaitDevice(SDL_AudioDevice *device)
|
||||
}
|
||||
}
|
||||
|
||||
static void DSOUND_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
static int DSOUND_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
// Unlock the buffer, allowing it to play
|
||||
SDL_assert(buflen == device->buffer_size);
|
||||
IDirectSoundBuffer_Unlock(device->hidden->mixbuf, (LPVOID) buffer, buflen, NULL, 0);
|
||||
if (IDirectSoundBuffer_Unlock(device->hidden->mixbuf, (LPVOID) buffer, buflen, NULL, 0) != DS_OK) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *DSOUND_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
@ -378,7 +381,7 @@ static int DSOUND_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int b
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_assert(ptr1len == buflen);
|
||||
SDL_assert(ptr1len == (DWORD)buflen);
|
||||
SDL_assert(ptr2 == NULL);
|
||||
SDL_assert(ptr2len == 0);
|
||||
|
||||
@ -614,7 +617,7 @@ static int DSOUND_OpenDevice(SDL_AudioDevice *device)
|
||||
}
|
||||
|
||||
wfmt.Format.wBitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
|
||||
wfmt.Format.nChannels = device->spec.channels;
|
||||
wfmt.Format.nChannels = (WORD)device->spec.channels;
|
||||
wfmt.Format.nSamplesPerSec = device->spec.freq;
|
||||
wfmt.Format.nBlockAlign = wfmt.Format.nChannels * (wfmt.Format.wBitsPerSample / 8);
|
||||
wfmt.Format.nAvgBytesPerSec = wfmt.Format.nSamplesPerSec * wfmt.Format.nBlockAlign;
|
||||
|
@ -40,15 +40,16 @@ static void DISKAUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
SDL_Delay(device->hidden->io_delay);
|
||||
}
|
||||
|
||||
static void DISKAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
static int DISKAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
{
|
||||
const int written = (int)SDL_RWwrite(device->hidden->io, buffer, (size_t)buffer_size);
|
||||
if (written != buffer_size) { // If we couldn't write, assume fatal error for now
|
||||
SDL_AudioDeviceDisconnected(device);
|
||||
return -1;
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
SDL_Log("DISKAUDIO: Wrote %d bytes of audio data", (int) written);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *DISKAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
|
@ -111,12 +111,12 @@ static int DSP_OpenDevice(SDL_AudioDevice *device)
|
||||
format = AFMT_U8;
|
||||
}
|
||||
break;
|
||||
case SDL_AUDIO_S16LSB:
|
||||
case SDL_AUDIO_S16LE:
|
||||
if (value & AFMT_S16_LE) {
|
||||
format = AFMT_S16_LE;
|
||||
}
|
||||
break;
|
||||
case SDL_AUDIO_S16MSB:
|
||||
case SDL_AUDIO_S16BE:
|
||||
if (value & AFMT_S16_BE) {
|
||||
format = AFMT_S16_BE;
|
||||
}
|
||||
@ -225,17 +225,17 @@ static void DSP_WaitDevice(SDL_AudioDevice *device)
|
||||
}
|
||||
}
|
||||
|
||||
static void DSP_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
static int DSP_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = device->hidden;
|
||||
if (write(h->audio_fd, buffer, buflen) == -1) {
|
||||
perror("Audio write");
|
||||
SDL_AudioDeviceDisconnected(device);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", h->mixlen);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *DSP_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
|
@ -36,9 +36,9 @@ static Uint8 *EMSCRIPTENAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_
|
||||
return device->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static void EMSCRIPTENAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
static int EMSCRIPTENAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
{
|
||||
const int framelen = (SDL_AUDIO_BITSIZE(device->spec.format) / 8) * device->spec.channels;
|
||||
const int framelen = SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL3 = Module['SDL3'];
|
||||
var numChannels = SDL3.audio.currentOutputBuffer['numberOfChannels'];
|
||||
@ -53,11 +53,7 @@ static void EMSCRIPTENAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buf
|
||||
}
|
||||
}
|
||||
}, buffer, buffer_size / framelen);
|
||||
}
|
||||
|
||||
static void HandleAudioProcess(SDL_AudioDevice *device) // this fires when the main thread is idle.
|
||||
{
|
||||
SDL_OutputAudioThreadIterate(device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -92,11 +88,6 @@ static int EMSCRIPTENAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buff
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static void HandleCaptureProcess(SDL_AudioDevice *device) // this fires when the main thread is idle.
|
||||
{
|
||||
SDL_CaptureAudioThreadIterate(device);
|
||||
}
|
||||
|
||||
static void EMSCRIPTENAUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (!device->hidden) {
|
||||
@ -107,32 +98,28 @@ static void EMSCRIPTENAUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
var SDL3 = Module['SDL3'];
|
||||
if ($0) {
|
||||
if (SDL3.capture.silenceTimer !== undefined) {
|
||||
clearTimeout(SDL3.capture.silenceTimer);
|
||||
clearInterval(SDL3.capture.silenceTimer);
|
||||
}
|
||||
if (SDL3.capture.stream !== undefined) {
|
||||
var tracks = SDL3.capture.stream.getAudioTracks();
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
SDL3.capture.stream.removeTrack(tracks[i]);
|
||||
}
|
||||
SDL3.capture.stream = undefined;
|
||||
}
|
||||
if (SDL3.capture.scriptProcessorNode !== undefined) {
|
||||
SDL3.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) {};
|
||||
SDL3.capture.scriptProcessorNode.disconnect();
|
||||
SDL3.capture.scriptProcessorNode = undefined;
|
||||
}
|
||||
if (SDL3.capture.mediaStreamNode !== undefined) {
|
||||
SDL3.capture.mediaStreamNode.disconnect();
|
||||
SDL3.capture.mediaStreamNode = undefined;
|
||||
}
|
||||
if (SDL3.capture.silenceBuffer !== undefined) {
|
||||
SDL3.capture.silenceBuffer = undefined
|
||||
}
|
||||
SDL3.capture = undefined;
|
||||
} else {
|
||||
if (SDL3.audio.scriptProcessorNode != undefined) {
|
||||
SDL3.audio.scriptProcessorNode.disconnect();
|
||||
SDL3.audio.scriptProcessorNode = undefined;
|
||||
}
|
||||
if (SDL3.audio.silenceTimer !== undefined) {
|
||||
clearInterval(SDL3.audio.silenceTimer);
|
||||
}
|
||||
SDL3.audio = undefined;
|
||||
}
|
||||
@ -174,7 +161,9 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
SDL3.audioContext = new webkitAudioContext();
|
||||
}
|
||||
if (SDL3.audioContext) {
|
||||
autoResumeAudioContext(SDL3.audioContext);
|
||||
if ((typeof navigator.userActivation) === 'undefined') { // Firefox doesn't have this (as of August 2023), use autoResumeAudioContext instead.
|
||||
autoResumeAudioContext(SDL3.audioContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
return SDL3.audioContext === undefined ? -1 : 0;
|
||||
@ -227,8 +216,9 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
var have_microphone = function(stream) {
|
||||
//console.log('SDL audio capture: we have a microphone! Replacing silence callback.');
|
||||
if (SDL3.capture.silenceTimer !== undefined) {
|
||||
clearTimeout(SDL3.capture.silenceTimer);
|
||||
clearInterval(SDL3.capture.silenceTimer);
|
||||
SDL3.capture.silenceTimer = undefined;
|
||||
SDL3.capture.silenceBuffer = undefined
|
||||
}
|
||||
SDL3.capture.mediaStreamNode = SDL3.audioContext.createMediaStreamSource(stream);
|
||||
SDL3.capture.scriptProcessorNode = SDL3.audioContext.createScriptProcessor($1, $0, 1);
|
||||
@ -255,14 +245,14 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
dynCall('vi', $2, [$3]);
|
||||
};
|
||||
|
||||
SDL3.capture.silenceTimer = setTimeout(silence_callback, ($1 / SDL3.audioContext.sampleRate) * 1000);
|
||||
SDL3.capture.silenceTimer = setInterval(silence_callback, ($1 / SDL3.audioContext.sampleRate) * 1000);
|
||||
|
||||
if ((navigator.mediaDevices !== undefined) && (navigator.mediaDevices.getUserMedia !== undefined)) {
|
||||
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(have_microphone).catch(no_microphone);
|
||||
} else if (navigator.webkitGetUserMedia !== undefined) {
|
||||
navigator.webkitGetUserMedia({ audio: true, video: false }, have_microphone, no_microphone);
|
||||
}
|
||||
}, device->spec.channels, device->sample_frames, HandleCaptureProcess, device);
|
||||
}, device->spec.channels, device->sample_frames, SDL_CaptureAudioThreadIterate, device);
|
||||
} else {
|
||||
// setup a ScriptProcessorNode
|
||||
MAIN_THREAD_EM_ASM({
|
||||
@ -270,11 +260,38 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
SDL3.audio.scriptProcessorNode = SDL3.audioContext['createScriptProcessor']($1, 0, $0);
|
||||
SDL3.audio.scriptProcessorNode['onaudioprocess'] = function (e) {
|
||||
if ((SDL3 === undefined) || (SDL3.audio === undefined)) { return; }
|
||||
// if we're actually running the node, we don't need the fake callback anymore, so kill it.
|
||||
if (SDL3.audio.silenceTimer !== undefined) {
|
||||
clearInterval(SDL3.audio.silenceTimer);
|
||||
SDL3.audio.silenceTimer = undefined;
|
||||
SDL3.audio.silenceBuffer = undefined;
|
||||
}
|
||||
SDL3.audio.currentOutputBuffer = e['outputBuffer'];
|
||||
dynCall('vi', $2, [$3]);
|
||||
};
|
||||
|
||||
SDL3.audio.scriptProcessorNode['connect'](SDL3.audioContext['destination']);
|
||||
}, device->spec.channels, device->sample_frames, HandleAudioProcess, device);
|
||||
|
||||
if (SDL3.audioContext.state === 'suspended') { // uhoh, autoplay is blocked.
|
||||
SDL3.audio.silenceBuffer = SDL3.audioContext.createBuffer($0, $1, SDL3.audioContext.sampleRate);
|
||||
SDL3.audio.silenceBuffer.getChannelData(0).fill(0.0);
|
||||
var silence_callback = function() {
|
||||
if ((typeof navigator.userActivation) !== 'undefined') { // Almost everything modern except Firefox (as of August 2023)
|
||||
if (navigator.userActivation.hasBeenActive) {
|
||||
SDL3.audioContext.resume();
|
||||
}
|
||||
}
|
||||
|
||||
// the buffer that gets filled here just gets ignored, so the app can make progress
|
||||
// and/or avoid flooding audio queues until we can actually play audio.
|
||||
SDL3.audio.currentOutputBuffer = SDL3.audio.silenceBuffer;
|
||||
dynCall('vi', $2, [$3]);
|
||||
SDL3.audio.currentOutputBuffer = undefined;
|
||||
};
|
||||
|
||||
SDL3.audio.silenceTimer = setInterval(silence_callback, ($1 / SDL3.audioContext.sampleRate) * 1000);
|
||||
}
|
||||
}, device->spec.channels, device->sample_frames, SDL_OutputAudioThreadIterate, device);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -46,13 +46,14 @@ static Uint8 *HAIKUAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
return device->hidden->current_buffer;
|
||||
}
|
||||
|
||||
static void HAIKUAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
static int HAIKUAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
{
|
||||
// We already wrote our output right into the BSoundPlayer's callback's stream. Just clean up our stuff.
|
||||
SDL_assert(device->hidden->current_buffer != NULL);
|
||||
SDL_assert(device->hidden->current_buffer_len > 0);
|
||||
device->hidden->current_buffer = NULL;
|
||||
device->hidden->current_buffer_len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The Haiku callback for handling the audio buffer
|
||||
@ -130,29 +131,29 @@ static int HAIKUAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
format.format = media_raw_audio_format::B_AUDIO_UCHAR;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_S16LSB:
|
||||
case SDL_AUDIO_S16LE:
|
||||
format.format = media_raw_audio_format::B_AUDIO_SHORT;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_S16MSB:
|
||||
case SDL_AUDIO_S16BE:
|
||||
format.format = media_raw_audio_format::B_AUDIO_SHORT;
|
||||
format.byte_order = B_MEDIA_BIG_ENDIAN;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_S32LSB:
|
||||
case SDL_AUDIO_S32LE:
|
||||
format.format = media_raw_audio_format::B_AUDIO_INT;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_S32MSB:
|
||||
case SDL_AUDIO_S32BE:
|
||||
format.format = media_raw_audio_format::B_AUDIO_INT;
|
||||
format.byte_order = B_MEDIA_BIG_ENDIAN;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_F32LSB:
|
||||
case SDL_AUDIO_F32LE:
|
||||
format.format = media_raw_audio_format::B_AUDIO_FLOAT;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_F32MSB:
|
||||
case SDL_AUDIO_F32BE:
|
||||
format.format = media_raw_audio_format::B_AUDIO_FLOAT;
|
||||
format.byte_order = B_MEDIA_BIG_ENDIAN;
|
||||
break;
|
||||
|
@ -149,7 +149,7 @@ static int jackProcessPlaybackCallback(jack_nframes_t nframes, void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void JACK_PlayDevice(SDL_AudioDevice *device, const Uint8 *ui8buffer, int buflen)
|
||||
static int JACK_PlayDevice(SDL_AudioDevice *device, const Uint8 *ui8buffer, int buflen)
|
||||
{
|
||||
const float *buffer = (float *) ui8buffer;
|
||||
jack_port_t **ports = device->hidden->sdlports;
|
||||
@ -167,6 +167,8 @@ static void JACK_PlayDevice(SDL_AudioDevice *device, const Uint8 *ui8buffer, int
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *JACK_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
@ -307,7 +309,7 @@ static int JACK_OpenDevice(SDL_AudioDevice *device)
|
||||
/* !!! FIXME: docs say about buffer size: "This size may change, clients that depend on it must register a bufsize_callback so they will be notified if it does." */
|
||||
|
||||
/* Jack pretty much demands what it wants. */
|
||||
device->spec.format = SDL_AUDIO_F32SYS;
|
||||
device->spec.format = SDL_AUDIO_F32;
|
||||
device->spec.freq = JACK_jack_get_sample_rate(client);
|
||||
device->spec.channels = channels;
|
||||
device->sample_frames = JACK_jack_get_buffer_size(client);
|
||||
|
@ -161,7 +161,7 @@ static int N3DSAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
|
||||
SDL_memset(device->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS);
|
||||
|
||||
const int sample_frame_size = device->spec.channels * (SDL_AUDIO_BITSIZE(device->spec.format) / 8);
|
||||
const int sample_frame_size = SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
for (unsigned i = 0; i < NUM_BUFFERS; i++) {
|
||||
device->hidden->waveBuf[i].data_vaddr = data_vaddr;
|
||||
device->hidden->waveBuf[i].nsamples = device->buffer_size / sample_frame_size;
|
||||
@ -176,7 +176,7 @@ static int N3DSAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
static int N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
contextLock(device);
|
||||
|
||||
@ -185,7 +185,7 @@ static void N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, i
|
||||
if (device->hidden->isCancelled ||
|
||||
device->hidden->waveBuf[nextbuf].status != NDSP_WBUF_FREE) {
|
||||
contextUnlock(device);
|
||||
return;
|
||||
return 0; // !!! FIXME: is this a fatal error? If so, this should return -1.
|
||||
}
|
||||
|
||||
device->hidden->nextbuf = (nextbuf + 1) % NUM_BUFFERS;
|
||||
@ -196,6 +196,8 @@ static void N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, i
|
||||
DSP_FlushDataCache(device->hidden->waveBuf[nextbuf].data_vaddr, buflen);
|
||||
|
||||
ndspChnWaveBufAdd(0, &device->hidden->waveBuf[nextbuf]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void N3DSAUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
|
@ -130,7 +130,7 @@ static void NETBSDAUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
SDL_AudioDeviceDisconnected(device);
|
||||
return;
|
||||
}
|
||||
const size_t remain = (size_t)((iscapture ? info.record.seek : info.play.seek) * (SDL_AUDIO_BITSIZE(device->spec.format) / 8));
|
||||
const size_t remain = (size_t)((iscapture ? info.record.seek : info.play.seek) * SDL_AUDIO_BYTESIZE(device->spec.format));
|
||||
if (!iscapture && (remain >= device->buffer_size)) {
|
||||
SDL_Delay(10);
|
||||
} else if (iscapture && (remain < device->buffer_size)) {
|
||||
@ -141,20 +141,18 @@ static void NETBSDAUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
}
|
||||
}
|
||||
|
||||
static void NETBSDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
static int NETBSDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = device->hidden;
|
||||
const int written = write(h->audio_fd, buffer, buflen);
|
||||
if (written == -1) {
|
||||
// Non recoverable error has occurred. It should be reported!!!
|
||||
SDL_AudioDeviceDisconnected(device);
|
||||
perror("audio");
|
||||
return;
|
||||
if (written != buflen) { // Treat even partial writes as fatal errors.
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *NETBSDAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
@ -183,7 +181,7 @@ static void NETBSDAUDIO_FlushCapture(SDL_AudioDevice *device)
|
||||
struct SDL_PrivateAudioData *h = device->hidden;
|
||||
audio_info_t info;
|
||||
if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) == 0) {
|
||||
size_t remain = (size_t)(info.record.seek * (SDL_AUDIO_BITSIZE(device->spec.format) / 8));
|
||||
size_t remain = (size_t)(info.record.seek * SDL_AUDIO_BYTESIZE(device->spec.format));
|
||||
while (remain > 0) {
|
||||
char buf[512];
|
||||
const size_t len = SDL_min(sizeof(buf), remain);
|
||||
@ -250,16 +248,16 @@ static int NETBSDAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
case SDL_AUDIO_S8:
|
||||
encoding = AUDIO_ENCODING_SLINEAR;
|
||||
break;
|
||||
case SDL_AUDIO_S16LSB:
|
||||
case SDL_AUDIO_S16LE:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_LE;
|
||||
break;
|
||||
case SDL_AUDIO_S16MSB:
|
||||
case SDL_AUDIO_S16BE:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_BE;
|
||||
break;
|
||||
case SDL_AUDIO_S32LSB:
|
||||
case SDL_AUDIO_S32LE:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_LE;
|
||||
break;
|
||||
case SDL_AUDIO_S32MSB:
|
||||
case SDL_AUDIO_S32BE:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_BE;
|
||||
break;
|
||||
default:
|
||||
|
@ -248,7 +248,7 @@ static int openslES_CreatePCMRecorder(SDL_AudioDevice *device)
|
||||
}
|
||||
|
||||
// Just go with signed 16-bit audio as it's the most compatible
|
||||
device->spec.format = SDL_AUDIO_S16SYS;
|
||||
device->spec.format = SDL_AUDIO_S16;
|
||||
device->spec.channels = 1;
|
||||
//device->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/
|
||||
|
||||
@ -424,12 +424,12 @@ static int openslES_CreatePCMPlayer(SDL_AudioDevice *device)
|
||||
if (!test_format) {
|
||||
// Didn't find a compatible format :
|
||||
LOGI("No compatible audio format, using signed 16-bit audio");
|
||||
test_format = SDL_AUDIO_S16SYS;
|
||||
test_format = SDL_AUDIO_S16;
|
||||
}
|
||||
device->spec.format = test_format;
|
||||
} else {
|
||||
// Just go with signed 16-bit audio as it's the most compatible
|
||||
device->spec.format = SDL_AUDIO_S16SYS;
|
||||
device->spec.format = SDL_AUDIO_S16;
|
||||
}
|
||||
|
||||
// Update the fragment size as size in bytes
|
||||
@ -638,7 +638,7 @@ static void openslES_WaitDevice(SDL_AudioDevice *device)
|
||||
SDL_WaitSemaphore(audiodata->playsem);
|
||||
}
|
||||
|
||||
static void openslES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
static int openslES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
|
||||
@ -657,6 +657,8 @@ static void openslES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, in
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
SDL_PostSemaphore(audiodata->playsem);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// n playn sem
|
||||
|
@ -898,22 +898,22 @@ static void initialize_spa_info(const SDL_AudioSpec *spec, struct spa_audio_info
|
||||
case SDL_AUDIO_S8:
|
||||
info->format = SPA_AUDIO_FORMAT_S8;
|
||||
break;
|
||||
case SDL_AUDIO_S16LSB:
|
||||
case SDL_AUDIO_S16LE:
|
||||
info->format = SPA_AUDIO_FORMAT_S16_LE;
|
||||
break;
|
||||
case SDL_AUDIO_S16MSB:
|
||||
case SDL_AUDIO_S16BE:
|
||||
info->format = SPA_AUDIO_FORMAT_S16_BE;
|
||||
break;
|
||||
case SDL_AUDIO_S32LSB:
|
||||
case SDL_AUDIO_S32LE:
|
||||
info->format = SPA_AUDIO_FORMAT_S32_LE;
|
||||
break;
|
||||
case SDL_AUDIO_S32MSB:
|
||||
case SDL_AUDIO_S32BE:
|
||||
info->format = SPA_AUDIO_FORMAT_S32_BE;
|
||||
break;
|
||||
case SDL_AUDIO_F32LSB:
|
||||
case SDL_AUDIO_F32LE:
|
||||
info->format = SPA_AUDIO_FORMAT_F32_LE;
|
||||
break;
|
||||
case SDL_AUDIO_F32MSB:
|
||||
case SDL_AUDIO_F32BE:
|
||||
info->format = SPA_AUDIO_FORMAT_F32_BE;
|
||||
break;
|
||||
}
|
||||
@ -940,7 +940,7 @@ static Uint8 *PIPEWIRE_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
return (Uint8 *) spa_buf->datas[0].data;
|
||||
}
|
||||
|
||||
static void PIPEWIRE_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
static int PIPEWIRE_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
{
|
||||
struct pw_stream *stream = device->hidden->stream;
|
||||
struct pw_buffer *pw_buf = device->hidden->pw_buf;
|
||||
@ -951,6 +951,8 @@ static void PIPEWIRE_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, in
|
||||
|
||||
PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf);
|
||||
device->hidden->pw_buf = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void output_callback(void *data)
|
||||
@ -1106,7 +1108,7 @@ static int PIPEWIRE_OpenDevice(SDL_AudioDevice *device)
|
||||
}
|
||||
|
||||
/* Size of a single audio frame in bytes */
|
||||
priv->stride = (SDL_AUDIO_BITSIZE(device->spec.format) / 8) * device->spec.channels;
|
||||
priv->stride = SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
|
||||
if (device->sample_frames < min_period) {
|
||||
device->sample_frames = min_period;
|
||||
|
@ -85,9 +85,10 @@ static int PS2AUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void PS2AUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
static int PS2AUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
audsrv_play_audio((char *)buffer, buflen);
|
||||
// this returns number of bytes accepted or a negative error. We assume anything other than buflen is a fatal error.
|
||||
return (audsrv_play_audio((char *)buffer, buflen) != buflen) ? -1 : 0;
|
||||
}
|
||||
|
||||
static void PS2AUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
|
@ -47,7 +47,7 @@ static int PSPAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
}
|
||||
|
||||
// device only natively supports S16LSB
|
||||
device->spec.format = SDL_AUDIO_S16LSB;
|
||||
device->spec.format = SDL_AUDIO_S16LE;
|
||||
|
||||
/* PSP has some limitations with the Audio. It fully supports 44.1KHz (Mono & Stereo),
|
||||
however with frequencies different than 44.1KHz, it just supports Stereo,
|
||||
@ -106,14 +106,16 @@ static int PSPAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void PSPAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
static int PSPAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
int rc;
|
||||
if (!isBasicAudioConfig(&device->spec)) {
|
||||
SDL_assert(device->spec.channels == 2);
|
||||
sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, (void *) buffer);
|
||||
rc = sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, (void *) buffer);
|
||||
} else {
|
||||
sceAudioOutputPannedBlocking(device->hidden->channel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, (void *) buffer);
|
||||
rc = sceAudioOutputPannedBlocking(device->hidden->channel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, (void *) buffer);
|
||||
}
|
||||
return (rc == 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
static void PSPAUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
|
@ -388,7 +388,7 @@ static void PULSEAUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
|
||||
}
|
||||
|
||||
static void PULSEAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
static int PULSEAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = device->hidden;
|
||||
|
||||
@ -401,14 +401,14 @@ static void PULSEAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer,
|
||||
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
|
||||
|
||||
if (rc < 0) {
|
||||
SDL_AudioDeviceDisconnected(device);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*printf("PULSEAUDIO FEED! nbytes=%d\n", buffer_size);*/
|
||||
h->bytes_requested -= buffer_size;
|
||||
|
||||
/*printf("PULSEAUDIO PLAYDEVICE END! written=%d\n", written);*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *PULSEAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
@ -606,22 +606,22 @@ static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
case SDL_AUDIO_U8:
|
||||
format = PA_SAMPLE_U8;
|
||||
break;
|
||||
case SDL_AUDIO_S16LSB:
|
||||
case SDL_AUDIO_S16LE:
|
||||
format = PA_SAMPLE_S16LE;
|
||||
break;
|
||||
case SDL_AUDIO_S16MSB:
|
||||
case SDL_AUDIO_S16BE:
|
||||
format = PA_SAMPLE_S16BE;
|
||||
break;
|
||||
case SDL_AUDIO_S32LSB:
|
||||
case SDL_AUDIO_S32LE:
|
||||
format = PA_SAMPLE_S32LE;
|
||||
break;
|
||||
case SDL_AUDIO_S32MSB:
|
||||
case SDL_AUDIO_S32BE:
|
||||
format = PA_SAMPLE_S32BE;
|
||||
break;
|
||||
case SDL_AUDIO_F32LSB:
|
||||
case SDL_AUDIO_F32LE:
|
||||
format = PA_SAMPLE_FLOAT32LE;
|
||||
break;
|
||||
case SDL_AUDIO_F32MSB:
|
||||
case SDL_AUDIO_F32BE:
|
||||
format = PA_SAMPLE_FLOAT32BE;
|
||||
break;
|
||||
default:
|
||||
@ -723,17 +723,17 @@ static SDL_AudioFormat PulseFormatToSDLFormat(pa_sample_format_t format)
|
||||
case PA_SAMPLE_U8:
|
||||
return SDL_AUDIO_U8;
|
||||
case PA_SAMPLE_S16LE:
|
||||
return SDL_AUDIO_S16LSB;
|
||||
return SDL_AUDIO_S16LE;
|
||||
case PA_SAMPLE_S16BE:
|
||||
return SDL_AUDIO_S16MSB;
|
||||
return SDL_AUDIO_S16BE;
|
||||
case PA_SAMPLE_S32LE:
|
||||
return SDL_AUDIO_S32LSB;
|
||||
return SDL_AUDIO_S32LE;
|
||||
case PA_SAMPLE_S32BE:
|
||||
return SDL_AUDIO_S32MSB;
|
||||
return SDL_AUDIO_S32BE;
|
||||
case PA_SAMPLE_FLOAT32LE:
|
||||
return SDL_AUDIO_F32LSB;
|
||||
return SDL_AUDIO_F32LE;
|
||||
case PA_SAMPLE_FLOAT32BE:
|
||||
return SDL_AUDIO_F32MSB;
|
||||
return SDL_AUDIO_F32BE;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
@ -110,10 +110,10 @@ static void QSA_WaitDevice(SDL_AudioDevice *device)
|
||||
}
|
||||
}
|
||||
|
||||
static void QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
static int QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
if (SDL_AtomicGet(&device->shutdown) || !device->hidden) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int towrite = buflen;
|
||||
@ -125,7 +125,7 @@ static void QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buf
|
||||
// Check if samples playback got stuck somewhere in hardware or in the audio device driver
|
||||
if ((errno == EAGAIN) && (bw == 0)) {
|
||||
if (device->hidden->timeout_on_wait) {
|
||||
return; // oh well, try again next time. !!! FIXME: Should we just disconnect the device in this case?
|
||||
return 0; // oh well, try again next time. !!! FIXME: Should we just disconnect the device in this case?
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,17 +145,17 @@ static void QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buf
|
||||
int status = snd_pcm_plugin_status(device->hidden->audio_handle, &cstatus);
|
||||
if (status < 0) {
|
||||
QSA_SetError("snd_pcm_plugin_status", status);
|
||||
return; // !!! FIXME: disconnect the device?
|
||||
return -1;
|
||||
} else if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY)) {
|
||||
status = snd_pcm_plugin_prepare(device->hidden->audio_handle, device->iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK);
|
||||
if (status < 0) {
|
||||
QSA_SetError("snd_pcm_plugin_prepare", status);
|
||||
return; // !!! FIXME: disconnect the device?
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
return; // !!! FIXME: disconnect the device?
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// we wrote all remaining data
|
||||
@ -165,9 +165,7 @@ static void QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buf
|
||||
}
|
||||
|
||||
// If we couldn't write, assume fatal error for now
|
||||
if (towrite != 0) {
|
||||
SDL_AudioDeviceDisconnected(device);
|
||||
}
|
||||
return (towrite != 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
static Uint8 *QSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
@ -311,7 +309,7 @@ static SDL_AudioFormat QnxFormatToSDLFormat(const int32_t qnxfmt)
|
||||
#undef CHECKFMT
|
||||
default: break;
|
||||
}
|
||||
return SDL_AUDIO_S16SYS; // oh well.
|
||||
return SDL_AUDIO_S16; // oh well.
|
||||
}
|
||||
|
||||
static void QSA_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
|
@ -175,16 +175,17 @@ static void SNDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
}
|
||||
}
|
||||
|
||||
static void SNDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
static int SNDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
// !!! FIXME: this should be non-blocking so we can check device->shutdown.
|
||||
// this is set to blocking, because we _have_ to send the entire buffer down, but hopefully WaitDevice took most of the delay time.
|
||||
if (SNDIO_sio_write(device->hidden->dev, buffer, buflen) != buflen) {
|
||||
SDL_AudioDeviceDisconnected(device); // If we couldn't write, assume fatal error for now
|
||||
return -1; // If we couldn't write, assume fatal error for now
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int SNDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
@ -283,13 +284,13 @@ static int SNDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
}
|
||||
|
||||
if ((par.bps == 4) && (par.sig) && (par.le)) {
|
||||
device->spec.format = SDL_AUDIO_S32LSB;
|
||||
device->spec.format = SDL_AUDIO_S32LE;
|
||||
} else if ((par.bps == 4) && (par.sig) && (!par.le)) {
|
||||
device->spec.format = SDL_AUDIO_S32MSB;
|
||||
device->spec.format = SDL_AUDIO_S32BE;
|
||||
} else if ((par.bps == 2) && (par.sig) && (par.le)) {
|
||||
device->spec.format = SDL_AUDIO_S16LSB;
|
||||
device->spec.format = SDL_AUDIO_S16LE;
|
||||
} else if ((par.bps == 2) && (par.sig) && (!par.le)) {
|
||||
device->spec.format = SDL_AUDIO_S16MSB;
|
||||
device->spec.format = SDL_AUDIO_S16BE;
|
||||
} else if ((par.bps == 1) && (par.sig)) {
|
||||
device->spec.format = SDL_AUDIO_S8;
|
||||
} else if ((par.bps == 1) && (!par.sig)) {
|
||||
|
@ -71,7 +71,7 @@ static int VITAAUD_OpenDevice(SDL_AudioDevice *device)
|
||||
|
||||
closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
if (test_format == SDL_AUDIO_S16LSB) {
|
||||
if (test_format == SDL_AUDIO_S16LE) {
|
||||
device->spec.format = test_format;
|
||||
break;
|
||||
}
|
||||
@ -130,9 +130,9 @@ static int VITAAUD_OpenDevice(SDL_AudioDevice *device)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void VITAAUD_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
static int VITAAUD_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
{
|
||||
sceAudioOutOutput(device->hidden->port, buffer);
|
||||
return (sceAudioOutOutput(device->hidden->port, buffer) == 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
// This function waits until it is possible to write a full sound buffer
|
||||
|
@ -403,22 +403,22 @@ static Uint8 *WASAPI_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
// get an endpoint buffer from WASAPI.
|
||||
BYTE *buffer = NULL;
|
||||
|
||||
while (RecoverWasapiIfLost(device) && device->hidden->render) {
|
||||
if (!WasapiFailed(device, IAudioRenderClient_GetBuffer(device->hidden->render, device->sample_frames, &buffer))) {
|
||||
return (Uint8 *)buffer;
|
||||
if (RecoverWasapiIfLost(device) && device->hidden->render) {
|
||||
if (WasapiFailed(device, IAudioRenderClient_GetBuffer(device->hidden->render, device->sample_frames, &buffer))) {
|
||||
SDL_assert(buffer == NULL);
|
||||
}
|
||||
SDL_assert(buffer == NULL);
|
||||
}
|
||||
|
||||
return (Uint8 *)buffer;
|
||||
}
|
||||
|
||||
static void WASAPI_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
static int WASAPI_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
if (device->hidden->render != NULL) { // definitely activated?
|
||||
// WasapiFailed() will mark the device for reacquisition or removal elsewhere.
|
||||
WasapiFailed(device, IAudioRenderClient_ReleaseBuffer(device->hidden->render, device->sample_frames, 0));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void WASAPI_WaitDevice(SDL_AudioDevice *device)
|
||||
@ -620,7 +620,7 @@ static int mgmtthrtask_PrepDevice(void *userdata)
|
||||
return -1;
|
||||
}
|
||||
|
||||
device->hidden->framesize = (SDL_AUDIO_BITSIZE(device->spec.format) / 8) * device->spec.channels;
|
||||
device->hidden->framesize = SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
|
||||
if (device->iscapture) {
|
||||
IAudioCaptureClient *capture = NULL;
|
||||
|
@ -214,3 +214,23 @@ SDL_GDKSuspendComplete()
|
||||
SetEvent(plmSuspendComplete);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" DECLSPEC int
|
||||
SDL_GDKGetDefaultUser(XUserHandle *outUserHandle)
|
||||
{
|
||||
XAsyncBlock block = { 0 };
|
||||
HRESULT result;
|
||||
|
||||
if (FAILED(result = XUserAddAsync(XUserAddOptions::AddDefaultUserAllowingUI, &block))) {
|
||||
return WIN_SetErrorFromHRESULT("XUserAddAsync", result);
|
||||
}
|
||||
|
||||
do {
|
||||
result = XUserAddResult(&block, outUserHandle);
|
||||
} while (result == E_PENDING);
|
||||
if (FAILED(result)) {
|
||||
return WIN_SetErrorFromHRESULT("XUserAddResult", result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -565,4 +565,70 @@ char *SDL_DBus_GetLocalMachineId(void)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert file drops with mime type "application/vnd.portal.filetransfer" to file paths
|
||||
* Result must be freed with dbus->free_string_array().
|
||||
* https://flatpak.github.io/xdg-desktop-portal/#gdbus-method-org-freedesktop-portal-FileTransfer.RetrieveFiles
|
||||
*/
|
||||
char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *path_count)
|
||||
{
|
||||
DBusError err;
|
||||
DBusMessageIter iter, iterDict;
|
||||
char **paths = NULL;
|
||||
DBusMessage *reply = NULL;
|
||||
DBusMessage *msg = dbus.message_new_method_call("org.freedesktop.portal.Documents", /* Node */
|
||||
"/org/freedesktop/portal/documents", /* Path */
|
||||
"org.freedesktop.portal.FileTransfer", /* Interface */
|
||||
"RetrieveFiles"); /* Method */
|
||||
|
||||
/* Make sure we have a connection to the dbus session bus */
|
||||
if (!SDL_DBus_GetContext() || !dbus.session_conn) {
|
||||
/* We either cannot connect to the session bus or were unable to
|
||||
* load the D-Bus library at all. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dbus.error_init(&err);
|
||||
|
||||
/* First argument is a "application/vnd.portal.filetransfer" key from a DnD or clipboard event */
|
||||
if (!dbus.message_append_args(msg, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID)) {
|
||||
SDL_OutOfMemory();
|
||||
dbus.message_unref(msg);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Second argument is a variant dictionary for options.
|
||||
* The spec doesn't define any entries yet so it's empty. */
|
||||
dbus.message_iter_init_append(msg, &iter);
|
||||
if (!dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &iterDict) ||
|
||||
!dbus.message_iter_close_container(&iter, &iterDict)) {
|
||||
SDL_OutOfMemory();
|
||||
dbus.message_unref(msg);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
reply = dbus.connection_send_with_reply_and_block(dbus.session_conn, msg, DBUS_TIMEOUT_USE_DEFAULT, &err);
|
||||
dbus.message_unref(msg);
|
||||
|
||||
if (reply) {
|
||||
dbus.message_get_args(reply, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &paths, path_count, DBUS_TYPE_INVALID);
|
||||
dbus.message_unref(reply);
|
||||
}
|
||||
|
||||
if (paths) {
|
||||
return paths;
|
||||
}
|
||||
|
||||
failed:
|
||||
if (dbus.error_is_set(&err)) {
|
||||
SDL_SetError("%s: %s", err.name, err.message);
|
||||
dbus.error_free(&err);
|
||||
} else {
|
||||
SDL_SetError("Error retrieving paths for documents portal \"%s\"", key);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -102,6 +102,8 @@ extern SDL_bool SDL_DBus_ScreensaverInhibit(SDL_bool inhibit);
|
||||
extern void SDL_DBus_PumpEvents(void);
|
||||
extern char *SDL_DBus_GetLocalMachineId(void);
|
||||
|
||||
extern char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *files_count);
|
||||
|
||||
#endif /* HAVE_DBUS_DBUS_H */
|
||||
|
||||
#endif /* SDL_dbus_h_ */
|
||||
|
@ -57,8 +57,14 @@ static const PROPERTYKEY SDL_PKEY_AudioEndpoint_GUID = { { 0x1da5d803, 0xd492, 0
|
||||
|
||||
static SDL_bool FindByDevIDCallback(SDL_AudioDevice *device, void *userdata)
|
||||
{
|
||||
const SDL_IMMDevice_HandleData *handle = (const SDL_IMMDevice_HandleData *) device->handle;
|
||||
return (SDL_wcscmp(handle->immdevice_id, (LPCWSTR) userdata) == 0) ? SDL_TRUE : SDL_FALSE;
|
||||
LPCWSTR devid = (LPCWSTR)userdata;
|
||||
if (devid && device && device->handle) {
|
||||
const SDL_IMMDevice_HandleData *handle = (const SDL_IMMDevice_HandleData *)device->handle;
|
||||
if (handle->immdevice_id && SDL_wcscmp(handle->immdevice_id, devid) == 0) {
|
||||
return SDL_TRUE;
|
||||
}
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
static SDL_AudioDevice *SDL_IMMDevice_FindByDevID(LPCWSTR devid)
|
||||
|
@ -344,19 +344,19 @@ static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x
|
||||
SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat)
|
||||
{
|
||||
if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_F32SYS;
|
||||
return SDL_AUDIO_F32;
|
||||
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
|
||||
return SDL_AUDIO_S16SYS;
|
||||
return SDL_AUDIO_S16;
|
||||
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_S32SYS;
|
||||
return SDL_AUDIO_S32;
|
||||
} else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
const WAVEFORMATEXTENSIBLE *ext = (const WAVEFORMATEXTENSIBLE *)waveformat;
|
||||
if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_F32SYS;
|
||||
return SDL_AUDIO_F32;
|
||||
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
|
||||
return SDL_AUDIO_S16SYS;
|
||||
return SDL_AUDIO_S16;
|
||||
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_S32SYS;
|
||||
return SDL_AUDIO_S32;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -172,6 +172,7 @@ SDL3_0.0.0 {
|
||||
SDL_GetGamepadAppleSFSymbolsNameForButton;
|
||||
SDL_GetGamepadAxis;
|
||||
SDL_GetGamepadAxisFromString;
|
||||
SDL_GetGamepadBindings;
|
||||
SDL_GetGamepadButton;
|
||||
SDL_GetGamepadButtonFromString;
|
||||
SDL_GetGamepadFirmwareVersion;
|
||||
@ -876,7 +877,7 @@ SDL3_0.0.0 {
|
||||
SDL_SetAudioStreamGetCallback;
|
||||
SDL_SetAudioStreamPutCallback;
|
||||
SDL_DestroyAudioStream;
|
||||
SDL_CreateAndBindAudioStream;
|
||||
SDL_OpenAudioDeviceStream;
|
||||
SDL_LoadWAV_RW;
|
||||
SDL_LoadWAV;
|
||||
SDL_MixAudioFormat;
|
||||
@ -885,8 +886,8 @@ SDL3_0.0.0 {
|
||||
SDL_LoadWAV;
|
||||
SDL_PauseAudioDevice;
|
||||
SDL_ResumeAudioDevice;
|
||||
SDL_IsAudioDevicePaused;
|
||||
SDL_GetAudioStreamBinding;
|
||||
SDL_AudioDevicePaused;
|
||||
SDL_GetAudioStreamDevice;
|
||||
SDL_ShowWindowSystemMenu;
|
||||
SDL_ReadS16LE;
|
||||
SDL_ReadS16BE;
|
||||
@ -899,6 +900,13 @@ SDL3_0.0.0 {
|
||||
SDL_WriteS32LE;
|
||||
SDL_WriteS32BE;
|
||||
SDL_WriteS64LE;
|
||||
SDL_WriteS64BE;
|
||||
SDL_GDKGetDefaultUser;
|
||||
SDL_SetWindowFocusable;
|
||||
SDL_GetAudioStreamFrequencyRatio;
|
||||
SDL_SetAudioStreamFrequencyRatio;
|
||||
SDL_SetAudioPostmixCallback;
|
||||
SDL_GetAudioStreamQueued;
|
||||
# extra symbols go here (don't modify this line)
|
||||
local: *;
|
||||
};
|
||||
|
@ -196,6 +196,7 @@
|
||||
#define SDL_GetGamepadAppleSFSymbolsNameForButton SDL_GetGamepadAppleSFSymbolsNameForButton_REAL
|
||||
#define SDL_GetGamepadAxis SDL_GetGamepadAxis_REAL
|
||||
#define SDL_GetGamepadAxisFromString SDL_GetGamepadAxisFromString_REAL
|
||||
#define SDL_GetGamepadBindings SDL_GetGamepadBindings_REAL
|
||||
#define SDL_GetGamepadButton SDL_GetGamepadButton_REAL
|
||||
#define SDL_GetGamepadButtonFromString SDL_GetGamepadButtonFromString_REAL
|
||||
#define SDL_GetGamepadFirmwareVersion SDL_GetGamepadFirmwareVersion_REAL
|
||||
@ -901,7 +902,7 @@
|
||||
#define SDL_SetAudioStreamGetCallback SDL_SetAudioStreamGetCallback_REAL
|
||||
#define SDL_SetAudioStreamPutCallback SDL_SetAudioStreamPutCallback_REAL
|
||||
#define SDL_DestroyAudioStream SDL_DestroyAudioStream_REAL
|
||||
#define SDL_CreateAndBindAudioStream SDL_CreateAndBindAudioStream_REAL
|
||||
#define SDL_OpenAudioDeviceStream SDL_OpenAudioDeviceStream_REAL
|
||||
#define SDL_LoadWAV_RW SDL_LoadWAV_RW_REAL
|
||||
#define SDL_LoadWAV SDL_LoadWAV_REAL
|
||||
#define SDL_MixAudioFormat SDL_MixAudioFormat_REAL
|
||||
@ -910,8 +911,8 @@
|
||||
#define SDL_LoadWAV SDL_LoadWAV_REAL
|
||||
#define SDL_PauseAudioDevice SDL_PauseAudioDevice_REAL
|
||||
#define SDL_ResumeAudioDevice SDL_ResumeAudioDevice_REAL
|
||||
#define SDL_IsAudioDevicePaused SDL_IsAudioDevicePaused_REAL
|
||||
#define SDL_GetAudioStreamBinding SDL_GetAudioStreamBinding_REAL
|
||||
#define SDL_AudioDevicePaused SDL_AudioDevicePaused_REAL
|
||||
#define SDL_GetAudioStreamDevice SDL_GetAudioStreamDevice_REAL
|
||||
#define SDL_ShowWindowSystemMenu SDL_ShowWindowSystemMenu_REAL
|
||||
#define SDL_ReadS16LE SDL_ReadS16LE_REAL
|
||||
#define SDL_ReadS16BE SDL_ReadS16BE_REAL
|
||||
@ -924,3 +925,10 @@
|
||||
#define SDL_WriteS32LE SDL_WriteS32LE_REAL
|
||||
#define SDL_WriteS32BE SDL_WriteS32BE_REAL
|
||||
#define SDL_WriteS64LE SDL_WriteS64LE_REAL
|
||||
#define SDL_WriteS64BE SDL_WriteS64BE_REAL
|
||||
#define SDL_GDKGetDefaultUser SDL_GDKGetDefaultUser_REAL
|
||||
#define SDL_SetWindowFocusable SDL_SetWindowFocusable_REAL
|
||||
#define SDL_GetAudioStreamFrequencyRatio SDL_GetAudioStreamFrequencyRatio_REAL
|
||||
#define SDL_SetAudioStreamFrequencyRatio SDL_SetAudioStreamFrequencyRatio_REAL
|
||||
#define SDL_SetAudioPostmixCallback SDL_SetAudioPostmixCallback_REAL
|
||||
#define SDL_GetAudioStreamQueued SDL_GetAudioStreamQueued_REAL
|
||||
|
@ -271,6 +271,7 @@ SDL_DYNAPI_PROC(const char*,SDL_GetGamepadAppleSFSymbolsNameForAxis,(SDL_Gamepad
|
||||
SDL_DYNAPI_PROC(const char*,SDL_GetGamepadAppleSFSymbolsNameForButton,(SDL_Gamepad *a, SDL_GamepadButton b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(Sint16,SDL_GetGamepadAxis,(SDL_Gamepad *a, SDL_GamepadAxis b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_GamepadAxis,SDL_GetGamepadAxisFromString,(const char *a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_GamepadBinding **,SDL_GetGamepadBindings,(SDL_Gamepad *a, int *b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(Uint8,SDL_GetGamepadButton,(SDL_Gamepad *a, SDL_GamepadButton b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_GamepadButton,SDL_GetGamepadButtonFromString,(const char *a),(a),return)
|
||||
SDL_DYNAPI_PROC(Uint16,SDL_GetGamepadFirmwareVersion,(SDL_Gamepad *a),(a),return)
|
||||
@ -926,7 +927,7 @@ SDL_DYNAPI_PROC(const char*,SDL_GetCurrentAudioDriver,(void),(),return)
|
||||
SDL_DYNAPI_PROC(SDL_AudioDeviceID*,SDL_GetAudioOutputDevices,(int *a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_AudioDeviceID*,SDL_GetAudioCaptureDevices,(int *a),(a),return)
|
||||
SDL_DYNAPI_PROC(char*,SDL_GetAudioDeviceName,(SDL_AudioDeviceID a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceFormat,(SDL_AudioDeviceID a, SDL_AudioSpec *b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceFormat,(SDL_AudioDeviceID a, SDL_AudioSpec *b, int *c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_OpenAudioDevice,(SDL_AudioDeviceID a, const SDL_AudioSpec *b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(void,SDL_CloseAudioDevice,(SDL_AudioDeviceID a),(a),)
|
||||
SDL_DYNAPI_PROC(int,SDL_BindAudioStreams,(SDL_AudioDeviceID a, SDL_AudioStream **b, int c),(a,b,c),return)
|
||||
@ -943,10 +944,10 @@ SDL_DYNAPI_PROC(int,SDL_FlushAudioStream,(SDL_AudioStream *a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_ClearAudioStream,(SDL_AudioStream *a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_LockAudioStream,(SDL_AudioStream *a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_UnlockAudioStream,(SDL_AudioStream *a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamGetCallback,(SDL_AudioStream *a, SDL_AudioStreamRequestCallback b, void *c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamPutCallback,(SDL_AudioStream *a, SDL_AudioStreamRequestCallback b, void *c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamGetCallback,(SDL_AudioStream *a, SDL_AudioStreamCallback b, void *c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamPutCallback,(SDL_AudioStream *a, SDL_AudioStreamCallback b, void *c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(void,SDL_DestroyAudioStream,(SDL_AudioStream *a),(a),)
|
||||
SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_CreateAndBindAudioStream,(SDL_AudioDeviceID a, const SDL_AudioSpec *b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_OpenAudioDeviceStream,(SDL_AudioDeviceID a, const SDL_AudioSpec *b, SDL_AudioStreamCallback c, void *d),(a,b,c,d),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_LoadWAV_RW,(SDL_RWops *a, SDL_bool b, SDL_AudioSpec *c, Uint8 **d, Uint32 *e),(a,b,c,d,e),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_MixAudioFormat,(Uint8 *a, const Uint8 *b, SDL_AudioFormat c, Uint32 d, int e),(a,b,c,d,e),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_ConvertAudioSamples,(const SDL_AudioSpec *a, const Uint8 *b, int c, const SDL_AudioSpec *d, Uint8 **e, int *f),(a,b,c,d,e,f),return)
|
||||
@ -954,8 +955,8 @@ SDL_DYNAPI_PROC(int,SDL_GetSilenceValueForFormat,(SDL_AudioFormat a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_LoadWAV,(const char *a, SDL_AudioSpec *b, Uint8 **c, Uint32 *d),(a,b,c,d),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_PauseAudioDevice,(SDL_AudioDeviceID a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_ResumeAudioDevice,(SDL_AudioDeviceID a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_IsAudioDevicePaused,(SDL_AudioDeviceID a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_GetAudioStreamBinding,(SDL_AudioStream *a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_AudioDevicePaused,(SDL_AudioDeviceID a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_GetAudioStreamDevice,(SDL_AudioStream *a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_ShowWindowSystemMenu,(SDL_Window *a, int b, int c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadS16LE,(SDL_RWops *a, Sint16 *b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_ReadS16BE,(SDL_RWops *a, Sint16 *b),(a,b),return)
|
||||
@ -968,3 +969,12 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS16BE,(SDL_RWops *a, Sint16 b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS32LE,(SDL_RWops *a, Sint32 b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS32BE,(SDL_RWops *a, Sint32 b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS64LE,(SDL_RWops *a, Sint64 b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_WriteS64BE,(SDL_RWops *a, Sint64 b),(a,b),return)
|
||||
#ifdef __GDK__
|
||||
SDL_DYNAPI_PROC(int,SDL_GDKGetDefaultUser,(XUserHandle *a),(a),return)
|
||||
#endif
|
||||
SDL_DYNAPI_PROC(int,SDL_SetWindowFocusable,(SDL_Window *a, SDL_bool b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(float,SDL_GetAudioStreamFrequencyRatio,(SDL_AudioStream *a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamFrequencyRatio,(SDL_AudioStream *a, float b),(a,b),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_SetAudioPostmixCallback,(SDL_AudioDeviceID a, SDL_AudioPostmixCallback b, void *c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamQueued,(SDL_AudioStream *a),(a),return)
|
||||
|
@ -418,7 +418,7 @@ static void SDL_LogEvent(const SDL_Event *event)
|
||||
break;
|
||||
#undef PRINT_FINGER_EVENT
|
||||
|
||||
#define PRINT_DROP_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (file='%s' timestamp=%u windowid=%u)", event->drop.file, (uint)event->drop.timestamp, (uint)event->drop.windowID)
|
||||
#define PRINT_DROP_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (file='%s' timestamp=%u windowid=%u x=%f y=%f)", event->drop.file, (uint)event->drop.timestamp, (uint)event->drop.windowID, event->drop.x, event->drop.y)
|
||||
SDL_EVENT_CASE(SDL_EVENT_DROP_FILE)
|
||||
PRINT_DROP_EVENT(event);
|
||||
break;
|
||||
@ -431,6 +431,9 @@ static void SDL_LogEvent(const SDL_Event *event)
|
||||
SDL_EVENT_CASE(SDL_EVENT_DROP_COMPLETE)
|
||||
PRINT_DROP_EVENT(event);
|
||||
break;
|
||||
SDL_EVENT_CASE(SDL_EVENT_DROP_POSITION)
|
||||
PRINT_DROP_EVENT(event);
|
||||
break;
|
||||
#undef PRINT_DROP_EVENT
|
||||
|
||||
#define PRINT_AUDIODEV_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%u iscapture=%s)", (uint)event->adevice.timestamp, (uint)event->adevice.which, event->adevice.iscapture ? "true" : "false")
|
||||
@ -440,6 +443,9 @@ static void SDL_LogEvent(const SDL_Event *event)
|
||||
SDL_EVENT_CASE(SDL_EVENT_AUDIO_DEVICE_REMOVED)
|
||||
PRINT_AUDIODEV_EVENT(event);
|
||||
break;
|
||||
SDL_EVENT_CASE(SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED)
|
||||
PRINT_AUDIODEV_EVENT(event);
|
||||
break;
|
||||
#undef PRINT_AUDIODEV_EVENT
|
||||
|
||||
SDL_EVENT_CASE(SDL_EVENT_SENSOR_UPDATE)
|
||||
|
@ -233,13 +233,31 @@ void SDL_SetDefaultCursor(SDL_Cursor *cursor)
|
||||
|
||||
if (mouse->def_cursor) {
|
||||
SDL_Cursor *default_cursor = mouse->def_cursor;
|
||||
SDL_Cursor *prev, *curr;
|
||||
|
||||
if (mouse->cur_cursor == mouse->def_cursor) {
|
||||
mouse->cur_cursor = NULL;
|
||||
}
|
||||
mouse->def_cursor = NULL;
|
||||
|
||||
SDL_DestroyCursor(default_cursor);
|
||||
for (prev = NULL, curr = mouse->cursors; curr;
|
||||
prev = curr, curr = curr->next) {
|
||||
if (curr == default_cursor) {
|
||||
if (prev) {
|
||||
prev->next = curr->next;
|
||||
} else {
|
||||
mouse->cursors = curr->next;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mouse->FreeCursor && default_cursor->driverdata) {
|
||||
mouse->FreeCursor(default_cursor);
|
||||
} else {
|
||||
SDL_free(default_cursor);
|
||||
}
|
||||
}
|
||||
|
||||
mouse->def_cursor = cursor;
|
||||
@ -591,6 +609,13 @@ static int SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_
|
||||
}
|
||||
}
|
||||
|
||||
if (mouse->has_position && xrel == 0.0f && yrel == 0.0f) { /* Drop events that don't change state */
|
||||
#ifdef DEBUG_MOUSE
|
||||
SDL_Log("Mouse event didn't change state - dropped!\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ignore relative motion positioning the first touch */
|
||||
if (mouseID == SDL_TOUCH_MOUSEID && !GetButtonState(mouse, SDL_TRUE)) {
|
||||
xrel = 0.0f;
|
||||
@ -598,13 +623,6 @@ static int SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_
|
||||
}
|
||||
|
||||
if (mouse->has_position) {
|
||||
if (xrel == 0.0f && yrel == 0.0f) { /* Drop events that don't change state */
|
||||
#ifdef DEBUG_MOUSE
|
||||
SDL_Log("Mouse event didn't change state - dropped!\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update internal mouse coordinates */
|
||||
if (!mouse->relative_mode) {
|
||||
mouse->x = x;
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
/*
|
||||
Copyright (C) 2003-2006,2008 Jamey Sharp, Josh Triplett
|
||||
Copyright © 2009 Red Hat, Inc.
|
||||
Copyright © 2009 Red Hat, Inc.
|
||||
Copyright 1990-1992,1999,2000,2004,2009,2010 Oracle and/or its affiliates.
|
||||
All rights reserved.
|
||||
|
||||
|
140
src/filesystem/gdk/SDL_sysfilesystem.cpp
Normal file
140
src/filesystem/gdk/SDL_sysfilesystem.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifdef SDL_FILESYSTEM_XBOX
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
/* System dependent filesystem routines */
|
||||
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
#include "SDL_hints.h"
|
||||
#include "SDL_system.h"
|
||||
#include "SDL_filesystem.h"
|
||||
#include <XGameSaveFiles.h>
|
||||
|
||||
char *
|
||||
SDL_GetBasePath(void)
|
||||
{
|
||||
/* NOTE: This function is a UTF8 version of the Win32 SDL_GetBasePath()!
|
||||
* The GDK actually _recommends_ the 'A' functions over the 'W' functions :o
|
||||
*/
|
||||
DWORD buflen = 128;
|
||||
CHAR *path = NULL;
|
||||
DWORD len = 0;
|
||||
int i;
|
||||
|
||||
while (SDL_TRUE) {
|
||||
void *ptr = SDL_realloc(path, buflen * sizeof(CHAR));
|
||||
if (ptr == NULL) {
|
||||
SDL_free(path);
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path = (CHAR *)ptr;
|
||||
|
||||
len = GetModuleFileNameA(NULL, path, buflen);
|
||||
/* if it truncated, then len >= buflen - 1 */
|
||||
/* if there was enough room (or failure), len < buflen - 1 */
|
||||
if (len < buflen - 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* buffer too small? Try again. */
|
||||
buflen *= 2;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
SDL_free(path);
|
||||
WIN_SetError("Couldn't locate our .exe");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = len - 1; i > 0; i--) {
|
||||
if (path[i] == '\\') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_assert(i > 0); /* Should have been an absolute path. */
|
||||
path[i + 1] = '\0'; /* chop off filename. */
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
char *
|
||||
SDL_GetPrefPath(const char *org, const char *app)
|
||||
{
|
||||
XUserHandle user = NULL;
|
||||
XAsyncBlock block = { 0 };
|
||||
char *folderPath;
|
||||
HRESULT result;
|
||||
const char *csid = SDL_GetHint("SDL_GDK_SERVICE_CONFIGURATION_ID");
|
||||
|
||||
if (app == NULL) {
|
||||
SDL_InvalidParamError("app");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This should be set before calling SDL_GetPrefPath! */
|
||||
if (csid == NULL) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_SYSTEM, "Set SDL_GDK_SERVICE_CONFIGURATION_ID before calling SDL_GetPrefPath!");
|
||||
return SDL_strdup("T:\\");
|
||||
}
|
||||
|
||||
if (SDL_GDKGetDefaultUser(&user) < 0) {
|
||||
/* Error already set, just return */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (FAILED(result = XGameSaveFilesGetFolderWithUiAsync(user, csid, &block))) {
|
||||
WIN_SetErrorFromHRESULT("XGameSaveFilesGetFolderWithUiAsync", result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
folderPath = (char*) SDL_malloc(MAX_PATH);
|
||||
do {
|
||||
result = XGameSaveFilesGetFolderWithUiResult(&block, MAX_PATH, folderPath);
|
||||
} while (result == E_PENDING);
|
||||
if (FAILED(result)) {
|
||||
WIN_SetErrorFromHRESULT("XGameSaveFilesGetFolderWithUiResult", result);
|
||||
SDL_free(folderPath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We aren't using 'app' here because the container rules are a lot more
|
||||
* strict than the NTFS rules, so it will most likely be invalid :(
|
||||
*/
|
||||
SDL_strlcat(folderPath, "\\SDLPrefPath\\", MAX_PATH);
|
||||
if (CreateDirectoryA(folderPath, NULL) == FALSE) {
|
||||
if (GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
WIN_SetError("CreateDirectoryA");
|
||||
SDL_free(folderPath);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return folderPath;
|
||||
}
|
||||
|
||||
#endif /* SDL_FILESYSTEM_XBOX */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
@ -332,23 +332,3 @@ done:
|
||||
return retval;
|
||||
}
|
||||
#endif /* SDL_FILESYSTEM_WINDOWS */
|
||||
|
||||
#ifdef SDL_FILESYSTEM_XBOX
|
||||
char *SDL_GetBasePath(void)
|
||||
{
|
||||
SDL_Unsupported();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *SDL_GetPrefPath(const char *org, const char *app)
|
||||
{
|
||||
SDL_Unsupported();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *SDL_GetUserFolder(SDL_Folder folder)
|
||||
{
|
||||
SDL_Unsupported();
|
||||
return NULL;
|
||||
}
|
||||
#endif /* SDL_FILESYSTEM_XBOX */
|
||||
|
@ -873,10 +873,47 @@ typedef struct LIBUSB_hid_device_ LIBUSB_hid_device;
|
||||
#undef read_thread
|
||||
#undef return_data
|
||||
|
||||
/* If the platform has any backend other than libusb, try to avoid using
|
||||
* libusb as the main backend for devices, since it detaches drivers and
|
||||
* therefore makes devices inaccessible to the rest of the OS.
|
||||
*
|
||||
* We do this by whitelisting devices we know to be accessible _exclusively_
|
||||
* via libusb; these are typically devices that look like HIDs but have a
|
||||
* quirk that requires direct access to the hardware.
|
||||
*/
|
||||
static const struct {
|
||||
Uint16 vendor;
|
||||
Uint16 product;
|
||||
} SDL_libusb_whitelist[] = {
|
||||
{ 0x057e, 0x0337 } /* Nintendo WUP-028, Wii U/Switch GameCube Adapter */
|
||||
};
|
||||
|
||||
static SDL_bool IsInWhitelist(Uint16 vendor, Uint16 product)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < SDL_arraysize(SDL_libusb_whitelist); i += 1) {
|
||||
if (vendor == SDL_libusb_whitelist[i].vendor &&
|
||||
product == SDL_libusb_whitelist[i].product) {
|
||||
return SDL_TRUE;
|
||||
}
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
#endif /* HAVE_LIBUSB */
|
||||
|
||||
#endif /* !SDL_HIDAPI_DISABLED */
|
||||
|
||||
#if defined(HAVE_PLATFORM_BACKEND) || defined(HAVE_DRIVER_BACKEND)
|
||||
/* We have another way to get HID devices, so use the whitelist to get devices where libusb is preferred */
|
||||
#define SDL_HIDAPI_LIBUSB_WHITELIST_DEFAULT SDL_TRUE
|
||||
#else
|
||||
/* libusb is the only way to get HID devices, so don't use the whitelist, get them all */
|
||||
#define SDL_HIDAPI_LIBUSB_WHITELIST_DEFAULT SDL_FALSE
|
||||
#endif /* HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND */
|
||||
|
||||
static SDL_bool use_libusb_whitelist = SDL_HIDAPI_LIBUSB_WHITELIST_DEFAULT;
|
||||
|
||||
/* Shared HIDAPI Implementation */
|
||||
|
||||
struct hidapi_backend
|
||||
@ -1117,6 +1154,8 @@ int SDL_hid_init(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
use_libusb_whitelist = SDL_GetHintBoolean("SDL_HIDAPI_LIBUSB_WHITELIST",
|
||||
SDL_HIDAPI_LIBUSB_WHITELIST_DEFAULT);
|
||||
#ifdef HAVE_LIBUSB
|
||||
if (SDL_getenv("SDL_HIDAPI_DISABLE_LIBUSB") != NULL) {
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
|
||||
@ -1280,144 +1319,170 @@ Uint32 SDL_hid_device_change_count(void)
|
||||
return counter;
|
||||
}
|
||||
|
||||
static void AddDeviceToEnumeration(const char *driver_name, struct hid_device_info *dev, struct SDL_hid_device_info **devs, struct SDL_hid_device_info **last)
|
||||
{
|
||||
struct SDL_hid_device_info *new_dev;
|
||||
|
||||
#ifdef DEBUG_HIDAPI
|
||||
SDL_Log("Adding %s device to enumeration: %ls %ls 0x%.4hx/0x%.4hx/%d",
|
||||
driver_name, dev->manufacturer_string, dev->product_string, dev->vendor_id, dev->product_id, dev->interface_number);
|
||||
#else
|
||||
(void)driver_name;
|
||||
#endif
|
||||
|
||||
new_dev = (struct SDL_hid_device_info *)SDL_malloc(sizeof(struct SDL_hid_device_info));
|
||||
if (new_dev == NULL) {
|
||||
/* Don't bother returning an error, get as many devices as possible */
|
||||
return;
|
||||
}
|
||||
CopyHIDDeviceInfo(dev, new_dev);
|
||||
|
||||
if ((*last) != NULL) {
|
||||
(*last)->next = new_dev;
|
||||
} else {
|
||||
*devs = new_dev;
|
||||
}
|
||||
*last = new_dev;
|
||||
}
|
||||
|
||||
#if defined(HAVE_LIBUSB) || defined(HAVE_PLATFORM_BACKEND)
|
||||
static void RemoveDeviceFromEnumeration(const char *driver_name, struct hid_device_info *dev, struct hid_device_info **devs, void (*free_device_info)(struct hid_device_info *))
|
||||
{
|
||||
struct hid_device_info *last = NULL, *curr, *next;
|
||||
|
||||
for (curr = *devs; curr; curr = next) {
|
||||
next = curr->next;
|
||||
|
||||
if (dev->vendor_id == curr->vendor_id &&
|
||||
dev->product_id == curr->product_id &&
|
||||
(dev->interface_number < 0 || curr->interface_number < 0 || dev->interface_number == curr->interface_number)) {
|
||||
#ifdef DEBUG_HIDAPI
|
||||
SDL_Log("Skipping %s device: %ls %ls 0x%.4hx/0x%.4hx/%d",
|
||||
driver_name, curr->manufacturer_string, curr->product_string, curr->vendor_id, curr->product_id, curr->interface_number);
|
||||
#else
|
||||
(void)driver_name;
|
||||
#endif
|
||||
if (last) {
|
||||
last->next = next;
|
||||
} else {
|
||||
*devs = next;
|
||||
}
|
||||
|
||||
curr->next = NULL;
|
||||
free_device_info(curr);
|
||||
continue;
|
||||
}
|
||||
last = curr;
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_LIBUSB || HAVE_PLATFORM_BACKEND */
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
static void RemoveNonWhitelistedDevicesFromEnumeration(struct hid_device_info **devs, void (*free_device_info)(struct hid_device_info *))
|
||||
{
|
||||
struct hid_device_info *last = NULL, *curr, *next;
|
||||
|
||||
for (curr = *devs; curr; curr = next) {
|
||||
next = curr->next;
|
||||
|
||||
if (!IsInWhitelist(curr->vendor_id, curr->product_id)) {
|
||||
#ifdef DEBUG_HIDAPI
|
||||
SDL_Log("Device was not in libusb whitelist, skipping: %ls %ls 0x%.4hx/0x%.4hx/%d",
|
||||
curr->manufacturer_string, curr->product_string, curr->vendor_id, curr->product_id, curr->interface_number);
|
||||
#endif
|
||||
if (last) {
|
||||
last->next = next;
|
||||
} else {
|
||||
*devs = next;
|
||||
}
|
||||
|
||||
curr->next = NULL;
|
||||
free_device_info(curr);
|
||||
continue;
|
||||
}
|
||||
last = curr;
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_LIBUSB */
|
||||
|
||||
struct SDL_hid_device_info *SDL_hid_enumerate(unsigned short vendor_id, unsigned short product_id)
|
||||
{
|
||||
#if defined(HAVE_PLATFORM_BACKEND) || defined(HAVE_DRIVER_BACKEND) || defined(HAVE_LIBUSB)
|
||||
#ifdef HAVE_LIBUSB
|
||||
struct hid_device_info *usb_devs = NULL;
|
||||
struct hid_device_info *usb_dev;
|
||||
#endif
|
||||
#ifdef HAVE_DRIVER_BACKEND
|
||||
struct hid_device_info *driver_devs = NULL;
|
||||
struct hid_device_info *driver_dev;
|
||||
#endif
|
||||
#ifdef HAVE_PLATFORM_BACKEND
|
||||
struct hid_device_info *usb_devs = NULL;
|
||||
struct hid_device_info *raw_devs = NULL;
|
||||
struct hid_device_info *raw_dev;
|
||||
#endif
|
||||
struct SDL_hid_device_info *devs = NULL, *last = NULL, *new_dev;
|
||||
struct hid_device_info *dev;
|
||||
struct SDL_hid_device_info *devs = NULL, *last = NULL;
|
||||
|
||||
if (SDL_hidapi_refcount == 0 && SDL_hid_init() != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Collect the available devices */
|
||||
#ifdef HAVE_DRIVER_BACKEND
|
||||
driver_devs = DRIVER_hid_enumerate(vendor_id, product_id);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
if (libusb_ctx.libhandle) {
|
||||
usb_devs = LIBUSB_hid_enumerate(vendor_id, product_id);
|
||||
#ifdef DEBUG_HIDAPI
|
||||
SDL_Log("libusb devices found:");
|
||||
#endif
|
||||
for (usb_dev = usb_devs; usb_dev; usb_dev = usb_dev->next) {
|
||||
new_dev = (struct SDL_hid_device_info *)SDL_malloc(sizeof(struct SDL_hid_device_info));
|
||||
if (new_dev == NULL) {
|
||||
LIBUSB_hid_free_enumeration(usb_devs);
|
||||
SDL_hid_free_enumeration(devs);
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
}
|
||||
CopyHIDDeviceInfo(usb_dev, new_dev);
|
||||
#ifdef DEBUG_HIDAPI
|
||||
SDL_Log(" - %ls %ls 0x%.4hx 0x%.4hx",
|
||||
usb_dev->manufacturer_string, usb_dev->product_string,
|
||||
usb_dev->vendor_id, usb_dev->product_id);
|
||||
#endif
|
||||
|
||||
if (last != NULL) {
|
||||
last->next = new_dev;
|
||||
} else {
|
||||
devs = new_dev;
|
||||
}
|
||||
last = new_dev;
|
||||
if (use_libusb_whitelist) {
|
||||
RemoveNonWhitelistedDevicesFromEnumeration(&usb_devs, LIBUSB_hid_free_enumeration);
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_LIBUSB */
|
||||
|
||||
#ifdef HAVE_DRIVER_BACKEND
|
||||
driver_devs = DRIVER_hid_enumerate(vendor_id, product_id);
|
||||
for (driver_dev = driver_devs; driver_dev; driver_dev = driver_dev->next) {
|
||||
new_dev = (struct SDL_hid_device_info *)SDL_malloc(sizeof(struct SDL_hid_device_info));
|
||||
CopyHIDDeviceInfo(driver_dev, new_dev);
|
||||
|
||||
if (last != NULL) {
|
||||
last->next = new_dev;
|
||||
} else {
|
||||
devs = new_dev;
|
||||
}
|
||||
last = new_dev;
|
||||
}
|
||||
#endif /* HAVE_DRIVER_BACKEND */
|
||||
|
||||
#ifdef HAVE_PLATFORM_BACKEND
|
||||
if (udev_ctx) {
|
||||
raw_devs = PLATFORM_hid_enumerate(vendor_id, product_id);
|
||||
#ifdef DEBUG_HIDAPI
|
||||
SDL_Log("hidraw devices found:");
|
||||
}
|
||||
#endif
|
||||
for (raw_dev = raw_devs; raw_dev; raw_dev = raw_dev->next) {
|
||||
SDL_bool bFound = SDL_FALSE;
|
||||
#ifdef DEBUG_HIDAPI
|
||||
SDL_Log(" - %ls %ls 0x%.4hx 0x%.4hx",
|
||||
raw_dev->manufacturer_string, raw_dev->product_string,
|
||||
raw_dev->vendor_id, raw_dev->product_id);
|
||||
#endif
|
||||
#ifdef HAVE_LIBUSB
|
||||
for (usb_dev = usb_devs; usb_dev; usb_dev = usb_dev->next) {
|
||||
if (raw_dev->vendor_id == usb_dev->vendor_id &&
|
||||
raw_dev->product_id == usb_dev->product_id &&
|
||||
(raw_dev->interface_number < 0 || raw_dev->interface_number == usb_dev->interface_number)) {
|
||||
bFound = SDL_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_DRIVER_BACKEND
|
||||
for (driver_dev = driver_devs; driver_dev; driver_dev = driver_dev->next) {
|
||||
if (raw_dev->vendor_id == driver_dev->vendor_id &&
|
||||
raw_dev->product_id == driver_dev->product_id &&
|
||||
(raw_dev->interface_number < 0 || raw_dev->interface_number == driver_dev->interface_number)) {
|
||||
bFound = SDL_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!bFound) {
|
||||
new_dev = (struct SDL_hid_device_info *)SDL_malloc(sizeof(struct SDL_hid_device_info));
|
||||
if (new_dev == NULL) {
|
||||
#ifdef HAVE_LIBUSB
|
||||
if (libusb_ctx.libhandle) {
|
||||
LIBUSB_hid_free_enumeration(usb_devs);
|
||||
}
|
||||
#endif
|
||||
PLATFORM_hid_free_enumeration(raw_devs);
|
||||
SDL_hid_free_enumeration(devs);
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
}
|
||||
CopyHIDDeviceInfo(raw_dev, new_dev);
|
||||
new_dev->next = NULL;
|
||||
|
||||
if (last != NULL) {
|
||||
last->next = new_dev;
|
||||
} else {
|
||||
devs = new_dev;
|
||||
}
|
||||
last = new_dev;
|
||||
}
|
||||
/* Highest priority are custom driver devices */
|
||||
for (dev = driver_devs; dev; dev = dev->next) {
|
||||
AddDeviceToEnumeration("driver", dev, &devs, &last);
|
||||
#ifdef HAVE_LIBUSB
|
||||
RemoveDeviceFromEnumeration("libusb", dev, &usb_devs, LIBUSB_hid_free_enumeration);
|
||||
#endif
|
||||
#ifdef HAVE_PLATFORM_BACKEND
|
||||
RemoveDeviceFromEnumeration("raw", dev, &raw_devs, PLATFORM_hid_free_enumeration);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* If whitelist is in effect, libusb has priority, otherwise raw devices do */
|
||||
if (use_libusb_whitelist) {
|
||||
for (dev = usb_devs; dev; dev = dev->next) {
|
||||
AddDeviceToEnumeration("libusb", dev, &devs, &last);
|
||||
#ifdef HAVE_PLATFORM_BACKEND
|
||||
RemoveDeviceFromEnumeration("raw", dev, &raw_devs, PLATFORM_hid_free_enumeration);
|
||||
#endif
|
||||
}
|
||||
PLATFORM_hid_free_enumeration(raw_devs);
|
||||
}
|
||||
#endif /* HAVE_PLATFORM_BACKEND */
|
||||
|
||||
for (dev = raw_devs; dev; dev = dev->next) {
|
||||
AddDeviceToEnumeration("platform", dev, &devs, &last);
|
||||
}
|
||||
} else {
|
||||
for (dev = raw_devs; dev; dev = dev->next) {
|
||||
AddDeviceToEnumeration("raw", dev, &devs, &last);
|
||||
#ifdef HAVE_LIBUSB
|
||||
if (libusb_ctx.libhandle) {
|
||||
LIBUSB_hid_free_enumeration(usb_devs);
|
||||
}
|
||||
RemoveDeviceFromEnumeration("libusb", dev, &usb_devs, LIBUSB_hid_free_enumeration);
|
||||
#endif
|
||||
return devs;
|
||||
}
|
||||
for (dev = usb_devs; dev; dev = dev->next) {
|
||||
AddDeviceToEnumeration("libusb", dev, &devs, &last);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
return NULL;
|
||||
#endif /* HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND || HAVE_LIBUSB */
|
||||
#ifdef HAVE_DRIVER_BACKEND
|
||||
DRIVER_hid_free_enumeration(driver_devs);
|
||||
#endif
|
||||
#ifdef HAVE_LIBUSB
|
||||
LIBUSB_hid_free_enumeration(usb_devs);
|
||||
#endif
|
||||
#ifdef HAVE_PLATFORM_BACKEND
|
||||
PLATFORM_hid_free_enumeration(raw_devs);
|
||||
#endif
|
||||
|
||||
return devs;
|
||||
}
|
||||
|
||||
void SDL_hid_free_enumeration(struct SDL_hid_device_info *devs)
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
/* #pragma push_macro/pop_macro works correctly only as of gcc >= 4.4.3
|
||||
clang-3.0 _seems_ to be OK. */
|
||||
#pragma push_macro("calloc")
|
||||
#pragma push_macro("malloc")
|
||||
#pragma push_macro("realloc")
|
||||
#pragma push_macro("free")
|
||||
@ -38,6 +39,7 @@
|
||||
#pragma push_macro("tolower")
|
||||
#pragma push_macro("wcsdup")
|
||||
|
||||
#undef calloc
|
||||
#undef malloc
|
||||
#undef realloc
|
||||
#undef free
|
||||
@ -53,6 +55,7 @@
|
||||
#undef tolower
|
||||
#undef wcsdup
|
||||
|
||||
#define calloc SDL_calloc
|
||||
#define malloc SDL_malloc
|
||||
#define realloc SDL_realloc
|
||||
#define free SDL_free
|
||||
@ -106,6 +109,7 @@ static int SDL_libusb_get_string_descriptor(libusb_device_handle *dev,
|
||||
#undef ICONV_CONST
|
||||
#undef UNDEF_ICONV_CONST
|
||||
#endif
|
||||
#pragma pop_macro("calloc")
|
||||
#pragma pop_macro("malloc")
|
||||
#pragma pop_macro("realloc")
|
||||
#pragma pop_macro("free")
|
||||
|
@ -1031,7 +1031,7 @@ extern "C"
|
||||
|
||||
int hid_init(void)
|
||||
{
|
||||
if ( !g_initialized )
|
||||
if ( !g_initialized && g_HIDDeviceManagerCallbackHandler )
|
||||
{
|
||||
// HIDAPI doesn't work well with Android < 4.3
|
||||
if (SDL_GetAndroidSDKVersion() >= 18) {
|
||||
@ -1040,12 +1040,6 @@ int hid_init(void)
|
||||
g_JVM->AttachCurrentThread( &env, NULL );
|
||||
pthread_setspecific( g_ThreadKey, (void*)env );
|
||||
|
||||
if ( !g_HIDDeviceManagerCallbackHandler )
|
||||
{
|
||||
LOGV( "hid_init() without callback handler" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Bluetooth is currently only used for Steam Controllers, so check that hint
|
||||
// before initializing Bluetooth, which will prompt the user for permission.
|
||||
bool init_usb = true;
|
||||
|
@ -74,7 +74,7 @@ case $host in
|
||||
backend="mac"
|
||||
os="darwin"
|
||||
threads="pthreads"
|
||||
LIBS="${LIBS} -framework IOKit -framework CoreFoundation -framework AppKit"
|
||||
LIBS="${LIBS} -framework IOKit -framework CoreFoundation"
|
||||
;;
|
||||
*-freebsd*)
|
||||
AC_MSG_RESULT([ (FreeBSD back-end)])
|
||||
|
2
src/hidapi/dist/hidapi.podspec
vendored
2
src/hidapi/dist/hidapi.podspec
vendored
@ -26,6 +26,6 @@ Pod::Spec.new do |spec|
|
||||
|
||||
spec.public_header_files = "hidapi/hidapi.h", "mac/hidapi_darwin.h"
|
||||
|
||||
spec.frameworks = "IOKit", "CoreFoundation", "AppKit"
|
||||
spec.frameworks = "IOKit", "CoreFoundation"
|
||||
|
||||
end
|
||||
|
@ -259,8 +259,16 @@ static int get_usage(uint8_t *report_descriptor, size_t size,
|
||||
//printf("Usage Page: %x\n", (uint32_t)*usage_page);
|
||||
}
|
||||
if (key_cmd == 0x8) {
|
||||
*usage = get_bytes(report_descriptor, size, data_len, i);
|
||||
usage_found = 1;
|
||||
if (data_len == 4) { /* Usages 5.5 / Usage Page 6.2.2.7 */
|
||||
*usage_page = get_bytes(report_descriptor, size, 2, i + 2);
|
||||
usage_page_found = 1;
|
||||
*usage = get_bytes(report_descriptor, size, 2, i);
|
||||
usage_found = 1;
|
||||
}
|
||||
else {
|
||||
*usage = get_bytes(report_descriptor, size, data_len, i);
|
||||
usage_found = 1;
|
||||
}
|
||||
//printf("Usage: %x\n", (uint32_t)*usage);
|
||||
}
|
||||
|
||||
@ -1015,6 +1023,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
||||
libusb_close(handle);
|
||||
handle = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} /* altsettings */
|
||||
} /* interfaces */
|
||||
|
@ -206,7 +206,7 @@ static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name)
|
||||
* Returns 1 if successful, 0 if an invalid key
|
||||
* Sets data_len and key_size when successful
|
||||
*/
|
||||
static int get_hid_item_size(__u8 *report_descriptor, unsigned int pos, __u32 size, int *data_len, int *key_size)
|
||||
static int get_hid_item_size(const __u8 *report_descriptor, __u32 size, unsigned int pos, int *data_len, int *key_size)
|
||||
{
|
||||
int key = report_descriptor[pos];
|
||||
int size_code;
|
||||
@ -262,7 +262,7 @@ static int get_hid_item_size(__u8 *report_descriptor, unsigned int pos, __u32 si
|
||||
* Get bytes from a HID Report Descriptor.
|
||||
* Only call with a num_bytes of 0, 1, 2, or 4.
|
||||
*/
|
||||
static __u32 get_hid_report_bytes(__u8 *rpt, size_t len, size_t num_bytes, size_t cur)
|
||||
static __u32 get_hid_report_bytes(const __u8 *rpt, size_t len, size_t num_bytes, size_t cur)
|
||||
{
|
||||
/* Return if there aren't enough bytes. */
|
||||
if (cur + num_bytes >= len)
|
||||
@ -285,6 +285,60 @@ static __u32 get_hid_report_bytes(__u8 *rpt, size_t len, size_t num_bytes, size_
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterates until the end of a Collection.
|
||||
* Assumes that *pos is exactly at the beginning of a Collection.
|
||||
* Skips all nested Collection, i.e. iterates until the end of current level Collection.
|
||||
*
|
||||
* The return value is non-0 when an end of current Collection is found,
|
||||
* 0 when error is occured (broken Descriptor, end of a Collection is found before its begin,
|
||||
* or no Collection is found at all).
|
||||
*/
|
||||
static int hid_iterate_over_collection(const __u8 *report_descriptor, __u32 size, unsigned int *pos, int *data_len, int *key_size)
|
||||
{
|
||||
int collection_level = 0;
|
||||
|
||||
while (*pos < size) {
|
||||
int key = report_descriptor[*pos];
|
||||
int key_cmd = key & 0xfc;
|
||||
|
||||
/* Determine data_len and key_size */
|
||||
if (!get_hid_item_size(report_descriptor, size, *pos, data_len, key_size))
|
||||
return 0; /* malformed report */
|
||||
|
||||
switch (key_cmd) {
|
||||
case 0xa0: /* Collection 6.2.2.4 (Main) */
|
||||
collection_level++;
|
||||
break;
|
||||
case 0xc0: /* End Collection 6.2.2.4 (Main) */
|
||||
collection_level--;
|
||||
break;
|
||||
}
|
||||
|
||||
if (collection_level < 0) {
|
||||
/* Broken descriptor or someone is using this function wrong,
|
||||
* i.e. should be called exactly at the collection start */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (collection_level == 0) {
|
||||
/* Found it!
|
||||
* Also possible when called not at the collection start, but should not happen if used correctly */
|
||||
return 1;
|
||||
}
|
||||
|
||||
*pos += *data_len + *key_size;
|
||||
}
|
||||
|
||||
return 0; /* Did not find the end of a Collection */
|
||||
}
|
||||
|
||||
struct hid_usage_iterator {
|
||||
unsigned int pos;
|
||||
int usage_page_found;
|
||||
unsigned short usage_page;
|
||||
};
|
||||
|
||||
/*
|
||||
* Retrieves the device's Usage Page and Usage from the report descriptor.
|
||||
* The algorithm returns the current Usage Page/Usage pair whenever a new
|
||||
@ -302,63 +356,64 @@ static __u32 get_hid_report_bytes(__u8 *rpt, size_t len, size_t num_bytes, size_
|
||||
* 1 when finished processing descriptor.
|
||||
* -1 on a malformed report.
|
||||
*/
|
||||
static int get_next_hid_usage(__u8 *report_descriptor, __u32 size, unsigned int *pos, unsigned short *usage_page, unsigned short *usage)
|
||||
static int get_next_hid_usage(const __u8 *report_descriptor, __u32 size, struct hid_usage_iterator *ctx, unsigned short *usage_page, unsigned short *usage)
|
||||
{
|
||||
int data_len, key_size;
|
||||
int initial = *pos == 0; /* Used to handle case where no top-level application collection is defined */
|
||||
int usage_pair_ready = 0;
|
||||
int initial = ctx->pos == 0; /* Used to handle case where no top-level application collection is defined */
|
||||
|
||||
/* Usage is a Local Item, it must be set before each Main Item (Collection) before a pair is returned */
|
||||
int usage_found = 0;
|
||||
|
||||
while (*pos < size) {
|
||||
int key = report_descriptor[*pos];
|
||||
while (ctx->pos < size) {
|
||||
int key = report_descriptor[ctx->pos];
|
||||
int key_cmd = key & 0xfc;
|
||||
|
||||
/* Determine data_len and key_size */
|
||||
if (!get_hid_item_size(report_descriptor, *pos, size, &data_len, &key_size))
|
||||
if (!get_hid_item_size(report_descriptor, size, ctx->pos, &data_len, &key_size))
|
||||
return -1; /* malformed report */
|
||||
|
||||
switch (key_cmd) {
|
||||
case 0x4: /* Usage Page 6.2.2.7 (Global) */
|
||||
*usage_page = get_hid_report_bytes(report_descriptor, size, data_len, *pos);
|
||||
ctx->usage_page = get_hid_report_bytes(report_descriptor, size, data_len, ctx->pos);
|
||||
ctx->usage_page_found = 1;
|
||||
break;
|
||||
|
||||
case 0x8: /* Usage 6.2.2.8 (Local) */
|
||||
*usage = get_hid_report_bytes(report_descriptor, size, data_len, *pos);
|
||||
usage_found = 1;
|
||||
if (data_len == 4) { /* Usages 5.5 / Usage Page 6.2.2.7 */
|
||||
ctx->usage_page = get_hid_report_bytes(report_descriptor, size, 2, ctx->pos + 2);
|
||||
ctx->usage_page_found = 1;
|
||||
*usage = get_hid_report_bytes(report_descriptor, size, 2, ctx->pos);
|
||||
usage_found = 1;
|
||||
}
|
||||
else {
|
||||
*usage = get_hid_report_bytes(report_descriptor, size, data_len, ctx->pos);
|
||||
usage_found = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xa0: /* Collection 6.2.2.4 (Main) */
|
||||
/* A Usage Item (Local) must be found for the pair to be valid */
|
||||
if (usage_found)
|
||||
usage_pair_ready = 1;
|
||||
if (!hid_iterate_over_collection(report_descriptor, size, &ctx->pos, &data_len, &key_size)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Usage is a Local Item, unset it */
|
||||
usage_found = 0;
|
||||
break;
|
||||
/* A pair is valid - to be reported when Collection is found */
|
||||
if (usage_found && ctx->usage_page_found) {
|
||||
*usage_page = ctx->usage_page;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case 0x80: /* Input 6.2.2.4 (Main) */
|
||||
case 0x90: /* Output 6.2.2.4 (Main) */
|
||||
case 0xb0: /* Feature 6.2.2.4 (Main) */
|
||||
case 0xc0: /* End Collection 6.2.2.4 (Main) */
|
||||
/* Usage is a Local Item, unset it */
|
||||
usage_found = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Skip over this key and its associated data */
|
||||
*pos += data_len + key_size;
|
||||
|
||||
/* Return usage pair */
|
||||
if (usage_pair_ready)
|
||||
return 0;
|
||||
ctx->pos += data_len + key_size;
|
||||
}
|
||||
|
||||
/* If no top-level application collection is found and usage page/usage pair is found, pair is valid
|
||||
https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/top-level-collections */
|
||||
if (initial && usage_found)
|
||||
return 0; /* success */
|
||||
if (initial && usage_found && ctx->usage_page_found) {
|
||||
*usage_page = ctx->usage_page;
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
return 1; /* finished processing */
|
||||
}
|
||||
@ -804,12 +859,14 @@ static struct hid_device_info * create_device_info_for_device(struct udev_device
|
||||
result = get_hid_report_descriptor_from_sysfs(sysfs_path, &report_desc);
|
||||
if (result >= 0) {
|
||||
unsigned short page = 0, usage = 0;
|
||||
unsigned int pos = 0;
|
||||
struct hid_usage_iterator usage_iterator;
|
||||
memset(&usage_iterator, 0, sizeof(usage_iterator));
|
||||
|
||||
/*
|
||||
* Parse the first usage and usage page
|
||||
* out of the report descriptor.
|
||||
*/
|
||||
if (!get_next_hid_usage(report_desc.value, report_desc.size, &pos, &page, &usage)) {
|
||||
if (!get_next_hid_usage(report_desc.value, report_desc.size, &usage_iterator, &page, &usage)) {
|
||||
cur_dev->usage_page = page;
|
||||
cur_dev->usage = usage;
|
||||
}
|
||||
@ -818,7 +875,7 @@ static struct hid_device_info * create_device_info_for_device(struct udev_device
|
||||
* Parse any additional usage and usage pages
|
||||
* out of the report descriptor.
|
||||
*/
|
||||
while (!get_next_hid_usage(report_desc.value, report_desc.size, &pos, &page, &usage)) {
|
||||
while (!get_next_hid_usage(report_desc.value, report_desc.size, &usage_iterator, &page, &usage)) {
|
||||
/* Create new record for additional usage pairs */
|
||||
struct hid_device_info *tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
|
||||
struct hid_device_info *prev_dev = cur_dev;
|
||||
@ -980,9 +1037,10 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
||||
|
||||
struct hidraw_report_descriptor report_desc;
|
||||
unsigned short page = 0, usage = 0;
|
||||
unsigned int pos = 0;
|
||||
if (get_hid_report_descriptor_from_sysfs(sysfs_path, &report_desc) >= 0) {
|
||||
get_next_hid_usage(report_desc.value, report_desc.size, &pos, &page, &usage);
|
||||
struct hid_usage_iterator usage_iterator;
|
||||
memset(&usage_iterator, 0, sizeof(usage_iterator));
|
||||
get_next_hid_usage(report_desc.value, report_desc.size, &usage_iterator, &page, &usage);
|
||||
}
|
||||
if (HIDAPI_IGNORE_DEVICE(bus_type, dev_vid, dev_pid, page, usage)) {
|
||||
continue;
|
||||
|
@ -12,7 +12,7 @@ find_package(Threads REQUIRED)
|
||||
target_link_libraries(hidapi_darwin
|
||||
PUBLIC hidapi_include
|
||||
PRIVATE Threads::Threads
|
||||
PRIVATE "-framework IOKit" "-framework CoreFoundation" "-framework AppKit"
|
||||
PRIVATE "-framework IOKit" "-framework CoreFoundation"
|
||||
)
|
||||
|
||||
set_target_properties(hidapi_darwin
|
||||
|
@ -12,7 +12,7 @@ CC=gcc
|
||||
COBJS=hid.o ../hidtest/test.o
|
||||
OBJS=$(COBJS)
|
||||
CFLAGS+=-I../hidapi -I. -Wall -g -c
|
||||
LIBS=-framework IOKit -framework CoreFoundation -framework AppKit
|
||||
LIBS=-framework IOKit -framework CoreFoundation
|
||||
|
||||
|
||||
hidtest: $(OBJS)
|
||||
|
@ -38,9 +38,6 @@
|
||||
|
||||
#include "hidapi_darwin.h"
|
||||
|
||||
/* As defined in AppKit.h, but we don't need the entire AppKit for a single constant. */
|
||||
extern const double NSAppKitVersionNumber;
|
||||
|
||||
/* Barrier implementation because Mac OSX doesn't have pthread_barrier.
|
||||
It also doesn't have clock_gettime(). So much for POSIX and SUSv2.
|
||||
This implementation came from Brent Priddy and was posted on
|
||||
@ -477,7 +474,7 @@ int HID_API_EXPORT hid_init(void)
|
||||
register_global_error(NULL);
|
||||
|
||||
if (!hid_mgr) {
|
||||
is_macos_10_10_or_greater = (NSAppKitVersionNumber >= 1343); /* NSAppKitVersionNumber10_10 */
|
||||
is_macos_10_10_or_greater = (kCFCoreFoundationVersionNumber >= 1151.16); /* kCFCoreFoundationVersionNumber10_10 */
|
||||
hid_darwin_set_open_exclusive(1); /* Backward compatibility */
|
||||
return init_hid_manager();
|
||||
}
|
||||
|
@ -57,52 +57,6 @@
|
||||
static SDL_bool SDL_gamepads_initialized;
|
||||
static SDL_Gamepad *SDL_gamepads SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SDL_GAMEPAD_BINDTYPE_NONE = 0,
|
||||
SDL_GAMEPAD_BINDTYPE_BUTTON,
|
||||
SDL_GAMEPAD_BINDTYPE_AXIS,
|
||||
SDL_GAMEPAD_BINDTYPE_HAT
|
||||
} SDL_GamepadBindingType;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SDL_GamepadBindingType inputType;
|
||||
union
|
||||
{
|
||||
int button;
|
||||
|
||||
struct
|
||||
{
|
||||
int axis;
|
||||
int axis_min;
|
||||
int axis_max;
|
||||
} axis;
|
||||
|
||||
struct
|
||||
{
|
||||
int hat;
|
||||
int hat_mask;
|
||||
} hat;
|
||||
|
||||
} input;
|
||||
|
||||
SDL_GamepadBindingType outputType;
|
||||
union
|
||||
{
|
||||
SDL_GamepadButton button;
|
||||
|
||||
struct
|
||||
{
|
||||
SDL_GamepadAxis axis;
|
||||
int axis_min;
|
||||
int axis_max;
|
||||
} axis;
|
||||
|
||||
} output;
|
||||
|
||||
} SDL_GamepadBinding;
|
||||
|
||||
/* our hard coded list of mapping support */
|
||||
typedef enum
|
||||
{
|
||||
@ -3180,6 +3134,45 @@ SDL_Gamepad *SDL_GetGamepadFromPlayerIndex(int player_index)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the SDL joystick layer bindings for this gamepad
|
||||
*/
|
||||
SDL_GamepadBinding **SDL_GetGamepadBindings(SDL_Gamepad *gamepad, int *count)
|
||||
{
|
||||
SDL_GamepadBinding **bindings = NULL;
|
||||
|
||||
if (count) {
|
||||
*count = 0;
|
||||
}
|
||||
|
||||
SDL_LockJoysticks();
|
||||
{
|
||||
CHECK_GAMEPAD_MAGIC(gamepad, NULL);
|
||||
|
||||
size_t pointers_size = ((gamepad->num_bindings + 1) * sizeof(SDL_GamepadBinding *));
|
||||
size_t elements_size = (gamepad->num_bindings * sizeof(SDL_GamepadBinding));
|
||||
bindings = (SDL_GamepadBinding **)SDL_malloc(pointers_size + elements_size);
|
||||
if (bindings) {
|
||||
SDL_GamepadBinding *binding = (SDL_GamepadBinding *)((Uint8 *)bindings + pointers_size);
|
||||
int i;
|
||||
for (i = 0; i < gamepad->num_bindings; ++i, ++binding) {
|
||||
bindings[i] = binding;
|
||||
SDL_copyp(binding, &gamepad->bindings[i]);
|
||||
}
|
||||
bindings[i] = NULL;
|
||||
|
||||
if (count) {
|
||||
*count = gamepad->num_bindings;
|
||||
}
|
||||
} else {
|
||||
SDL_OutOfMemory();
|
||||
}
|
||||
}
|
||||
SDL_UnlockJoysticks();
|
||||
|
||||
return bindings;
|
||||
}
|
||||
|
||||
int SDL_RumbleGamepad(SDL_Gamepad *gamepad, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
|
||||
{
|
||||
SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
|
||||
|
@ -2640,8 +2640,11 @@ static SDL_bool SDL_IsJoystickProductWheel(Uint32 vidpid)
|
||||
MAKE_VIDPID(0x046d, 0xc262), /* Logitech G920 (active mode) */
|
||||
MAKE_VIDPID(0x046d, 0xc268), /* Logitech PRO Racing Wheel (PC mode) */
|
||||
MAKE_VIDPID(0x046d, 0xc269), /* Logitech PRO Racing Wheel (PS4/PS5 mode) */
|
||||
MAKE_VIDPID(0x046d, 0xc272), /* Logitech PRO Racing Wheel for Xbox (PC mode) */
|
||||
MAKE_VIDPID(0x046d, 0xc26d), /* Logitech G923 (Xbox) */
|
||||
MAKE_VIDPID(0x046d, 0xc26e), /* Logitech G923 */
|
||||
MAKE_VIDPID(0x046d, 0xc266), /* Logitech G923 for Playstation 4 and PC (PC mode) */
|
||||
MAKE_VIDPID(0x046d, 0xc267), /* Logitech G923 for Playstation 4 and PC (PS4 mode)*/
|
||||
MAKE_VIDPID(0x046d, 0xca03), /* Logitech Momo Racing */
|
||||
MAKE_VIDPID(0x044f, 0xb65d), /* Thrustmaster Wheel FFB */
|
||||
MAKE_VIDPID(0x044f, 0xb66d), /* Thrustmaster Wheel FFB */
|
||||
@ -2653,6 +2656,17 @@ static SDL_bool SDL_IsJoystickProductWheel(Uint32 vidpid)
|
||||
MAKE_VIDPID(0x044f, 0xb65e), /* Thrustmaster T500RS */
|
||||
MAKE_VIDPID(0x044f, 0xb664), /* Thrustmaster TX (initial mode) */
|
||||
MAKE_VIDPID(0x044f, 0xb669), /* Thrustmaster TX (active mode) */
|
||||
MAKE_VIDPID(0x0483, 0x0522), /* Simagic Wheelbase (including M10, Alpha Mini, Alpha, Alpha U) */
|
||||
MAKE_VIDPID(0x0eb7, 0x0001), /* Fanatec ClubSport Wheel Base V2 */
|
||||
MAKE_VIDPID(0x0eb7, 0x0004), /* Fanatec ClubSport Wheel Base V2.5 */
|
||||
MAKE_VIDPID(0x0eb7, 0x0005), /* Fanatec CSL Elite Wheel Base+ (PS4) */
|
||||
MAKE_VIDPID(0x0eb7, 0x0006), /* Fanatec Podium Wheel Base DD1 */
|
||||
MAKE_VIDPID(0x0eb7, 0x0007), /* Fanatec Podium Wheel Base DD2 */
|
||||
MAKE_VIDPID(0x0eb7, 0x0011), /* Fanatec Forza Motorsport (CSR Wheel / CSR Elite Wheel) */
|
||||
MAKE_VIDPID(0x0eb7, 0x0020), /* Fanatec generic wheel / CSL DD / GT DD Pro */
|
||||
MAKE_VIDPID(0x0eb7, 0x0197), /* Fanatec Porsche Wheel (Turbo / GT3 RS / Turbo S / GT3 V2 / GT2) */
|
||||
MAKE_VIDPID(0x0eb7, 0x038e), /* Fanatec ClubSport Wheel Base V1 */
|
||||
MAKE_VIDPID(0x0eb7, 0x0e03), /* Fanatec CSL Elite Wheel Base */
|
||||
MAKE_VIDPID(0x11ff, 0x0511), /* DragonRise Inc. Wired Wheel (initial mode) (also known as PXN V900 (PS3), Superdrive SV-750, or a Genesis Seaborg 400) */
|
||||
};
|
||||
int i;
|
||||
|
@ -102,7 +102,7 @@ typedef struct
|
||||
Uint8 rgucAccelX[2]; /* 21 */
|
||||
Uint8 rgucAccelY[2]; /* 23 */
|
||||
Uint8 rgucAccelZ[2]; /* 25 */
|
||||
Uint8 rgucSensorTimestamp[4]; /* 27 - 32 bit little endian */
|
||||
Uint8 rgucSensorTimestamp[4]; /* 27 - 16/32 bit little endian */
|
||||
|
||||
} PS5StatePacketCommon_t;
|
||||
|
||||
@ -154,7 +154,9 @@ typedef struct
|
||||
Uint8 rgucAccelX[2]; /* 21 */
|
||||
Uint8 rgucAccelY[2]; /* 23 */
|
||||
Uint8 rgucAccelZ[2]; /* 25 */
|
||||
Uint8 rgucSensorTimestamp[4]; /* 27 - 32 bit little endian */
|
||||
Uint8 rgucSensorTimestamp[2]; /* 27 - 16 bit little endian */
|
||||
Uint8 ucBatteryLevel; /* 29 */
|
||||
Uint8 ucUnknown; /* 30 */
|
||||
Uint8 ucTouchpadCounter1; /* 31 - high bit clear + counter */
|
||||
Uint8 rgucTouchpadData1[3]; /* 32 - X/Y, 12 bits per axis */
|
||||
Uint8 ucTouchpadCounter2; /* 35 - high bit clear + counter */
|
||||
@ -494,6 +496,7 @@ static SDL_bool HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device)
|
||||
/* The Razer Wolverine V2 Pro doesn't respond to the detection protocol, but has a touchpad and sensors, but no vibration */
|
||||
ctx->sensors_supported = SDL_TRUE;
|
||||
ctx->touchpad_supported = SDL_TRUE;
|
||||
ctx->use_alternate_report = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
ctx->effects_supported = (ctx->lightbar_supported || ctx->vibration_supported || ctx->playerled_supported);
|
||||
@ -722,7 +725,7 @@ static void HIDAPI_DriverPS5_CheckPendingLEDReset(SDL_DriverPS5_Context *ctx)
|
||||
{
|
||||
SDL_bool led_reset_complete = SDL_FALSE;
|
||||
|
||||
if (ctx->enhanced_reports && ctx->sensors_supported) {
|
||||
if (ctx->enhanced_reports && ctx->sensors_supported && !ctx->use_alternate_report) {
|
||||
const PS5StatePacketCommon_t *packet = &ctx->last_state.state;
|
||||
|
||||
/* Check the timer to make sure the Bluetooth connection LED animation is complete */
|
||||
@ -1102,10 +1105,9 @@ static int HIDAPI_DriverPS5_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void HIDAPI_DriverPS5_HandleSimpleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5SimpleStatePacket_t *packet)
|
||||
static void HIDAPI_DriverPS5_HandleSimpleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5SimpleStatePacket_t *packet, Uint64 timestamp)
|
||||
{
|
||||
Sint16 axis;
|
||||
Uint64 timestamp = SDL_GetTicksNS();
|
||||
|
||||
if (ctx->last_state.simple.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) {
|
||||
{
|
||||
@ -1204,10 +1206,9 @@ static void HIDAPI_DriverPS5_HandleSimpleStatePacket(SDL_Joystick *joystick, SDL
|
||||
SDL_memcpy(&ctx->last_state.simple, packet, sizeof(ctx->last_state.simple));
|
||||
}
|
||||
|
||||
static void HIDAPI_DriverPS5_HandleStatePacketCommon(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketCommon_t *packet)
|
||||
static void HIDAPI_DriverPS5_HandleStatePacketCommon(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketCommon_t *packet, Uint64 timestamp)
|
||||
{
|
||||
Sint16 axis;
|
||||
Uint64 timestamp = SDL_GetTicksNS();
|
||||
|
||||
if (ctx->last_state.state.rgucButtonsAndHat[0] != packet->rgucButtonsAndHat[0]) {
|
||||
{
|
||||
@ -1309,25 +1310,42 @@ static void HIDAPI_DriverPS5_HandleStatePacketCommon(SDL_Joystick *joystick, SDL
|
||||
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
|
||||
|
||||
if (ctx->report_sensors) {
|
||||
Uint32 tick;
|
||||
Uint32 delta;
|
||||
Uint64 sensor_timestamp;
|
||||
float data[3];
|
||||
|
||||
tick = LOAD32(packet->rgucSensorTimestamp[0],
|
||||
packet->rgucSensorTimestamp[1],
|
||||
packet->rgucSensorTimestamp[2],
|
||||
packet->rgucSensorTimestamp[3]);
|
||||
if (ctx->last_tick < tick) {
|
||||
delta = (tick - ctx->last_tick);
|
||||
} else {
|
||||
delta = (SDL_MAX_UINT32 - ctx->last_tick + tick + 1);
|
||||
}
|
||||
ctx->sensor_ticks += delta;
|
||||
ctx->last_tick = tick;
|
||||
if (ctx->use_alternate_report) {
|
||||
/* 16-bit timestamp */
|
||||
Uint32 delta;
|
||||
Uint16 tick = LOAD16(packet->rgucSensorTimestamp[0],
|
||||
packet->rgucSensorTimestamp[1]);
|
||||
if (ctx->last_tick < tick) {
|
||||
delta = (tick - ctx->last_tick);
|
||||
} else {
|
||||
delta = (SDL_MAX_UINT16 - ctx->last_tick + tick + 1);
|
||||
}
|
||||
ctx->last_tick = tick;
|
||||
ctx->sensor_ticks += delta;
|
||||
|
||||
/* Sensor timestamp is in 0.33us units */
|
||||
sensor_timestamp = (ctx->sensor_ticks * SDL_NS_PER_US) / 3;
|
||||
/* Sensor timestamp is in 1us units */
|
||||
sensor_timestamp = SDL_US_TO_NS(ctx->sensor_ticks);
|
||||
} else {
|
||||
/* 32-bit timestamp */
|
||||
Uint32 delta;
|
||||
Uint32 tick = LOAD32(packet->rgucSensorTimestamp[0],
|
||||
packet->rgucSensorTimestamp[1],
|
||||
packet->rgucSensorTimestamp[2],
|
||||
packet->rgucSensorTimestamp[3]);
|
||||
if (ctx->last_tick < tick) {
|
||||
delta = (tick - ctx->last_tick);
|
||||
} else {
|
||||
delta = (SDL_MAX_UINT32 - ctx->last_tick + tick + 1);
|
||||
}
|
||||
ctx->last_tick = tick;
|
||||
ctx->sensor_ticks += delta;
|
||||
|
||||
/* Sensor timestamp is in 0.33us units */
|
||||
sensor_timestamp = (ctx->sensor_ticks * SDL_NS_PER_US) / 3;
|
||||
}
|
||||
|
||||
data[0] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 0, LOAD16(packet->rgucGyroX[0], packet->rgucGyroX[1]));
|
||||
data[1] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 1, LOAD16(packet->rgucGyroY[0], packet->rgucGyroY[1]));
|
||||
@ -1341,13 +1359,12 @@ static void HIDAPI_DriverPS5_HandleStatePacketCommon(SDL_Joystick *joystick, SDL
|
||||
}
|
||||
}
|
||||
|
||||
static void HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacket_t *packet)
|
||||
static void HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacket_t *packet, Uint64 timestamp)
|
||||
{
|
||||
static const float TOUCHPAD_SCALEX = 1.0f / 1920;
|
||||
static const float TOUCHPAD_SCALEY = 1.0f / 1070;
|
||||
Uint8 touchpad_state;
|
||||
int touchpad_x, touchpad_y;
|
||||
Uint64 timestamp = SDL_GetTicksNS();
|
||||
|
||||
if (ctx->report_touchpad) {
|
||||
touchpad_state = !(packet->ucTouchpadCounter1 & 0x80) ? SDL_PRESSED : SDL_RELEASED;
|
||||
@ -1378,13 +1395,12 @@ static void HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_d
|
||||
SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state));
|
||||
}
|
||||
|
||||
static void HIDAPI_DriverPS5_HandleStatePacketAlt(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketAlt_t *packet)
|
||||
static void HIDAPI_DriverPS5_HandleStatePacketAlt(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketAlt_t *packet, Uint64 timestamp)
|
||||
{
|
||||
static const float TOUCHPAD_SCALEX = 1.0f / 1920;
|
||||
static const float TOUCHPAD_SCALEY = 1.0f / 1070;
|
||||
Uint8 touchpad_state;
|
||||
int touchpad_x, touchpad_y;
|
||||
Uint64 timestamp = SDL_GetTicksNS();
|
||||
|
||||
if (ctx->report_touchpad) {
|
||||
touchpad_state = !(packet->ucTouchpadCounter1 & 0x80) ? SDL_PRESSED : SDL_RELEASED;
|
||||
@ -1447,6 +1463,8 @@ static SDL_bool HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
}
|
||||
|
||||
while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
|
||||
Uint64 timestamp = SDL_GetTicksNS();
|
||||
|
||||
#ifdef DEBUG_PS5_PROTOCOL
|
||||
HIDAPI_DumpPacket("PS5 packet: size = %d", data, size);
|
||||
#endif
|
||||
@ -1464,13 +1482,13 @@ static SDL_bool HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
switch (data[0]) {
|
||||
case k_EPS5ReportIdState:
|
||||
if (size == 10 || size == 78) {
|
||||
HIDAPI_DriverPS5_HandleSimpleStatePacket(joystick, device->dev, ctx, (PS5SimpleStatePacket_t *)&data[1]);
|
||||
HIDAPI_DriverPS5_HandleSimpleStatePacket(joystick, device->dev, ctx, (PS5SimpleStatePacket_t *)&data[1], timestamp);
|
||||
} else {
|
||||
HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, device->dev, ctx, (PS5StatePacketCommon_t *)&data[1]);
|
||||
HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, device->dev, ctx, (PS5StatePacketCommon_t *)&data[1], timestamp);
|
||||
if (ctx->use_alternate_report) {
|
||||
HIDAPI_DriverPS5_HandleStatePacketAlt(joystick, device->dev, ctx, (PS5StatePacketAlt_t *)&data[1]);
|
||||
HIDAPI_DriverPS5_HandleStatePacketAlt(joystick, device->dev, ctx, (PS5StatePacketAlt_t *)&data[1], timestamp);
|
||||
} else {
|
||||
HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[1]);
|
||||
HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[1], timestamp);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1478,11 +1496,11 @@ static SDL_bool HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
/* This is the extended report, we can enable effects now in default mode */
|
||||
HIDAPI_DriverPS5_UpdateEnhancedModeOnEnhancedReport(ctx);
|
||||
|
||||
HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, device->dev, ctx, (PS5StatePacketCommon_t *)&data[2]);
|
||||
HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, device->dev, ctx, (PS5StatePacketCommon_t *)&data[2], timestamp);
|
||||
if (ctx->use_alternate_report) {
|
||||
HIDAPI_DriverPS5_HandleStatePacketAlt(joystick, device->dev, ctx, (PS5StatePacketAlt_t *)&data[2]);
|
||||
HIDAPI_DriverPS5_HandleStatePacketAlt(joystick, device->dev, ctx, (PS5StatePacketAlt_t *)&data[2], timestamp);
|
||||
} else {
|
||||
HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[2]);
|
||||
HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[2], timestamp);
|
||||
}
|
||||
if (ctx->led_reset_state == k_EDS5LEDResetStatePending) {
|
||||
HIDAPI_DriverPS5_CheckPendingLEDReset(ctx);
|
||||
|
@ -663,6 +663,8 @@ static void HIDAPI_UpdateJoystickSerial(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
int i;
|
||||
|
||||
SDL_AssertJoysticksLocked();
|
||||
|
||||
for (i = 0; i < device->num_joysticks; ++i) {
|
||||
SDL_Joystick *joystick = SDL_GetJoystickFromInstanceID(device->joysticks[i]);
|
||||
if (joystick && device->serial) {
|
||||
|
@ -561,9 +561,17 @@ static void check_upower_device(DBusConnection *conn, const char *path, SDL_Powe
|
||||
st = SDL_POWERSTATE_UNKNOWN; /* uh oh */
|
||||
} else if (ui32 == 1) { /* 1 == charging */
|
||||
st = SDL_POWERSTATE_CHARGING;
|
||||
} else if ((ui32 == 2) || (ui32 == 3)) { /* 2 == discharging, 3 == empty. */
|
||||
} else if ((ui32 == 2) || (ui32 == 3) || (ui32 == 6)) {
|
||||
/* 2 == discharging;
|
||||
* 3 == empty;
|
||||
* 6 == "pending discharge" which GNOME interprets as equivalent
|
||||
* to discharging */
|
||||
st = SDL_POWERSTATE_ON_BATTERY;
|
||||
} else if (ui32 == 4) { /* 4 == full */
|
||||
} else if ((ui32 == 4) || (ui32 == 5)) {
|
||||
/* 4 == full;
|
||||
* 5 == "pending charge" which GNOME shows as "Not charging",
|
||||
* used when a battery is configured to stop charging at a
|
||||
* lower than 100% threshold */
|
||||
st = SDL_POWERSTATE_CHARGED;
|
||||
} else {
|
||||
st = SDL_POWERSTATE_UNKNOWN; /* uh oh */
|
||||
|
@ -2534,7 +2534,7 @@ int SDL_SetRenderClipRect(SDL_Renderer *renderer, const SDL_Rect *rect)
|
||||
int retval;
|
||||
CHECK_RENDERER_MAGIC(renderer, -1)
|
||||
|
||||
if (rect && rect->w > 0 && rect->h > 0) {
|
||||
if (rect && rect->w >= 0 && rect->h >= 0) {
|
||||
renderer->view->clipping_enabled = SDL_TRUE;
|
||||
renderer->view->clip_rect.x = rect->x;
|
||||
renderer->view->clip_rect.y = rect->y;
|
||||
|
@ -1754,8 +1754,22 @@ static SDL_Renderer *METAL_CreateRenderer(SDL_Window *window, Uint32 flags)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// !!! FIXME: MTLCopyAllDevices() can find other GPUs on macOS...
|
||||
mtldevice = MTLCreateSystemDefaultDevice();
|
||||
#ifdef __MACOS__
|
||||
if (SDL_GetHintBoolean(SDL_HINT_RENDER_METAL_PREFER_LOW_POWER_DEVICE, SDL_TRUE)) {
|
||||
NSArray<id<MTLDevice>> *devices = MTLCopyAllDevices();
|
||||
|
||||
for (id<MTLDevice> device in devices) {
|
||||
if (device.isLowPower) {
|
||||
mtldevice = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mtldevice == nil) {
|
||||
mtldevice = MTLCreateSystemDefaultDevice();
|
||||
}
|
||||
|
||||
if (mtldevice == nil) {
|
||||
SDL_free(renderer);
|
||||
|
@ -25,6 +25,7 @@
|
||||
static const char *common_usage[] = {
|
||||
"[-h | --help]",
|
||||
"[--trackmem]",
|
||||
"[--randmem]",
|
||||
"[--log all|error|system|audio|video|render|input]",
|
||||
};
|
||||
|
||||
@ -70,7 +71,7 @@ static const char *video_usage[] = {
|
||||
|
||||
/* !!! FIXME: Float32? Sint32? */
|
||||
static const char *audio_usage[] = {
|
||||
"[--audio driver]", "[--rate N]", "[--format U8|S8|S16|S16LE|S16BE]",
|
||||
"[--audio driver]", "[--rate N]", "[--format U8|S8|S16|S16LE|S16BE|S32|S32LE|S32BE|F32|F32LE|F32BE]",
|
||||
"[--channels N]"
|
||||
};
|
||||
|
||||
@ -95,7 +96,8 @@ SDLTest_CommonState *SDLTest_CommonCreateState(char **argv, Uint32 flags)
|
||||
for (i = 1; argv[i]; ++i) {
|
||||
if (SDL_strcasecmp(argv[i], "--trackmem") == 0) {
|
||||
SDLTest_TrackAllocations();
|
||||
break;
|
||||
} else if (SDL_strcasecmp(argv[i], "--randmem") == 0) {
|
||||
SDLTest_RandFillAllocations();
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,8 +147,8 @@ SDLTest_CommonState *SDLTest_CommonCreateState(char **argv, Uint32 flags)
|
||||
}
|
||||
|
||||
void SDLTest_CommonDestroyState(SDLTest_CommonState *state) {
|
||||
SDLTest_LogAllocations();
|
||||
SDL_free(state);
|
||||
SDLTest_LogAllocations();
|
||||
}
|
||||
|
||||
#define SEARCHARG(dim) \
|
||||
@ -170,6 +172,10 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index)
|
||||
/* Already handled in SDLTest_CommonCreateState() */
|
||||
return 1;
|
||||
}
|
||||
if (SDL_strcasecmp(argv[index], "--randmem") == 0) {
|
||||
/* Already handled in SDLTest_CommonCreateState() */
|
||||
return 1;
|
||||
}
|
||||
if (SDL_strcasecmp(argv[index], "--log") == 0) {
|
||||
++index;
|
||||
if (!argv[index]) {
|
||||
@ -624,16 +630,37 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index)
|
||||
return 2;
|
||||
}
|
||||
if (SDL_strcasecmp(argv[index], "S16LE") == 0) {
|
||||
state->audio_format = SDL_AUDIO_S16LSB;
|
||||
state->audio_format = SDL_AUDIO_S16LE;
|
||||
return 2;
|
||||
}
|
||||
if (SDL_strcasecmp(argv[index], "S16BE") == 0) {
|
||||
state->audio_format = SDL_AUDIO_S16MSB;
|
||||
state->audio_format = SDL_AUDIO_S16BE;
|
||||
return 2;
|
||||
}
|
||||
if (SDL_strcasecmp(argv[index], "S32") == 0) {
|
||||
state->audio_format = SDL_AUDIO_S32;
|
||||
return 2;
|
||||
}
|
||||
if (SDL_strcasecmp(argv[index], "S32LE") == 0) {
|
||||
state->audio_format = SDL_AUDIO_S32LE;
|
||||
return 2;
|
||||
}
|
||||
if (SDL_strcasecmp(argv[index], "S32BE") == 0) {
|
||||
state->audio_format = SDL_AUDIO_S32BE;
|
||||
return 2;
|
||||
}
|
||||
if (SDL_strcasecmp(argv[index], "F32") == 0) {
|
||||
state->audio_format = SDL_AUDIO_F32;
|
||||
return 2;
|
||||
}
|
||||
if (SDL_strcasecmp(argv[index], "F32LE") == 0) {
|
||||
state->audio_format = SDL_AUDIO_F32LE;
|
||||
return 2;
|
||||
}
|
||||
if (SDL_strcasecmp(argv[index], "F32BE") == 0) {
|
||||
state->audio_format = SDL_AUDIO_F32BE;
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* !!! FIXME: Float32? Sint32? */
|
||||
|
||||
return -1;
|
||||
}
|
||||
if (SDL_strcasecmp(argv[index], "--channels") == 0) {
|
||||
@ -2019,6 +2046,8 @@ void SDLTest_CommonEvent(SDLTest_CommonState *state, SDL_Event *event, int *done
|
||||
{
|
||||
SDL_Window *window = SDL_GetWindowFromID(event->window.windowID);
|
||||
if (window) {
|
||||
/* Clear cache to avoid stale textures */
|
||||
SDLTest_CleanupTextDrawing();
|
||||
for (i = 0; i < state->num_windows; ++i) {
|
||||
if (window == state->windows[i]) {
|
||||
if (state->targets[i]) {
|
||||
@ -2408,7 +2437,6 @@ void SDLTest_CommonQuit(SDLTest_CommonState *state)
|
||||
common_usage_audio = NULL;
|
||||
common_usage_videoaudio = NULL;
|
||||
|
||||
SDL_free(state->windows);
|
||||
if (state->targets) {
|
||||
for (i = 0; i < state->num_windows; ++i) {
|
||||
if (state->targets[i]) {
|
||||
@ -2425,6 +2453,12 @@ void SDLTest_CommonQuit(SDLTest_CommonState *state)
|
||||
}
|
||||
SDL_free(state->renderers);
|
||||
}
|
||||
if (state->windows) {
|
||||
for (i = 0; i < state->num_windows; i++) {
|
||||
SDL_DestroyWindow(state->windows[i]);
|
||||
}
|
||||
SDL_free(state->windows);
|
||||
}
|
||||
if (state->flags & SDL_INIT_VIDEO) {
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
}
|
||||
|
@ -3126,11 +3126,12 @@ struct SDLTest_CharTextureCache
|
||||
*/
|
||||
static struct SDLTest_CharTextureCache *SDLTest_CharTextureCacheList;
|
||||
|
||||
int FONT_CHARACTER_SIZE = 8;
|
||||
|
||||
int SDLTest_DrawCharacter(SDL_Renderer *renderer, float x, float y, Uint32 c)
|
||||
{
|
||||
const Uint32 charWidth = FONT_CHARACTER_SIZE;
|
||||
const Uint32 charHeight = FONT_CHARACTER_SIZE;
|
||||
const Uint32 charSize = FONT_CHARACTER_SIZE;
|
||||
SDL_FRect srect;
|
||||
SDL_FRect drect;
|
||||
int result;
|
||||
@ -3149,8 +3150,8 @@ int SDLTest_DrawCharacter(SDL_Renderer *renderer, float x, float y, Uint32 c)
|
||||
*/
|
||||
srect.x = 0.0f;
|
||||
srect.y = 0.0f;
|
||||
srect.w = (float)charWidth;
|
||||
srect.h = (float)charHeight;
|
||||
srect.w = 8.0f;
|
||||
srect.h = 8.0f;
|
||||
|
||||
/*
|
||||
* Setup destination rectangle
|
||||
@ -3190,7 +3191,7 @@ int SDLTest_DrawCharacter(SDL_Renderer *renderer, float x, float y, Uint32 c)
|
||||
return -1;
|
||||
}
|
||||
|
||||
charpos = SDLTest_FontData + ci * charSize;
|
||||
charpos = SDLTest_FontData + ci * 8;
|
||||
linepos = (Uint8 *)character->pixels;
|
||||
pitch = character->pitch;
|
||||
|
||||
@ -3221,6 +3222,8 @@ int SDLTest_DrawCharacter(SDL_Renderer *renderer, float x, float y, Uint32 c)
|
||||
if (cache->charTextureCache[ci] == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_SetTextureScaleMode(cache->charTextureCache[ci], SDL_SCALEMODE_NEAREST);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -25,6 +25,26 @@
|
||||
#include <libunwind.h>
|
||||
#endif
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
static void *s_dbghelp;
|
||||
|
||||
typedef BOOL (__stdcall *dbghelp_SymInitialize_fn)(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess);
|
||||
|
||||
typedef BOOL (__stdcall *dbghelp_SymFromAddr_fn)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol);
|
||||
static dbghelp_SymFromAddr_fn dbghelp_SymFromAddr;
|
||||
|
||||
#ifdef _WIN64
|
||||
typedef BOOL (__stdcall *dbghelp_SymGetLineFromAddr_fn)(HANDLE hProcess, DWORD64 qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line);
|
||||
#else
|
||||
typedef BOOL (__stdcall *dbghelp_SymGetLineFromAddr_fn)(HANDLE hProcess, DWORD qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE Line);
|
||||
#endif
|
||||
static dbghelp_SymGetLineFromAddr_fn dbghelp_SymGetLineFromAddr;
|
||||
|
||||
#endif
|
||||
|
||||
/* This is a simple tracking allocator to demonstrate the use of SDL's
|
||||
memory allocation replacement functionality.
|
||||
|
||||
@ -32,12 +52,14 @@
|
||||
for production code.
|
||||
*/
|
||||
|
||||
#define MAXIMUM_TRACKED_STACK_DEPTH 32
|
||||
|
||||
typedef struct SDL_tracked_allocation
|
||||
{
|
||||
void *mem;
|
||||
size_t size;
|
||||
Uint64 stack[10];
|
||||
char stack_names[10][256];
|
||||
Uint64 stack[MAXIMUM_TRACKED_STACK_DEPTH];
|
||||
char stack_names[MAXIMUM_TRACKED_STACK_DEPTH][256];
|
||||
struct SDL_tracked_allocation *next;
|
||||
} SDL_tracked_allocation;
|
||||
|
||||
@ -48,6 +70,7 @@ static SDL_realloc_func SDL_realloc_orig = NULL;
|
||||
static SDL_free_func SDL_free_orig = NULL;
|
||||
static int s_previous_allocations = 0;
|
||||
static SDL_tracked_allocation *s_tracked_allocations[256];
|
||||
static SDL_bool s_randfill_allocations = SDL_FALSE;
|
||||
|
||||
static unsigned int get_allocation_bucket(void *mem)
|
||||
{
|
||||
@ -58,16 +81,28 @@ static unsigned int get_allocation_bucket(void *mem)
|
||||
return index;
|
||||
}
|
||||
|
||||
static SDL_bool SDL_IsAllocationTracked(void *mem)
|
||||
static SDL_tracked_allocation* SDL_GetTrackedAllocation(void *mem)
|
||||
{
|
||||
SDL_tracked_allocation *entry;
|
||||
int index = get_allocation_bucket(mem);
|
||||
for (entry = s_tracked_allocations[index]; entry; entry = entry->next) {
|
||||
if (mem == entry->mem) {
|
||||
return SDL_TRUE;
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return SDL_FALSE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static size_t SDL_GetTrackedAllocationSize(void *mem)
|
||||
{
|
||||
SDL_tracked_allocation *entry = SDL_GetTrackedAllocation(mem);
|
||||
|
||||
return entry ? entry->size : SIZE_MAX;
|
||||
}
|
||||
|
||||
static SDL_bool SDL_IsAllocationTracked(void *mem)
|
||||
{
|
||||
return SDL_GetTrackedAllocation(mem) != NULL;
|
||||
}
|
||||
|
||||
static void SDL_TrackAllocation(void *mem, size_t size)
|
||||
@ -114,6 +149,40 @@ static void SDL_TrackAllocation(void *mem, size_t size)
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(__WINDOWS__)
|
||||
{
|
||||
Uint32 count;
|
||||
PVOID frames[63];
|
||||
Uint32 i;
|
||||
|
||||
count = CaptureStackBackTrace(1, SDL_arraysize(frames), frames, NULL);
|
||||
|
||||
entry->size = SDL_min(count, MAXIMUM_TRACKED_STACK_DEPTH);
|
||||
for (i = 0; i < entry->size; i++) {
|
||||
char symbol_buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
|
||||
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbol_buffer;
|
||||
DWORD64 dwDisplacement = 0;
|
||||
DWORD lineColumn = 0;
|
||||
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
pSymbol->MaxNameLen = MAX_SYM_NAME;
|
||||
IMAGEHLP_LINE line;
|
||||
line.SizeOfStruct = sizeof(line);
|
||||
|
||||
entry->stack[i] = (Uint64)(uintptr_t)frames[i];
|
||||
if (s_dbghelp) {
|
||||
if (!dbghelp_SymFromAddr(GetCurrentProcess(), (DWORD64)(uintptr_t)frames[i], &dwDisplacement, pSymbol)) {
|
||||
SDL_strlcpy(pSymbol->Name, "???", MAX_SYM_NAME);
|
||||
dwDisplacement = 0;
|
||||
}
|
||||
if (!dbghelp_SymGetLineFromAddr(GetCurrentProcess(), (DWORD64)(uintptr_t)frames[i], &lineColumn, &line)) {
|
||||
line.FileName = "";
|
||||
line.LineNumber = 0;
|
||||
}
|
||||
|
||||
SDL_snprintf(entry->stack_names[i], sizeof(entry->stack_names[i]), "%s+0x%llx %s:%u", pSymbol->Name, (unsigned long long)dwDisplacement, line.FileName, (Uint32)line.LineNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_LIBUNWIND_H */
|
||||
|
||||
entry->next = s_tracked_allocations[index];
|
||||
@ -140,6 +209,19 @@ static void SDL_UntrackAllocation(void *mem)
|
||||
}
|
||||
}
|
||||
|
||||
static void rand_fill_memory(void* ptr, size_t start, size_t end)
|
||||
{
|
||||
Uint8* mem = (Uint8*) ptr;
|
||||
size_t i;
|
||||
|
||||
if (!s_randfill_allocations)
|
||||
return;
|
||||
|
||||
for (i = start; i < end; ++i) {
|
||||
mem[i] = SDLTest_RandomUint8();
|
||||
}
|
||||
}
|
||||
|
||||
static void *SDLCALL SDLTest_TrackedMalloc(size_t size)
|
||||
{
|
||||
void *mem;
|
||||
@ -147,6 +229,7 @@ static void *SDLCALL SDLTest_TrackedMalloc(size_t size)
|
||||
mem = SDL_malloc_orig(size);
|
||||
if (mem) {
|
||||
SDL_TrackAllocation(mem, size);
|
||||
rand_fill_memory(mem, 0, size);
|
||||
}
|
||||
return mem;
|
||||
}
|
||||
@ -165,14 +248,20 @@ static void *SDLCALL SDLTest_TrackedCalloc(size_t nmemb, size_t size)
|
||||
static void *SDLCALL SDLTest_TrackedRealloc(void *ptr, size_t size)
|
||||
{
|
||||
void *mem;
|
||||
|
||||
SDL_assert(ptr == NULL || SDL_IsAllocationTracked(ptr));
|
||||
size_t old_size = 0;
|
||||
if (ptr) {
|
||||
old_size = SDL_GetTrackedAllocationSize(ptr);
|
||||
SDL_assert(old_size != SIZE_MAX);
|
||||
}
|
||||
mem = SDL_realloc_orig(ptr, size);
|
||||
if (mem && mem != ptr) {
|
||||
if (ptr) {
|
||||
SDL_UntrackAllocation(ptr);
|
||||
}
|
||||
if (ptr) {
|
||||
SDL_UntrackAllocation(ptr);
|
||||
}
|
||||
if (mem) {
|
||||
SDL_TrackAllocation(mem, size);
|
||||
if (size > old_size) {
|
||||
rand_fill_memory(mem, old_size, size);
|
||||
}
|
||||
}
|
||||
return mem;
|
||||
}
|
||||
@ -190,10 +279,10 @@ static void SDLCALL SDLTest_TrackedFree(void *ptr)
|
||||
SDL_free_orig(ptr);
|
||||
}
|
||||
|
||||
int SDLTest_TrackAllocations(void)
|
||||
void SDLTest_TrackAllocations(void)
|
||||
{
|
||||
if (SDL_malloc_orig) {
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
SDLTest_Crc32Init(&s_crc32_context);
|
||||
@ -202,6 +291,30 @@ int SDLTest_TrackAllocations(void)
|
||||
if (s_previous_allocations != 0) {
|
||||
SDL_Log("SDLTest_TrackAllocations(): There are %d previous allocations, disabling free() validation", s_previous_allocations);
|
||||
}
|
||||
#ifdef __WINDOWS__
|
||||
{
|
||||
s_dbghelp = SDL_LoadObject("dbghelp.dll");
|
||||
if (s_dbghelp) {
|
||||
dbghelp_SymInitialize_fn dbghelp_SymInitialize;
|
||||
dbghelp_SymInitialize = (dbghelp_SymInitialize_fn)SDL_LoadFunction(s_dbghelp, "SymInitialize");
|
||||
dbghelp_SymFromAddr = (dbghelp_SymFromAddr_fn)SDL_LoadFunction(s_dbghelp, "SymFromAddr");
|
||||
#ifdef _WIN64
|
||||
dbghelp_SymGetLineFromAddr = (dbghelp_SymGetLineFromAddr_fn)SDL_LoadFunction(s_dbghelp, "SymGetLineFromAddr64");
|
||||
#else
|
||||
dbghelp_SymGetLineFromAddr = (dbghelp_SymGetLineFromAddr_fn)SDL_LoadFunction(s_dbghelp, "SymGetLineFromAddr");
|
||||
#endif
|
||||
if (!dbghelp_SymFromAddr || !dbghelp_SymFromAddr || !dbghelp_SymGetLineFromAddr) {
|
||||
SDL_UnloadObject(s_dbghelp);
|
||||
s_dbghelp = NULL;
|
||||
} else {
|
||||
if (!dbghelp_SymInitialize(GetCurrentProcess(), NULL, TRUE)) {
|
||||
SDL_UnloadObject(s_dbghelp);
|
||||
s_dbghelp = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SDL_GetMemoryFunctions(&SDL_malloc_orig,
|
||||
&SDL_calloc_orig,
|
||||
@ -212,7 +325,13 @@ int SDLTest_TrackAllocations(void)
|
||||
SDLTest_TrackedCalloc,
|
||||
SDLTest_TrackedRealloc,
|
||||
SDLTest_TrackedFree);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SDLTest_RandFillAllocations()
|
||||
{
|
||||
SDLTest_TrackAllocations();
|
||||
|
||||
s_randfill_allocations = SDL_TRUE;
|
||||
}
|
||||
|
||||
void SDLTest_LogAllocations(void)
|
||||
@ -245,8 +364,6 @@ void SDLTest_LogAllocations(void)
|
||||
|
||||
SDL_strlcpy(line, "Memory allocations:\n", sizeof(line));
|
||||
ADD_LINE();
|
||||
SDL_strlcpy(line, "Expect 2 allocations from within SDL_GetErrBuf()\n", sizeof(line));
|
||||
ADD_LINE();
|
||||
|
||||
count = 0;
|
||||
total_allocated = 0;
|
||||
|
@ -560,7 +560,7 @@ SDL_Surface *SDL_LoadBMP_RW(SDL_RWops *src, SDL_bool freesrc)
|
||||
} break;
|
||||
|
||||
default:
|
||||
if (SDL_RWread(src, bits, surface->pitch) != surface->pitch) {
|
||||
if (SDL_RWread(src, bits, surface->pitch) != (size_t)surface->pitch) {
|
||||
goto done;
|
||||
}
|
||||
if (biBitCount == 8 && palette && biClrUsed < (1u << biBitCount)) {
|
||||
@ -743,7 +743,7 @@ int SDL_SaveBMP_RW(SDL_Surface *surface, SDL_RWops *dst, SDL_bool freedst)
|
||||
}
|
||||
|
||||
if (SDL_LockSurface(intermediate_surface) == 0) {
|
||||
const int bw = intermediate_surface->w * intermediate_surface->format->BytesPerPixel;
|
||||
const size_t bw = intermediate_surface->w * intermediate_surface->format->BytesPerPixel;
|
||||
|
||||
/* Set the BMP file header values */
|
||||
bfSize = 0; /* We'll write this when we're done */
|
||||
|
@ -23,7 +23,7 @@
|
||||
#ifndef SDL_sysvideo_h_
|
||||
#define SDL_sysvideo_h_
|
||||
|
||||
#include "SDL_vulkan_internal.h"
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
|
||||
/* The SDL video driver */
|
||||
|
||||
@ -264,6 +264,7 @@ struct SDL_VideoDevice
|
||||
void (*DestroyWindowFramebuffer)(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
void (*OnWindowEnter)(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
int (*FlashWindow)(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
|
||||
int (*SetWindowFocusable)(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable);
|
||||
|
||||
/* * * */
|
||||
/*
|
||||
@ -442,8 +443,8 @@ struct SDL_VideoDevice
|
||||
/* Data used by the Vulkan drivers */
|
||||
struct
|
||||
{
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
|
||||
PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
|
||||
SDL_FunctionPointer vkGetInstanceProcAddr;
|
||||
SDL_FunctionPointer vkEnumerateInstanceExtensionProperties;
|
||||
int loader_loaded;
|
||||
char loader_path[256];
|
||||
void *loader_handle;
|
||||
@ -543,7 +544,7 @@ extern void SDL_OnWindowFocusGained(SDL_Window *window);
|
||||
extern void SDL_OnWindowFocusLost(SDL_Window *window);
|
||||
extern void SDL_OnWindowDisplayChanged(SDL_Window *window);
|
||||
extern void SDL_UpdateWindowGrab(SDL_Window *window);
|
||||
extern SDL_Window *SDL_GetFocusWindow(void);
|
||||
extern SDL_Window *SDL_GetToplevelForKeyboardFocus(void);
|
||||
|
||||
extern SDL_bool SDL_ShouldAllowTopmost(void);
|
||||
|
||||
|
@ -1740,7 +1740,7 @@ Uint32 SDL_GetWindowPixelFormat(SDL_Window *window)
|
||||
}
|
||||
|
||||
#define CREATE_FLAGS \
|
||||
(SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED | SDL_WINDOW_METAL | SDL_WINDOW_TRANSPARENT)
|
||||
(SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED | SDL_WINDOW_METAL | SDL_WINDOW_TRANSPARENT | SDL_WINDOW_NOT_FOCUSABLE)
|
||||
|
||||
static SDL_INLINE SDL_bool IsAcceptingDragAndDrop(void)
|
||||
{
|
||||
@ -3205,6 +3205,24 @@ int SDL_SetWindowInputFocus(SDL_Window *window)
|
||||
return _this->SetWindowInputFocus(_this, window);
|
||||
}
|
||||
|
||||
int SDL_SetWindowFocusable(SDL_Window *window, SDL_bool focusable)
|
||||
{
|
||||
CHECK_WINDOW_MAGIC(window, -1);
|
||||
|
||||
const int want = (focusable != SDL_FALSE); /* normalize the flag. */
|
||||
const int have = !(window->flags & SDL_WINDOW_NOT_FOCUSABLE);
|
||||
if ((want != have) && (_this->SetWindowFocusable)) {
|
||||
if (want) {
|
||||
window->flags &= ~SDL_WINDOW_NOT_FOCUSABLE;
|
||||
} else {
|
||||
window->flags |= SDL_WINDOW_NOT_FOCUSABLE;
|
||||
}
|
||||
_this->SetWindowFocusable(_this, window, (SDL_bool)want);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SDL_UpdateWindowGrab(SDL_Window *window)
|
||||
{
|
||||
SDL_bool keyboard_grabbed, mouse_grabbed;
|
||||
@ -3543,21 +3561,18 @@ void SDL_OnWindowFocusLost(SDL_Window *window)
|
||||
}
|
||||
}
|
||||
|
||||
/* !!! FIXME: is this different than SDL_GetKeyboardFocus()?
|
||||
!!! FIXME: Also, SDL_GetKeyboardFocus() is O(1), this isn't. */
|
||||
SDL_Window *SDL_GetFocusWindow(void)
|
||||
SDL_Window *SDL_GetToplevelForKeyboardFocus(void)
|
||||
{
|
||||
SDL_Window *window;
|
||||
SDL_Window *focus = SDL_GetKeyboardFocus();
|
||||
|
||||
if (_this == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
for (window = _this->windows; window; window = window->next) {
|
||||
if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
|
||||
return window;
|
||||
if (focus) {
|
||||
/* Get the toplevel parent window. */
|
||||
while (focus->parent) {
|
||||
focus = focus->parent;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
||||
return focus;
|
||||
}
|
||||
|
||||
void SDL_DestroyWindow(SDL_Window *window)
|
||||
@ -4741,7 +4756,7 @@ void SDL_StartTextInput(void)
|
||||
|
||||
/* Then show the on-screen keyboard, if any */
|
||||
if (SDL_GetHintBoolean(SDL_HINT_ENABLE_SCREEN_KEYBOARD, SDL_TRUE)) {
|
||||
window = SDL_GetFocusWindow();
|
||||
window = SDL_GetKeyboardFocus();
|
||||
if (window && _this && _this->ShowScreenKeyboard) {
|
||||
_this->ShowScreenKeyboard(_this, window);
|
||||
}
|
||||
@ -4785,7 +4800,7 @@ void SDL_StopTextInput(void)
|
||||
|
||||
/* Hide the on-screen keyboard, if any */
|
||||
if (SDL_GetHintBoolean(SDL_HINT_ENABLE_SCREEN_KEYBOARD, SDL_TRUE)) {
|
||||
window = SDL_GetFocusWindow();
|
||||
window = SDL_GetKeyboardFocus();
|
||||
if (window && _this && _this->HideScreenKeyboard) {
|
||||
_this->HideScreenKeyboard(_this, window);
|
||||
}
|
||||
@ -5101,9 +5116,9 @@ void SDL_OnApplicationWillResignActive(void)
|
||||
if (_this) {
|
||||
SDL_Window *window;
|
||||
for (window = _this->windows; window != NULL; window = window->next) {
|
||||
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_FOCUS_LOST, 0, 0);
|
||||
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
|
||||
}
|
||||
SDL_SetKeyboardFocus(NULL);
|
||||
}
|
||||
SDL_SendAppEvent(SDL_EVENT_WILL_ENTER_BACKGROUND);
|
||||
}
|
||||
@ -5125,7 +5140,7 @@ void SDL_OnApplicationDidBecomeActive(void)
|
||||
if (_this) {
|
||||
SDL_Window *window;
|
||||
for (window = _this->windows; window != NULL; window = window->next) {
|
||||
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_FOCUS_GAINED, 0, 0);
|
||||
SDL_SetKeyboardFocus(window);
|
||||
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,8 @@
|
||||
|
||||
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_ANDROID)
|
||||
|
||||
#include "../SDL_vulkan_internal.h"
|
||||
|
||||
#include "SDL_androidvideo.h"
|
||||
#include "SDL_androidwindow.h"
|
||||
|
||||
|
@ -119,6 +119,7 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void)
|
||||
device->SetWindowHitTest = Cocoa_SetWindowHitTest;
|
||||
device->AcceptDragAndDrop = Cocoa_AcceptDragAndDrop;
|
||||
device->FlashWindow = Cocoa_FlashWindow;
|
||||
device->SetWindowFocusable = Cocoa_SetWindowFocusable;
|
||||
|
||||
device->shape_driver.CreateShaper = Cocoa_CreateShaper;
|
||||
device->shape_driver.SetWindowShape = Cocoa_SetWindowShape;
|
||||
|
@ -169,5 +169,6 @@ extern int Cocoa_GetWindowWMInfo(SDL_VideoDevice *_this, SDL_Window *window, str
|
||||
extern int Cocoa_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
|
||||
extern void Cocoa_AcceptDragAndDrop(SDL_Window *window, SDL_bool accept);
|
||||
extern int Cocoa_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
|
||||
extern int Cocoa_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable);
|
||||
|
||||
#endif /* SDL_cocoawindow_h_ */
|
||||
|
@ -116,7 +116,7 @@
|
||||
- (BOOL)canBecomeKeyWindow
|
||||
{
|
||||
SDL_Window *window = [self findSDLWindow];
|
||||
if (window && !(window->flags & SDL_WINDOW_TOOLTIP)) {
|
||||
if (window && !(window->flags & (SDL_WINDOW_TOOLTIP | SDL_WINDOW_NOT_FOCUSABLE))) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
@ -643,7 +643,7 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
|
||||
int newVisibility = [[change objectForKey:@"new"] intValue];
|
||||
if (newVisibility) {
|
||||
SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_SHOWN, 0, 0);
|
||||
} else {
|
||||
} else if (![_data.nswindow isMiniaturized]) {
|
||||
SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_HIDDEN, 0, 0);
|
||||
}
|
||||
}
|
||||
@ -662,7 +662,7 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
|
||||
if (wasVisible != isVisible) {
|
||||
if (isVisible) {
|
||||
SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_SHOWN, 0, 0);
|
||||
} else {
|
||||
} else if (![_data.nswindow isMiniaturized]) {
|
||||
SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_HIDDEN, 0, 0);
|
||||
}
|
||||
|
||||
@ -955,7 +955,12 @@ static void Cocoa_SendExposedEventIfVisible(SDL_Window *window)
|
||||
|
||||
- (void)windowDidDeminiaturize:(NSNotification *)aNotification
|
||||
{
|
||||
SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
|
||||
/* isZoomed always returns true if the window is not resizable */
|
||||
if ((_data.window->flags & SDL_WINDOW_RESIZABLE) && [_data.nswindow isZoomed]) {
|
||||
SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
|
||||
} else {
|
||||
SDL_SendWindowEvent(_data.window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowDidBecomeKey:(NSNotification *)aNotification
|
||||
@ -2165,16 +2170,17 @@ void Cocoa_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
if (SDL_WINDOW_IS_POPUP(window)) {
|
||||
NSWindow *nsparent = ((__bridge SDL_CocoaWindowData *)window->parent->driverdata).nswindow;
|
||||
[nsparent addChildWindow:nswindow ordered:NSWindowAbove];
|
||||
}
|
||||
if (bActivate) {
|
||||
[nswindow makeKeyAndOrderFront:nil];
|
||||
} else {
|
||||
/* Order this window below the key window if we're not activating it */
|
||||
if ([NSApp keyWindow]) {
|
||||
[nswindow orderWindow:NSWindowBelow relativeTo:[[NSApp keyWindow] windowNumber]];
|
||||
if (bActivate) {
|
||||
[nswindow makeKeyAndOrderFront:nil];
|
||||
} else {
|
||||
/* Order this window below the key window if we're not activating it */
|
||||
if ([NSApp keyWindow]) {
|
||||
[nswindow orderWindow:NSWindowBelow relativeTo:[[NSApp keyWindow] windowNumber]];
|
||||
}
|
||||
}
|
||||
[nswindow setIsVisible:YES];
|
||||
}
|
||||
[nswindow setIsVisible:YES];
|
||||
[windowData.listener resumeVisibleObservation];
|
||||
}
|
||||
}
|
||||
@ -2185,7 +2191,18 @@ void Cocoa_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
@autoreleasepool {
|
||||
NSWindow *nswindow = ((__bridge SDL_CocoaWindowData *)window->driverdata).nswindow;
|
||||
|
||||
[nswindow orderOut:nil];
|
||||
/* orderOut has no effect on miniaturized windows, so close must be used to remove
|
||||
* the window from the desktop and window list in this case.
|
||||
*
|
||||
* SDL holds a strong reference to the window (oneShot/releasedWhenClosed are 'NO'),
|
||||
* and calling 'close' doesn't send a 'windowShouldClose' message, so it's safe to
|
||||
* use for this purpose as nothing is implicitly released.
|
||||
*/
|
||||
if (![nswindow isMiniaturized]) {
|
||||
[nswindow orderOut:nil];
|
||||
} else {
|
||||
[nswindow close];
|
||||
}
|
||||
|
||||
/* Transfer keyboard focus back to the parent */
|
||||
if (window->flags & SDL_WINDOW_POPUP_MENU) {
|
||||
@ -2218,13 +2235,13 @@ void Cocoa_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
if (SDL_WINDOW_IS_POPUP(window)) {
|
||||
NSWindow *nsparent = ((__bridge SDL_CocoaWindowData *)window->parent->driverdata).nswindow;
|
||||
[nsparent addChildWindow:nswindow ordered:NSWindowAbove];
|
||||
}
|
||||
|
||||
if (bActivate) {
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
[nswindow makeKeyAndOrderFront:nil];
|
||||
} else {
|
||||
[nswindow orderFront:nil];
|
||||
if (bActivate) {
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
[nswindow makeKeyAndOrderFront:nil];
|
||||
} else {
|
||||
[nswindow orderFront:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
[windowData.listener resumeVisibleObservation];
|
||||
@ -2662,6 +2679,11 @@ int Cocoa_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOpera
|
||||
}
|
||||
}
|
||||
|
||||
int Cocoa_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable)
|
||||
{
|
||||
return 0; /* just succeed, the real work is done elsewhere. */
|
||||
}
|
||||
|
||||
int Cocoa_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
@ -732,7 +732,7 @@ static EM_BOOL Emscripten_HandleFocus(int eventType, const EmscriptenFocusEvent
|
||||
}
|
||||
|
||||
sdl_event_type = (eventType == EMSCRIPTEN_EVENT_FOCUS) ? SDL_EVENT_WINDOW_FOCUS_GAINED : SDL_EVENT_WINDOW_FOCUS_LOST;
|
||||
SDL_SendWindowEvent(window_data->window, sdl_event_type, 0, 0);
|
||||
SDL_SetKeyboardFocus(sdl_event_type == SDL_EVENT_WINDOW_FOCUS_GAINED ? window_data->window : NULL);
|
||||
return SDL_EventEnabled(sdl_event_type);
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,15 @@
|
||||
|
||||
#include "../../events/SDL_mouse_c.h"
|
||||
|
||||
/* older Emscriptens don't have this, but we need to for wasm64 compatibility. */
|
||||
#ifndef MAIN_THREAD_EM_ASM_PTR
|
||||
#ifdef __wasm64__
|
||||
#error You need to upgrade your Emscripten compiler to support wasm64
|
||||
#else
|
||||
#define MAIN_THREAD_EM_ASM_PTR MAIN_THREAD_EM_ASM_INT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static SDL_Cursor *Emscripten_CreateCursorFromString(const char *cursor_str, SDL_bool is_custom)
|
||||
{
|
||||
SDL_Cursor *cursor;
|
||||
@ -74,7 +83,7 @@ static SDL_Cursor *Emscripten_CreateCursor(SDL_Surface *surface, int hot_x, int
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
cursor_url = (const char *)MAIN_THREAD_EM_ASM_INT({
|
||||
cursor_url = (const char *)MAIN_THREAD_EM_ASM_PTR({
|
||||
var w = $0;
|
||||
var h = $1;
|
||||
var hot_x = $2;
|
||||
|
@ -28,6 +28,8 @@
|
||||
|
||||
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_KMSDRM)
|
||||
|
||||
#include "../SDL_vulkan_internal.h"
|
||||
|
||||
#include "SDL_kmsdrmvideo.h"
|
||||
#include "SDL_kmsdrmdyn.h"
|
||||
#include "SDL_kmsdrmvulkan.h"
|
||||
|
@ -249,7 +249,7 @@ void UIKit_ForceUpdateHomeIndicator(void)
|
||||
{
|
||||
#if !TARGET_OS_TV
|
||||
/* Force the main SDL window to re-evaluate home indicator state */
|
||||
SDL_Window *focus = SDL_GetFocusWindow();
|
||||
SDL_Window *focus = SDL_GetKeyboardFocus();
|
||||
if (focus) {
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)focus->driverdata;
|
||||
if (data != nil) {
|
||||
|
@ -624,7 +624,7 @@ SDL_bool UIKit_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
int UIKit_SetTextInputRect(SDL_VideoDevice *_this, const SDL_Rect *rect)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_uikitviewcontroller *vc = GetWindowViewController(SDL_GetFocusWindow());
|
||||
SDL_uikitviewcontroller *vc = GetWindowViewController(SDL_GetKeyboardFocus());
|
||||
if (vc != nil) {
|
||||
vc.textInputRect = *rect;
|
||||
|
||||
|
@ -28,6 +28,8 @@
|
||||
|
||||
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_VIVANTE)
|
||||
|
||||
#include "../SDL_vulkan_internal.h"
|
||||
|
||||
#include "SDL_vivantevideo.h"
|
||||
|
||||
#include "SDL_vivantevulkan.h"
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#define TEXT_MIME "text/plain;charset=utf-8"
|
||||
#define FILE_MIME "text/uri-list"
|
||||
#define FILE_PORTAL_MIME "application/vnd.portal.filetransfer"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
@ -34,34 +34,30 @@ typedef struct
|
||||
const char *libname;
|
||||
} waylanddynlib;
|
||||
|
||||
#ifndef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL
|
||||
#define SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL NULL
|
||||
#endif
|
||||
#ifndef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR
|
||||
#define SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR NULL
|
||||
#endif
|
||||
#ifndef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON
|
||||
#define SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON NULL
|
||||
#endif
|
||||
#ifndef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR
|
||||
#define SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR NULL
|
||||
#endif
|
||||
|
||||
static waylanddynlib waylandlibs[] = {
|
||||
{ NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC },
|
||||
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL
|
||||
{ NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL },
|
||||
#endif
|
||||
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR
|
||||
{ NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR },
|
||||
#endif
|
||||
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON
|
||||
{ NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON },
|
||||
{ NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR }
|
||||
#endif
|
||||
#ifdef SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR
|
||||
{ NULL, SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR },
|
||||
#endif
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static void *WAYLAND_GetSym(const char *fnname, int *pHasModule, SDL_bool required)
|
||||
{
|
||||
int i;
|
||||
void *fn = NULL;
|
||||
for (i = 0; i < SDL_TABLESIZE(waylandlibs); i++) {
|
||||
if (waylandlibs[i].lib != NULL) {
|
||||
fn = SDL_LoadFunction(waylandlibs[i].lib, fnname);
|
||||
waylanddynlib *dynlib;
|
||||
for (dynlib = waylandlibs; dynlib->libname; dynlib++) {
|
||||
if (dynlib->lib != NULL) {
|
||||
fn = SDL_LoadFunction(dynlib->lib, fnname);
|
||||
if (fn != NULL) {
|
||||
break;
|
||||
}
|
||||
@ -69,10 +65,11 @@ static void *WAYLAND_GetSym(const char *fnname, int *pHasModule, SDL_bool requir
|
||||
}
|
||||
|
||||
#if DEBUG_DYNAMIC_WAYLAND
|
||||
if (fn != NULL)
|
||||
SDL_Log("WAYLAND: Found '%s' in %s (%p)\n", fnname, waylandlibs[i].libname, fn);
|
||||
else
|
||||
if (fn != NULL) {
|
||||
SDL_Log("WAYLAND: Found '%s' in %s (%p)\n", fnname, dynlib->libname, fn);
|
||||
} else {
|
||||
SDL_Log("WAYLAND: Symbol '%s' NOT FOUND!\n", fnname);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fn == NULL && required) {
|
||||
|
@ -823,7 +823,7 @@ static void pointer_handle_axis(void *data, struct wl_pointer *pointer,
|
||||
{
|
||||
struct SDL_WaylandInput *input = data;
|
||||
|
||||
if (wl_seat_get_version(input->seat) >= 5) {
|
||||
if (wl_seat_get_version(input->seat) >= WL_POINTER_FRAME_SINCE_VERSION) {
|
||||
input->pointer_curr_axis_info.timestamp_ns = Wayland_GetPointerTimestamp(input, time);
|
||||
pointer_handle_axis_common(input, AXIS_EVENT_CONTINUOUS, axis, value);
|
||||
} else {
|
||||
@ -1848,28 +1848,40 @@ static void data_device_handle_enter(void *data, struct wl_data_device *wl_data_
|
||||
data_device->drag_offer = wl_data_offer_get_user_data(id);
|
||||
|
||||
/* TODO: SDL Support more mime types */
|
||||
has_mime = Wayland_data_offer_has_mime(
|
||||
data_device->drag_offer, FILE_MIME);
|
||||
|
||||
/* If drag_mime is NULL this will decline the offer */
|
||||
wl_data_offer_accept(id, serial,
|
||||
(has_mime == SDL_TRUE) ? FILE_MIME : NULL);
|
||||
#ifdef SDL_USE_LIBDBUS
|
||||
if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_PORTAL_MIME)) {
|
||||
has_mime = SDL_TRUE;
|
||||
wl_data_offer_accept(id, serial, FILE_PORTAL_MIME);
|
||||
}
|
||||
#endif
|
||||
if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_MIME)) {
|
||||
has_mime = SDL_TRUE;
|
||||
wl_data_offer_accept(id, serial, FILE_MIME);
|
||||
}
|
||||
|
||||
/* SDL only supports "copy" style drag and drop */
|
||||
if (has_mime == SDL_TRUE) {
|
||||
if (has_mime) {
|
||||
dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
|
||||
} else {
|
||||
/* drag_mime is NULL this will decline the offer */
|
||||
wl_data_offer_accept(id, serial, NULL);
|
||||
}
|
||||
if (wl_data_offer_get_version(data_device->drag_offer->offer) >= 3) {
|
||||
if (wl_data_offer_get_version(data_device->drag_offer->offer) >=
|
||||
WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) {
|
||||
wl_data_offer_set_actions(data_device->drag_offer->offer,
|
||||
dnd_action, dnd_action);
|
||||
}
|
||||
|
||||
/* find the current window */
|
||||
if (surface && SDL_WAYLAND_own_surface(surface)) {
|
||||
SDL_WindowData *window = (SDL_WindowData *)wl_surface_get_user_data(surface);
|
||||
if (window) {
|
||||
data_device->dnd_window = window->sdlwindow;
|
||||
}
|
||||
if (surface) {
|
||||
if (SDL_WAYLAND_own_surface(surface)) {
|
||||
SDL_WindowData *window = (SDL_WindowData *)wl_surface_get_user_data(surface);
|
||||
if (window) {
|
||||
data_device->dnd_window = window->sdlwindow;
|
||||
}
|
||||
} else {
|
||||
data_device->dnd_window = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1877,11 +1889,10 @@ static void data_device_handle_enter(void *data, struct wl_data_device *wl_data_
|
||||
static void data_device_handle_leave(void *data, struct wl_data_device *wl_data_device)
|
||||
{
|
||||
SDL_WaylandDataDevice *data_device = data;
|
||||
SDL_WaylandDataOffer *offer = NULL;
|
||||
|
||||
if (data_device->selection_offer != NULL) {
|
||||
data_device->selection_offer = NULL;
|
||||
Wayland_data_offer_destroy(offer);
|
||||
if (data_device->drag_offer != NULL) {
|
||||
Wayland_data_offer_destroy(data_device->drag_offer);
|
||||
data_device->drag_offer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1890,27 +1901,15 @@ static void data_device_handle_motion(void *data, struct wl_data_device *wl_data
|
||||
{
|
||||
SDL_WaylandDataDevice *data_device = data;
|
||||
|
||||
if (data_device->drag_offer != NULL) {
|
||||
/* TODO: SDL Support more mime types */
|
||||
size_t length;
|
||||
void *buffer = Wayland_data_offer_receive(data_device->drag_offer,
|
||||
FILE_MIME, &length);
|
||||
if (buffer) {
|
||||
char *saveptr = NULL;
|
||||
char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr);
|
||||
while (token != NULL) {
|
||||
char *fn = Wayland_URIToLocal(token);
|
||||
if (fn) {
|
||||
double dx;
|
||||
double dy;
|
||||
dx = wl_fixed_to_double(x);
|
||||
dy = wl_fixed_to_double(y);
|
||||
SDL_SendDropPosition(data_device->dnd_window, fn, (float)dx, (float)dy);
|
||||
}
|
||||
token = SDL_strtok_r(NULL, "\r\n", &saveptr);
|
||||
}
|
||||
SDL_free(buffer);
|
||||
}
|
||||
if (data_device->drag_offer != NULL && data_device->dnd_window) {
|
||||
const float dx = (float)wl_fixed_to_double(x);
|
||||
const float dy = (float)wl_fixed_to_double(y);
|
||||
|
||||
/* XXX: Send the filename here if the event system ever starts passing it though.
|
||||
* Any future implementation should cache the filenames, as otherwise this could
|
||||
* hammer the DBus interface hundreds or even thousands of times per second.
|
||||
*/
|
||||
SDL_SendDropPosition(data_device->dnd_window, NULL, dx, dy);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2039,25 +2038,66 @@ static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_d
|
||||
{
|
||||
SDL_WaylandDataDevice *data_device = data;
|
||||
|
||||
if (data_device->drag_offer != NULL) {
|
||||
if (data_device->drag_offer != NULL && data_device->dnd_window) {
|
||||
/* TODO: SDL Support more mime types */
|
||||
size_t length;
|
||||
void *buffer = Wayland_data_offer_receive(data_device->drag_offer,
|
||||
FILE_MIME, &length);
|
||||
if (buffer) {
|
||||
char *saveptr = NULL;
|
||||
char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr);
|
||||
while (token != NULL) {
|
||||
char *fn = Wayland_URIToLocal(token);
|
||||
if (fn) {
|
||||
SDL_SendDropFile(data_device->dnd_window, fn);
|
||||
SDL_bool drop_handled = SDL_FALSE;
|
||||
#ifdef SDL_USE_LIBDBUS
|
||||
if (Wayland_data_offer_has_mime(
|
||||
data_device->drag_offer, FILE_PORTAL_MIME)) {
|
||||
void *buffer = Wayland_data_offer_receive(data_device->drag_offer,
|
||||
FILE_PORTAL_MIME, &length);
|
||||
if (buffer) {
|
||||
SDL_DBusContext *dbus = SDL_DBus_GetContext();
|
||||
if (dbus) {
|
||||
int path_count = 0;
|
||||
char **paths = SDL_DBus_DocumentsPortalRetrieveFiles(buffer, &path_count);
|
||||
/* If dropped files contain a directory the list is empty */
|
||||
if (paths && path_count > 0) {
|
||||
for (int i = 0; i < path_count; i++) {
|
||||
SDL_SendDropFile(data_device->dnd_window, paths[i]);
|
||||
}
|
||||
dbus->free_string_array(paths);
|
||||
SDL_SendDropComplete(data_device->dnd_window);
|
||||
drop_handled = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
token = SDL_strtok_r(NULL, "\r\n", &saveptr);
|
||||
SDL_free(buffer);
|
||||
}
|
||||
SDL_SendDropComplete(data_device->dnd_window);
|
||||
SDL_free(buffer);
|
||||
}
|
||||
#endif
|
||||
/* If XDG document portal fails fallback.
|
||||
* When running a flatpak sandbox this will most likely be a list of
|
||||
* non paths that are not visible to the application
|
||||
*/
|
||||
if (!drop_handled && Wayland_data_offer_has_mime(
|
||||
data_device->drag_offer, FILE_MIME)) {
|
||||
void *buffer = Wayland_data_offer_receive(data_device->drag_offer,
|
||||
FILE_MIME, &length);
|
||||
if (buffer) {
|
||||
char *saveptr = NULL;
|
||||
char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr);
|
||||
while (token != NULL) {
|
||||
char *fn = Wayland_URIToLocal(token);
|
||||
if (fn) {
|
||||
SDL_SendDropFile(data_device->dnd_window, fn);
|
||||
}
|
||||
token = SDL_strtok_r(NULL, "\r\n", &saveptr);
|
||||
}
|
||||
SDL_SendDropComplete(data_device->dnd_window);
|
||||
SDL_free(buffer);
|
||||
drop_handled = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (drop_handled && wl_data_offer_get_version(data_device->drag_offer->offer) >=
|
||||
WL_DATA_OFFER_FINISH_SINCE_VERSION) {
|
||||
wl_data_offer_finish(data_device->drag_offer->offer);
|
||||
}
|
||||
}
|
||||
|
||||
Wayland_data_offer_destroy(data_device->drag_offer);
|
||||
data_device->drag_offer = NULL;
|
||||
}
|
||||
|
||||
static void data_device_handle_selection(void *data, struct wl_data_device *wl_data_device,
|
||||
|
@ -916,7 +916,7 @@ static int Wayland_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *di
|
||||
/* When an emulated, exclusive fullscreen window has focus, treat the mode dimensions as the display bounds. */
|
||||
if (display->fullscreen_window &&
|
||||
display->fullscreen_window->fullscreen_exclusive &&
|
||||
display->fullscreen_window == SDL_GetFocusWindow() &&
|
||||
display->fullscreen_window->driverdata->active &&
|
||||
display->fullscreen_window->current_fullscreen_mode.w != 0 &&
|
||||
display->fullscreen_window->current_fullscreen_mode.h != 0) {
|
||||
rect->w = display->fullscreen_window->current_fullscreen_mode.w;
|
||||
|
@ -28,9 +28,12 @@
|
||||
|
||||
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_WAYLAND)
|
||||
|
||||
#include "../SDL_vulkan_internal.h"
|
||||
|
||||
#include "SDL_waylandvideo.h"
|
||||
#include "SDL_waylandwindow.h"
|
||||
|
||||
#include "../SDL_vulkan_internal.h"
|
||||
#include "SDL_waylandvulkan.h"
|
||||
|
||||
#include <SDL3/SDL_syswm.h>
|
||||
|
@ -29,7 +29,7 @@
|
||||
#ifndef SDL_waylandvulkan_h_
|
||||
#define SDL_waylandvulkan_h_
|
||||
|
||||
#include "../SDL_vulkan_internal.h"
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_WAYLAND)
|
||||
|
@ -639,7 +639,7 @@ static void handle_configure_xdg_toplevel(void *data,
|
||||
SDL_bool fullscreen = SDL_FALSE;
|
||||
SDL_bool maximized = SDL_FALSE;
|
||||
SDL_bool floating = SDL_TRUE;
|
||||
SDL_bool focused = SDL_FALSE;
|
||||
SDL_bool active = SDL_FALSE;
|
||||
SDL_bool suspended = SDL_FALSE;
|
||||
wl_array_for_each (state, states) {
|
||||
switch (*state) {
|
||||
@ -652,7 +652,7 @@ static void handle_configure_xdg_toplevel(void *data,
|
||||
floating = SDL_FALSE;
|
||||
break;
|
||||
case XDG_TOPLEVEL_STATE_ACTIVATED:
|
||||
focused = SDL_TRUE;
|
||||
active = SDL_TRUE;
|
||||
break;
|
||||
case XDG_TOPLEVEL_STATE_TILED_LEFT:
|
||||
case XDG_TOPLEVEL_STATE_TILED_RIGHT:
|
||||
@ -715,7 +715,7 @@ static void handle_configure_xdg_toplevel(void *data,
|
||||
* dependent, but in general, we can assume that the flag should remain set until
|
||||
* the next focused configure event occurs.
|
||||
*/
|
||||
if (focused || !(window->flags & SDL_WINDOW_MINIMIZED)) {
|
||||
if (active || !(window->flags & SDL_WINDOW_MINIMIZED)) {
|
||||
SDL_SendWindowEvent(window,
|
||||
maximized ? SDL_EVENT_WINDOW_MAXIMIZED : SDL_EVENT_WINDOW_RESTORED,
|
||||
0, 0);
|
||||
@ -745,15 +745,11 @@ static void handle_configure_xdg_toplevel(void *data,
|
||||
}
|
||||
}
|
||||
|
||||
/* Similar to maximized/restore events above, send focus events too! */
|
||||
SDL_SendWindowEvent(window,
|
||||
focused ? SDL_EVENT_WINDOW_FOCUS_GAINED : SDL_EVENT_WINDOW_FOCUS_LOST,
|
||||
0, 0);
|
||||
|
||||
wind->requested_window_width = width;
|
||||
wind->requested_window_height = height;
|
||||
wind->floating = floating;
|
||||
wind->suspended = suspended;
|
||||
wind->active = active;
|
||||
if (wind->surface_status == WAYLAND_SURFACE_STATUS_WAITING_FOR_CONFIGURE) {
|
||||
wind->surface_status = WAYLAND_SURFACE_STATUS_WAITING_FOR_FRAME;
|
||||
}
|
||||
@ -855,11 +851,10 @@ static void handle_configure_zxdg_decoration(void *data,
|
||||
WAYLAND_wl_display_roundtrip(driverdata->waylandData->display);
|
||||
|
||||
Wayland_HideWindow(device, window);
|
||||
SDL_zero(driverdata->shell_surface);
|
||||
driverdata->shell_surface_type = WAYLAND_SURFACE_LIBDECOR;
|
||||
|
||||
if (!window->is_hiding && !(window->flags & SDL_WINDOW_HIDDEN)) {
|
||||
Wayland_ShowWindow(device, window);
|
||||
}
|
||||
Wayland_ShowWindow(device, window);
|
||||
}
|
||||
}
|
||||
|
||||
@ -921,7 +916,7 @@ static void decoration_frame_configure(struct libdecor_frame *frame,
|
||||
int width, height;
|
||||
|
||||
SDL_bool prev_fullscreen = wind->is_fullscreen;
|
||||
SDL_bool focused = SDL_FALSE;
|
||||
SDL_bool active = SDL_FALSE;
|
||||
SDL_bool fullscreen = SDL_FALSE;
|
||||
SDL_bool maximized = SDL_FALSE;
|
||||
SDL_bool tiled = SDL_FALSE;
|
||||
@ -935,7 +930,7 @@ static void decoration_frame_configure(struct libdecor_frame *frame,
|
||||
if (libdecor_configuration_get_window_state(configuration, &window_state)) {
|
||||
fullscreen = (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) != 0;
|
||||
maximized = (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) != 0;
|
||||
focused = (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) != 0;
|
||||
active = (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) != 0;
|
||||
tiled = (window_state & tiled_states) != 0;
|
||||
#ifdef SDL_HAVE_LIBDECOR_VER_0_1_2
|
||||
suspended = (window_state & LIBDECOR_WINDOW_STATE_SUSPENDED) != 0;
|
||||
@ -954,18 +949,13 @@ static void decoration_frame_configure(struct libdecor_frame *frame,
|
||||
* dependent, but in general, we can assume that the flag should remain set until
|
||||
* the next focused configure event occurs.
|
||||
*/
|
||||
if (focused || !(window->flags & SDL_WINDOW_MINIMIZED)) {
|
||||
if (active || !(window->flags & SDL_WINDOW_MINIMIZED)) {
|
||||
SDL_SendWindowEvent(window,
|
||||
maximized ? SDL_EVENT_WINDOW_MAXIMIZED : SDL_EVENT_WINDOW_RESTORED,
|
||||
0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Similar to maximized/restore events above, send focus events too! */
|
||||
SDL_SendWindowEvent(window,
|
||||
focused ? SDL_EVENT_WINDOW_FOCUS_GAINED : SDL_EVENT_WINDOW_FOCUS_LOST,
|
||||
0, 0);
|
||||
|
||||
/* For fullscreen or fixed-size windows we know our size.
|
||||
* Always assume the configure is wrong.
|
||||
*/
|
||||
@ -1055,6 +1045,7 @@ static void decoration_frame_configure(struct libdecor_frame *frame,
|
||||
/* Store the new state. */
|
||||
wind->floating = floating;
|
||||
wind->suspended = suspended;
|
||||
wind->active = active;
|
||||
|
||||
/* Calculate the new window geometry */
|
||||
wind->requested_window_width = width;
|
||||
@ -1529,12 +1520,6 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
|
||||
/* Restore state that was set prior to this call */
|
||||
Wayland_SetWindowTitle(_this, window);
|
||||
if (window->flags & SDL_WINDOW_MAXIMIZED) {
|
||||
Wayland_MaximizeWindow(_this, window);
|
||||
}
|
||||
if (window->flags & SDL_WINDOW_MINIMIZED) {
|
||||
Wayland_MinimizeWindow(_this, window);
|
||||
}
|
||||
|
||||
/* We have to wait until the surface gets a "configure" event, or use of
|
||||
* this surface will fail. This is a new rule for xdg_shell.
|
||||
@ -1940,11 +1925,6 @@ void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set this flag now even if we never actually maximized, eventually
|
||||
* ShowWindow will take care of it along with the other window state.
|
||||
*/
|
||||
window->flags &= ~SDL_WINDOW_MAXIMIZED;
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
|
||||
if (wind->shell_surface.libdecor.frame == NULL) {
|
||||
@ -2025,11 +2005,6 @@ void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set this flag now even if we don't actually maximize yet, eventually
|
||||
* ShowWindow will take care of it along with the other window state.
|
||||
*/
|
||||
window->flags |= SDL_WINDOW_MAXIMIZED;
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
|
||||
if (wind->shell_surface.libdecor.frame == NULL) {
|
||||
@ -2057,6 +2032,8 @@ void Wayland_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
SDL_VideoData *viddata = _this->driverdata;
|
||||
SDL_WindowData *wind = window->driverdata;
|
||||
|
||||
/* Maximized and minimized flags are mutually exclusive */
|
||||
window->flags &= ~SDL_WINDOW_MAXIMIZED;
|
||||
window->flags |= SDL_WINDOW_MINIMIZED;
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
|
@ -123,6 +123,7 @@ struct SDL_WindowData
|
||||
SDL_DisplayID last_displayID;
|
||||
SDL_bool floating;
|
||||
SDL_bool suspended;
|
||||
SDL_bool active;
|
||||
SDL_bool is_fullscreen;
|
||||
SDL_bool in_fullscreen_transition;
|
||||
SDL_bool fullscreen_was_positioned;
|
||||
|
@ -22,6 +22,9 @@
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_WINDOWS
|
||||
|
||||
#ifdef SDL_VIDEO_VULKAN
|
||||
#include "../SDL_vulkan_internal.h"
|
||||
#endif
|
||||
#include "../SDL_sysvideo.h"
|
||||
#include "../SDL_pixels_c.h"
|
||||
#include "../../SDL_hints_c.h"
|
||||
@ -208,6 +211,7 @@ static SDL_VideoDevice *WIN_CreateDevice(void)
|
||||
device->AcceptDragAndDrop = WIN_AcceptDragAndDrop;
|
||||
device->FlashWindow = WIN_FlashWindow;
|
||||
device->ShowWindowSystemMenu = WIN_ShowWindowSystemMenu;
|
||||
device->SetWindowFocusable = WIN_SetWindowFocusable;
|
||||
|
||||
device->shape_driver.CreateShaper = Win32_CreateShaper;
|
||||
device->shape_driver.SetWindowShape = Win32_SetWindowShape;
|
||||
|
@ -28,6 +28,8 @@
|
||||
|
||||
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_WINDOWS)
|
||||
|
||||
#include "../SDL_vulkan_internal.h"
|
||||
|
||||
#include "SDL_windowsvideo.h"
|
||||
#include "SDL_windowswindow.h"
|
||||
|
||||
|
@ -29,11 +29,11 @@
|
||||
#ifndef SDL_windowsvulkan_h_
|
||||
#define SDL_windowsvulkan_h_
|
||||
|
||||
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_WINDOWS)
|
||||
|
||||
#include "../SDL_vulkan_internal.h"
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_WINDOWS)
|
||||
|
||||
int WIN_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path);
|
||||
void WIN_Vulkan_UnloadLibrary(SDL_VideoDevice *_this);
|
||||
SDL_bool WIN_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
|
||||
|
@ -46,6 +46,22 @@
|
||||
#endif
|
||||
typedef HRESULT (WINAPI *DwmSetWindowAttribute_t)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);
|
||||
|
||||
/* Transparent window support */
|
||||
#ifndef DWM_BB_ENABLE
|
||||
#define DWM_BB_ENABLE 0x00000001
|
||||
#endif
|
||||
#ifndef DWM_BB_BLURREGION
|
||||
#define DWM_BB_BLURREGION 0x00000002
|
||||
#endif
|
||||
typedef struct
|
||||
{
|
||||
DWORD flags;
|
||||
BOOL enable;
|
||||
HRGN blur_region;
|
||||
BOOL transition_on_maxed;
|
||||
} DWM_BLURBEHIND;
|
||||
typedef HRESULT(WINAPI *DwmEnableBlurBehindWindow_t)(HWND hwnd, const DWM_BLURBEHIND *pBlurBehind);
|
||||
|
||||
/* Windows CE compatibility */
|
||||
#ifndef SWP_NOCOPYBITS
|
||||
#define SWP_NOCOPYBITS 0
|
||||
@ -128,10 +144,11 @@ static DWORD GetWindowStyleEx(SDL_Window *window)
|
||||
{
|
||||
DWORD style = 0;
|
||||
|
||||
if (SDL_WINDOW_IS_POPUP(window)) {
|
||||
style = WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE;
|
||||
} else if (window->flags & SDL_WINDOW_UTILITY) {
|
||||
style = WS_EX_TOOLWINDOW;
|
||||
if (SDL_WINDOW_IS_POPUP(window) || (window->flags & SDL_WINDOW_UTILITY)) {
|
||||
style |= WS_EX_TOOLWINDOW;
|
||||
}
|
||||
if (SDL_WINDOW_IS_POPUP(window) || (window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
|
||||
style |= WS_EX_NOACTIVATE;
|
||||
}
|
||||
return style;
|
||||
}
|
||||
@ -173,23 +190,9 @@ static int WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL m
|
||||
if (WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice())) {
|
||||
/* With per-monitor v2, the window border/titlebar size depend on the DPI, so we need to call AdjustWindowRectExForDpi instead of
|
||||
AdjustWindowRectEx. */
|
||||
UINT unused;
|
||||
RECT screen_rect;
|
||||
HMONITOR mon;
|
||||
|
||||
screen_rect.left = *x;
|
||||
screen_rect.top = *y;
|
||||
screen_rect.right = (LONG)*x + *width;
|
||||
screen_rect.bottom = (LONG)*y + *height;
|
||||
|
||||
mon = MonitorFromRect(&screen_rect, MONITOR_DEFAULTTONEAREST);
|
||||
|
||||
if (videodata != NULL) {
|
||||
/* GetDpiForMonitor docs promise to return the same hdpi / vdpi */
|
||||
if (videodata->GetDpiForMonitor(mon, MDT_EFFECTIVE_DPI, &frame_dpi, &unused) != S_OK) {
|
||||
frame_dpi = 96;
|
||||
}
|
||||
|
||||
SDL_WindowData *data = window->driverdata;
|
||||
frame_dpi = (data && videodata->GetDpiForWindow) ? videodata->GetDpiForWindow(data->hwnd) : 96;
|
||||
if (videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, frame_dpi) == 0) {
|
||||
return WIN_SetError("AdjustWindowRectExForDpi()");
|
||||
}
|
||||
@ -583,6 +586,25 @@ int WIN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
ShowWindow(hwnd, SW_SHOWMINNOACTIVE);
|
||||
}
|
||||
|
||||
/* FIXME: does not work on all hardware configurations with different renders (i.e. hybrid GPUs) */
|
||||
if (window->flags & SDL_WINDOW_TRANSPARENT) {
|
||||
void *handle = SDL_LoadObject("dwmapi.dll");
|
||||
if (handle) {
|
||||
DwmEnableBlurBehindWindow_t DwmEnableBlurBehindWindowFunc = (DwmEnableBlurBehindWindow_t)SDL_LoadFunction(handle, "DwmEnableBlurBehindWindow");
|
||||
if (DwmEnableBlurBehindWindowFunc) {
|
||||
/* The region indicates which part of the window will be blurred and rest will be transparent. This
|
||||
is because the alpha value of the window will be used for non-blurred areas
|
||||
We can use (-1, -1, 0, 0) boundary to make sure no pixels are being blurred
|
||||
*/
|
||||
HRGN rgn = CreateRectRgn(-1, -1, 0, 0);
|
||||
DWM_BLURBEHIND bb = {DWM_BB_ENABLE | DWM_BB_BLURREGION, TRUE, rgn, FALSE};
|
||||
DwmEnableBlurBehindWindowFunc(hwnd, &bb);
|
||||
DeleteObject(rgn);
|
||||
}
|
||||
SDL_UnloadObject(handle);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(window->flags & SDL_WINDOW_OPENGL)) {
|
||||
return 0;
|
||||
}
|
||||
@ -1504,6 +1526,31 @@ void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
|
||||
ClientToScreen(data->hwnd, &pt);
|
||||
SendMessage(data->hwnd, WM_POPUPSYSTEMMENU, 0, MAKELPARAM(pt.x, pt.y));
|
||||
}
|
||||
|
||||
int WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable)
|
||||
{
|
||||
SDL_WindowData *data = window->driverdata;
|
||||
HWND hwnd = data->hwnd;
|
||||
const LONG style = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||||
|
||||
SDL_assert(style != 0);
|
||||
|
||||
if (focusable) {
|
||||
if (style & WS_EX_NOACTIVATE) {
|
||||
if (SetWindowLong(hwnd, GWL_EXSTYLE, style & ~WS_EX_NOACTIVATE) == 0) {
|
||||
return WIN_SetError("SetWindowLong()");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!(style & WS_EX_NOACTIVATE)) {
|
||||
if (SetWindowLong(hwnd, GWL_EXSTYLE, style | WS_EX_NOACTIVATE) == 0) {
|
||||
return WIN_SetError("SetWindowLong()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/
|
||||
|
||||
void WIN_UpdateDarkModeForHWND(HWND hwnd)
|
||||
|
@ -109,6 +109,7 @@ extern int WIN_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Flash
|
||||
extern void WIN_UpdateDarkModeForHWND(HWND hwnd);
|
||||
extern int WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags);
|
||||
extern void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
|
||||
extern int WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable);
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
|
@ -92,9 +92,7 @@ static int SetSelectionData(SDL_VideoDevice *_this, Atom selection, SDL_Clipboar
|
||||
clipboard->mime_count = mime_count;
|
||||
clipboard->sequence = sequence;
|
||||
|
||||
if (!clipboard_owner) {
|
||||
X11_XSetSelectionOwner(display, selection, window, CurrentTime);
|
||||
}
|
||||
X11_XSetSelectionOwner(display, selection, window, CurrentTime);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1579,12 +1579,19 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
|
||||
}
|
||||
}
|
||||
|
||||
if (changed & SDL_WINDOW_MAXIMIZED) {
|
||||
if (flags & SDL_WINDOW_MAXIMIZED) {
|
||||
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
|
||||
} else {
|
||||
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
|
||||
}
|
||||
if ((changed & SDL_WINDOW_MAXIMIZED) && ((flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) {
|
||||
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
|
||||
}
|
||||
if ((changed & SDL_WINDOW_MINIMIZED) && (flags & SDL_WINDOW_MINIMIZED)) {
|
||||
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
|
||||
}
|
||||
if (((changed & SDL_WINDOW_MAXIMIZED) || (changed & SDL_WINDOW_MINIMIZED)) &&
|
||||
(!(flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) {
|
||||
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
|
||||
}
|
||||
|
||||
if (changed & SDL_WINDOW_OCCLUDED) {
|
||||
SDL_SendWindowEvent(data->window, (flags & SDL_WINDOW_OCCLUDED) ? SDL_EVENT_WINDOW_OCCLUDED : SDL_EVENT_WINDOW_EXPOSED, 0, 0);
|
||||
}
|
||||
} else if (xevent->xproperty.atom == videodata->XKLAVIER_STATE) {
|
||||
/* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify
|
||||
|
@ -39,8 +39,6 @@
|
||||
#include "SDL_x11opengles.h"
|
||||
#endif
|
||||
|
||||
#include "SDL_x11vulkan.h"
|
||||
|
||||
/* Initialization/Query functions */
|
||||
static int X11_VideoInit(SDL_VideoDevice *_this);
|
||||
static void X11_VideoQuit(SDL_VideoDevice *_this);
|
||||
@ -214,6 +212,7 @@ static SDL_VideoDevice *X11_CreateDevice(void)
|
||||
device->AcceptDragAndDrop = X11_AcceptDragAndDrop;
|
||||
device->FlashWindow = X11_FlashWindow;
|
||||
device->ShowWindowSystemMenu = X11_ShowWindowSystemMenu;
|
||||
device->SetWindowFocusable = X11_SetWindowFocusable;
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
|
||||
device->SetWindowMouseRect = X11_SetWindowMouseRect;
|
||||
|
@ -25,30 +25,6 @@
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xresource.h>
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR
|
||||
#include <X11/Xcursor/Xcursor.h>
|
||||
#endif
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_XDBE
|
||||
#include <X11/extensions/Xdbe.h>
|
||||
#endif
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
|
||||
#include <X11/extensions/XInput2.h>
|
||||
#endif
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#endif
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER
|
||||
#include <X11/extensions/scrnsaver.h>
|
||||
#endif
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
|
||||
#include <X11/extensions/shape.h>
|
||||
#endif
|
||||
|
||||
#include "../../core/linux/SDL_dbus.h"
|
||||
#include "../../core/linux/SDL_ime.h"
|
||||
|
||||
|
@ -22,6 +22,8 @@
|
||||
|
||||
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_X11)
|
||||
|
||||
#include "../SDL_vulkan_internal.h"
|
||||
|
||||
#include "SDL_x11video.h"
|
||||
|
||||
#include "SDL_x11vulkan.h"
|
||||
|
@ -23,11 +23,11 @@
|
||||
#ifndef SDL_x11vulkan_h_
|
||||
#define SDL_x11vulkan_h_
|
||||
|
||||
#include "../SDL_vulkan_internal.h"
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
|
||||
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_X11)
|
||||
|
||||
/*typedef struct xcb_connection_t xcb_connection_t;*/
|
||||
typedef struct xcb_connection_t xcb_connection_t;
|
||||
typedef xcb_connection_t *(*PFN_XGetXCBConnection)(Display *dpy);
|
||||
|
||||
int X11_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user