update sdl Merge commit '4d48f9d23713d94b861da7b5d41baf2a41334994'

This commit is contained in:
2023-08-12 20:17:29 +02:00
215 changed files with 12672 additions and 17114 deletions

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -122,8 +122,11 @@ typedef struct
void *driverdata;
} SDL_Mouse;
/* Initialize the mouse subsystem */
extern int SDL_InitMouse(void);
/* Initialize the mouse subsystem, called before the main video driver is initialized */
extern int SDL_PreInitMouse(void);
/* Finish initializing the mouse subsystem, called after the main video driver was initialized */
extern void SDL_PostInitMouse(void);
/* Get the mouse state structure */
SDL_Mouse *SDL_GetMouse(void);

View File

@ -20,7 +20,7 @@
*/
#include "SDL_internal.h"
#if defined(__WIN32__) || defined(__GDK__)
#if defined(__WIN32__) || defined(__GDK__) || defined(__WINRT__)
#include "../core/windows/SDL_windows.h"
#endif
@ -47,10 +47,9 @@
#include "../core/android/SDL_android.h"
#endif
#if defined(__WIN32__) || defined(__GDK__)
#if defined(__WIN32__) || defined(__GDK__) || defined(__WINRT__)
/* Functions to read/write Win32 API file pointers */
#ifndef INVALID_SET_FILE_POINTER
#define INVALID_SET_FILE_POINTER 0xFFFFFFFF
#endif
@ -59,7 +58,7 @@
static int SDLCALL windows_file_open(SDL_RWops *context, const char *filename, const char *mode)
{
#if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
#if !defined(__XBOXONE__) && !defined(__XBOXSERIES__) && !defined(__WINRT__)
UINT old_error_mode;
#endif
HANDLE h;
@ -67,10 +66,6 @@ static int SDLCALL windows_file_open(SDL_RWops *context, const char *filename, c
DWORD must_exist, truncate;
int a_mode;
if (context == NULL) {
return -1; /* failed (invalid call) */
}
context->hidden.windowsio.h = INVALID_HANDLE_VALUE; /* mark this as unusable */
context->hidden.windowsio.buffer.data = NULL;
context->hidden.windowsio.buffer.size = 0;
@ -99,7 +94,7 @@ static int SDLCALL windows_file_open(SDL_RWops *context, const char *filename, c
if (!context->hidden.windowsio.buffer.data) {
return SDL_OutOfMemory();
}
#if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
#if !defined(__XBOXONE__) && !defined(__XBOXSERIES__) && !defined(__WINRT__)
/* Do not open a dialog box if failure */
old_error_mode =
SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
@ -107,14 +102,29 @@ static int SDLCALL windows_file_open(SDL_RWops *context, const char *filename, c
{
LPTSTR tstr = WIN_UTF8ToString(filename);
h = CreateFile(tstr, (w_right | r_right),
(w_right) ? 0 : FILE_SHARE_READ, NULL,
#if defined(__WINRT__)
CREATEFILE2_EXTENDED_PARAMETERS extparams;
SDL_zero(extparams);
extparams.dwSize = sizeof(extparams);
extparams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
h = CreateFile2(tstr,
(w_right | r_right),
(w_right) ? 0 : FILE_SHARE_READ,
(must_exist | truncate | a_mode),
&extparams);
#else
h = CreateFile(tstr,
(w_right | r_right),
(w_right) ? 0 : FILE_SHARE_READ,
NULL,
(must_exist | truncate | a_mode),
FILE_ATTRIBUTE_NORMAL, NULL);
FILE_ATTRIBUTE_NORMAL,
NULL);
#endif
SDL_free(tstr);
}
#if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
#if !defined(__XBOXONE__) && !defined(__XBOXSERIES__) && !defined(__WINRT__)
/* restore old behavior */
SetErrorMode(old_error_mode);
#endif
@ -135,10 +145,6 @@ static Sint64 SDLCALL windows_file_size(SDL_RWops *context)
{
LARGE_INTEGER size;
if (context == NULL || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
return SDL_SetError("windows_file_size: invalid context/file not opened");
}
if (!GetFileSizeEx(context->hidden.windowsio.h, &size)) {
return WIN_SetError("windows_file_size");
}
@ -151,13 +157,9 @@ static Sint64 SDLCALL windows_file_seek(SDL_RWops *context, Sint64 offset, int w
DWORD windowswhence;
LARGE_INTEGER windowsoffset;
if (context == NULL || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
return SDL_SetError("windows_file_seek: invalid context/file not opened");
}
/* FIXME: We may be able to satisfy the seek within buffered data */
if (whence == SDL_RW_SEEK_CUR && context->hidden.windowsio.buffer.left) {
offset -= (long)context->hidden.windowsio.buffer.left;
offset -= context->hidden.windowsio.buffer.left;
}
context->hidden.windowsio.buffer.left = 0;
@ -182,27 +184,18 @@ static Sint64 SDLCALL windows_file_seek(SDL_RWops *context, Sint64 offset, int w
return windowsoffset.QuadPart;
}
static Sint64 SDLCALL
windows_file_read(SDL_RWops *context, void *ptr, Sint64 size)
static size_t SDLCALL windows_file_read(SDL_RWops *context, void *ptr, size_t size)
{
size_t total_need = (size_t) size;
size_t total_need = size;
size_t total_read = 0;
size_t read_ahead;
DWORD byte_read;
if (context == NULL || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
return SDL_SetError("Invalid file handle");
} else if (!total_need) {
return 0;
}
DWORD bytes;
if (context->hidden.windowsio.buffer.left > 0) {
void *data = (char *)context->hidden.windowsio.buffer.data +
context->hidden.windowsio.buffer.size -
context->hidden.windowsio.buffer.left;
read_ahead =
SDL_min(total_need, context->hidden.windowsio.buffer.left);
read_ahead = SDL_min(total_need, context->hidden.windowsio.buffer.left);
SDL_memcpy(ptr, data, read_ahead);
context->hidden.windowsio.buffer.left -= read_ahead;
@ -216,39 +209,37 @@ windows_file_read(SDL_RWops *context, void *ptr, Sint64 size)
if (total_need < READAHEAD_BUFFER_SIZE) {
if (!ReadFile(context->hidden.windowsio.h, context->hidden.windowsio.buffer.data,
READAHEAD_BUFFER_SIZE, &byte_read, NULL)) {
return SDL_Error(SDL_EFREAD);
READAHEAD_BUFFER_SIZE, &bytes, NULL)) {
SDL_Error(SDL_EFREAD);
return 0;
}
read_ahead = SDL_min(total_need, (int)byte_read);
read_ahead = SDL_min(total_need, bytes);
SDL_memcpy(ptr, context->hidden.windowsio.buffer.data, read_ahead);
context->hidden.windowsio.buffer.size = byte_read;
context->hidden.windowsio.buffer.left = byte_read - read_ahead;
context->hidden.windowsio.buffer.size = bytes;
context->hidden.windowsio.buffer.left = bytes - read_ahead;
total_read += read_ahead;
} else {
if (!ReadFile(context->hidden.windowsio.h, ptr, (DWORD)total_need, &byte_read, NULL)) {
return SDL_Error(SDL_EFREAD);
if (!ReadFile(context->hidden.windowsio.h, ptr, (DWORD)total_need, &bytes, NULL)) {
SDL_Error(SDL_EFREAD);
return 0;
}
total_read += byte_read;
total_read += bytes;
}
return total_read;
}
static Sint64 SDLCALL
windows_file_write(SDL_RWops *context, const void *ptr, Sint64 size)
static size_t SDLCALL windows_file_write(SDL_RWops *context, const void *ptr, size_t size)
{
const size_t total_bytes = (size_t) size;
DWORD byte_written;
if (context == NULL || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
return SDL_SetError("Invalid file handle");
} else if (!total_bytes) {
return 0;
}
const size_t total_bytes = size;
DWORD bytes;
if (context->hidden.windowsio.buffer.left) {
SetFilePointer(context->hidden.windowsio.h,
if (!SetFilePointer(context->hidden.windowsio.h,
-(LONG)context->hidden.windowsio.buffer.left, NULL,
FILE_CURRENT);
FILE_CURRENT)) {
SDL_Error(SDL_EFSEEK);
return 0;
}
context->hidden.windowsio.buffer.left = 0;
}
@ -257,28 +248,30 @@ windows_file_write(SDL_RWops *context, const void *ptr, Sint64 size)
LARGE_INTEGER windowsoffset;
windowsoffset.QuadPart = 0;
if (!SetFilePointerEx(context->hidden.windowsio.h, windowsoffset, &windowsoffset, FILE_END)) {
return SDL_Error(SDL_EFWRITE);
SDL_Error(SDL_EFSEEK);
return 0;
}
}
if (!WriteFile(context->hidden.windowsio.h, ptr, (DWORD)total_bytes, &byte_written, NULL)) {
return SDL_Error(SDL_EFWRITE);
if (!WriteFile(context->hidden.windowsio.h, ptr, (DWORD)total_bytes, &bytes, NULL)) {
SDL_Error(SDL_EFWRITE);
return 0;
}
return (Sint64) byte_written;
return bytes;
}
static int SDLCALL windows_file_close(SDL_RWops *context)
{
if (context) {
if (context->hidden.windowsio.h != INVALID_HANDLE_VALUE) {
CloseHandle(context->hidden.windowsio.h);
context->hidden.windowsio.h = INVALID_HANDLE_VALUE; /* to be sure */
}
if (context->hidden.windowsio.h != INVALID_HANDLE_VALUE) {
CloseHandle(context->hidden.windowsio.h);
context->hidden.windowsio.h = INVALID_HANDLE_VALUE; /* to be sure */
}
if (context->hidden.windowsio.buffer.data) {
SDL_free(context->hidden.windowsio.buffer.data);
context->hidden.windowsio.buffer.data = NULL;
SDL_DestroyRW(context);
}
SDL_DestroyRW(context);
return 0;
}
#endif /* defined(__WIN32__) || defined(__GDK__) */
@ -323,20 +316,6 @@ static int SDLCALL windows_file_close(SDL_RWops *context)
#define fseek_off_t long
#endif
static Sint64 SDLCALL stdio_size(SDL_RWops *context)
{
Sint64 pos, size;
pos = SDL_RWseek(context, 0, SDL_RW_SEEK_CUR);
if (pos < 0) {
return -1;
}
size = SDL_RWseek(context, 0, SDL_RW_SEEK_END);
SDL_RWseek(context, pos, SDL_RW_SEEK_SET);
return size;
}
static Sint64 SDLCALL stdio_seek(SDL_RWops *context, Sint64 offset, int whence)
{
int stdiowhence;
@ -371,41 +350,37 @@ static Sint64 SDLCALL stdio_seek(SDL_RWops *context, Sint64 offset, int whence)
return SDL_Error(SDL_EFSEEK);
}
static Sint64 SDLCALL
stdio_read(SDL_RWops *context, void *ptr, Sint64 size)
static size_t SDLCALL stdio_read(SDL_RWops *context, void *ptr, size_t size)
{
size_t nread;
size_t bytes;
nread = fread(ptr, 1, (size_t)size, (FILE *)context->hidden.stdio.fp);
if (nread == 0 && ferror((FILE *)context->hidden.stdio.fp)) {
return SDL_Error(SDL_EFREAD);
bytes = fread(ptr, 1, size, (FILE *)context->hidden.stdio.fp);
if (bytes == 0 && ferror((FILE *)context->hidden.stdio.fp)) {
SDL_Error(SDL_EFREAD);
}
return (Sint64) nread;
return bytes;
}
static Sint64 SDLCALL
stdio_write(SDL_RWops *context, const void *ptr, Sint64 size)
static size_t SDLCALL stdio_write(SDL_RWops *context, const void *ptr, size_t size)
{
size_t nwrote;
size_t bytes;
nwrote = fwrite(ptr, 1, (size_t)size, (FILE *)context->hidden.stdio.fp);
if (nwrote == 0 && ferror((FILE *)context->hidden.stdio.fp)) {
return SDL_Error(SDL_EFWRITE);
bytes = fwrite(ptr, 1, size, (FILE *)context->hidden.stdio.fp);
if (bytes == 0 && ferror((FILE *)context->hidden.stdio.fp)) {
SDL_Error(SDL_EFWRITE);
}
return (Sint64) nwrote;
return bytes;
}
static int SDLCALL stdio_close(SDL_RWops *context)
{
int status = 0;
if (context) {
if (context->hidden.stdio.autoclose) {
if (fclose((FILE *)context->hidden.stdio.fp) != 0) {
status = SDL_Error(SDL_EFWRITE);
}
if (context->hidden.stdio.autoclose) {
if (fclose((FILE *)context->hidden.stdio.fp) != 0) {
status = SDL_Error(SDL_EFWRITE);
}
SDL_DestroyRW(context);
}
SDL_DestroyRW(context);
return status;
}
@ -415,7 +390,6 @@ static SDL_RWops *SDL_RWFromFP(void *fp, SDL_bool autoclose)
rwops = SDL_CreateRW();
if (rwops != NULL) {
rwops->size = stdio_size;
rwops->seek = stdio_seek;
rwops->read = stdio_read;
rwops->write = stdio_write;
@ -432,7 +406,7 @@ static SDL_RWops *SDL_RWFromFP(void *fp, SDL_bool autoclose)
static Sint64 SDLCALL mem_size(SDL_RWops *context)
{
return (Sint64)(context->hidden.mem.stop - context->hidden.mem.base);
return (context->hidden.mem.stop - context->hidden.mem.base);
}
static Sint64 SDLCALL mem_seek(SDL_RWops *context, Sint64 offset, int whence)
@ -462,43 +436,27 @@ static Sint64 SDLCALL mem_seek(SDL_RWops *context, Sint64 offset, int whence)
return (Sint64)(context->hidden.mem.here - context->hidden.mem.base);
}
static Sint64 mem_io(SDL_RWops *context, void *dst, const void *src, Sint64 size)
static size_t mem_io(SDL_RWops *context, void *dst, const void *src, size_t size)
{
const Sint64 mem_available = (Sint64) (context->hidden.mem.stop - context->hidden.mem.here);
const size_t mem_available = (context->hidden.mem.stop - context->hidden.mem.here);
if (size > mem_available) {
size = mem_available;
}
SDL_memcpy(dst, src, (size_t) size);
SDL_memcpy(dst, src, size);
context->hidden.mem.here += size;
return size;
}
static Sint64 SDLCALL
mem_read(SDL_RWops *context, void *ptr, Sint64 size)
static size_t SDLCALL mem_read(SDL_RWops *context, void *ptr, size_t size)
{
return mem_io(context, ptr, context->hidden.mem.here, size);
}
static Sint64 SDLCALL
mem_write(SDL_RWops *context, const void *ptr, Sint64 size)
static size_t SDLCALL mem_write(SDL_RWops *context, const void *ptr, size_t size)
{
return mem_io(context, context->hidden.mem.here, ptr, size);
}
static Sint64 SDLCALL
mem_writeconst(SDL_RWops *context, const void *ptr, Sint64 size)
{
return SDL_SetError("Can't write to read-only memory");
}
static int SDLCALL mem_close(SDL_RWops *context)
{
if (context) {
SDL_DestroyRW(context);
}
return 0;
}
/* Functions to create SDL_RWops structures from various data sources */
SDL_RWops *SDL_RWFromFile(const char *file, const char *mode)
@ -552,7 +510,7 @@ SDL_RWops *SDL_RWFromFile(const char *file, const char *mode)
rwops->close = Android_JNI_FileClose;
rwops->type = SDL_RWOPS_JNIFILE;
#elif defined(__WIN32__) || defined(__GDK__)
#elif defined(__WIN32__) || defined(__GDK__) || defined(__WINRT__)
rwops = SDL_CreateRW();
if (rwops == NULL) {
return NULL; /* SDL_SetError already setup by SDL_CreateRW() */
@ -596,13 +554,14 @@ SDL_RWops *SDL_RWFromFile(const char *file, const char *mode)
SDL_RWops *SDL_RWFromMem(void *mem, size_t size)
{
SDL_RWops *rwops = NULL;
if (mem == NULL) {
SDL_InvalidParamError("mem");
return rwops;
return NULL;
}
if (!size) {
SDL_InvalidParamError("size");
return rwops;
return NULL;
}
rwops = SDL_CreateRW();
@ -611,7 +570,6 @@ SDL_RWops *SDL_RWFromMem(void *mem, size_t size)
rwops->seek = mem_seek;
rwops->read = mem_read;
rwops->write = mem_write;
rwops->close = mem_close;
rwops->hidden.mem.base = (Uint8 *)mem;
rwops->hidden.mem.here = rwops->hidden.mem.base;
rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
@ -623,13 +581,14 @@ SDL_RWops *SDL_RWFromMem(void *mem, size_t size)
SDL_RWops *SDL_RWFromConstMem(const void *mem, size_t size)
{
SDL_RWops *rwops = NULL;
if (mem == NULL) {
SDL_InvalidParamError("mem");
return rwops;
return NULL;
}
if (!size) {
SDL_InvalidParamError("size");
return rwops;
return NULL;
}
rwops = SDL_CreateRW();
@ -637,8 +596,6 @@ SDL_RWops *SDL_RWFromConstMem(const void *mem, size_t size)
rwops->size = mem_size;
rwops->seek = mem_seek;
rwops->read = mem_read;
rwops->write = mem_writeconst;
rwops->close = mem_close;
rwops->hidden.mem.base = (Uint8 *)mem;
rwops->hidden.mem.here = rwops->hidden.mem.base;
rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
@ -649,29 +606,30 @@ SDL_RWops *SDL_RWFromConstMem(const void *mem, size_t size)
SDL_RWops *SDL_CreateRW(void)
{
SDL_RWops *area;
SDL_RWops *context;
area = (SDL_RWops *)SDL_malloc(sizeof(*area));
if (area == NULL) {
context = (SDL_RWops *)SDL_calloc(1, sizeof(*context));
if (context == NULL) {
SDL_OutOfMemory();
} else {
area->type = SDL_RWOPS_UNKNOWN;
context->type = SDL_RWOPS_UNKNOWN;
}
return area;
return context;
}
void SDL_DestroyRW(SDL_RWops *area)
void SDL_DestroyRW(SDL_RWops *context)
{
SDL_free(area);
SDL_free(context);
}
/* Load all the data from an SDL data stream */
void *SDL_LoadFile_RW(SDL_RWops *src, size_t *datasize, SDL_bool freesrc)
{
static const Sint64 FILE_CHUNK_SIZE = 1024;
Sint64 size;
Sint64 size_read, size_total;
void *data = NULL, *newdata;
const int FILE_CHUNK_SIZE = 1024;
Sint64 size, size_total;
size_t size_read;
char *data = NULL, *newdata;
SDL_bool loading_chunks = SDL_FALSE;
if (src == NULL) {
SDL_InvalidParamError("src");
@ -681,46 +639,52 @@ void *SDL_LoadFile_RW(SDL_RWops *src, size_t *datasize, SDL_bool freesrc)
size = SDL_RWsize(src);
if (size < 0) {
size = FILE_CHUNK_SIZE;
loading_chunks = SDL_TRUE;
}
if (size >= SDL_SIZE_MAX) {
SDL_OutOfMemory();
goto done;
}
data = (char *)SDL_malloc((size_t)(size + 1));
if (!data) {
SDL_OutOfMemory();
goto done;
}
data = SDL_malloc((size_t)(size + 1));
size_total = 0;
for (;;) {
if ((size_total + FILE_CHUNK_SIZE) > size) {
size = (size_total + FILE_CHUNK_SIZE);
newdata = SDL_realloc(data, (size_t)(size + 1));
if (newdata == NULL) {
SDL_free(data);
data = NULL;
SDL_OutOfMemory();
goto done;
if (loading_chunks) {
if ((size_total + FILE_CHUNK_SIZE) > size) {
size = (size_total + FILE_CHUNK_SIZE);
if (size >= SDL_SIZE_MAX) {
newdata = NULL;
} else {
newdata = SDL_realloc(data, (size_t)(size + 1));
}
if (newdata == NULL) {
SDL_free(data);
data = NULL;
SDL_OutOfMemory();
goto done;
}
data = newdata;
}
data = newdata;
}
size_read = SDL_RWread(src, (char *)data + size_total, size - size_total);
size_read = SDL_RWread(src, data + size_total, (size_t)(size - size_total));
if (size_read > 0) {
size_total += size_read;
continue;
}
if (size_read == 0) {
/* End of file */
break;
}
if (size_read == -2) {
/* Non-blocking I/O, should we wait here? */
}
/* Read error */
SDL_free(data);
data = NULL;
goto done;
/* The stream status will remain set for the caller to check */
break;
}
if (datasize) {
*datasize = (size_t) size_total;
*datasize = (size_t)size_total;
}
((char *)data)[size_total] = '\0';
data[size_total] = '\0';
done:
if (freesrc && src) {
@ -731,134 +695,314 @@ done:
void *SDL_LoadFile(const char *file, size_t *datasize)
{
return SDL_LoadFile_RW(SDL_RWFromFile(file, "rb"), datasize, 1);
return SDL_LoadFile_RW(SDL_RWFromFile(file, "rb"), datasize, SDL_TRUE);
}
Sint64 SDL_RWsize(SDL_RWops *context)
{
if (!context) {
return SDL_InvalidParamError("context");
}
if (!context->size) {
Sint64 pos, size;
pos = SDL_RWseek(context, 0, SDL_RW_SEEK_CUR);
if (pos < 0) {
return -1;
}
size = SDL_RWseek(context, 0, SDL_RW_SEEK_END);
SDL_RWseek(context, pos, SDL_RW_SEEK_SET);
return size;
}
return context->size(context);
}
Sint64 SDL_RWseek(SDL_RWops *context, Sint64 offset, int whence)
{
if (!context) {
return SDL_InvalidParamError("context");
}
if (!context->seek) {
return SDL_Unsupported();
}
return context->seek(context, offset, whence);
}
Sint64 SDL_RWtell(SDL_RWops *context)
{
return context->seek(context, 0, SDL_RW_SEEK_CUR);
return SDL_RWseek(context, 0, SDL_RW_SEEK_CUR);
}
Sint64 SDL_RWread(SDL_RWops *context, void *ptr, Sint64 size)
size_t SDL_RWread(SDL_RWops *context, void *ptr, size_t size)
{
return context->read(context, ptr, size);
size_t bytes;
if (!context) {
SDL_InvalidParamError("context");
return 0;
}
if (!context->read) {
context->status = SDL_RWOPS_STATUS_WRITEONLY;
SDL_Unsupported();
return 0;
}
context->status = SDL_RWOPS_STATUS_READY;
SDL_ClearError();
if (size == 0) {
return 0;
}
bytes = context->read(context, ptr, size);
if (bytes == 0 && context->status == SDL_RWOPS_STATUS_READY) {
if (*SDL_GetError()) {
context->status = SDL_RWOPS_STATUS_ERROR;
} else {
context->status = SDL_RWOPS_STATUS_EOF;
}
}
return bytes;
}
Sint64 SDL_RWwrite(SDL_RWops *context, const void *ptr, Sint64 size)
size_t SDL_RWwrite(SDL_RWops *context, const void *ptr, size_t size)
{
return context->write(context, ptr, size);
size_t bytes;
if (!context) {
SDL_InvalidParamError("context");
return 0;
}
if (!context->write) {
context->status = SDL_RWOPS_STATUS_READONLY;
SDL_Unsupported();
return 0;
}
context->status = SDL_RWOPS_STATUS_READY;
SDL_ClearError();
if (size == 0) {
return 0;
}
bytes = context->write(context, ptr, size);
if (bytes == 0 && context->status == SDL_RWOPS_STATUS_READY) {
context->status = SDL_RWOPS_STATUS_ERROR;
}
return bytes;
}
int SDL_RWclose(SDL_RWops *context)
{
if (!context) {
return SDL_InvalidParamError("context");
}
if (!context->close) {
SDL_DestroyRW(context);
return 0;
}
return context->close(context);
}
/* Functions for dynamically reading and writing endian-specific values */
Uint8 SDL_ReadU8(SDL_RWops *src)
SDL_bool SDL_ReadU8(SDL_RWops *src, Uint8 *value)
{
Uint8 value = 0;
Uint8 data = 0;
SDL_bool result = SDL_FALSE;
SDL_RWread(src, &value, sizeof(value));
return value;
if (SDL_RWread(src, &data, sizeof(data)) == sizeof(data)) {
result = SDL_TRUE;
}
if (value) {
*value = data;
}
return result;
}
Uint16 SDL_ReadLE16(SDL_RWops *src)
SDL_bool SDL_ReadU16LE(SDL_RWops *src, Uint16 *value)
{
Uint16 value = 0;
Uint16 data = 0;
SDL_bool result = SDL_FALSE;
SDL_RWread(src, &value, sizeof(value));
return SDL_SwapLE16(value);
if (SDL_RWread(src, &data, sizeof(data)) == sizeof(data)) {
result = SDL_TRUE;
}
if (value) {
*value = SDL_SwapLE16(data);
}
return result;
}
Uint16 SDL_ReadBE16(SDL_RWops *src)
SDL_bool SDL_ReadS16LE(SDL_RWops *src, Sint16 *value)
{
Uint16 value = 0;
SDL_RWread(src, &value, sizeof(value));
return SDL_SwapBE16(value);
return SDL_ReadU16LE(src, (Uint16 *)value);
}
Uint32 SDL_ReadLE32(SDL_RWops *src)
SDL_bool SDL_ReadU16BE(SDL_RWops *src, Uint16 *value)
{
Uint32 value = 0;
Uint16 data = 0;
SDL_bool result = SDL_FALSE;
SDL_RWread(src, &value, sizeof(value));
return SDL_SwapLE32(value);
if (SDL_RWread(src, &data, sizeof(data)) == sizeof(data)) {
result = SDL_TRUE;
}
if (value) {
*value = SDL_SwapBE16(data);
}
return result;
}
Uint32 SDL_ReadBE32(SDL_RWops *src)
SDL_bool SDL_ReadS16BE(SDL_RWops *src, Sint16 *value)
{
Uint32 value = 0;
SDL_RWread(src, &value, sizeof(value));
return SDL_SwapBE32(value);
return SDL_ReadU16BE(src, (Uint16 *)value);
}
Uint64 SDL_ReadLE64(SDL_RWops *src)
SDL_bool SDL_ReadU32LE(SDL_RWops *src, Uint32 *value)
{
Uint64 value = 0;
Uint32 data = 0;
SDL_bool result = SDL_FALSE;
SDL_RWread(src, &value, sizeof(value));
return SDL_SwapLE64(value);
if (SDL_RWread(src, &data, sizeof(data)) == sizeof(data)) {
result = SDL_TRUE;
}
if (value) {
*value = SDL_SwapLE32(data);
}
return result;
}
Uint64 SDL_ReadBE64(SDL_RWops *src)
SDL_bool SDL_ReadS32LE(SDL_RWops *src, Sint32 *value)
{
Uint64 value = 0;
SDL_RWread(src, &value, sizeof(value));
return SDL_SwapBE64(value);
return SDL_ReadU32LE(src, (Uint32 *)value);
}
size_t SDL_WriteU8(SDL_RWops *dst, Uint8 value)
SDL_bool SDL_ReadU32BE(SDL_RWops *src, Uint32 *value)
{
return (SDL_RWwrite(dst, &value, sizeof(value)) == sizeof(value)) ? 1 : 0;
Uint32 data = 0;
SDL_bool result = SDL_FALSE;
if (SDL_RWread(src, &data, sizeof(data)) == sizeof(data)) {
result = SDL_TRUE;
}
if (value) {
*value = SDL_SwapBE32(data);
}
return result;
}
size_t SDL_WriteLE16(SDL_RWops *dst, Uint16 value)
SDL_bool SDL_ReadS32BE(SDL_RWops *src, Sint32 *value)
{
return SDL_ReadU32BE(src, (Uint32 *)value);
}
SDL_bool SDL_ReadU64LE(SDL_RWops *src, Uint64 *value)
{
Uint64 data = 0;
SDL_bool result = SDL_FALSE;
if (SDL_RWread(src, &data, sizeof(data)) == sizeof(data)) {
result = SDL_TRUE;
}
if (value) {
*value = SDL_SwapLE64(data);
}
return result;
}
SDL_bool SDL_ReadS64LE(SDL_RWops *src, Sint64 *value)
{
return SDL_ReadU64LE(src, (Uint64 *)value);
}
SDL_bool SDL_ReadU64BE(SDL_RWops *src, Uint64 *value)
{
Uint64 data = 0;
SDL_bool result = SDL_FALSE;
if (SDL_RWread(src, &data, sizeof(data)) == sizeof(data)) {
result = SDL_TRUE;
}
if (value) {
*value = SDL_SwapBE64(data);
}
return result;
}
SDL_bool SDL_ReadS64BE(SDL_RWops *src, Sint64 *value)
{
return SDL_ReadU64BE(src, (Uint64 *)value);
}
SDL_bool SDL_WriteU8(SDL_RWops *dst, Uint8 value)
{
return (SDL_RWwrite(dst, &value, sizeof(value)) == sizeof(value)) ? SDL_TRUE : SDL_FALSE;
}
SDL_bool SDL_WriteU16LE(SDL_RWops *dst, Uint16 value)
{
const Uint16 swapped = SDL_SwapLE16(value);
return (SDL_RWwrite(dst, &swapped, sizeof(swapped)) == sizeof(swapped)) ? 1 : 0;
return (SDL_RWwrite(dst, &swapped, sizeof(swapped)) == sizeof(swapped)) ? SDL_TRUE : SDL_FALSE;
}
size_t SDL_WriteBE16(SDL_RWops *dst, Uint16 value)
SDL_bool SDL_WriteS16LE(SDL_RWops *dst, Sint16 value)
{
return SDL_WriteU16LE(dst, (Uint16)value);
}
SDL_bool SDL_WriteU16BE(SDL_RWops *dst, Uint16 value)
{
const Uint16 swapped = SDL_SwapBE16(value);
return (SDL_RWwrite(dst, &swapped, sizeof(swapped)) == sizeof(swapped)) ? 1 : 0;
return (SDL_RWwrite(dst, &swapped, sizeof(swapped)) == sizeof(swapped)) ? SDL_TRUE : SDL_FALSE;
}
size_t SDL_WriteLE32(SDL_RWops *dst, Uint32 value)
SDL_bool SDL_WriteS16BE(SDL_RWops *dst, Sint16 value)
{
return SDL_WriteU16BE(dst, (Uint16)value);
}
SDL_bool SDL_WriteU32LE(SDL_RWops *dst, Uint32 value)
{
const Uint32 swapped = SDL_SwapLE32(value);
return (SDL_RWwrite(dst, &swapped, sizeof(swapped)) == sizeof(swapped)) ? 1 : 0;
return (SDL_RWwrite(dst, &swapped, sizeof(swapped)) == sizeof(swapped)) ? SDL_TRUE : SDL_FALSE;
}
size_t SDL_WriteBE32(SDL_RWops *dst, Uint32 value)
SDL_bool SDL_WriteS32LE(SDL_RWops *dst, Sint32 value)
{
return SDL_WriteU32LE(dst, (Uint32)value);
}
SDL_bool SDL_WriteU32BE(SDL_RWops *dst, Uint32 value)
{
const Uint32 swapped = SDL_SwapBE32(value);
return (SDL_RWwrite(dst, &swapped, sizeof(swapped)) == sizeof(swapped)) ? 1 : 0;
return (SDL_RWwrite(dst, &swapped, sizeof(swapped)) == sizeof(swapped)) ? SDL_TRUE : SDL_FALSE;
}
size_t SDL_WriteLE64(SDL_RWops *dst, Uint64 value)
SDL_bool SDL_WriteS32BE(SDL_RWops *dst, Sint32 value)
{
return SDL_WriteU32BE(dst, (Uint32)value);
}
SDL_bool SDL_WriteU64LE(SDL_RWops *dst, Uint64 value)
{
const Uint64 swapped = SDL_SwapLE64(value);
return (SDL_RWwrite(dst, &swapped, sizeof(swapped)) == sizeof(swapped)) ? 1 : 0;
return (SDL_RWwrite(dst, &swapped, sizeof(swapped)) == sizeof(swapped)) ? SDL_TRUE : SDL_FALSE;
}
size_t SDL_WriteBE64(SDL_RWops *dst, Uint64 value)
SDL_bool SDL_WriteS64LE(SDL_RWops *dst, Sint64 value)
{
return SDL_WriteU64LE(dst, (Uint64)value);
}
SDL_bool SDL_WriteU64BE(SDL_RWops *dst, Uint64 value)
{
const Uint64 swapped = SDL_SwapBE64(value);
return (SDL_RWwrite(dst, &swapped, sizeof(swapped)) == sizeof(swapped)) ? 1 : 0;
return (SDL_RWwrite(dst, &swapped, sizeof(swapped)) == sizeof(swapped)) ? SDL_TRUE : SDL_FALSE;
}
SDL_bool SDL_WriteS64BE(SDL_RWops *dst, Sint64 value)
{
return SDL_WriteU64BE(dst, (Uint64)value);
}

View File

@ -50,7 +50,7 @@ char *SDL_GetPrefPath(const char *org, const char *app)
return NULL;
}
char *SDL_GetPath(SDL_Folder folder)
char *SDL_GetUserFolder(SDL_Folder folder)
{
/* TODO: see https://developer.android.com/reference/android/os/Environment#lfields
and https://stackoverflow.com/questions/39332085/get-path-to-pictures-directory */

View File

@ -130,7 +130,7 @@ char *SDL_GetPrefPath(const char *org, const char *app)
}
}
char *SDL_GetPath(SDL_Folder folder)
char *SDL_GetUserFolder(SDL_Folder folder)
{
@autoreleasepool {
#if TARGET_OS_TV

View File

@ -37,7 +37,7 @@ char *SDL_GetPrefPath(const char *org, const char *app)
return NULL;
}
char *SDL_GetPath(SDL_Folder folder)
char *SDL_GetUserFolder(SDL_Folder folder)
{
SDL_Unsupported();
return NULL;

View File

@ -83,7 +83,7 @@ char *SDL_GetPrefPath(const char *org, const char *app)
return retval;
}
char *SDL_GetPath(SDL_Folder folder)
char *SDL_GetUserFolder(SDL_Folder folder)
{
const char *home = NULL;
char *retval;

View File

@ -97,7 +97,7 @@ char *SDL_GetPrefPath(const char *org, const char *app)
return retval;
}
char *SDL_GetPath(SDL_Folder folder)
char *SDL_GetUserFolder(SDL_Folder folder)
{
const char *home = NULL;
char *retval;

View File

@ -60,7 +60,7 @@ char *SDL_GetPrefPath(const char *org, const char *app)
}
/* TODO */
char *SDL_GetPath(SDL_Folder folder)
char *SDL_GetUserFolder(SDL_Folder folder)
{
SDL_Unsupported();
return NULL;

View File

@ -103,7 +103,7 @@ char *SDL_GetPrefPath(const char *org, const char *app)
}
/* TODO */
char *SDL_GetPath(SDL_Folder folder)
char *SDL_GetUserFolder(SDL_Folder folder)
{
SDL_Unsupported();
return NULL;

View File

@ -70,7 +70,7 @@ char *SDL_GetPrefPath(const char *org, const char *app)
}
/* TODO */
char *SDL_GetPath(SDL_Folder folder)
char *SDL_GetUserFolder(SDL_Folder folder)
{
SDL_Unsupported();
return NULL;

View File

@ -200,7 +200,7 @@ char *SDL_GetPrefPath(const char *org, const char *app)
}
/* TODO */
char *SDL_GetPath(SDL_Folder folder)
char *SDL_GetUserFolder(SDL_Folder folder)
{
SDL_Unsupported();
return NULL;

View File

@ -517,7 +517,7 @@ static char *xdg_user_dir_lookup (const char *type)
return NULL;
}
char *SDL_GetPath(SDL_Folder folder)
char *SDL_GetUserFolder(SDL_Folder folder)
{
const char *param = NULL;
char *retval;

View File

@ -84,7 +84,7 @@ char *SDL_GetPrefPath(const char *org, const char *app)
}
/* TODO */
char *SDL_GetPath(SDL_Folder folder)
char *SDL_GetUserFolder(SDL_Folder folder)
{
SDL_Unsupported();
return NULL;

View File

@ -177,7 +177,7 @@ char *SDL_GetPrefPath(const char *org, const char *app)
return retval;
}
char *SDL_GetPath(SDL_Folder folder)
char *SDL_GetUserFolder(SDL_Folder folder)
{
typedef HRESULT (WINAPI *pfnSHGetKnownFolderPath)(REFGUID /* REFKNOWNFOLDERID */, DWORD, HANDLE, PWSTR*);
HMODULE lib = LoadLibrary(L"Shell32.dll");
@ -346,7 +346,7 @@ char *SDL_GetPrefPath(const char *org, const char *app)
return NULL;
}
char *SDL_GetPath(SDL_Folder folder)
char *SDL_GetUserFolder(SDL_Folder folder)
{
SDL_Unsupported();
return NULL;

View File

@ -233,7 +233,7 @@ SDL_GetPrefPath(const char *org, const char *app)
}
/* TODO */
char *SDL_GetPath(SDL_Folder folder)
char *SDL_GetUserFolder(SDL_Folder folder)
{
SDL_Unsupported();
return NULL;

View File

@ -20,6 +20,8 @@
*/
#include "SDL_internal.h"
#if defined(__IOS__) || defined(__TVOS__)
#ifndef SDL_HIDAPI_DISABLED
#include "../SDL_hidapi_c.h"
@ -1031,3 +1033,5 @@ HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev)
}
#endif /* !SDL_HIDAPI_DISABLED */
#endif /* __IOS__ || __TVOS__ */

View File

@ -31,6 +31,7 @@
#pragma warning(push)
#pragma warning(disable: 4200)
#pragma warning(disable: 4201)
#pragma warning(disable: 4214)
#endif
#include <windows.h>
@ -118,9 +119,9 @@ typedef struct hid_pp_link_collection_node_ {
USHORT NumberOfChildren;
USHORT NextSibling;
USHORT FirstChild;
UINT CollectionType : 8;
UINT IsAlias : 1;
UINT Reserved : 23;
ULONG CollectionType : 8;
ULONG IsAlias : 1;
ULONG Reserved : 23;
// Same as the public API structure HIDP_LINK_COLLECTION_NODE, but without PVOID UserContext at the end
} hid_pp_link_collection_node, *phid_pp_link_collection_node;
@ -145,17 +146,17 @@ typedef struct hid_pp_cap_ {
USAGE LinkUsage;
// Start of 8 Flags in one byte
UINT IsMultipleItemsForArray:1;
BOOLEAN IsMultipleItemsForArray:1;
UINT IsPadding:1;
UINT IsButtonCap:1;
UINT IsAbsolute:1;
UINT IsRange:1;
UINT IsAlias:1; // IsAlias is set to TRUE in the first n-1 capability structures added to the capability array. IsAlias set to FALSE in the nth capability structure.
UINT IsStringRange:1;
UINT IsDesignatorRange:1;
BOOLEAN IsPadding:1;
BOOLEAN IsButtonCap:1;
BOOLEAN IsAbsolute:1;
BOOLEAN IsRange:1;
BOOLEAN IsAlias:1; // IsAlias is set to TRUE in the first n-1 capability structures added to the capability array. IsAlias set to FALSE in the nth capability structure.
BOOLEAN IsStringRange:1;
BOOLEAN IsDesignatorRange:1;
// End of 8 Flags in one byte
//BOOLEAN Reserved1[3];
BOOLEAN Reserved1[3];
hidp_unknown_token UnknownTokens[4]; // 4 x 8 Byte

View File

@ -362,6 +362,8 @@ void SDL_PrivateGamepadRemoved(SDL_JoystickID instance_id)
SDL_Event event;
SDL_Gamepad *gamepad;
SDL_AssertJoysticksLocked();
if (!SDL_gamepads_initialized) {
return;
}
@ -466,6 +468,8 @@ static void AdjustSensorOrientation(SDL_Joystick *joystick, float *src, float *d
{
unsigned int i, j;
SDL_AssertJoysticksLocked();
for (i = 0; i < 3; ++i) {
dst[i] = 0.0f;
for (j = 0; j < 3; ++j) {
@ -559,6 +563,8 @@ static SDL_bool HasMappingChangeTracking(MappingChangeTracker *tracker, GamepadM
{
int i;
SDL_AssertJoysticksLocked();
for (i = 0; i < tracker->num_changed_mappings; ++i) {
if (tracker->changed_mappings[i] == mapping) {
return SDL_TRUE;
@ -746,6 +752,7 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_JoystickGUID guid
switch (guid.data[15]) {
case k_eSwitchDeviceInfoControllerType_HVCLeft:
SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,rightshoulder:b10,start:b6,", sizeof(mapping_string));
break;
case k_eSwitchDeviceInfoControllerType_HVCRight:
SDL_strlcat(mapping_string, "a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,rightshoulder:b10,", sizeof(mapping_string));
break;
@ -1653,10 +1660,12 @@ static GamepadMapping_t *SDL_PrivateGenerateAutomaticGamepadMapping(const char *
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpleft", &raw_map->dpleft);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpright", &raw_map->dpright);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc1", &raw_map->misc1);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle1", &raw_map->paddle1);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle2", &raw_map->paddle2);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle3", &raw_map->paddle3);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle4", &raw_map->paddle4);
/* Keep using paddle1-4 in the generated mapping so that it can be
* reused with SDL2 */
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle1", &raw_map->right_paddle1);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle2", &raw_map->left_paddle1);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle3", &raw_map->right_paddle2);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle4", &raw_map->left_paddle2);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "leftx", &raw_map->leftx);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "lefty", &raw_map->lefty);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightx", &raw_map->rightx);
@ -1693,40 +1702,18 @@ static GamepadMapping_t *SDL_PrivateGetGamepadMapping(SDL_JoystickID instance_id
/*
* Add or update an entry into the Mappings Database
*/
int SDL_AddGamepadMappingsFromRW(SDL_RWops *src, int freesrc)
int SDL_AddGamepadMappingsFromRW(SDL_RWops *src, SDL_bool freesrc)
{
const char *platform = SDL_GetPlatform();
int gamepads = 0;
char *buf, *line, *line_end, *tmp, *comma, line_platform[64];
Sint64 db_size;
size_t db_size;
size_t platform_len;
if (src == NULL) {
return SDL_InvalidParamError("src");
}
db_size = SDL_RWsize(src);
buf = (char *)SDL_malloc((size_t)db_size + 1);
buf = (char *)SDL_LoadFile_RW(src, &db_size, freesrc);
if (buf == NULL) {
if (freesrc) {
SDL_RWclose(src);
}
return SDL_SetError("Could not allocate space to read DB into memory");
}
if (SDL_RWread(src, buf, db_size) != db_size) {
if (freesrc) {
SDL_RWclose(src);
}
SDL_free(buf);
return SDL_SetError("Could not read DB");
}
if (freesrc) {
SDL_RWclose(src);
}
buf[db_size] = '\0';
line = buf;
PushMappingChangeTracking();

View File

@ -134,6 +134,7 @@ static const char *s_GamepadMappings[] = {
"03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,",
"03000000852100000201000000000000,FF-GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
"03000000790000000600000000000000,G-Shark GS-GP702,crc:8e4f,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
"030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"03000000300f00000b01000000000000,GGE909 Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
"03000000790000002201000000000000,Game Controller for PC,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
@ -815,6 +816,7 @@ static const char *s_GamepadMappings[] = {
"05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
"05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
"03000000de2800000512000011010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,misc1:b2,paddle1:b21,paddle2:b20,paddle3:b23,paddle4:b22,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,",
"03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
"0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
"05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b18,leftshoulder:b6,leftstick:b13,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:+a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",

View File

@ -150,26 +150,33 @@ void SDL_LockJoysticks(void)
void SDL_UnlockJoysticks(void)
{
SDL_Mutex *joystick_lock = SDL_joystick_lock;
SDL_bool last_unlock = SDL_FALSE;
--SDL_joysticks_locked;
if (!SDL_joysticks_initialized) {
/* NOTE: There's a small window here where another thread could lock the mutex after we've checked for pending locks */
if (!SDL_joysticks_locked && SDL_AtomicGet(&SDL_joystick_lock_pending) == 0) {
/* NOTE: There's a small window here where another thread could lock the mutex */
SDL_joystick_lock = NULL;
last_unlock = SDL_TRUE;
}
}
SDL_UnlockMutex(joystick_lock);
/* The last unlock after joysticks are uninitialized will cleanup the mutex,
* allowing applications to lock joysticks while reinitializing the system.
*/
if (last_unlock) {
SDL_Mutex *joystick_lock = SDL_joystick_lock;
SDL_LockMutex(joystick_lock);
{
SDL_UnlockMutex(SDL_joystick_lock);
SDL_joystick_lock = NULL;
}
SDL_UnlockMutex(joystick_lock);
SDL_DestroyMutex(joystick_lock);
} else {
SDL_UnlockMutex(SDL_joystick_lock);
}
}
@ -560,6 +567,8 @@ static SDL_bool ShouldAttemptSensorFusion(SDL_Joystick *joystick, SDL_bool *inve
const char *hint;
int hint_value;
SDL_AssertJoysticksLocked();
*invert_sensors = SDL_FALSE;
/* The SDL controller sensor API is only available for gamepads (at the moment) */
@ -620,6 +629,8 @@ static void AttemptSensorFusion(SDL_Joystick *joystick, SDL_bool invert_sensors)
SDL_SensorID *sensors;
unsigned int i, j;
SDL_AssertJoysticksLocked();
if (SDL_InitSubSystem(SDL_INIT_SENSOR) < 0) {
return;
}
@ -686,6 +697,8 @@ static void AttemptSensorFusion(SDL_Joystick *joystick, SDL_bool invert_sensors)
static void CleanupSensorFusion(SDL_Joystick *joystick)
{
SDL_AssertJoysticksLocked();
if (joystick->accel_sensor || joystick->gyro_sensor) {
if (joystick->accel_sensor) {
if (joystick->accel) {
@ -1782,6 +1795,7 @@ int SDL_SendJoystickAxis(Uint64 timestamp, SDL_Joystick *joystick, Uint8 axis, S
}
/* Update internal joystick state */
SDL_assert(timestamp != 0);
info->value = value;
joystick->update_complete = timestamp;
@ -1825,6 +1839,7 @@ int SDL_SendJoystickHat(Uint64 timestamp, SDL_Joystick *joystick, Uint8 hat, Uin
}
/* Update internal joystick state */
SDL_assert(timestamp != 0);
joystick->hats[hat] = value;
joystick->update_complete = timestamp;
@ -1884,6 +1899,7 @@ int SDL_SendJoystickButton(Uint64 timestamp, SDL_Joystick *joystick, Uint8 butto
}
/* Update internal joystick state */
SDL_assert(timestamp != 0);
joystick->buttons[button] = state;
joystick->update_complete = timestamp;
@ -1953,7 +1969,7 @@ void SDL_UpdateJoysticks(void)
event.type = SDL_EVENT_JOYSTICK_UPDATE_COMPLETE;
event.common.timestamp = joystick->update_complete;
event.gdevice.which = joystick->instance_id;
event.jdevice.which = joystick->instance_id;
SDL_PushEvent(&event);
joystick->update_complete = 0;
@ -3225,6 +3241,7 @@ int SDL_SendJoystickTouchpad(Uint64 timestamp, SDL_Joystick *joystick, int touch
}
/* Update internal joystick state */
SDL_assert(timestamp != 0);
finger_info->state = state;
finger_info->x = x;
finger_info->y = y;

View File

@ -196,10 +196,10 @@ typedef struct SDL_GamepadMapping
SDL_InputMapping dpleft;
SDL_InputMapping dpright;
SDL_InputMapping misc1;
SDL_InputMapping paddle1;
SDL_InputMapping paddle2;
SDL_InputMapping paddle3;
SDL_InputMapping paddle4;
SDL_InputMapping right_paddle1;
SDL_InputMapping left_paddle1;
SDL_InputMapping right_paddle2;
SDL_InputMapping left_paddle2;
SDL_InputMapping leftx;
SDL_InputMapping lefty;
SDL_InputMapping rightx;

View File

@ -198,15 +198,16 @@ static SDL_Scancode button_to_scancode(int button)
int Android_OnPadDown(int device_id, int keycode)
{
Uint64 timestamp = SDL_GetTicksNS();
SDL_joylist_item *item;
int button = keycode_to_SDL(keycode);
if (button >= 0) {
SDL_LockJoysticks();
item = JoystickByDeviceId(device_id);
if (item && item->joystick) {
SDL_SendJoystickButton(0, item->joystick, button, SDL_PRESSED);
SDL_SendJoystickButton(timestamp, item->joystick, button, SDL_PRESSED);
} else {
SDL_SendKeyboardKey(0, SDL_PRESSED, button_to_scancode(button));
SDL_SendKeyboardKey(timestamp, SDL_PRESSED, button_to_scancode(button));
}
SDL_UnlockJoysticks();
return 0;
@ -217,15 +218,16 @@ int Android_OnPadDown(int device_id, int keycode)
int Android_OnPadUp(int device_id, int keycode)
{
Uint64 timestamp = SDL_GetTicksNS();
SDL_joylist_item *item;
int button = keycode_to_SDL(keycode);
if (button >= 0) {
SDL_LockJoysticks();
item = JoystickByDeviceId(device_id);
if (item && item->joystick) {
SDL_SendJoystickButton(0, item->joystick, button, SDL_RELEASED);
SDL_SendJoystickButton(timestamp, item->joystick, button, SDL_RELEASED);
} else {
SDL_SendKeyboardKey(0, SDL_RELEASED, button_to_scancode(button));
SDL_SendKeyboardKey(timestamp, SDL_RELEASED, button_to_scancode(button));
}
SDL_UnlockJoysticks();
return 0;
@ -236,13 +238,14 @@ int Android_OnPadUp(int device_id, int keycode)
int Android_OnJoy(int device_id, int axis, float value)
{
Uint64 timestamp = SDL_GetTicksNS();
/* Android gives joy info normalized as [-1.0, 1.0] or [0.0, 1.0] */
SDL_joylist_item *item;
SDL_LockJoysticks();
item = JoystickByDeviceId(device_id);
if (item && item->joystick) {
SDL_SendJoystickAxis(0, item->joystick, axis, (Sint16)(32767. * value));
SDL_SendJoystickAxis(timestamp, item->joystick, axis, (Sint16)(32767. * value));
}
SDL_UnlockJoysticks();
@ -251,6 +254,7 @@ int Android_OnJoy(int device_id, int axis, float value)
int Android_OnHat(int device_id, int hat_id, int x, int y)
{
Uint64 timestamp = SDL_GetTicksNS();
const int DPAD_UP_MASK = (1 << SDL_GAMEPAD_BUTTON_DPAD_UP);
const int DPAD_DOWN_MASK = (1 << SDL_GAMEPAD_BUTTON_DPAD_DOWN);
const int DPAD_LEFT_MASK = (1 << SDL_GAMEPAD_BUTTON_DPAD_LEFT);
@ -278,16 +282,16 @@ int Android_OnHat(int device_id, int hat_id, int x, int y)
dpad_delta = (dpad_state ^ item->dpad_state);
if (dpad_delta) {
if (dpad_delta & DPAD_UP_MASK) {
SDL_SendJoystickButton(0, item->joystick, SDL_GAMEPAD_BUTTON_DPAD_UP, (dpad_state & DPAD_UP_MASK) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, item->joystick, SDL_GAMEPAD_BUTTON_DPAD_UP, (dpad_state & DPAD_UP_MASK) ? SDL_PRESSED : SDL_RELEASED);
}
if (dpad_delta & DPAD_DOWN_MASK) {
SDL_SendJoystickButton(0, item->joystick, SDL_GAMEPAD_BUTTON_DPAD_DOWN, (dpad_state & DPAD_DOWN_MASK) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, item->joystick, SDL_GAMEPAD_BUTTON_DPAD_DOWN, (dpad_state & DPAD_DOWN_MASK) ? SDL_PRESSED : SDL_RELEASED);
}
if (dpad_delta & DPAD_LEFT_MASK) {
SDL_SendJoystickButton(0, item->joystick, SDL_GAMEPAD_BUTTON_DPAD_LEFT, (dpad_state & DPAD_LEFT_MASK) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, item->joystick, SDL_GAMEPAD_BUTTON_DPAD_LEFT, (dpad_state & DPAD_LEFT_MASK) ? SDL_PRESSED : SDL_RELEASED);
}
if (dpad_delta & DPAD_RIGHT_MASK) {
SDL_SendJoystickButton(0, item->joystick, SDL_GAMEPAD_BUTTON_DPAD_RIGHT, (dpad_state & DPAD_RIGHT_MASK) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, item->joystick, SDL_GAMEPAD_BUTTON_DPAD_RIGHT, (dpad_state & DPAD_RIGHT_MASK) ? SDL_PRESSED : SDL_RELEASED);
}
item->dpad_state = dpad_state;
}

View File

@ -349,22 +349,24 @@ static BOOL IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCControlle
}
if (controller.physicalInputProfile.buttons[GCInputXboxPaddleOne] != nil) {
device->has_xbox_paddles = SDL_TRUE;
device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_PADDLE1);
device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1);
++nbuttons;
}
if (controller.physicalInputProfile.buttons[GCInputXboxPaddleTwo] != nil) {
/* TODO: Is this right? SDL_gamepad.h says P2 is the lower right */
device->has_xbox_paddles = SDL_TRUE;
device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_PADDLE2);
device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE1);
++nbuttons;
}
if (controller.physicalInputProfile.buttons[GCInputXboxPaddleThree] != nil) {
/* TODO: Is this right? SDL_gamepad.h says P3 is the upper left */
device->has_xbox_paddles = SDL_TRUE;
device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_PADDLE3);
device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2);
++nbuttons;
}
if (controller.physicalInputProfile.buttons[GCInputXboxPaddleFour] != nil) {
device->has_xbox_paddles = SDL_TRUE;
device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_PADDLE4);
device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE2);
++nbuttons;
}
if (controller.physicalInputProfile.buttons[GCInputXboxShareButton] != nil) {
@ -1055,16 +1057,16 @@ static void IOS_MFIJoystickUpdate(SDL_Joystick *joystick)
}
if (joystick->hwdata->has_xbox_paddles) {
if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_PADDLE1)) {
if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1)) {
buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleOne].isPressed;
}
if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_PADDLE2)) {
if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE1)) {
buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleTwo].isPressed;
}
if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_PADDLE3)) {
if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2)) {
buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleThree].isPressed;
}
if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_PADDLE4)) {
if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE2)) {
buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleFour].isPressed;
}
@ -1826,16 +1828,18 @@ const char *IOS_GetAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_Gamepad
case SDL_GAMEPAD_BUTTON_MISC1:
GetAppleSFSymbolsNameForElement(elements[GCInputDualShockTouchpadButton], elementName);
break;
case SDL_GAMEPAD_BUTTON_PADDLE1:
case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1:
GetAppleSFSymbolsNameForElement(elements[GCInputXboxPaddleOne], elementName);
break;
case SDL_GAMEPAD_BUTTON_PADDLE2:
case SDL_GAMEPAD_BUTTON_LEFT_PADDLE1:
/* TODO: Is this right? SDL_gamepad.h says P2 is the lower right */
GetAppleSFSymbolsNameForElement(elements[GCInputXboxPaddleTwo], elementName);
break;
case SDL_GAMEPAD_BUTTON_PADDLE3:
case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2:
/* TODO: Is this right? SDL_gamepad.h says P3 is the upper left */
GetAppleSFSymbolsNameForElement(elements[GCInputXboxPaddleThree], elementName);
break;
case SDL_GAMEPAD_BUTTON_PADDLE4:
case SDL_GAMEPAD_BUTTON_LEFT_PADDLE2:
GetAppleSFSymbolsNameForElement(elements[GCInputXboxPaddleFour], elementName);
break;
case SDL_GAMEPAD_BUTTON_TOUCHPAD:

View File

@ -97,6 +97,7 @@ static const ControllerDescription_t arrControllers[] = {
{ MAKE_CONTROLLER_ID( 0x0c12, 0x1e10 ), k_eControllerType_PS4Controller, NULL }, // P4 Wired Gamepad generic knock off - lightbar but not trackpad or gyro
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0203 ), k_eControllerType_PS4Controller, NULL }, // Victrix Pro FS (PS4 peripheral but no trackpad/lightbar)
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0207 ), k_eControllerType_PS4Controller, NULL }, // Victrix Pro FS V2 w/ Touchpad for PS4
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x020a ), k_eControllerType_PS4Controller, NULL }, // Victrix Pro FS PS4/PS5 (PS4 mode)
{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0055 ), k_eControllerType_PS4Controller, NULL }, // HORIPAD 4 FPS
{ MAKE_CONTROLLER_ID( 0x0f0d, 0x005e ), k_eControllerType_PS4Controller, NULL }, // HORI Fighting Commander 4 PS4
{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0066 ), k_eControllerType_PS4Controller, NULL }, // HORIPAD 4 FPS Plus
@ -148,6 +149,7 @@ static const ControllerDescription_t arrControllers[] = {
{ MAKE_CONTROLLER_ID( 0x054c, 0x0ce6 ), k_eControllerType_PS5Controller, NULL }, // Sony DualSense Controller
{ MAKE_CONTROLLER_ID( 0x054c, 0x0df2 ), k_eControllerType_PS5Controller, NULL }, // Sony DualSense Edge Controller
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0209 ), k_eControllerType_PS5Controller, NULL }, // Victrix Pro FS PS4/PS5 (PS5 mode)
{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0163 ), k_eControllerType_PS5Controller, NULL }, // HORI Fighting Commander OCTA
{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0184 ), k_eControllerType_PS5Controller, NULL }, // Hori Fighting Stick α
{ MAKE_CONTROLLER_ID( 0x1532, 0x100b ), k_eControllerType_PS5Controller, NULL }, // Razer Wolverine V2 Pro (Wired)

View File

@ -694,20 +694,21 @@ static void HIDAPI_DriverPS4_TickleBluetooth(SDL_HIDAPI_Device *device)
static void HIDAPI_DriverPS4_SetEnhancedModeAvailable(SDL_DriverPS4_Context *ctx)
{
if (!ctx->effects_supported) {
/* We shouldn't be sending any packets to the controller */
return;
}
ctx->enhanced_mode_available = SDL_TRUE;
if (ctx->touchpad_supported) {
SDL_PrivateJoystickAddTouchpad(ctx->joystick, 2);
ctx->report_touchpad = SDL_TRUE;
}
if (ctx->sensors_supported) {
SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO, 250.0f);
SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, 250.0f);
}
if (ctx->device->is_bluetooth && ctx->official_controller) {
ctx->report_battery = SDL_TRUE;
}
}
static void HIDAPI_DriverPS4_SetEnhancedMode(SDL_DriverPS4_Context *ctx)
@ -716,17 +717,10 @@ static void HIDAPI_DriverPS4_SetEnhancedMode(SDL_DriverPS4_Context *ctx)
HIDAPI_DriverPS4_SetEnhancedModeAvailable(ctx);
}
if (!ctx->enhanced_mode && ctx->enhanced_mode_available) {
if (!ctx->enhanced_mode) {
ctx->enhanced_mode = SDL_TRUE;
if (ctx->touchpad_supported) {
ctx->report_touchpad = SDL_TRUE;
}
if (ctx->device->is_bluetooth && ctx->official_controller) {
ctx->report_battery = SDL_TRUE;
}
/* Switch into enhanced report mode */
HIDAPI_DriverPS4_UpdateEffects(ctx, SDL_FALSE);
}
}
@ -888,12 +882,20 @@ static int HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ct
Uint8 data[78];
int report_size, offset;
if (application_usage) {
HIDAPI_DriverPS4_UpdateEnhancedModeOnApplicationUsage(ctx);
if (!ctx->effects_supported) {
/* We shouldn't be sending packets to this controller */
return SDL_Unsupported();
}
if (!ctx->enhanced_mode_available) {
return SDL_Unsupported();
if (!ctx->enhanced_mode) {
if (application_usage) {
HIDAPI_DriverPS4_UpdateEnhancedModeOnApplicationUsage(ctx);
}
if (!ctx->enhanced_mode) {
/* We're not in enhanced mode, effects aren't allowed */
return SDL_Unsupported();
}
}
SDL_zeroa(data);
@ -1059,7 +1061,7 @@ static void HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_d
axis = ((int)packet->ucRightJoystickY * 257) - 32768;
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
if (size > 9 && ctx->report_battery) {
if (size > 9 && ctx->report_battery && ctx->enhanced_reports) {
/* Battery level ranges from 0 to 10 */
int level = (packet->ucBatteryLevel & 0xF);
if (level == 0) {
@ -1073,7 +1075,7 @@ static void HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_d
}
}
if (size > 9 && ctx->report_touchpad) {
if (size > 9 && ctx->report_touchpad && ctx->enhanced_reports) {
touchpad_state = !(packet->ucTouchpadCounter1 & 0x80) ? SDL_PRESSED : SDL_RELEASED;
touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8);
touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4);

View File

@ -412,7 +412,7 @@ static SDL_bool HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device)
/* Connected over Bluetooth, using simple reports (DirectInput enabled) */
}
if (ctx->enhanced_reports) {
if (device->vendor_id == USB_VENDOR_SONY && ctx->enhanced_reports) {
/* Read the serial number (Bluetooth address in reverse byte order)
This will also enable enhanced reports over Bluetooth
*/
@ -774,16 +774,13 @@ static void HIDAPI_DriverPS5_TickleBluetooth(SDL_HIDAPI_Device *device)
static void HIDAPI_DriverPS5_SetEnhancedModeAvailable(SDL_DriverPS5_Context *ctx)
{
if (!ctx->effects_supported) {
/* We shouldn't be sending any packets to the controller */
return;
}
ctx->enhanced_mode_available = SDL_TRUE;
if (ctx->touchpad_supported) {
SDL_PrivateJoystickAddTouchpad(ctx->joystick, 2);
ctx->report_touchpad = SDL_TRUE;
}
if (ctx->sensors_supported) {
if (ctx->device->is_bluetooth) {
/* Bluetooth sensor update rate appears to be 1000 Hz */
@ -794,6 +791,10 @@ static void HIDAPI_DriverPS5_SetEnhancedModeAvailable(SDL_DriverPS5_Context *ctx
SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, 250.0f);
}
}
if (ctx->device->is_bluetooth) {
ctx->report_battery = SDL_TRUE;
}
}
static void HIDAPI_DriverPS5_SetEnhancedMode(SDL_DriverPS5_Context *ctx)
@ -805,14 +806,6 @@ static void HIDAPI_DriverPS5_SetEnhancedMode(SDL_DriverPS5_Context *ctx)
if (!ctx->enhanced_mode && ctx->enhanced_mode_available) {
ctx->enhanced_mode = SDL_TRUE;
if (ctx->touchpad_supported) {
ctx->report_touchpad = SDL_TRUE;
}
if (ctx->device->is_bluetooth) {
ctx->report_battery = SDL_TRUE;
}
/* Switch into enhanced report mode */
HIDAPI_DriverPS5_UpdateEffects(ctx, 0, SDL_FALSE);
@ -1017,12 +1010,20 @@ static int HIDAPI_DriverPS5_InternalSendJoystickEffect(SDL_DriverPS5_Context *ct
int *pending_size;
int maximum_size;
if (application_usage) {
HIDAPI_DriverPS5_UpdateEnhancedModeOnApplicationUsage(ctx);
if (!ctx->effects_supported) {
/* We shouldn't be sending packets to this controller */
return SDL_Unsupported();
}
if (!ctx->enhanced_mode_available) {
return SDL_Unsupported();
if (!ctx->enhanced_mode) {
if (application_usage) {
HIDAPI_DriverPS5_UpdateEnhancedModeOnApplicationUsage(ctx);
}
if (!ctx->enhanced_mode) {
/* We're not in enhanced mode, effects aren't allowed */
return SDL_Unsupported();
}
}
SDL_zeroa(data);

View File

@ -1153,23 +1153,15 @@ static SDL_bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device)
(ctx->m_state.ulButtons & STEAM_BUTTON_BACK_LEFT_MASK) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_MISC1 + 1,
(ctx->m_state.ulButtons & STEAM_BUTTON_BACK_RIGHT_MASK) ? SDL_PRESSED : SDL_RELEASED);
}
{
/* Minimum distance from center of pad to register a direction */
const int kPadDeadZone = 10000;
/* Pad coordinates are like math grid coordinates: negative is bottom left */
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_DPAD_UP,
(ctx->m_state.sLeftPadY > kPadDeadZone) ? SDL_PRESSED : SDL_RELEASED);
(ctx->m_state.ulButtons & STEAM_TOUCH_0_MASK) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_DPAD_DOWN,
(ctx->m_state.sLeftPadY < -kPadDeadZone) ? SDL_PRESSED : SDL_RELEASED);
(ctx->m_state.ulButtons & STEAM_TOUCH_3_MASK) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_DPAD_LEFT,
(ctx->m_state.sLeftPadX < -kPadDeadZone) ? SDL_PRESSED : SDL_RELEASED);
(ctx->m_state.ulButtons & STEAM_TOUCH_2_MASK) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_DPAD_RIGHT,
(ctx->m_state.sLeftPadX > kPadDeadZone) ? SDL_PRESSED : SDL_RELEASED);
(ctx->m_state.ulButtons & STEAM_TOUCH_1_MASK) ? SDL_PRESSED : SDL_RELEASED);
}
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, (int)ctx->m_state.sTriggerL * 2 - 32768);

View File

@ -1888,8 +1888,8 @@ static void HandleCombinedControllerStateL(Uint64 timestamp, SDL_Joystick *joyst
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_DPAD_UP, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_DPAD_RIGHT, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_DPAD_LEFT, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PADDLE4, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PADDLE2, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_PADDLE2, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_PADDLE1, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
axis = (data & 0x80) ? 32767 : -32768;
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
@ -1923,8 +1923,8 @@ static void HandleMiniControllerStateL(Uint64 timestamp, SDL_Joystick *joystick,
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_B), (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PADDLE2, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PADDLE4, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_PADDLE1, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_PADDLE2, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED);
}
axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);
@ -1946,8 +1946,8 @@ static void HandleCombinedControllerStateR(Uint64 timestamp, SDL_Joystick *joyst
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_B), (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_X), (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_Y), (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PADDLE1, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PADDLE3, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
axis = (data & 0x80) ? 32767 : -32768;
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
@ -1981,8 +1981,8 @@ static void HandleMiniControllerStateR(Uint64 timestamp, SDL_Joystick *joystick,
SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_X), (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PADDLE1, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PADDLE3, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED);
}
if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {

View File

@ -1149,7 +1149,7 @@ static void ConfigJoystick(SDL_Joystick *joystick, int fd, int fd_sensor)
}
for (i = 0; i < ABS_MAX; ++i) {
/* Skip digital hats */
if (joystick->hwdata->has_hat[(i - ABS_HAT0X) / 2]) {
if (i >= ABS_HAT0X && i <= ABS_HAT3Y && joystick->hwdata->has_hat[(i - ABS_HAT0X) / 2]) {
continue;
}
if (test_bit(i, absbit)) {
@ -1553,6 +1553,8 @@ static int LINUX_JoystickSendEffect(SDL_Joystick *joystick, const void *data, in
static int LINUX_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled)
{
SDL_AssertJoysticksLocked();
if (!joystick->hwdata->has_accelerometer && !joystick->hwdata->has_gyro) {
return SDL_Unsupported();
}
@ -1723,6 +1725,8 @@ static void PollAllSensors(Uint64 timestamp, SDL_Joystick *joystick)
struct input_absinfo absinfo;
int i;
SDL_AssertJoysticksLocked();
SDL_assert(joystick->hwdata->fd_sensor >= 0);
if (joystick->hwdata->has_gyro) {
@ -2513,19 +2517,19 @@ static SDL_bool LINUX_JoystickGetGamepadMapping(int device_index, SDL_GamepadMap
joystick->hwdata->has_key[BTN_TRIGGER_HAPPY6] &&
joystick->hwdata->has_key[BTN_TRIGGER_HAPPY7] &&
joystick->hwdata->has_key[BTN_TRIGGER_HAPPY8]) {
out->paddle1.kind = EMappingKind_Button;
out->paddle1.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY5];
out->paddle2.kind = EMappingKind_Button;
out->paddle2.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY7];
out->paddle3.kind = EMappingKind_Button;
out->paddle3.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY6];
out->paddle4.kind = EMappingKind_Button;
out->paddle4.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY8];
out->right_paddle1.kind = EMappingKind_Button;
out->right_paddle1.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY5];
out->left_paddle1.kind = EMappingKind_Button;
out->left_paddle1.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY7];
out->right_paddle2.kind = EMappingKind_Button;
out->right_paddle2.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY6];
out->left_paddle2.kind = EMappingKind_Button;
out->left_paddle2.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY8];
#ifdef DEBUG_GAMEPAD_MAPPING
SDL_Log("Mapped PADDLE1 to button %d (BTN_TRIGGER_HAPPY5)", out->paddle1.target);
SDL_Log("Mapped PADDLE2 to button %d (BTN_TRIGGER_HAPPY7)", out->paddle2.target);
SDL_Log("Mapped PADDLE3 to button %d (BTN_TRIGGER_HAPPY6)", out->paddle3.target);
SDL_Log("Mapped PADDLE4 to button %d (BTN_TRIGGER_HAPPY8)", out->paddle4.target);
SDL_Log("Mapped RIGHT_PADDLE1 to button %d (BTN_TRIGGER_HAPPY5)", out->right_paddle1.target);
SDL_Log("Mapped LEFT_PADDLE1 to button %d (BTN_TRIGGER_HAPPY7)", out->left_paddle1.target);
SDL_Log("Mapped RIGHT_PADDLE2 to button %d (BTN_TRIGGER_HAPPY6)", out->right_paddle2.target);
SDL_Log("Mapped LEFT_PADDLE2 to button %d (BTN_TRIGGER_HAPPY8)", out->left_paddle2.target);
#endif
}

View File

@ -198,10 +198,10 @@ static SDL_bool N3DS_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapp
.dpleft = { EMappingKind_Button, 5 },
.dpright = { EMappingKind_Button, 4 },
.misc1 = { EMappingKind_None, 255 },
.paddle1 = { EMappingKind_None, 255 },
.paddle2 = { EMappingKind_None, 255 },
.paddle3 = { EMappingKind_None, 255 },
.paddle4 = { EMappingKind_None, 255 },
.right_paddle1 = { EMappingKind_None, 255 },
.left_paddle1 = { EMappingKind_None, 255 },
.right_paddle2 = { EMappingKind_None, 255 },
.left_paddle2 = { EMappingKind_None, 255 },
.leftx = { EMappingKind_Axis, 0 },
.lefty = { EMappingKind_Axis, 1 },
.rightx = { EMappingKind_Axis, 2 },

View File

@ -669,24 +669,24 @@ static SDL_bool VIRTUAL_JoystickGetGamepadMapping(int device_index, SDL_GamepadM
out->misc1.target = current_button++;
}
if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_PADDLE1))) {
out->paddle1.kind = EMappingKind_Button;
out->paddle1.target = current_button++;
if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1))) {
out->right_paddle1.kind = EMappingKind_Button;
out->right_paddle1.target = current_button++;
}
if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_PADDLE2))) {
out->paddle2.kind = EMappingKind_Button;
out->paddle2.target = current_button++;
if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE1))) {
out->left_paddle1.kind = EMappingKind_Button;
out->left_paddle1.target = current_button++;
}
if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_PADDLE3))) {
out->paddle3.kind = EMappingKind_Button;
out->paddle3.target = current_button++;
if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2))) {
out->right_paddle2.kind = EMappingKind_Button;
out->right_paddle2.target = current_button++;
}
if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_PADDLE4))) {
out->paddle4.kind = EMappingKind_Button;
out->paddle4.target = current_button++;
if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE2))) {
out->left_paddle2.kind = EMappingKind_Button;
out->left_paddle2.target = current_button++;
}
if (current_axis < hwdata->desc.naxes && (hwdata->desc.axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFTX))) {

View File

@ -20,6 +20,8 @@
*/
#include "SDL_internal.h"
#if defined(__IOS__) || defined(__TVOS__)
#include "../SDL_sysurl.h"
#import <UIKit/UIKit.h>
@ -28,8 +30,14 @@ int SDL_SYS_OpenURL(const char *url)
{
@autoreleasepool {
#if TARGET_OS_XR
return SDL_Unsupported(); // openURL is not suported on visionOS
#else
NSString *nsstr = [NSString stringWithUTF8String:url];
NSURL *nsurl = [NSURL URLWithString:nsstr];
return [[UIApplication sharedApplication] openURL:nsurl] ? 0 : -1;
#endif
}
}
#endif /* __IOS__ || __TVOS__ */

View File

@ -20,6 +20,8 @@
*/
#include "SDL_internal.h"
#if defined(__MACOS__)
#include "../SDL_sysurl.h"
#import <Cocoa/Cocoa.h>
@ -33,3 +35,5 @@ int SDL_SYS_OpenURL(const char *url)
return status == noErr ? 0 : -1;
}
}
#endif /* __MACOS__ */

View File

@ -49,18 +49,79 @@ static SDL_SensorDriver *SDL_sensor_drivers[] = {
&SDL_DUMMY_SensorDriver
#endif
};
static SDL_Mutex *SDL_sensor_lock = NULL; /* This needs to support recursive locks */
#ifndef SDL_THREAD_SAFETY_ANALYSIS
static
#endif
SDL_Mutex *SDL_sensor_lock = NULL; /* This needs to support recursive locks */
static SDL_AtomicInt SDL_sensor_lock_pending;
static int SDL_sensors_locked;
static SDL_bool SDL_sensors_initialized;
static SDL_Sensor *SDL_sensors SDL_GUARDED_BY(SDL_sensor_lock) = NULL;
static SDL_AtomicInt SDL_last_sensor_instance_id SDL_GUARDED_BY(SDL_sensor_lock);
static char SDL_sensor_magic;
void SDL_LockSensors(void) SDL_ACQUIRE(SDL_sensor_lock)
#define CHECK_SENSOR_MAGIC(sensor, retval) \
if (!sensor || sensor->magic != &SDL_sensor_magic) { \
SDL_InvalidParamError("sensor"); \
SDL_UnlockSensors(); \
return retval; \
}
SDL_bool SDL_SensorsInitialized(void)
{
SDL_LockMutex(SDL_sensor_lock);
return SDL_sensors_initialized;
}
void SDL_UnlockSensors(void) SDL_RELEASE(SDL_sensor_lock)
void SDL_LockSensors(void)
{
SDL_UnlockMutex(SDL_sensor_lock);
(void)SDL_AtomicIncRef(&SDL_sensor_lock_pending);
SDL_LockMutex(SDL_sensor_lock);
(void)SDL_AtomicDecRef(&SDL_sensor_lock_pending);
++SDL_sensors_locked;
}
void SDL_UnlockSensors(void)
{
SDL_bool last_unlock = SDL_FALSE;
--SDL_sensors_locked;
if (!SDL_sensors_initialized) {
/* NOTE: There's a small window here where another thread could lock the mutex after we've checked for pending locks */
if (!SDL_sensors_locked && SDL_AtomicGet(&SDL_sensor_lock_pending) == 0) {
last_unlock = SDL_TRUE;
}
}
/* The last unlock after sensors are uninitialized will cleanup the mutex,
* allowing applications to lock sensors while reinitializing the system.
*/
if (last_unlock) {
SDL_Mutex *sensor_lock = SDL_sensor_lock;
SDL_LockMutex(sensor_lock);
{
SDL_UnlockMutex(SDL_sensor_lock);
SDL_sensor_lock = NULL;
}
SDL_UnlockMutex(sensor_lock);
SDL_DestroyMutex(sensor_lock);
} else {
SDL_UnlockMutex(SDL_sensor_lock);
}
}
SDL_bool SDL_SensorsLocked(void)
{
return (SDL_sensors_locked > 0) ? SDL_TRUE : SDL_FALSE;
}
void SDL_AssertSensorsLocked(void)
{
SDL_assert(SDL_SensorsLocked());
}
int SDL_InitSensors(void)
@ -78,12 +139,23 @@ int SDL_InitSensors(void)
}
#endif /* !SDL_EVENTS_DISABLED */
SDL_LockSensors();
SDL_sensors_initialized = SDL_TRUE;
status = -1;
for (i = 0; i < SDL_arraysize(SDL_sensor_drivers); ++i) {
if (SDL_sensor_drivers[i]->Init() >= 0) {
status = 0;
}
}
SDL_UnlockSensors();
if (status < 0) {
SDL_QuitSensors();
}
return status;
}
@ -272,6 +344,7 @@ SDL_Sensor *SDL_OpenSensor(SDL_SensorID instance_id)
SDL_UnlockSensors();
return NULL;
}
sensor->magic = &SDL_sensor_magic;
sensor->driver = driver;
sensor->instance_id = instance_id;
sensor->type = driver->GetDeviceType(device_index);
@ -320,33 +393,22 @@ SDL_Sensor *SDL_GetSensorFromInstanceID(SDL_SensorID instance_id)
return sensor;
}
/*
* Checks to make sure the sensor is valid.
*/
static int SDL_IsSensorValid(SDL_Sensor *sensor)
{
int valid;
if (sensor == NULL) {
SDL_SetError("Sensor hasn't been opened yet");
valid = 0;
} else {
valid = 1;
}
return valid;
}
/*
* Get the friendly name of this sensor
*/
const char *SDL_GetSensorName(SDL_Sensor *sensor)
{
if (!SDL_IsSensorValid(sensor)) {
return NULL;
}
const char *retval;
return sensor->name;
SDL_LockSensors();
{
CHECK_SENSOR_MAGIC(sensor, NULL);
retval = sensor->name;
}
SDL_UnlockSensors();
return retval;
}
/*
@ -354,11 +416,17 @@ const char *SDL_GetSensorName(SDL_Sensor *sensor)
*/
SDL_SensorType SDL_GetSensorType(SDL_Sensor *sensor)
{
if (!SDL_IsSensorValid(sensor)) {
return SDL_SENSOR_INVALID;
}
SDL_SensorType retval;
return sensor->type;
SDL_LockSensors();
{
CHECK_SENSOR_MAGIC(sensor, SDL_SENSOR_INVALID);
retval = sensor->type;
}
SDL_UnlockSensors();
return retval;
}
/*
@ -366,11 +434,17 @@ SDL_SensorType SDL_GetSensorType(SDL_Sensor *sensor)
*/
int SDL_GetSensorNonPortableType(SDL_Sensor *sensor)
{
if (!SDL_IsSensorValid(sensor)) {
return -1;
}
int retval;
return sensor->non_portable_type;
SDL_LockSensors();
{
CHECK_SENSOR_MAGIC(sensor, -1);
retval = sensor->non_portable_type;
}
SDL_UnlockSensors();
return retval;
}
/*
@ -378,11 +452,17 @@ int SDL_GetSensorNonPortableType(SDL_Sensor *sensor)
*/
SDL_SensorID SDL_GetSensorInstanceID(SDL_Sensor *sensor)
{
if (!SDL_IsSensorValid(sensor)) {
return 0;
}
SDL_SensorID retval;
return sensor->instance_id;
SDL_LockSensors();
{
CHECK_SENSOR_MAGIC(sensor, 0);
retval = sensor->instance_id;
}
SDL_UnlockSensors();
return retval;
}
/*
@ -390,12 +470,15 @@ SDL_SensorID SDL_GetSensorInstanceID(SDL_Sensor *sensor)
*/
int SDL_GetSensorData(SDL_Sensor *sensor, float *data, int num_values)
{
if (!SDL_IsSensorValid(sensor)) {
return -1;
}
SDL_LockSensors();
{
CHECK_SENSOR_MAGIC(sensor, -1);
num_values = SDL_min(num_values, SDL_arraysize(sensor->data));
SDL_memcpy(data, sensor->data, num_values * sizeof(*data));
}
SDL_UnlockSensors();
num_values = SDL_min(num_values, SDL_arraysize(sensor->data));
SDL_memcpy(data, sensor->data, num_values * sizeof(*data));
return 0;
}
@ -407,42 +490,39 @@ void SDL_CloseSensor(SDL_Sensor *sensor)
SDL_Sensor *sensorlist;
SDL_Sensor *sensorlistprev;
if (!SDL_IsSensorValid(sensor)) {
return;
}
SDL_LockSensors();
{
CHECK_SENSOR_MAGIC(sensor,);
/* First decrement ref count */
if (--sensor->ref_count > 0) {
SDL_UnlockSensors();
return;
}
sensor->driver->Close(sensor);
sensor->hwdata = NULL;
sensorlist = SDL_sensors;
sensorlistprev = NULL;
while (sensorlist) {
if (sensor == sensorlist) {
if (sensorlistprev) {
/* unlink this entry */
sensorlistprev->next = sensorlist->next;
} else {
SDL_sensors = sensor->next;
}
break;
/* First decrement ref count */
if (--sensor->ref_count > 0) {
SDL_UnlockSensors();
return;
}
sensorlistprev = sensorlist;
sensorlist = sensorlist->next;
sensor->driver->Close(sensor);
sensor->hwdata = NULL;
sensorlist = SDL_sensors;
sensorlistprev = NULL;
while (sensorlist) {
if (sensor == sensorlist) {
if (sensorlistprev) {
/* unlink this entry */
sensorlistprev->next = sensorlist->next;
} else {
SDL_sensors = sensor->next;
}
break;
}
sensorlistprev = sensorlist;
sensorlist = sensorlist->next;
}
/* Free the data associated with this sensor */
SDL_free(sensor->name);
SDL_free(sensor);
}
SDL_free(sensor->name);
/* Free the data associated with this sensor */
SDL_free(sensor);
SDL_UnlockSensors();
}
@ -463,16 +543,13 @@ void SDL_QuitSensors(void)
SDL_sensor_drivers[i]->Quit();
}
SDL_UnlockSensors();
#ifndef SDL_EVENTS_DISABLED
SDL_QuitSubSystem(SDL_INIT_EVENTS);
#endif
if (SDL_sensor_lock) {
SDL_DestroyMutex(SDL_sensor_lock);
SDL_sensor_lock = NULL;
}
SDL_sensors_initialized = SDL_FALSE;
SDL_UnlockSensors();
}
/* These are global for SDL_syssensor.c and SDL_events.c */
@ -481,6 +558,8 @@ int SDL_SendSensorUpdate(Uint64 timestamp, SDL_Sensor *sensor, Uint64 sensor_tim
{
int posted;
SDL_AssertSensorsLocked();
/* Allow duplicate events, for things like steps and heartbeats */
/* Update internal sensor state */
@ -510,7 +589,13 @@ int SDL_SendSensorUpdate(Uint64 timestamp, SDL_Sensor *sensor, Uint64 sensor_tim
void SDL_UpdateSensor(SDL_Sensor *sensor)
{
sensor->driver->Update(sensor);
SDL_LockSensors();
{
CHECK_SENSOR_MAGIC(sensor,);
sensor->driver->Update(sensor);
}
SDL_UnlockSensors();
}
void SDL_UpdateSensors(void)

View File

@ -23,6 +23,10 @@
#ifndef SDL_sensor_c_h_
#define SDL_sensor_c_h_
#ifdef SDL_THREAD_SAFETY_ANALYSIS
extern SDL_Mutex *SDL_sensor_lock;
#endif
struct SDL_SensorDriver;
/* Useful functions and variables from SDL_sensor.c */
@ -34,8 +38,17 @@ extern SDL_SensorID SDL_GetNextSensorInstanceID(void);
extern int SDL_InitSensors(void);
extern void SDL_QuitSensors(void);
extern void SDL_LockSensors(void);
extern void SDL_UnlockSensors(void);
/* Return whether the sensor system is currently initialized */
extern SDL_bool SDL_SensorsInitialized(void);
/* Return whether the sensors are currently locked */
extern SDL_bool SDL_SensorsLocked(void);
/* Make sure we currently have the sensors locked */
extern void SDL_AssertSensorsLocked(void) SDL_ASSERT_CAPABILITY(SDL_sensor_lock);
extern void SDL_LockSensors(void) SDL_ACQUIRE(SDL_sensor_lock);
extern void SDL_UnlockSensors(void) SDL_RELEASE(SDL_sensor_lock);
/* Function to return whether there are any sensors opened by the application */
extern SDL_bool SDL_SensorsOpened(void);

View File

@ -27,25 +27,31 @@
#include "SDL_sensor_c.h"
#define _guarded SDL_GUARDED_BY(SDL_sensor_lock)
/* The SDL sensor structure */
struct SDL_Sensor
{
SDL_SensorID instance_id; /* Device instance, monotonically increasing from 0 */
char *name; /* Sensor name - system dependent */
SDL_SensorType type; /* Type of the sensor */
int non_portable_type; /* Platform dependent type of the sensor */
const void *magic _guarded;
float data[16]; /* The current state of the sensor */
SDL_SensorID instance_id _guarded; /* Device instance, monotonically increasing from 0 */
char *name _guarded; /* Sensor name - system dependent */
SDL_SensorType type _guarded; /* Type of the sensor */
int non_portable_type _guarded; /* Platform dependent type of the sensor */
struct SDL_SensorDriver *driver;
float data[16] _guarded; /* The current state of the sensor */
struct sensor_hwdata *hwdata; /* Driver dependent information */
struct SDL_SensorDriver *driver _guarded;
int ref_count; /* Reference count for multiple opens */
struct sensor_hwdata *hwdata _guarded; /* Driver dependent information */
struct SDL_Sensor *next; /* pointer to next sensor we have allocated */
int ref_count _guarded; /* Reference count for multiple opens */
struct SDL_Sensor *next _guarded; /* pointer to next sensor we have allocated */
};
#undef _guarded
typedef struct SDL_SensorDriver
{
/* Function to scan the system for sensors.

View File

@ -52,7 +52,6 @@ typedef struct
static ASensorManager *SDL_sensor_manager;
static ALooper *SDL_sensor_looper;
static SDL_AndroidSensorThreadContext SDL_sensor_thread_context;
static SDL_Mutex *SDL_sensors_lock;
static SDL_AndroidSensor *SDL_sensors SDL_GUARDED_BY(SDL_sensors_lock);
static int SDL_sensors_count;
@ -72,7 +71,7 @@ static int SDLCALL SDL_ANDROID_SensorThread(void *data)
Uint64 timestamp = SDL_GetTicksNS();
if (ALooper_pollAll(-1, NULL, &events, (void **)&source) == LOOPER_ID_USER) {
SDL_LockMutex(SDL_sensors_lock);
SDL_LockSensors();
for (i = 0; i < SDL_sensors_count; ++i) {
if (!SDL_sensors[i].event_queue) {
continue;
@ -83,7 +82,7 @@ static int SDLCALL SDL_ANDROID_SensorThread(void *data)
SDL_SendSensorUpdate(timestamp, SDL_sensors[i].sensor, timestamp, event.data, SDL_arraysize(event.data));
}
}
SDL_UnlockMutex(SDL_sensors_lock);
SDL_UnlockSensors();
}
}
@ -138,11 +137,6 @@ static int SDL_ANDROID_SensorInit(void)
int i, sensors_count;
ASensorList sensors;
SDL_sensors_lock = SDL_CreateMutex();
if (!SDL_sensors_lock) {
return SDL_SetError("Couldn't create sensor lock");
}
SDL_sensor_manager = ASensorManager_getInstance();
if (SDL_sensor_manager == NULL) {
return SDL_SetError("Couldn't create sensor manager");
@ -209,19 +203,19 @@ static int SDL_ANDROID_SensorOpen(SDL_Sensor *sensor, int device_index)
{
int delay_us, min_delay_us;
SDL_LockMutex(SDL_sensors_lock);
SDL_LockSensors();
{
SDL_sensors[device_index].sensor = sensor;
SDL_sensors[device_index].event_queue = ASensorManager_createEventQueue(SDL_sensor_manager, SDL_sensor_looper, LOOPER_ID_USER, NULL, NULL);
if (!SDL_sensors[device_index].event_queue) {
SDL_UnlockMutex(SDL_sensors_lock);
SDL_UnlockSensors();
return SDL_SetError("Couldn't create sensor event queue");
}
if (ASensorEventQueue_enableSensor(SDL_sensors[device_index].event_queue, SDL_sensors[device_index].asensor) < 0) {
ASensorManager_destroyEventQueue(SDL_sensor_manager, SDL_sensors[device_index].event_queue);
SDL_sensors[device_index].event_queue = NULL;
SDL_UnlockMutex(SDL_sensors_lock);
SDL_UnlockSensors();
return SDL_SetError("Couldn't enable sensor");
}
@ -234,7 +228,7 @@ static int SDL_ANDROID_SensorOpen(SDL_Sensor *sensor, int device_index)
}
ASensorEventQueue_setEventRate(SDL_sensors[device_index].event_queue, SDL_sensors[device_index].asensor, delay_us);
}
SDL_UnlockMutex(SDL_sensors_lock);
SDL_UnlockSensors();
return 0;
}
@ -249,14 +243,14 @@ static void SDL_ANDROID_SensorClose(SDL_Sensor *sensor)
for (i = 0; i < SDL_sensors_count; ++i) {
if (SDL_sensors[i].sensor == sensor) {
SDL_LockMutex(SDL_sensors_lock);
SDL_LockSensors();
{
ASensorEventQueue_disableSensor(SDL_sensors[i].event_queue, SDL_sensors[i].asensor);
ASensorManager_destroyEventQueue(SDL_sensor_manager, SDL_sensors[i].event_queue);
SDL_sensors[i].event_queue = NULL;
SDL_sensors[i].sensor = NULL;
}
SDL_UnlockMutex(SDL_sensors_lock);
SDL_UnlockSensors();
break;
}
}
@ -264,18 +258,17 @@ static void SDL_ANDROID_SensorClose(SDL_Sensor *sensor)
static void SDL_ANDROID_SensorQuit(void)
{
/* All sensors are closed, but we need to unblock the sensor thread */
SDL_AssertSensorsLocked();
SDL_UnlockSensors();
SDL_ANDROID_StopSensorThread(&SDL_sensor_thread_context);
SDL_LockSensors();
if (SDL_sensors) {
SDL_free(SDL_sensors);
SDL_sensors = NULL;
SDL_sensors_count = 0;
}
if (SDL_sensors_lock) {
SDL_DestroyMutex(SDL_sensors_lock);
SDL_sensors_lock = NULL;
}
}
SDL_SensorDriver SDL_ANDROID_SensorDriver = {

View File

@ -1275,7 +1275,9 @@ int SDL_vsscanf(const char *text, const char *fmt, va_list ap)
suppress = SDL_TRUE;
break;
case 'h':
if (inttype > DO_SHORT) {
if (inttype == DO_INT) {
inttype = DO_SHORT;
} else if (inttype > DO_SHORT) {
++inttype;
}
break;
@ -1758,16 +1760,17 @@ static size_t SDL_PrintFloat(char *text, size_t maxlen, SDL_FormatInfo *info, do
}
if (num[i] == '9') {
num[i] = '0';
if (i == 0 || num[i - 1] == '-' || num[i - 1] == '+') {
SDL_memmove(&num[i+1], &num[i], length - i);
num[i] = '1';
++length;
break;
}
} else {
++num[i];
break;
}
}
if (i == 0 || num[i] == '-' || num[i] == '+') {
SDL_memmove(&num[i+1], &num[i], length - i);
num[i] = '1';
++length;
}
SDL_assert(length < sizeof(num));
num[length++] = '0';
} else {

View File

@ -71,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]",
"[--channels N]", "[--samples N]"
"[--channels N]"
};
static void SDL_snprintfcat(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
@ -117,10 +117,9 @@ SDLTest_CommonState *SDLTest_CommonCreateState(char **argv, Uint32 flags)
state->logical_presentation = SDL_LOGICAL_PRESENTATION_DISABLED;
state->logical_scale_mode = SDL_SCALEMODE_LINEAR;
state->num_windows = 1;
state->audiospec.freq = 22050;
state->audiospec.format = SDL_AUDIO_S16;
state->audiospec.channels = 2;
state->audiospec.samples = 2048;
state->audio_freq = 22050;
state->audio_format = SDL_AUDIO_S16;
state->audio_channels = 2;
/* Set some very sane GL defaults */
state->gl_red_size = 8;
@ -604,7 +603,7 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index)
if (!argv[index]) {
return -1;
}
state->audiospec.freq = SDL_atoi(argv[index]);
state->audio_freq = SDL_atoi(argv[index]);
return 2;
}
if (SDL_strcasecmp(argv[index], "--format") == 0) {
@ -613,23 +612,23 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index)
return -1;
}
if (SDL_strcasecmp(argv[index], "U8") == 0) {
state->audiospec.format = SDL_AUDIO_U8;
state->audio_format = SDL_AUDIO_U8;
return 2;
}
if (SDL_strcasecmp(argv[index], "S8") == 0) {
state->audiospec.format = SDL_AUDIO_S8;
state->audio_format = SDL_AUDIO_S8;
return 2;
}
if (SDL_strcasecmp(argv[index], "S16") == 0) {
state->audiospec.format = SDL_AUDIO_S16;
state->audio_format = SDL_AUDIO_S16;
return 2;
}
if (SDL_strcasecmp(argv[index], "S16LE") == 0) {
state->audiospec.format = SDL_AUDIO_S16LSB;
state->audio_format = SDL_AUDIO_S16LSB;
return 2;
}
if (SDL_strcasecmp(argv[index], "S16BE") == 0) {
state->audiospec.format = SDL_AUDIO_S16MSB;
state->audio_format = SDL_AUDIO_S16MSB;
return 2;
}
@ -642,15 +641,7 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index)
if (!argv[index]) {
return -1;
}
state->audiospec.channels = (Uint8) SDL_atoi(argv[index]);
return 2;
}
if (SDL_strcasecmp(argv[index], "--samples") == 0) {
++index;
if (!argv[index]) {
return -1;
}
state->audiospec.samples = (Uint16) SDL_atoi(argv[index]);
state->audio_channels = (Uint8) SDL_atoi(argv[index]);
return 2;
}
}
@ -1452,7 +1443,8 @@ SDL_bool SDLTest_CommonInit(SDLTest_CommonState *state)
SDL_GetCurrentAudioDriver());
}
state->audio_id = SDL_OpenAudioDevice(NULL, 0, &state->audiospec, NULL, 0);
const SDL_AudioSpec spec = { state->audio_format, state->audio_channels, state->audio_freq };
state->audio_id = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec);
if (!state->audio_id) {
SDL_Log("Couldn't open audio: %s\n", SDL_GetError());
return SDL_FALSE;

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