forked from Green-Sky/tomato
update sdl Merge commit '4d48f9d23713d94b861da7b5d41baf2a41334994'
This commit is contained in:
2735
external/sdl/SDL/src/audio/SDL_audio.c
vendored
2735
external/sdl/SDL/src/audio/SDL_audio.c
vendored
File diff suppressed because it is too large
Load Diff
55
external/sdl/SDL/src/audio/SDL_audio_c.h
vendored
55
external/sdl/SDL/src/audio/SDL_audio_c.h
vendored
@ -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_ */
|
||||
|
615
external/sdl/SDL/src/audio/SDL_audiocvt.c
vendored
615
external/sdl/SDL/src/audio/SDL_audiocvt.c
vendored
File diff suppressed because it is too large
Load Diff
12
external/sdl/SDL/src/audio/SDL_audiodev.c
vendored
12
external/sdl/SDL/src/audio/SDL_audiodev.c
vendored
@ -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);
|
||||
|
2
external/sdl/SDL/src/audio/SDL_audiodev_c.h
vendored
2
external/sdl/SDL/src/audio/SDL_audiodev_c.h
vendored
@ -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_ */
|
||||
|
324
external/sdl/SDL/src/audio/SDL_sysaudio.h
vendored
324
external/sdl/SDL/src/audio/SDL_sysaudio.h
vendored
@ -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_
|
||||
|
76
external/sdl/SDL/src/audio/SDL_wave.c
vendored
76
external/sdl/SDL/src/audio/SDL_wave.c
vendored
@ -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);
|
||||
}
|
||||
|
||||
|
679
external/sdl/SDL/src/audio/aaudio/SDL_aaudio.c
vendored
679
external/sdl/SDL/src/audio/aaudio/SDL_aaudio.c
vendored
@ -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
|
||||
|
12
external/sdl/SDL/src/audio/aaudio/SDL_aaudio.h
vendored
12
external/sdl/SDL/src/audio/aaudio/SDL_aaudio.h
vendored
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
428
external/sdl/SDL/src/audio/alsa/SDL_alsa_audio.c
vendored
428
external/sdl/SDL/src/audio/alsa/SDL_alsa_audio.c
vendored
@ -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. */
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
1299
external/sdl/SDL/src/audio/coreaudio/SDL_coreaudio.m
vendored
1299
external/sdl/SDL/src/audio/coreaudio/SDL_coreaudio.m
vendored
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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_
|
||||
|
136
external/sdl/SDL/src/audio/disk/SDL_diskaudio.c
vendored
136
external/sdl/SDL/src/audio/disk/SDL_diskaudio.c
vendored
@ -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
|
||||
|
215
external/sdl/SDL/src/audio/dsp/SDL_dspaudio.c
vendored
215
external/sdl/SDL/src/audio/dsp/SDL_dspaudio.c
vendored
@ -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 */
|
||||
|
@ -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_ */
|
||||
|
@ -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 = {
|
||||
|
@ -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_
|
||||
|
@ -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
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
int unused;
|
||||
Uint8 *mixbuf;
|
||||
};
|
||||
|
||||
#endif /* SDL_emscriptenaudio_h_ */
|
||||
|
152
external/sdl/SDL/src/audio/haiku/SDL_haikuaudio.cc
vendored
152
external/sdl/SDL/src/audio/haiku/SDL_haikuaudio.cc
vendored
@ -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
|
||||
|
@ -28,6 +28,8 @@
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
BSoundPlayer *audio_obj;
|
||||
Uint8 *current_buffer;
|
||||
int current_buffer_len;
|
||||
};
|
||||
|
||||
#endif /* SDL_haikuaudio_h_ */
|
||||
|
199
external/sdl/SDL/src/audio/jack/SDL_jackaudio.c
vendored
199
external/sdl/SDL/src/audio/jack/SDL_jackaudio.c
vendored
@ -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. */
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
265
external/sdl/SDL/src/audio/n3ds/SDL_n3dsaudio.c
vendored
265
external/sdl/SDL/src/audio/n3ds/SDL_n3dsaudio.c
vendored
@ -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(¤t_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
|
||||
|
@ -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;
|
||||
|
209
external/sdl/SDL/src/audio/netbsd/SDL_netbsdaudio.c
vendored
209
external/sdl/SDL/src/audio/netbsd/SDL_netbsdaudio.c
vendored
@ -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
|
||||
|
352
external/sdl/SDL/src/audio/openslES/SDL_openslES.c
vendored
352
external/sdl/SDL/src/audio/openslES/SDL_openslES.c
vendored
@ -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
|
||||
|
377
external/sdl/SDL/src/audio/pipewire/SDL_pipewire.c
vendored
377
external/sdl/SDL/src/audio/pipewire/SDL_pipewire.c
vendored
@ -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;
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
111
external/sdl/SDL/src/audio/ps2/SDL_ps2audio.c
vendored
111
external/sdl/SDL/src/audio/ps2/SDL_ps2audio.c
vendored
@ -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 = {
|
||||
|
137
external/sdl/SDL/src/audio/psp/SDL_pspaudio.c
vendored
137
external/sdl/SDL/src/audio/psp/SDL_pspaudio.c
vendored
@ -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
|
||||
|
@ -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. */
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ struct SDL_PrivateAudioData
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
|
||||
int bytes_requested; /* bytes of data the hardware wants _now_. */
|
||||
|
||||
|
616
external/sdl/SDL/src/audio/qnx/SDL_qsa_audio.c
vendored
616
external/sdl/SDL/src/audio/qnx/SDL_qsa_audio.c
vendored
@ -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, ¶m) != -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: */
|
||||
|
21
external/sdl/SDL/src/audio/qnx/SDL_qsa_audio.h
vendored
21
external/sdl/SDL/src/audio/qnx/SDL_qsa_audio.h
vendored
@ -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__ */
|
||||
|
207
external/sdl/SDL/src/audio/sndio/SDL_sndioaudio.c
vendored
207
external/sdl/SDL/src/audio/sndio/SDL_sndioaudio.c
vendored
@ -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
|
||||
|
@ -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_ */
|
||||
|
160
external/sdl/SDL/src/audio/vita/SDL_vitaaudio.c
vendored
160
external/sdl/SDL/src/audio/vita/SDL_vitaaudio.c
vendored
@ -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
|
||||
|
805
external/sdl/SDL/src/audio/wasapi/SDL_wasapi.c
vendored
805
external/sdl/SDL/src/audio/wasapi/SDL_wasapi.c
vendored
File diff suppressed because it is too large
Load Diff
25
external/sdl/SDL/src/audio/wasapi/SDL_wasapi.h
vendored
25
external/sdl/SDL/src/audio/wasapi/SDL_wasapi.h
vendored
@ -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
|
||||
}
|
||||
|
@ -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__) */
|
||||
|
@ -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__)
|
||||
|
Reference in New Issue
Block a user