Merge the SDL3 audio subsystem redesign!

This rips up the entire SDL audio subsystem! While we still feed the audio device from a separate thread, the audio callback into the app is now gone a totally optional alternative.

Now the app will bind an SDL_AudioStream to a given device and feed data to it. As many streams as one likes can be bound to a device; SDL will mix them all into a single buffer and feed the device from there.

So not only does this function as a basic mixer, it also means that multiple device opens are handled seamlessly (so if you want to open the device for your game, but you also link to a library that provides VoIP and it wants to open the device separately, you don't have to worry about stepping on each other, or that the OS will fail to allow multiple opens of the same device, etc).

Merged from pull request #7704.

Fixes #7379.
Reference Issue #6889.
Reference Issue #6632.
This commit is contained in:
Ryan C. Gordon 2023-08-03 21:27:54 -04:00 committed by GitHub
commit 18c59cc969
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
87 changed files with 8340 additions and 8236 deletions

View File

@ -122,6 +122,7 @@
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>SDL_internal.h</PrecompiledHeaderFile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -153,6 +154,7 @@
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>SDL_internal.h</PrecompiledHeaderFile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -188,6 +190,7 @@
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>SDL_internal.h</PrecompiledHeaderFile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -220,6 +223,7 @@
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>SDL_internal.h</PrecompiledHeaderFile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -656,4 +660,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -100,6 +100,7 @@
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>OldStyle</DebugInformationFormat>
<OmitDefaultLibName>true</OmitDefaultLibName>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -116,6 +117,7 @@
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>OldStyle</DebugInformationFormat>
<OmitDefaultLibName>true</OmitDefaultLibName>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
@ -134,6 +136,7 @@
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>OldStyle</DebugInformationFormat>
<OmitDefaultLibName>true</OmitDefaultLibName>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -150,6 +153,7 @@
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>OldStyle</DebugInformationFormat>
<OmitDefaultLibName>true</OmitDefaultLibName>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
@ -168,4 +172,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

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

View File

@ -308,10 +308,10 @@ expression e;
+ SDL_PauseAudioDevice(e)
|
- SDL_PauseAudioDevice(e, 0)
+ SDL_PlayAudioDevice(e)
+ SDL_UnpauseAudioDevice(e)
|
- SDL_PauseAudioDevice(e, SDL_FALSE)
+ SDL_PlayAudioDevice(e)
+ SDL_UnpauseAudioDevice(e)
)
@@
@ -321,7 +321,7 @@ expression e, pause_on;
+ if (pause_on) {
+ SDL_PauseAudioDevice(e);
+ } else {
+ SDL_PlayAudioDevice(e);
+ SDL_UnpauseAudioDevice(e);
+ }

View File

@ -53,13 +53,128 @@ The following structures have been renamed:
## SDL_audio.h
The audio subsystem in SDL3 is dramatically different than SDL2. The primary way to play audio is no longer an audio callback; instead you bind SDL_AudioStreams to devices.
The SDL 1.2 audio compatibility API has also been removed, as it was a simplified version of the audio callback interface.
SDL3 will not implicitly initialize the audio subsystem on your behalf if you open a device without doing so. Please explicitly call SDL_Init(SDL_INIT_AUDIO) at some point.
If your app depends on the callback method, there is a similar approach you can take. But first, this is the new approach:
In SDL2, you might have done something like this to play audio:
```c
void SDLCALL MyAudioCallback(void *userdata, Uint8 * stream, int len)
{
/* calculate a little more audio here, maybe using `userdata`, write it to `stream` */
}
/* ...somewhere near startup... */
SDL_AudioSpec my_desired_audio_format;
SDL_zero(my_desired_audio_format);
my_desired_audio_format.format = AUDIO_S16;
my_desired_audio_format.channels = 2;
my_desired_audio_format.freq = 44100;
my_desired_audio_format.samples = 1024;
my_desired_audio_format.callback = MyAudioCallback;
my_desired_audio_format.userdata = &my_audio_callback_user_data;
SDL_AudioDeviceID my_audio_device = SDL_OpenAudioDevice(NULL, 0, &my_desired_audio_format, NULL, 0);
SDL_PauseAudioDevice(my_audio_device, 0);
```
in SDL3:
```c
/* ...somewhere near startup... */
SDL_AudioSpec spec = { SDL_AUDIO_S16, 2, 44100 };
SDL_AudioDeviceID my_audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec);
SDL_AudioSteam *stream = SDL_CreateAndBindAudioStream(my_audio_device, &spec);
/* ...in your main loop... */
/* calculate a little more audio into `buf`, add it to `stream` */
SDL_PutAudioStreamData(stream, buf, buflen);
```
If you absolutely require the callback method, SDL_AudioStreams can use a callback whenever more data is to be read from them, which can be used to simulate SDL2 semantics:
```c
void SDLCALL MyAudioCallback(SDL_AudioStream *stream, int len, void *userdata)
{
/* calculate a little more audio here, maybe using `userdata`, write it to `stream` */
SDL_PutAudioStreamData(stream, newdata, len);
}
/* ...somewhere near startup... */
SDL_AudioSpec spec = { SDL_AUDIO_S16, 2, 44100 };
SDL_AudioDeviceID my_audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec);
SDL_AudioSteam *stream = SDL_CreateAndBindAudioStream(my_audio_device, &spec);
SDL_SetAudioStreamGetCallback(stream, MyAudioCallback);
/* MyAudioCallback will be called whenever the device requests more audio data. */
```
SDL_AudioInit() and SDL_AudioQuit() have been removed. Instead you can call SDL_InitSubSystem() and SDL_QuitSubSystem() with SDL_INIT_AUDIO, which will properly refcount the subsystems. You can choose a specific audio driver using SDL_AUDIO_DRIVER hint.
SDL_PauseAudioDevice() is only used to pause audio playback. Use SDL_PlayAudioDevice() to start playing audio.
The `SDL_AUDIO_ALLOW_*` symbols have been removed; now one may request the format they desire from the audio device, but ultimately SDL_AudioStream will manage the difference. One can use SDL_GetAudioDeviceFormat() to see what the final format is, if any "allowed" changes should be accomodated by the app.
SDL_AudioDeviceID now represents both an open audio device's handle (a "logical" device) and the instance ID that the hardware owns as long as it exists on the system (a "physical" device). The separation between device instances and device indexes is gone.
Devices are opened by physical device instance ID, and a new logical instance ID is generated by the open operation; This allows any device to be opened multiple times, possibly by unrelated pieces of code. SDL will manage the logical devices to provide a single stream of audio to the physical device behind the scenes.
Devices are not opened by an arbitrary string name anymore, but by device instance ID (or magic numbers to request a reasonable default, like a NULL string in SDL2). In SDL2, the string was used to open both a standard list of system devices, but also allowed for arbitrary devices, such as hostnames of network sound servers. In SDL3, many of the backends that supported arbitrary device names are obsolete and have been removed; of those that remain, arbitrary devices will be opened with a default device ID and an SDL_hint, so specific end-users can set an environment variable to fit their needs and apps don't have to concern themselves with it.
Many functions that would accept a device index and an `iscapture` parameter now just take an SDL_AudioDeviceID, as they are unique across all devices, instead of separate indices into output and capture device lists.
Rather than iterating over audio devices using a device index, there is a new function, SDL_GetAudioDevices(), to get the current list of devices, and new functions to get information about devices from their instance ID:
```c
{
if (SDL_InitSubSystem(SDL_INIT_AUDIO) == 0) {
int i, num_devices;
SDL_AudioDeviceID *devices = SDL_GetAudioDevices(/*iscapture=*/SDL_FALSE, &num_devices);
if (devices) {
for (i = 0; i < num_devices; ++i) {
SDL_AudioDeviceID instance_id = devices[i];
char *name = SDL_GetAudioDeviceName(instance_id);
SDL_Log("AudioDevice %" SDL_PRIu32 ": %s\n", instance_id, name);
SDL_free(name);
}
SDL_free(devices);
}
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
}
```
SDL_LockAudioDevice() and SDL_UnlockAudioDevice() have been removed, since there is no callback in another thread to protect. SDL's audio subsystem and SDL_AudioStream maintain their own locks internally, so audio streams are safe to use from any thread. If the app assigns a callback to a specific stream, it can use the stream's lock through SDL_LockAudioStream() if necessary.
SDL_PauseAudioDevice() no longer takes a second argument; it always pauses the device. To unpause, use SDL_UnpauseAudioDevice().
Audio devices, opened by SDL_OpenAudioDevice(), no longer start in a paused state, as they don't begin processing audio until a stream is bound.
SDL_GetAudioDeviceStatus() has been removed; there is now SDL_IsAudioDevicePaused().
SDL_QueueAudio(), SDL_DequeueAudio, and SDL_ClearQueuedAudio and SDL_GetQueuedAudioSize() have been removed; an SDL_AudioStream bound to a device provides the exact same functionality.
APIs that use channel counts used to use a Uint8 for the channel; now they use int.
SDL_AudioSpec has been reduced; now it only holds format, channel, and sample rate. SDL_GetSilenceValueForFormat() can provide the information from the SDL_AudioSpec's `silence` field. The other SDL2 SDL_AudioSpec fields aren't relevant anymore.
SDL_GetAudioDeviceSpec() is removed; use SDL_GetAudioDeviceFormat() instead.
SDL_GetDefaultAudioInfo() is removed; SDL_GetAudioDeviceFormat() with SDL_AUDIO_DEVICE_DEFAULT_OUTPUT or SDL_AUDIO_DEVICE_DEFAULT_CAPTURE. There is no replacement for querying the default device name; the string is no longer used to open devices, and SDL3 will migrate between physical devices on the fly if the system default changes, so if you must show this to the user, a generic name like "System default" is recommended.
SDL_MixAudio() has been removed, as it relied on legacy SDL 1.2 quirks; SDL_MixAudioFormat() remains and offers the same functionality.
SDL_AudioInit() and SDL_AudioQuit() have been removed. Instead you can call SDL_InitSubSystem() and SDL_QuitSubSystem() with SDL_INIT_AUDIO, which will properly refcount the subsystems. You can choose a specific audio driver using SDL_AUDIO_DRIVER hint.
SDL_FreeWAV has been removed and calls can be replaced with SDL_free.
SDL_AudioCVT interface is removed, SDL_AudioStream interface or SDL_ConvertAudioSamples() helper function can be used.
SDL_LoadWAV() is a proper function now and no longer a macro (but offers the same functionality otherwise).
SDL_LoadWAV_RW() and SDL_LoadWAV() return an int now: zero on success, -1 on error, like most of SDL. They no longer return a pointer to an SDL_AudioSpec.
SDL_AudioCVT interface has been removed, the SDL_AudioStream interface (for audio supplied in pieces) or the new SDL_ConvertAudioSamples() function (for converting a complete audio buffer in one call) can be used instead.
Code that used to look like this:
```c
@ -75,8 +190,9 @@ should be changed to:
```c
Uint8 *dst_data = NULL;
int dst_len = 0;
if (SDL_ConvertAudioSamples(src_format, src_channels, src_rate, src_data, src_len
dst_format, dst_channels, dst_rate, &dst_data, &dst_len) < 0) {
const SDL_AudioSpec src_spec = { src_format, src_channels, src_rate };
const SDL_AudioSpec dst_spec = { dst_format, dst_channels, dst_rate };
if (SDL_ConvertAudioSamples(&src_spec, src_data, src_len, &dst_spec, &dst_data, &dst_len) < 0) {
/* error */
}
do_something(dst_data, dst_len);
@ -103,9 +219,13 @@ If you need to convert U16 audio data to a still-supported format at runtime, th
}
```
In SDL2, SDL_AudioStream would convert/resample audio data during input (via SDL_AudioStreamPut). In SDL3, it does this work when requesting audio (via SDL_GetAudioStreamData, which would have been SDL_AudioStreamPut in SDL2. The way you use an AudioStream is roughly the same, just be aware that the workload moved to a different phase.
All remaining `AUDIO_*` symbols have been renamed to `SDL_AUDIO_*` for API consistency, but othewise are identical in value and usage.
In SDL2, SDL_AudioStream would convert/resample audio data during input (via SDL_AudioStreamPut). In SDL3, it does this work when requesting audio (via SDL_GetAudioStreamData, which would have been SDL_AudioStreamGet in SDL2). The way you use an AudioStream is roughly the same, just be aware that the workload moved to a different phase.
In SDL2, SDL_AudioStreamAvailable() returns 0 if passed a NULL stream. In SDL3, the equivalent SDL_GetAudioStreamAvailable() call returns -1 and sets an error string, which matches other audiostream APIs' behavior.
In SDL2, SDL_AUDIODEVICEREMOVED events would fire for open devices with the `which` field set to the SDL_AudioDeviceID of the lost device, and in later SDL2 releases, would also fire this event with a `which` field of zero for unopened devices, to signify that the app might want to refresh the available device list. In SDL3, this event works the same, except it won't ever fire with a zero; in this case it'll return the physical device's SDL_AudioDeviceID. Any still-open SDL_AudioDeviceIDs generated from this device with SDL_OpenAudioDevice() will also fire a separate event.
The following functions have been renamed:
* SDL_AudioStreamAvailable() => SDL_GetAudioStreamAvailable()
@ -118,17 +238,25 @@ The following functions have been renamed:
The following functions have been removed:
* SDL_GetNumAudioDevices()
* SDL_GetAudioDeviceSpec()
* SDL_ConvertAudio()
* SDL_BuildAudioCVT()
* SDL_OpenAudio()
* SDL_CloseAudio()
* SDL_PauseAudio()
* SDL_GetAudioStatus()
* SDL_GetAudioDeviceStatus()
* SDL_GetDefaultAudioInfo()
* SDL_LockAudio()
* SDL_LockAudioDevice()
* SDL_UnlockAudio()
* SDL_UnlockAudioDevice()
* SDL_MixAudio()
Use the SDL_AudioDevice functions instead.
* SDL_QueueAudio()
* SDL_DequeueAudio()
* SDL_ClearAudioQueue()
* SDL_GetQueuedAudioSize()
The following symbols have been renamed:
* AUDIO_F32 => SDL_AUDIO_F32

File diff suppressed because it is too large Load Diff

View File

@ -493,7 +493,7 @@ typedef struct SDL_AudioDeviceEvent
{
Uint32 type; /**< ::SDL_EVENT_AUDIO_DEVICE_ADDED, or ::SDL_EVENT_AUDIO_DEVICE_REMOVED */
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
SDL_AudioDeviceID which; /**< The audio device index for the ADDED event (valid until next SDL_GetNumAudioDevices() call), SDL_AudioDeviceID for the REMOVED event */
SDL_AudioDeviceID which; /**< SDL_AudioDeviceID for the device being added or removed */
Uint8 iscapture; /**< zero if an output device, non-zero if a capture device. */
Uint8 padding1;
Uint8 padding2;

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -1241,7 +1241,7 @@ static int LAW_Decode(WaveFile *file, Uint8 **audio_buf, Uint32 *audio_len)
dst = (Sint16 *)src;
/* Work backwards, since we're expanding in-place. SDL_AudioSpec.format will
/* Work backwards, since we're expanding in-place. `format` will
* inform the caller about the byte order.
*/
i = sample_count;
@ -1667,15 +1667,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);
}
@ -2025,13 +2021,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 +2056,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,31 +2070,26 @@ 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, int 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;
return -1; /* Error may come from RWops. */
} else if (spec == NULL) {
SDL_InvalidParamError("spec");
goto done;
return SDL_InvalidParamError("spec");
} else if (audio_buf == NULL) {
SDL_InvalidParamError("audio_buf");
goto done;
return SDL_InvalidParamError("audio_buf");
} else if (audio_len == NULL) {
SDL_InvalidParamError("audio_len");
goto done;
return SDL_InvalidParamError("audio_len");
}
*audio_buf = NULL;
*audio_len = 0;
SDL_zero(file);
file.riffhint = WaveGetRiffSizeHint();
file.trunchint = WaveGetTruncationHint();
file.facthint = WaveGetFactChunkHint();
@ -2107,7 +2097,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;
}
@ -2119,13 +2108,11 @@ SDL_AudioSpec *SDL_LoadWAV_RW(SDL_RWops *src, SDL_bool freesrc, SDL_AudioSpec *s
WaveFreeChunkData(&file.chunk);
SDL_free(file.decoderdata);
done:
if (freesrc && src) {
SDL_RWclose(src);
}
if (result == 0) {
return spec;
} else {
return NULL;
}
return result;
}
int SDL_LoadWAV(const char *path, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
{
return SDL_LoadWAV_RW(SDL_RWFromFile(path, "rb"), 1, spec, audio_buf, audio_len);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -233,7 +233,7 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
JNIEnv *env, jclass jcls);
JNIEXPORT void JNICALL
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture,
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture, jstring name,
jint device_id);
JNIEXPORT void JNICALL
@ -242,7 +242,7 @@ JNIEXPORT void JNICALL
static JNINativeMethod SDLAudioManager_tab[] = {
{ "nativeSetupJNI", "()I", SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI) },
{ "addAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(addAudioDevice) },
{ "addAudioDevice", "(ZLjava/lang/String;I)V", SDL_JAVA_AUDIO_INTERFACE(addAudioDevice) },
{ "removeAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice) }
};
@ -350,8 +350,8 @@ static jmethodID midSupportsRelativeMouse;
static jclass mAudioManagerClass;
/* method signatures */
static jmethodID midGetAudioOutputDevices;
static jmethodID midGetAudioInputDevices;
static jmethodID midRegisterAudioDeviceCallback;
static jmethodID midUnregisterAudioDeviceCallback;
static jmethodID midAudioOpen;
static jmethodID midAudioWriteByteBuffer;
static jmethodID midAudioWriteShortBuffer;
@ -681,12 +681,12 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jcl
mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
midGetAudioOutputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"getAudioOutputDevices",
"()[I");
midGetAudioInputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"getAudioInputDevices",
"()[I");
midRegisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"registerAudioDeviceCallback",
"()V");
midUnregisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"unregisterAudioDeviceCallback",
"()V");
midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"audioOpen", "(IIIII)[I");
midAudioWriteByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
@ -710,7 +710,7 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jcl
midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"audioSetThreadPriority", "(ZI)V");
if (!midGetAudioOutputDevices || !midGetAudioInputDevices || !midAudioOpen ||
if (!midRegisterAudioDeviceCallback || !midUnregisterAudioDeviceCallback || !midAudioOpen ||
!midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer ||
!midAudioClose ||
!midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer ||
@ -1002,18 +1002,17 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)(
SDL_AtomicSet(&bPermissionRequestPending, SDL_FALSE);
}
extern void SDL_AddAudioDevice(const SDL_bool iscapture, const char *name, SDL_AudioSpec *spec, void *handle);
extern void SDL_RemoveAudioDevice(const SDL_bool iscapture, void *handle);
JNIEXPORT void JNICALL
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture,
jint device_id)
jstring name, jint device_id)
{
if (SDL_GetCurrentAudioDriver() != NULL) {
char device_name[64];
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id);
SDL_Log("Adding device with name %s, capture %d", device_name, is_capture);
SDL_AddAudioDevice(is_capture, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1));
void *handle = (void *)((size_t)device_id);
if (!SDL_FindPhysicalAudioDeviceByHandle(handle)) {
const char *utf8name = (*env)->GetStringUTFChars(env, name, NULL);
SDL_AddAudioDevice(is_capture ? SDL_TRUE : SDL_FALSE, SDL_strdup(utf8name), NULL, handle);
(*env)->ReleaseStringUTFChars(env, name, utf8name);
}
}
}
@ -1022,8 +1021,8 @@ SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice)(JNIEnv *env, jclass jcls, jboolean i
jint device_id)
{
if (SDL_GetCurrentAudioDriver() != NULL) {
SDL_Log("Removing device with handle %d, capture %d", device_id + 1, is_capture);
SDL_RemoveAudioDevice(is_capture, (void *)((size_t)device_id + 1));
SDL_Log("Removing device with handle %d, capture %d", device_id, is_capture);
SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle((void *)((size_t)device_id)));
}
}
@ -1564,58 +1563,25 @@ static void *audioBufferPinned = NULL;
static int captureBufferFormat = 0;
static jobject captureBuffer = NULL;
static void Android_JNI_GetAudioDevices(int *devices, int *length, int max_len, int is_input)
void Android_StartAudioHotplug(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{
JNIEnv *env = Android_JNI_GetEnv();
jintArray result;
if (is_input) {
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioInputDevices);
} else {
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioOutputDevices);
}
*length = (*env)->GetArrayLength(env, result);
*length = SDL_min(*length, max_len);
(*env)->GetIntArrayRegion(env, result, 0, *length, devices);
// this will fire the callback for each existing device right away (which will eventually SDL_AddAudioDevice), and again later when things change.
(*env)->CallStaticVoidMethod(env, mAudioManagerClass, midRegisterAudioDeviceCallback);
*default_output = *default_capture = NULL; // !!! FIXME: how do you decide the default device id?
}
void Android_DetectDevices(void)
void Android_StopAudioHotplug(void)
{
int inputs[100];
int outputs[100];
int inputs_length = 0;
int outputs_length = 0;
SDL_zeroa(inputs);
Android_JNI_GetAudioDevices(inputs, &inputs_length, 100, 1 /* input devices */);
for (int i = 0; i < inputs_length; ++i) {
int device_id = inputs[i];
char device_name[64];
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id);
SDL_Log("Adding input device with name %s", device_name);
SDL_AddAudioDevice(SDL_TRUE, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1));
}
SDL_zeroa(outputs);
Android_JNI_GetAudioDevices(outputs, &outputs_length, 100, 0 /* output devices */);
for (int i = 0; i < outputs_length; ++i) {
int device_id = outputs[i];
char device_name[64];
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id);
SDL_Log("Adding output device with name %s", device_name);
SDL_AddAudioDevice(SDL_FALSE, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1));
}
JNIEnv *env = Android_JNI_GetEnv();
(*env)->CallStaticVoidMethod(env, mAudioManagerClass, midUnregisterAudioDeviceCallback);
}
int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spec)
int Android_JNI_OpenAudioDevice(SDL_AudioDevice *device)
{
const SDL_bool iscapture = device->iscapture;
SDL_AudioSpec *spec = &device->spec;
const int device_id = (int) ((size_t) device->handle);
int audioformat;
jobject jbufobj = NULL;
jobject result;
@ -1640,10 +1606,10 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
if (iscapture) {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, spec->samples, device_id);
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, device->sample_frames, device_id);
} else {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, spec->samples, device_id);
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, device->sample_frames, device_id);
}
if (result == NULL) {
/* Error during audio initialization, error printed from Java */
@ -1668,10 +1634,10 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
spec->format = SDL_AUDIO_F32;
break;
default:
return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
return SDL_SetError("Unexpected audio format from Java: %d", audioformat);
}
spec->channels = resultElements[2];
spec->samples = resultElements[3];
device->sample_frames = resultElements[3];
(*env)->ReleaseIntArrayElements(env, (jintArray)result, resultElements, JNI_ABORT);
(*env)->DeleteLocalRef(env, result);
@ -1680,7 +1646,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
switch (audioformat) {
case ENCODING_PCM_8BIT:
{
jbyteArray audioBufferLocal = (*env)->NewByteArray(env, spec->samples * spec->channels);
jbyteArray audioBufferLocal = (*env)->NewByteArray(env, device->sample_frames * spec->channels);
if (audioBufferLocal) {
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
(*env)->DeleteLocalRef(env, audioBufferLocal);
@ -1688,7 +1654,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
} break;
case ENCODING_PCM_16BIT:
{
jshortArray audioBufferLocal = (*env)->NewShortArray(env, spec->samples * spec->channels);
jshortArray audioBufferLocal = (*env)->NewShortArray(env, device->sample_frames * spec->channels);
if (audioBufferLocal) {
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
(*env)->DeleteLocalRef(env, audioBufferLocal);
@ -1696,7 +1662,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
} break;
case ENCODING_PCM_FLOAT:
{
jfloatArray audioBufferLocal = (*env)->NewFloatArray(env, spec->samples * spec->channels);
jfloatArray audioBufferLocal = (*env)->NewFloatArray(env, device->sample_frames * spec->channels);
if (audioBufferLocal) {
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
(*env)->DeleteLocalRef(env, audioBufferLocal);
@ -1887,12 +1853,17 @@ void Android_JNI_CloseAudioDevice(const int iscapture)
}
}
void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id)
static void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id)
{
JNIEnv *env = Android_JNI_GetEnv();
(*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioSetThreadPriority, iscapture, device_id);
}
void Android_AudioThreadInit(SDL_AudioDevice *device)
{
Android_JNI_AudioSetThreadPriority((int) device->iscapture, (int)device->instance_id);
}
/* Test for an exception and call SDL_SetError with its detail if one occurs */
/* If the parameter silent is truthy then SDL_SetError() will not be called. */
static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)

View File

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

View File

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

View File

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

View File

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

View File

@ -78,6 +78,7 @@
#include <windows.h>
#include <basetyps.h> /* for REFIID with broken mingw.org headers */
#include <mmreg.h>
/* Older Visual C++ headers don't have the Win64-compatible typedefs... */
#if defined(_MSC_VER) && (_MSC_VER <= 1200)
@ -154,6 +155,8 @@ extern void WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect);
/* Returns SDL_TRUE if the rect is empty */
extern BOOL WIN_IsRectEmpty(const RECT *rect);
extern SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}

View File

@ -35,22 +35,17 @@ SDL3_0.0.0 {
SDL_BroadcastCondition;
SDL_CaptureMouse;
SDL_CleanupTLS;
SDL_ClearAudioStream;
SDL_ClearComposition;
SDL_ClearError;
SDL_ClearHints;
SDL_ClearQueuedAudio;
SDL_CloseAudioDevice;
SDL_CloseGamepad;
SDL_CloseJoystick;
SDL_CloseSensor;
SDL_ComposeCustomBlendMode;
SDL_ConvertAudioSamples;
SDL_ConvertEventToRenderCoordinates;
SDL_ConvertPixels;
SDL_ConvertSurface;
SDL_ConvertSurfaceFormat;
SDL_CreateAudioStream;
SDL_CreateColorCursor;
SDL_CreateCondition;
SDL_CreateCursor;
@ -82,8 +77,6 @@ SDL3_0.0.0 {
SDL_DelHintCallback;
SDL_Delay;
SDL_DelayNS;
SDL_DequeueAudio;
SDL_DestroyAudioStream;
SDL_DestroyCondition;
SDL_DestroyCursor;
SDL_DestroyMutex;
@ -114,7 +107,6 @@ SDL3_0.0.0 {
SDL_FillSurfaceRects;
SDL_FilterEvents;
SDL_FlashWindow;
SDL_FlushAudioStream;
SDL_FlushEvent;
SDL_FlushEvents;
SDL_GDKGetTaskQueue;
@ -150,27 +142,18 @@ SDL3_0.0.0 {
SDL_GetAndroidSDKVersion;
SDL_GetAssertionHandler;
SDL_GetAssertionReport;
SDL_GetAudioDeviceName;
SDL_GetAudioDeviceSpec;
SDL_GetAudioDeviceStatus;
SDL_GetAudioDriver;
SDL_GetAudioStreamAvailable;
SDL_GetAudioStreamData;
SDL_GetAudioStreamFormat;
SDL_GetBasePath;
SDL_GetCPUCacheLineSize;
SDL_GetCPUCount;
SDL_GetClipboardData;
SDL_GetClipboardText;
SDL_GetClosestFullscreenDisplayMode;
SDL_GetCurrentAudioDriver;
SDL_GetCurrentDisplayMode;
SDL_GetCurrentDisplayOrientation;
SDL_GetCurrentRenderOutputSize;
SDL_GetCurrentVideoDriver;
SDL_GetCursor;
SDL_GetDefaultAssertionHandler;
SDL_GetDefaultAudioInfo;
SDL_GetDefaultCursor;
SDL_GetDesktopDisplayMode;
SDL_GetDisplayBounds;
@ -267,8 +250,6 @@ SDL3_0.0.0 {
SDL_GetMouseState;
SDL_GetNaturalDisplayOrientation;
SDL_GetNumAllocations;
SDL_GetNumAudioDevices;
SDL_GetNumAudioDrivers;
SDL_GetNumGamepadMappings;
SDL_GetNumGamepadTouchpadFingers;
SDL_GetNumGamepadTouchpads;
@ -291,7 +272,6 @@ SDL3_0.0.0 {
SDL_GetPreferredLocales;
SDL_GetPrimaryDisplay;
SDL_GetPrimarySelectionText;
SDL_GetQueuedAudioSize;
SDL_GetRGB;
SDL_GetRGBA;
SDL_GetRectAndLineIntersection;
@ -461,8 +441,6 @@ SDL3_0.0.0 {
SDL_LoadFile_RW;
SDL_LoadFunction;
SDL_LoadObject;
SDL_LoadWAV_RW;
SDL_LockAudioDevice;
SDL_LockJoysticks;
SDL_LockMutex;
SDL_LockRWLockForReading;
@ -494,7 +472,6 @@ SDL3_0.0.0 {
SDL_Metal_DestroyView;
SDL_Metal_GetLayer;
SDL_MinimizeWindow;
SDL_MixAudioFormat;
SDL_MouseIsHaptic;
SDL_NumHaptics;
SDL_OnApplicationDidBecomeActive;
@ -504,22 +481,17 @@ SDL3_0.0.0 {
SDL_OnApplicationWillEnterForeground;
SDL_OnApplicationWillResignActive;
SDL_OnApplicationWillTerminate;
SDL_OpenAudioDevice;
SDL_OpenGamepad;
SDL_OpenJoystick;
SDL_OpenSensor;
SDL_OpenURL;
SDL_PauseAudioDevice;
SDL_PeepEvents;
SDL_PlayAudioDevice;
SDL_PollEvent;
SDL_PostSemaphore;
SDL_PremultiplyAlpha;
SDL_PumpEvents;
SDL_PushEvent;
SDL_PutAudioStreamData;
SDL_QueryTexture;
SDL_QueueAudio;
SDL_Quit;
SDL_QuitSubSystem;
SDL_RWFromConstMem;
@ -672,7 +644,6 @@ SDL3_0.0.0 {
SDL_TryLockRWLockForWriting;
SDL_TryWaitSemaphore;
SDL_UnloadObject;
SDL_UnlockAudioDevice;
SDL_UnlockJoysticks;
SDL_UnlockMutex;
SDL_UnlockRWLock;
@ -879,6 +850,43 @@ SDL3_0.0.0 {
SDL_strnlen;
SDL_AddGamepadMappingsFromFile;
SDL_ReloadGamepadMappings;
SDL_GetNumAudioDrivers;
SDL_GetAudioDriver;
SDL_GetCurrentAudioDriver;
SDL_GetAudioOutputDevices;
SDL_GetAudioCaptureDevices;
SDL_GetAudioDeviceName;
SDL_GetAudioDeviceFormat;
SDL_OpenAudioDevice;
SDL_CloseAudioDevice;
SDL_BindAudioStreams;
SDL_BindAudioStream;
SDL_UnbindAudioStreams;
SDL_UnbindAudioStream;
SDL_CreateAudioStream;
SDL_GetAudioStreamFormat;
SDL_SetAudioStreamFormat;
SDL_PutAudioStreamData;
SDL_GetAudioStreamData;
SDL_GetAudioStreamAvailable;
SDL_FlushAudioStream;
SDL_ClearAudioStream;
SDL_LockAudioStream;
SDL_UnlockAudioStream;
SDL_SetAudioStreamGetCallback;
SDL_SetAudioStreamPutCallback;
SDL_DestroyAudioStream;
SDL_CreateAndBindAudioStream;
SDL_LoadWAV_RW;
SDL_LoadWAV;
SDL_MixAudioFormat;
SDL_ConvertAudioSamples;
SDL_GetSilenceValueForFormat;
SDL_LoadWAV;
SDL_PauseAudioDevice;
SDL_UnpauseAudioDevice;
SDL_IsAudioDevicePaused;
SDL_GetAudioStreamBinding;
# extra symbols go here (don't modify this line)
local: *;
};

View File

@ -59,22 +59,17 @@
#define SDL_BroadcastCondition SDL_BroadcastCondition_REAL
#define SDL_CaptureMouse SDL_CaptureMouse_REAL
#define SDL_CleanupTLS SDL_CleanupTLS_REAL
#define SDL_ClearAudioStream SDL_ClearAudioStream_REAL
#define SDL_ClearComposition SDL_ClearComposition_REAL
#define SDL_ClearError SDL_ClearError_REAL
#define SDL_ClearHints SDL_ClearHints_REAL
#define SDL_ClearQueuedAudio SDL_ClearQueuedAudio_REAL
#define SDL_CloseAudioDevice SDL_CloseAudioDevice_REAL
#define SDL_CloseGamepad SDL_CloseGamepad_REAL
#define SDL_CloseJoystick SDL_CloseJoystick_REAL
#define SDL_CloseSensor SDL_CloseSensor_REAL
#define SDL_ComposeCustomBlendMode SDL_ComposeCustomBlendMode_REAL
#define SDL_ConvertAudioSamples SDL_ConvertAudioSamples_REAL
#define SDL_ConvertEventToRenderCoordinates SDL_ConvertEventToRenderCoordinates_REAL
#define SDL_ConvertPixels SDL_ConvertPixels_REAL
#define SDL_ConvertSurface SDL_ConvertSurface_REAL
#define SDL_ConvertSurfaceFormat SDL_ConvertSurfaceFormat_REAL
#define SDL_CreateAudioStream SDL_CreateAudioStream_REAL
#define SDL_CreateColorCursor SDL_CreateColorCursor_REAL
#define SDL_CreateCondition SDL_CreateCondition_REAL
#define SDL_CreateCursor SDL_CreateCursor_REAL
@ -106,8 +101,6 @@
#define SDL_DelHintCallback SDL_DelHintCallback_REAL
#define SDL_Delay SDL_Delay_REAL
#define SDL_DelayNS SDL_DelayNS_REAL
#define SDL_DequeueAudio SDL_DequeueAudio_REAL
#define SDL_DestroyAudioStream SDL_DestroyAudioStream_REAL
#define SDL_DestroyCondition SDL_DestroyCondition_REAL
#define SDL_DestroyCursor SDL_DestroyCursor_REAL
#define SDL_DestroyMutex SDL_DestroyMutex_REAL
@ -138,7 +131,6 @@
#define SDL_FillSurfaceRects SDL_FillSurfaceRects_REAL
#define SDL_FilterEvents SDL_FilterEvents_REAL
#define SDL_FlashWindow SDL_FlashWindow_REAL
#define SDL_FlushAudioStream SDL_FlushAudioStream_REAL
#define SDL_FlushEvent SDL_FlushEvent_REAL
#define SDL_FlushEvents SDL_FlushEvents_REAL
#define SDL_GDKGetTaskQueue SDL_GDKGetTaskQueue_REAL
@ -174,27 +166,18 @@
#define SDL_GetAndroidSDKVersion SDL_GetAndroidSDKVersion_REAL
#define SDL_GetAssertionHandler SDL_GetAssertionHandler_REAL
#define SDL_GetAssertionReport SDL_GetAssertionReport_REAL
#define SDL_GetAudioDeviceName SDL_GetAudioDeviceName_REAL
#define SDL_GetAudioDeviceSpec SDL_GetAudioDeviceSpec_REAL
#define SDL_GetAudioDeviceStatus SDL_GetAudioDeviceStatus_REAL
#define SDL_GetAudioDriver SDL_GetAudioDriver_REAL
#define SDL_GetAudioStreamAvailable SDL_GetAudioStreamAvailable_REAL
#define SDL_GetAudioStreamData SDL_GetAudioStreamData_REAL
#define SDL_GetAudioStreamFormat SDL_GetAudioStreamFormat_REAL
#define SDL_GetBasePath SDL_GetBasePath_REAL
#define SDL_GetCPUCacheLineSize SDL_GetCPUCacheLineSize_REAL
#define SDL_GetCPUCount SDL_GetCPUCount_REAL
#define SDL_GetClipboardData SDL_GetClipboardData_REAL
#define SDL_GetClipboardText SDL_GetClipboardText_REAL
#define SDL_GetClosestFullscreenDisplayMode SDL_GetClosestFullscreenDisplayMode_REAL
#define SDL_GetCurrentAudioDriver SDL_GetCurrentAudioDriver_REAL
#define SDL_GetCurrentDisplayMode SDL_GetCurrentDisplayMode_REAL
#define SDL_GetCurrentDisplayOrientation SDL_GetCurrentDisplayOrientation_REAL
#define SDL_GetCurrentRenderOutputSize SDL_GetCurrentRenderOutputSize_REAL
#define SDL_GetCurrentVideoDriver SDL_GetCurrentVideoDriver_REAL
#define SDL_GetCursor SDL_GetCursor_REAL
#define SDL_GetDefaultAssertionHandler SDL_GetDefaultAssertionHandler_REAL
#define SDL_GetDefaultAudioInfo SDL_GetDefaultAudioInfo_REAL
#define SDL_GetDefaultCursor SDL_GetDefaultCursor_REAL
#define SDL_GetDesktopDisplayMode SDL_GetDesktopDisplayMode_REAL
#define SDL_GetDisplayBounds SDL_GetDisplayBounds_REAL
@ -291,8 +274,6 @@
#define SDL_GetMouseState SDL_GetMouseState_REAL
#define SDL_GetNaturalDisplayOrientation SDL_GetNaturalDisplayOrientation_REAL
#define SDL_GetNumAllocations SDL_GetNumAllocations_REAL
#define SDL_GetNumAudioDevices SDL_GetNumAudioDevices_REAL
#define SDL_GetNumAudioDrivers SDL_GetNumAudioDrivers_REAL
#define SDL_GetNumGamepadMappings SDL_GetNumGamepadMappings_REAL
#define SDL_GetNumGamepadTouchpadFingers SDL_GetNumGamepadTouchpadFingers_REAL
#define SDL_GetNumGamepadTouchpads SDL_GetNumGamepadTouchpads_REAL
@ -315,7 +296,6 @@
#define SDL_GetPreferredLocales SDL_GetPreferredLocales_REAL
#define SDL_GetPrimaryDisplay SDL_GetPrimaryDisplay_REAL
#define SDL_GetPrimarySelectionText SDL_GetPrimarySelectionText_REAL
#define SDL_GetQueuedAudioSize SDL_GetQueuedAudioSize_REAL
#define SDL_GetRGB SDL_GetRGB_REAL
#define SDL_GetRGBA SDL_GetRGBA_REAL
#define SDL_GetRectAndLineIntersection SDL_GetRectAndLineIntersection_REAL
@ -485,8 +465,6 @@
#define SDL_LoadFile_RW SDL_LoadFile_RW_REAL
#define SDL_LoadFunction SDL_LoadFunction_REAL
#define SDL_LoadObject SDL_LoadObject_REAL
#define SDL_LoadWAV_RW SDL_LoadWAV_RW_REAL
#define SDL_LockAudioDevice SDL_LockAudioDevice_REAL
#define SDL_LockJoysticks SDL_LockJoysticks_REAL
#define SDL_LockMutex SDL_LockMutex_REAL
#define SDL_LockRWLockForReading SDL_LockRWLockForReading_REAL
@ -518,7 +496,6 @@
#define SDL_Metal_DestroyView SDL_Metal_DestroyView_REAL
#define SDL_Metal_GetLayer SDL_Metal_GetLayer_REAL
#define SDL_MinimizeWindow SDL_MinimizeWindow_REAL
#define SDL_MixAudioFormat SDL_MixAudioFormat_REAL
#define SDL_MouseIsHaptic SDL_MouseIsHaptic_REAL
#define SDL_NumHaptics SDL_NumHaptics_REAL
#define SDL_OnApplicationDidBecomeActive SDL_OnApplicationDidBecomeActive_REAL
@ -528,22 +505,17 @@
#define SDL_OnApplicationWillEnterForeground SDL_OnApplicationWillEnterForeground_REAL
#define SDL_OnApplicationWillResignActive SDL_OnApplicationWillResignActive_REAL
#define SDL_OnApplicationWillTerminate SDL_OnApplicationWillTerminate_REAL
#define SDL_OpenAudioDevice SDL_OpenAudioDevice_REAL
#define SDL_OpenGamepad SDL_OpenGamepad_REAL
#define SDL_OpenJoystick SDL_OpenJoystick_REAL
#define SDL_OpenSensor SDL_OpenSensor_REAL
#define SDL_OpenURL SDL_OpenURL_REAL
#define SDL_PauseAudioDevice SDL_PauseAudioDevice_REAL
#define SDL_PeepEvents SDL_PeepEvents_REAL
#define SDL_PlayAudioDevice SDL_PlayAudioDevice_REAL
#define SDL_PollEvent SDL_PollEvent_REAL
#define SDL_PostSemaphore SDL_PostSemaphore_REAL
#define SDL_PremultiplyAlpha SDL_PremultiplyAlpha_REAL
#define SDL_PumpEvents SDL_PumpEvents_REAL
#define SDL_PushEvent SDL_PushEvent_REAL
#define SDL_PutAudioStreamData SDL_PutAudioStreamData_REAL
#define SDL_QueryTexture SDL_QueryTexture_REAL
#define SDL_QueueAudio SDL_QueueAudio_REAL
#define SDL_Quit SDL_Quit_REAL
#define SDL_QuitSubSystem SDL_QuitSubSystem_REAL
#define SDL_RWFromConstMem SDL_RWFromConstMem_REAL
@ -605,7 +577,6 @@
#define SDL_SendGamepadEffect SDL_SendGamepadEffect_REAL
#define SDL_SendJoystickEffect SDL_SendJoystickEffect_REAL
#define SDL_SetAssertionHandler SDL_SetAssertionHandler_REAL
#define SDL_SetAudioStreamFormat SDL_SetAudioStreamFormat_REAL
#define SDL_SetClipboardData SDL_SetClipboardData_REAL
#define SDL_SetClipboardText SDL_SetClipboardText_REAL
#define SDL_SetCursor SDL_SetCursor_REAL
@ -696,7 +667,6 @@
#define SDL_TryLockRWLockForWriting SDL_TryLockRWLockForWriting_REAL
#define SDL_TryWaitSemaphore SDL_TryWaitSemaphore_REAL
#define SDL_UnloadObject SDL_UnloadObject_REAL
#define SDL_UnlockAudioDevice SDL_UnlockAudioDevice_REAL
#define SDL_UnlockJoysticks SDL_UnlockJoysticks_REAL
#define SDL_UnlockMutex SDL_UnlockMutex_REAL
#define SDL_UnlockRWLock SDL_UnlockRWLock_REAL
@ -905,3 +875,40 @@
#define SDL_strnlen SDL_strnlen_REAL
#define SDL_AddGamepadMappingsFromFile SDL_AddGamepadMappingsFromFile_REAL
#define SDL_ReloadGamepadMappings SDL_ReloadGamepadMappings_REAL
#define SDL_GetNumAudioDrivers SDL_GetNumAudioDrivers_REAL
#define SDL_GetAudioDriver SDL_GetAudioDriver_REAL
#define SDL_GetCurrentAudioDriver SDL_GetCurrentAudioDriver_REAL
#define SDL_GetAudioOutputDevices SDL_GetAudioOutputDevices_REAL
#define SDL_GetAudioCaptureDevices SDL_GetAudioCaptureDevices_REAL
#define SDL_GetAudioDeviceName SDL_GetAudioDeviceName_REAL
#define SDL_GetAudioDeviceFormat SDL_GetAudioDeviceFormat_REAL
#define SDL_OpenAudioDevice SDL_OpenAudioDevice_REAL
#define SDL_CloseAudioDevice SDL_CloseAudioDevice_REAL
#define SDL_BindAudioStreams SDL_BindAudioStreams_REAL
#define SDL_BindAudioStream SDL_BindAudioStream_REAL
#define SDL_UnbindAudioStreams SDL_UnbindAudioStreams_REAL
#define SDL_UnbindAudioStream SDL_UnbindAudioStream_REAL
#define SDL_CreateAudioStream SDL_CreateAudioStream_REAL
#define SDL_GetAudioStreamFormat SDL_GetAudioStreamFormat_REAL
#define SDL_SetAudioStreamFormat SDL_SetAudioStreamFormat_REAL
#define SDL_PutAudioStreamData SDL_PutAudioStreamData_REAL
#define SDL_GetAudioStreamData SDL_GetAudioStreamData_REAL
#define SDL_GetAudioStreamAvailable SDL_GetAudioStreamAvailable_REAL
#define SDL_FlushAudioStream SDL_FlushAudioStream_REAL
#define SDL_ClearAudioStream SDL_ClearAudioStream_REAL
#define SDL_LockAudioStream SDL_LockAudioStream_REAL
#define SDL_UnlockAudioStream SDL_UnlockAudioStream_REAL
#define SDL_SetAudioStreamGetCallback SDL_SetAudioStreamGetCallback_REAL
#define SDL_SetAudioStreamPutCallback SDL_SetAudioStreamPutCallback_REAL
#define SDL_DestroyAudioStream SDL_DestroyAudioStream_REAL
#define SDL_CreateAndBindAudioStream SDL_CreateAndBindAudioStream_REAL
#define SDL_LoadWAV_RW SDL_LoadWAV_RW_REAL
#define SDL_LoadWAV SDL_LoadWAV_REAL
#define SDL_MixAudioFormat SDL_MixAudioFormat_REAL
#define SDL_ConvertAudioSamples SDL_ConvertAudioSamples_REAL
#define SDL_GetSilenceValueForFormat SDL_GetSilenceValueForFormat_REAL
#define SDL_LoadWAV SDL_LoadWAV_REAL
#define SDL_PauseAudioDevice SDL_PauseAudioDevice_REAL
#define SDL_UnpauseAudioDevice SDL_UnpauseAudioDevice_REAL
#define SDL_IsAudioDevicePaused SDL_IsAudioDevicePaused_REAL
#define SDL_GetAudioStreamBinding SDL_GetAudioStreamBinding_REAL

View File

@ -141,22 +141,17 @@ SDL_DYNAPI_PROC(int,SDL_BlitSurfaceUncheckedScaled,(SDL_Surface *a, const SDL_Re
SDL_DYNAPI_PROC(int,SDL_BroadcastCondition,(SDL_Condition *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_CaptureMouse,(SDL_bool a),(a),return)
SDL_DYNAPI_PROC(void,SDL_CleanupTLS,(void),(),)
SDL_DYNAPI_PROC(int,SDL_ClearAudioStream,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(void,SDL_ClearComposition,(void),(),)
SDL_DYNAPI_PROC(void,SDL_ClearError,(void),(),)
SDL_DYNAPI_PROC(void,SDL_ClearHints,(void),(),)
SDL_DYNAPI_PROC(int,SDL_ClearQueuedAudio,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(void,SDL_CloseAudioDevice,(SDL_AudioDeviceID a),(a),)
SDL_DYNAPI_PROC(void,SDL_CloseGamepad,(SDL_Gamepad *a),(a),)
SDL_DYNAPI_PROC(void,SDL_CloseJoystick,(SDL_Joystick *a),(a),)
SDL_DYNAPI_PROC(void,SDL_CloseSensor,(SDL_Sensor *a),(a),)
SDL_DYNAPI_PROC(SDL_BlendMode,SDL_ComposeCustomBlendMode,(SDL_BlendFactor a, SDL_BlendFactor b, SDL_BlendOperation c, SDL_BlendFactor d, SDL_BlendFactor e, SDL_BlendOperation f),(a,b,c,d,e,f),return)
SDL_DYNAPI_PROC(int,SDL_ConvertAudioSamples,(SDL_AudioFormat a, Uint8 b, int c, const Uint8 *d, int e, SDL_AudioFormat f, Uint8 g, int h, Uint8 **i, int *j),(a,b,c,d,e,f,g,h,i,j),return)
SDL_DYNAPI_PROC(int,SDL_ConvertEventToRenderCoordinates,(SDL_Renderer *a, SDL_Event *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_ConvertPixels,(int a, int b, Uint32 c, const void *d, int e, Uint32 f, void *g, int h),(a,b,c,d,e,f,g,h),return)
SDL_DYNAPI_PROC(SDL_Surface*,SDL_ConvertSurface,(SDL_Surface *a, const SDL_PixelFormat *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_Surface*,SDL_ConvertSurfaceFormat,(SDL_Surface *a, Uint32 b),(a,b),return)
SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_CreateAudioStream,(SDL_AudioFormat a, int b, int c, SDL_AudioFormat d, int e, int f),(a,b,c,d,e,f),return)
SDL_DYNAPI_PROC(SDL_Cursor*,SDL_CreateColorCursor,(SDL_Surface *a, int b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_Condition*,SDL_CreateCondition,(void),(),return)
SDL_DYNAPI_PROC(SDL_Cursor*,SDL_CreateCursor,(const Uint8 *a, const Uint8 *b, int c, int d, int e, int f),(a,b,c,d,e,f),return)
@ -185,8 +180,6 @@ SDL_DYNAPI_PROC(void,SDL_DelEventWatch,(SDL_EventFilter a, void *b),(a,b),)
SDL_DYNAPI_PROC(void,SDL_DelHintCallback,(const char *a, SDL_HintCallback b, void *c),(a,b,c),)
SDL_DYNAPI_PROC(void,SDL_Delay,(Uint32 a),(a),)
SDL_DYNAPI_PROC(void,SDL_DelayNS,(Uint64 a),(a),)
SDL_DYNAPI_PROC(Uint32,SDL_DequeueAudio,(SDL_AudioDeviceID a, void *b, Uint32 c),(a,b,c),return)
SDL_DYNAPI_PROC(void,SDL_DestroyAudioStream,(SDL_AudioStream *a),(a),)
SDL_DYNAPI_PROC(void,SDL_DestroyCondition,(SDL_Condition *a),(a),)
SDL_DYNAPI_PROC(void,SDL_DestroyCursor,(SDL_Cursor *a),(a),)
SDL_DYNAPI_PROC(void,SDL_DestroyMutex,(SDL_Mutex *a),(a),)
@ -216,7 +209,6 @@ SDL_DYNAPI_PROC(int,SDL_FillSurfaceRect,(SDL_Surface *a, const SDL_Rect *b, Uint
SDL_DYNAPI_PROC(int,SDL_FillSurfaceRects,(SDL_Surface *a, const SDL_Rect *b, int c, Uint32 d),(a,b,c,d),return)
SDL_DYNAPI_PROC(void,SDL_FilterEvents,(SDL_EventFilter a, void *b),(a,b),)
SDL_DYNAPI_PROC(int,SDL_FlashWindow,(SDL_Window *a, SDL_FlashOperation b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_FlushAudioStream,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(void,SDL_FlushEvent,(Uint32 a),(a),)
SDL_DYNAPI_PROC(void,SDL_FlushEvents,(Uint32 a, Uint32 b),(a,b),)
SDL_DYNAPI_PROC(int,SDL_GL_BindTexture,(SDL_Texture *a, float *b, float *c),(a,b,c),return)
@ -249,27 +241,18 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadHasSensor,(SDL_Gamepad *a, SDL_SensorType b)
SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadSensorEnabled,(SDL_Gamepad *a, SDL_SensorType b),(a,b),return)
SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetAssertionHandler,(void **a),(a),return)
SDL_DYNAPI_PROC(const SDL_AssertData*,SDL_GetAssertionReport,(void),(),return)
SDL_DYNAPI_PROC(const char*,SDL_GetAudioDeviceName,(int a, int b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceSpec,(int a, int b, SDL_AudioSpec *c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_AudioStatus,SDL_GetAudioDeviceStatus,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_GetAudioDriver,(int a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamAvailable,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamData,(SDL_AudioStream *a, void *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioFormat *b, int *c, int *d, SDL_AudioFormat *e, int *f, int *g),(a,b,c,d,e,f,g),return)
SDL_DYNAPI_PROC(char*,SDL_GetBasePath,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetCPUCacheLineSize,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetCPUCount,(void),(),return)
SDL_DYNAPI_PROC(void*,SDL_GetClipboardData,(const char *a, size_t *b),(a,b),return)
SDL_DYNAPI_PROC(char*,SDL_GetClipboardText,(void),(),return)
SDL_DYNAPI_PROC(const SDL_DisplayMode*,SDL_GetClosestFullscreenDisplayMode,(SDL_DisplayID a, int b, int c, float d, SDL_bool e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(const char*,SDL_GetCurrentAudioDriver,(void),(),return)
SDL_DYNAPI_PROC(const SDL_DisplayMode*,SDL_GetCurrentDisplayMode,(SDL_DisplayID a),(a),return)
SDL_DYNAPI_PROC(SDL_DisplayOrientation,SDL_GetCurrentDisplayOrientation,(SDL_DisplayID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetCurrentRenderOutputSize,(SDL_Renderer *a, int *b, int *c),(a,b,c),return)
SDL_DYNAPI_PROC(const char*,SDL_GetCurrentVideoDriver,(void),(),return)
SDL_DYNAPI_PROC(SDL_Cursor*,SDL_GetCursor,(void),(),return)
SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetDefaultAssertionHandler,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetDefaultAudioInfo,(char **a, SDL_AudioSpec *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_Cursor*,SDL_GetDefaultCursor,(void),(),return)
SDL_DYNAPI_PROC(const SDL_DisplayMode*,SDL_GetDesktopDisplayMode,(SDL_DisplayID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetDisplayBounds,(SDL_DisplayID a, SDL_Rect *b),(a,b),return)
@ -366,8 +349,6 @@ SDL_DYNAPI_PROC(SDL_Window*,SDL_GetMouseFocus,(void),(),return)
SDL_DYNAPI_PROC(Uint32,SDL_GetMouseState,(float *a, float *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_DisplayOrientation,SDL_GetNaturalDisplayOrientation,(SDL_DisplayID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetNumAllocations,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetNumAudioDevices,(int a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetNumAudioDrivers,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetNumGamepadMappings,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetNumGamepadTouchpadFingers,(SDL_Gamepad *a, int b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GetNumGamepadTouchpads,(SDL_Gamepad *a),(a),return)
@ -390,7 +371,6 @@ SDL_DYNAPI_PROC(char*,SDL_GetPrefPath,(const char *a, const char *b),(a,b),retur
SDL_DYNAPI_PROC(SDL_Locale*,SDL_GetPreferredLocales,(void),(),return)
SDL_DYNAPI_PROC(SDL_DisplayID,SDL_GetPrimaryDisplay,(void),(),return)
SDL_DYNAPI_PROC(char*,SDL_GetPrimarySelectionText,(void),(),return)
SDL_DYNAPI_PROC(Uint32,SDL_GetQueuedAudioSize,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(void,SDL_GetRGB,(Uint32 a, const SDL_PixelFormat *b, Uint8 *c, Uint8 *d, Uint8 *e),(a,b,c,d,e),)
SDL_DYNAPI_PROC(void,SDL_GetRGBA,(Uint32 a, const SDL_PixelFormat *b, Uint8 *c, Uint8 *d, Uint8 *e, Uint8 *f),(a,b,c,d,e,f),)
SDL_DYNAPI_PROC(SDL_bool,SDL_GetRectAndLineIntersection,(const SDL_Rect *a, int *b, int *c, int *d, int *e),(a,b,c,d,e),return)
@ -553,8 +533,6 @@ SDL_DYNAPI_PROC(void*,SDL_LoadFile,(const char *a, size_t *b),(a,b),return)
SDL_DYNAPI_PROC(void*,SDL_LoadFile_RW,(SDL_RWops *a, size_t *b, SDL_bool c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_FunctionPointer,SDL_LoadFunction,(void *a, const char *b),(a,b),return)
SDL_DYNAPI_PROC(void*,SDL_LoadObject,(const char *a),(a),return)
SDL_DYNAPI_PROC(SDL_AudioSpec*,SDL_LoadWAV_RW,(SDL_RWops *a, SDL_bool b, SDL_AudioSpec *c, Uint8 **d, Uint32 *e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_LockAudioDevice,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(void,SDL_LockJoysticks,(void),(),)
SDL_DYNAPI_PROC(int,SDL_LockMutex,(SDL_Mutex *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_LockRWLockForReading,(SDL_RWLock *a),(a),return)
@ -578,7 +556,6 @@ SDL_DYNAPI_PROC(SDL_MetalView,SDL_Metal_CreateView,(SDL_Window *a),(a),return)
SDL_DYNAPI_PROC(void,SDL_Metal_DestroyView,(SDL_MetalView a),(a),)
SDL_DYNAPI_PROC(void*,SDL_Metal_GetLayer,(SDL_MetalView a),(a),return)
SDL_DYNAPI_PROC(int,SDL_MinimizeWindow,(SDL_Window *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_MixAudioFormat,(Uint8 *a, const Uint8 *b, SDL_AudioFormat c, Uint32 d, int e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_MouseIsHaptic,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_NumHaptics,(void),(),return)
SDL_DYNAPI_PROC(void,SDL_OnApplicationDidBecomeActive,(void),(),)
@ -587,22 +564,17 @@ SDL_DYNAPI_PROC(void,SDL_OnApplicationDidReceiveMemoryWarning,(void),(),)
SDL_DYNAPI_PROC(void,SDL_OnApplicationWillEnterForeground,(void),(),)
SDL_DYNAPI_PROC(void,SDL_OnApplicationWillResignActive,(void),(),)
SDL_DYNAPI_PROC(void,SDL_OnApplicationWillTerminate,(void),(),)
SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_OpenAudioDevice,(const char *a, int b, const SDL_AudioSpec *c, SDL_AudioSpec *d, int e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(SDL_Gamepad*,SDL_OpenGamepad,(SDL_JoystickID a),(a),return)
SDL_DYNAPI_PROC(SDL_Joystick*,SDL_OpenJoystick,(SDL_JoystickID a),(a),return)
SDL_DYNAPI_PROC(SDL_Sensor*,SDL_OpenSensor,(SDL_SensorID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_OpenURL,(const char *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_PauseAudioDevice,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_PeepEvents,(SDL_Event *a, int b, SDL_eventaction c, Uint32 d, Uint32 e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_PlayAudioDevice,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_PollEvent,(SDL_Event *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_PostSemaphore,(SDL_Semaphore *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_PremultiplyAlpha,(int a, int b, Uint32 c, const void *d, int e, Uint32 f, void *g, int h),(a,b,c,d,e,f,g,h),return)
SDL_DYNAPI_PROC(void,SDL_PumpEvents,(void),(),)
SDL_DYNAPI_PROC(int,SDL_PushEvent,(SDL_Event *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_PutAudioStreamData,(SDL_AudioStream *a, const void *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_QueryTexture,(SDL_Texture *a, Uint32 *b, int *c, int *d, int *e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_QueueAudio,(SDL_AudioDeviceID a, const void *b, Uint32 c),(a,b,c),return)
SDL_DYNAPI_PROC(void,SDL_Quit,(void),(),)
SDL_DYNAPI_PROC(void,SDL_QuitSubSystem,(Uint32 a),(a),)
SDL_DYNAPI_PROC(SDL_RWops*,SDL_RWFromConstMem,(const void *a, size_t b),(a,b),return)
@ -662,7 +634,6 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_ScreenSaverEnabled,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_SendGamepadEffect,(SDL_Gamepad *a, const void *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SendJoystickEffect,(SDL_Joystick *a, const void *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(void,SDL_SetAssertionHandler,(SDL_AssertionHandler a, void *b),(a,b),)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioFormat b, int c, int d, SDL_AudioFormat e, int f, int g),(a,b,c,d,e,f,g),return)
SDL_DYNAPI_PROC(int,SDL_SetClipboardData,(SDL_ClipboardDataCallback a, SDL_ClipboardCleanupCallback b, void *c, const char **d, size_t e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_SetClipboardText,(const char *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_SetCursor,(SDL_Cursor *a),(a),return)
@ -751,7 +722,6 @@ SDL_DYNAPI_PROC(int,SDL_TryLockRWLockForReading,(SDL_RWLock *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_TryLockRWLockForWriting,(SDL_RWLock *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_TryWaitSemaphore,(SDL_Semaphore *a),(a),return)
SDL_DYNAPI_PROC(void,SDL_UnloadObject,(void *a),(a),)
SDL_DYNAPI_PROC(void,SDL_UnlockAudioDevice,(SDL_AudioDeviceID a),(a),)
SDL_DYNAPI_PROC(void,SDL_UnlockJoysticks,(void),(),)
SDL_DYNAPI_PROC(int,SDL_UnlockMutex,(SDL_Mutex *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_UnlockRWLock,(SDL_RWLock *a),(a),return)
@ -950,3 +920,39 @@ SDL_DYNAPI_PROC(size_t,SDL_wcsnlen,(const wchar_t *a, size_t b),(a,b),return)
SDL_DYNAPI_PROC(size_t,SDL_strnlen,(const char *a, size_t b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_AddGamepadMappingsFromFile,(const char *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_ReloadGamepadMappings,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetNumAudioDrivers,(void),(),return)
SDL_DYNAPI_PROC(const char*,SDL_GetAudioDriver,(int a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_GetCurrentAudioDriver,(void),(),return)
SDL_DYNAPI_PROC(SDL_AudioDeviceID*,SDL_GetAudioOutputDevices,(int *a),(a),return)
SDL_DYNAPI_PROC(SDL_AudioDeviceID*,SDL_GetAudioCaptureDevices,(int *a),(a),return)
SDL_DYNAPI_PROC(char*,SDL_GetAudioDeviceName,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceFormat,(SDL_AudioDeviceID a, SDL_AudioSpec *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_OpenAudioDevice,(SDL_AudioDeviceID a, const SDL_AudioSpec *b),(a,b),return)
SDL_DYNAPI_PROC(void,SDL_CloseAudioDevice,(SDL_AudioDeviceID a),(a),)
SDL_DYNAPI_PROC(int,SDL_BindAudioStreams,(SDL_AudioDeviceID a, SDL_AudioStream **b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_BindAudioStream,(SDL_AudioDeviceID a, SDL_AudioStream *b),(a,b),return)
SDL_DYNAPI_PROC(void,SDL_UnbindAudioStreams,(SDL_AudioStream **a, int b),(a,b),)
SDL_DYNAPI_PROC(void,SDL_UnbindAudioStream,(SDL_AudioStream *a),(a),)
SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_CreateAudioStream,(const SDL_AudioSpec *a, const SDL_AudioSpec *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioSpec *b, SDL_AudioSpec *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamFormat,(SDL_AudioStream *a, const SDL_AudioSpec *b, const SDL_AudioSpec *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_PutAudioStreamData,(SDL_AudioStream *a, const void *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamData,(SDL_AudioStream *a, void *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamAvailable,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_FlushAudioStream,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_ClearAudioStream,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_LockAudioStream,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_UnlockAudioStream,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamGetCallback,(SDL_AudioStream *a, SDL_AudioStreamRequestCallback b, void *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamPutCallback,(SDL_AudioStream *a, SDL_AudioStreamRequestCallback b, void *c),(a,b,c),return)
SDL_DYNAPI_PROC(void,SDL_DestroyAudioStream,(SDL_AudioStream *a),(a),)
SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_CreateAndBindAudioStream,(SDL_AudioDeviceID a, const SDL_AudioSpec *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_LoadWAV_RW,(SDL_RWops *a, int b, SDL_AudioSpec *c, Uint8 **d, Uint32 *e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_MixAudioFormat,(Uint8 *a, const Uint8 *b, SDL_AudioFormat c, Uint32 d, int e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_ConvertAudioSamples,(const SDL_AudioSpec *a, const Uint8 *b, int c, const SDL_AudioSpec *d, Uint8 **e, int *f),(a,b,c,d,e,f),return)
SDL_DYNAPI_PROC(int,SDL_GetSilenceValueForFormat,(SDL_AudioFormat a),(a),return)
SDL_DYNAPI_PROC(int,SDL_LoadWAV,(const char *a, SDL_AudioSpec *b, Uint8 **c, Uint32 *d),(a,b,c,d),return)
SDL_DYNAPI_PROC(int,SDL_PauseAudioDevice,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_UnpauseAudioDevice,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_IsAudioDevicePaused,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_GetAudioStreamBinding,(SDL_AudioStream *a),(a),return)

View File

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

View File

@ -108,7 +108,7 @@ void Android_PumpEvents_Blocking(SDL_VideoDevice *_this)
ANDROIDAUDIO_PauseDevices();
openslES_PauseDevices();
aaudio_PauseDevices();
AAUDIO_PauseDevices();
if (SDL_WaitSemaphore(Android_ResumeSem) == 0) {
@ -119,7 +119,7 @@ void Android_PumpEvents_Blocking(SDL_VideoDevice *_this)
ANDROIDAUDIO_ResumeDevices();
openslES_ResumeDevices();
aaudio_ResumeDevices();
AAUDIO_ResumeDevices();
/* Restore the GL Context from here, as this operation is thread dependent */
#ifdef SDL_VIDEO_OPENGL_EGL
@ -160,9 +160,9 @@ void Android_PumpEvents_Blocking(SDL_VideoDevice *_this)
}
}
if (aaudio_DetectBrokenPlayState()) {
aaudio_PauseDevices();
aaudio_ResumeDevices();
if (AAUDIO_DetectBrokenPlayState()) {
AAUDIO_PauseDevices();
AAUDIO_ResumeDevices();
}
}
@ -187,7 +187,7 @@ void Android_PumpEvents_NonBlocking(SDL_VideoDevice *_this)
if (videodata->pauseAudio) {
ANDROIDAUDIO_PauseDevices();
openslES_PauseDevices();
aaudio_PauseDevices();
AAUDIO_PauseDevices();
}
backup_context = 0;
@ -203,7 +203,7 @@ void Android_PumpEvents_NonBlocking(SDL_VideoDevice *_this)
if (videodata->pauseAudio) {
ANDROIDAUDIO_ResumeDevices();
openslES_ResumeDevices();
aaudio_ResumeDevices();
AAUDIO_ResumeDevices();
}
#ifdef SDL_VIDEO_OPENGL_EGL
@ -246,9 +246,9 @@ void Android_PumpEvents_NonBlocking(SDL_VideoDevice *_this)
}
}
if (aaudio_DetectBrokenPlayState()) {
aaudio_PauseDevices();
aaudio_ResumeDevices();
if (AAUDIO_DetectBrokenPlayState()) {
AAUDIO_PauseDevices();
AAUDIO_ResumeDevices();
}
}

View File

@ -119,11 +119,10 @@ endif()
add_sdl_test_executable(checkkeys SOURCES checkkeys.c)
add_sdl_test_executable(checkkeysthreads SOURCES checkkeysthreads.c)
add_sdl_test_executable(loopwave NEEDS_RESOURCES TESTUTILS SOURCES loopwave.c)
add_sdl_test_executable(loopwavequeue NEEDS_RESOURCES TESTUTILS SOURCES loopwavequeue.c)
add_sdl_test_executable(testsurround SOURCES testsurround.c)
add_sdl_test_executable(testresample NEEDS_RESOURCES SOURCES testresample.c)
add_sdl_test_executable(testaudioinfo SOURCES testaudioinfo.c)
add_sdl_test_executable(testaudiostreamdynamicresample SOURCES testaudiostreamdynamicresample.c)
add_sdl_test_executable(testaudiostreamdynamicresample NEEDS_RESOURCES TESTUTILS SOURCES testaudiostreamdynamicresample.c)
file(GLOB TESTAUTOMATION_SOURCE_FILES testautomation*.c)
add_sdl_test_executable(testautomation NEEDS_RESOURCES NO_C90 SOURCES ${TESTAUTOMATION_SOURCE_FILES})
@ -203,6 +202,7 @@ if(Python3_FOUND AND Python3_VERSION VERSION_GREATER_EQUAL "3.2")
endforeach()
endif()
add_sdl_test_executable(testaudio NEEDS_RESOURCES TESTUTILS SOURCES testaudio.c)
add_sdl_test_executable(testfile NONINTERACTIVE SOURCES testfile.c)
add_sdl_test_executable(testcontroller TESTUTILS SOURCES testcontroller.c gamepadutils.c ${gamepad_image_headers})
add_sdl_test_executable(testgeometry TESTUTILS SOURCES testgeometry.c)

View File

@ -3,7 +3,6 @@ These are test programs for the SDL library:
checkkeys Watch the key events to check the keyboard
loopwave Audio test -- loop playing a WAV file
loopwavequeue Audio test -- loop playing a WAV file with SDL_QueueAudio
testsurround Audio test -- play test tone on each audio channel
testaudioinfo Lists audio device capabilities
testerror Tests multi-threaded error handling

BIN
test/audiofile.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
test/logaudiodev.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -31,10 +31,18 @@ static struct
SDL_AudioSpec spec;
Uint8 *sound; /* Pointer to wave data */
Uint32 soundlen; /* Length of wave data */
int soundpos; /* Current play position */
Uint32 soundpos;
} wave;
static SDL_AudioDeviceID device;
static SDL_AudioStream *stream;
static void fillerup(void)
{
if (SDL_GetAudioStreamAvailable(stream) < (int) ((wave.soundlen / 2))) {
SDL_PutAudioStreamData(stream, wave.sound, wave.soundlen);
}
}
/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
static void
@ -51,6 +59,8 @@ static void
close_audio(void)
{
if (device != 0) {
SDL_DestroyAudioStream(stream);
stream = NULL;
SDL_CloseAudioDevice(device);
device = 0;
}
@ -59,56 +69,33 @@ close_audio(void)
static void
open_audio(void)
{
/* Initialize fillerup() variables */
device = SDL_OpenAudioDevice(NULL, SDL_FALSE, &wave.spec, NULL, 0);
device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &wave.spec);
if (!device) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", SDL_GetError());
SDL_free(wave.sound);
quit(2);
}
/* Let the audio run */
SDL_PlayAudioDevice(device);
}
#ifndef __EMSCRIPTEN__
static void reopen_audio(void)
{
close_audio();
open_audio();
}
#endif
static void SDLCALL
fillerup(void *unused, Uint8 *stream, int len)
{
Uint8 *waveptr;
int waveleft;
/* Set up the pointers */
waveptr = wave.sound + wave.soundpos;
waveleft = wave.soundlen - wave.soundpos;
/* Go! */
while (waveleft <= len) {
SDL_memcpy(stream, waveptr, waveleft);
stream += waveleft;
len -= waveleft;
waveptr = wave.sound;
waveleft = wave.soundlen;
wave.soundpos = 0;
stream = SDL_CreateAndBindAudioStream(device, &wave.spec);
if (!stream) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create audio stream: %s\n", SDL_GetError());
SDL_CloseAudioDevice(device);
SDL_free(wave.sound);
quit(2);
}
SDL_memcpy(stream, waveptr, len);
wave.soundpos += len;
}
static int done = 0;
#ifdef __EMSCRIPTEN__
static void loop(void)
{
if (done || (SDL_GetAudioDeviceStatus(device) != SDL_AUDIO_PLAYING)) {
if (done) {
emscripten_cancel_main_loop();
} else {
fillerup();
}
}
#endif
@ -162,13 +149,11 @@ int main(int argc, char *argv[])
}
/* Load the wave file into memory */
if (SDL_LoadWAV(filename, &wave.spec, &wave.sound, &wave.soundlen) == NULL) {
if (SDL_LoadWAV(filename, &wave.spec, &wave.sound, &wave.soundlen) == -1) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename, SDL_GetError());
quit(1);
}
wave.spec.callback = fillerup;
/* Show the list of available drivers */
SDL_Log("Available audio drivers:");
for (i = 0; i < SDL_GetNumAudioDrivers(); ++i) {
@ -179,8 +164,6 @@ int main(int argc, char *argv[])
open_audio();
SDL_FlushEvents(SDL_EVENT_AUDIO_DEVICE_ADDED, SDL_EVENT_AUDIO_DEVICE_REMOVED);
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop(loop, 0, 1);
#else
@ -191,11 +174,9 @@ int main(int argc, char *argv[])
if (event.type == SDL_EVENT_QUIT) {
done = 1;
}
if ((event.type == SDL_EVENT_AUDIO_DEVICE_ADDED && !event.adevice.iscapture) ||
(event.type == SDL_EVENT_AUDIO_DEVICE_REMOVED && !event.adevice.iscapture && event.adevice.which == device)) {
reopen_audio();
}
}
fillerup();
SDL_Delay(100);
}
#endif

View File

@ -1,183 +0,0 @@
/*
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely.
*/
/* Program to load a wave file and loop playing it using SDL sound queueing */
#include <stdlib.h>
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_test.h>
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#include "testutils.h"
static struct
{
SDL_AudioSpec spec;
Uint8 *sound; /* Pointer to wave data */
Uint32 soundlen; /* Length of wave data */
} wave;
/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
static void
quit(int rc)
{
SDL_Quit();
/* Let 'main()' return normally */
if (rc != 0) {
exit(rc);
}
}
static int done = 0;
static void poked(int sig)
{
done = 1;
}
static SDL_AudioDeviceID g_audio_id = 0;
static void loop(void)
{
#ifdef __EMSCRIPTEN__
if (done || (SDL_GetAudioDeviceStatus(g_audio_id) != SDL_AUDIO_PLAYING)) {
emscripten_cancel_main_loop();
} else
#endif
{
/* The device from SDL_OpenAudio() is always device #1. */
const Uint32 queued = SDL_GetQueuedAudioSize(g_audio_id);
SDL_Log("Device has %u bytes queued.\n", (unsigned int)queued);
if (queued <= 8192) { /* time to requeue the whole thing? */
if (SDL_QueueAudio(g_audio_id, wave.sound, wave.soundlen) == 0) {
SDL_Log("Device queued %u more bytes.\n", (unsigned int)wave.soundlen);
} else {
SDL_Log("Device FAILED to queue %u more bytes: %s\n", (unsigned int)wave.soundlen, SDL_GetError());
}
}
}
}
int main(int argc, char *argv[])
{
int i;
char *filename = NULL;
SDLTest_CommonState *state;
/* Initialize test framework */
state = SDLTest_CommonCreateState(argv, 0);
if (state == NULL) {
return 1;
}
/* Enable standard application logging */
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
/* Parse commandline */
for (i = 1; i < argc;) {
int consumed;
consumed = SDLTest_CommonArg(state, i);
if (!consumed) {
if (!filename) {
filename = argv[i];
consumed = 1;
}
}
if (consumed <= 0) {
static const char *options[] = { "[sample.wav]", NULL };
SDLTest_CommonLogUsage(state, argv[0], options);
exit(1);
}
i += consumed;
}
/* Load the SDL library */
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
return 1;
}
filename = GetResourceFilename(filename, "sample.wav");
if (filename == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s\n", SDL_GetError());
quit(1);
}
/* Load the wave file into memory */
if (SDL_LoadWAV(filename, &wave.spec, &wave.sound, &wave.soundlen) == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename, SDL_GetError());
quit(1);
}
wave.spec.callback = NULL; /* we'll push audio. */
#ifdef HAVE_SIGNAL_H
/* Set the signals */
#ifdef SIGHUP
(void)signal(SIGHUP, poked);
#endif
(void)signal(SIGINT, poked);
#ifdef SIGQUIT
(void)signal(SIGQUIT, poked);
#endif
(void)signal(SIGTERM, poked);
#endif /* HAVE_SIGNAL_H */
/* Initialize fillerup() variables */
g_audio_id = SDL_OpenAudioDevice(NULL, 0, &wave.spec, NULL, 0);
if (!g_audio_id) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", SDL_GetError());
SDL_free(wave.sound);
quit(2);
}
/*static x[99999]; SDL_QueueAudio(1, x, sizeof (x));*/
/* Let the audio run */
SDL_PlayAudioDevice(g_audio_id);
done = 0;
/* Note that we stuff the entire audio buffer into the queue in one
shot. Most apps would want to feed it a little at a time, as it
plays, but we're going for simplicity here. */
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop(loop, 0, 1);
#else
while (!done && (SDL_GetAudioDeviceStatus(g_audio_id) == SDL_AUDIO_PLAYING)) {
loop();
SDL_Delay(100); /* let it play for a while. */
}
#endif
/* Clean up on signal */
SDL_CloseAudioDevice(g_audio_id);
SDL_free(wave.sound);
SDL_free(filename);
SDL_Quit();
SDLTest_CommonDestroyState(state);
return 0;
}

BIN
test/physaudiodev.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
test/soundboard.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
test/soundboard_levels.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
test/speaker.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

8
test/testaudio-art.txt Normal file
View File

@ -0,0 +1,8 @@
The .bmp files used by testaudio.c were made by AlDraw:
https://linktr.ee/AlexDraw
They may be distributed as public domain files!

1064
test/testaudio.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -22,13 +22,14 @@
static SDL_Window *window = NULL;
static SDL_Renderer *renderer = NULL;
static SDL_AudioSpec spec;
static SDL_AudioDeviceID devid_in = 0;
static SDL_AudioDeviceID devid_out = 0;
static SDL_AudioStream *stream_in = NULL;
static SDL_AudioStream *stream_out = NULL;
static int done = 0;
static void loop(void)
{
const SDL_AudioDeviceID devid_in = SDL_GetAudioStreamBinding(stream_in);
const SDL_AudioDeviceID devid_out = SDL_GetAudioStreamBinding(stream_out);
SDL_bool please_quit = SDL_FALSE;
SDL_Event e;
@ -42,17 +43,18 @@ static void loop(void)
} else if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
if (e.button.button == 1) {
SDL_PauseAudioDevice(devid_out);
SDL_PlayAudioDevice(devid_in);
SDL_UnpauseAudioDevice(devid_in);
}
} else if (e.type == SDL_EVENT_MOUSE_BUTTON_UP) {
if (e.button.button == 1) {
SDL_PauseAudioDevice(devid_in);
SDL_PlayAudioDevice(devid_out);
SDL_FlushAudioStream(stream_in); /* so no samples are held back for resampling purposes. */
SDL_UnpauseAudioDevice(devid_out);
}
}
}
if (SDL_GetAudioDeviceStatus(devid_in) == SDL_AUDIO_PLAYING) {
if (!SDL_IsAudioDevicePaused(devid_in)) {
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
} else {
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
@ -60,13 +62,26 @@ static void loop(void)
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
/* Feed any new data we captured to the output stream. It'll play when we unpause the device. */
while (!please_quit && (SDL_GetAudioStreamAvailable(stream_in) > 0)) {
Uint8 buf[1024];
const int br = SDL_GetAudioStreamData(stream_in, buf, sizeof(buf));
if (br < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to read from input audio stream: %s\n", SDL_GetError());
please_quit = 1;
} else if (SDL_PutAudioStreamData(stream_out, buf, br) < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to write to output audio stream: %s\n", SDL_GetError());
please_quit = 1;
}
}
if (please_quit) {
/* stop playing back, quit. */
SDL_Log("Shutting down.\n");
SDL_PauseAudioDevice(devid_in);
SDL_CloseAudioDevice(devid_in);
SDL_PauseAudioDevice(devid_out);
SDL_CloseAudioDevice(devid_out);
SDL_DestroyAudioStream(stream_in);
SDL_DestroyAudioStream(stream_out);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
@ -77,28 +92,17 @@ static void loop(void)
done = 1;
return;
}
/* Note that it would be easier to just have a one-line function that
calls SDL_QueueAudio() as a capture device callback, but we're
trying to test the API, so we use SDL_DequeueAudio() here. */
while (SDL_TRUE) {
Uint8 buf[1024];
const Uint32 br = SDL_DequeueAudio(devid_in, buf, sizeof(buf));
SDL_QueueAudio(devid_out, buf, br);
if (br < sizeof(buf)) {
break;
}
}
}
int main(int argc, char **argv)
{
/* (NULL means "open default device.") */
const char *devname = NULL;
SDL_AudioSpec wanted;
int devcount;
int i;
SDL_AudioDeviceID *devices;
SDLTest_CommonState *state;
SDL_AudioSpec spec;
SDL_AudioDeviceID device;
SDL_AudioDeviceID want_device = SDL_AUDIO_DEVICE_DEFAULT_CAPTURE;
const char *devname = NULL;
int i;
/* Initialize test framework */
state = SDLTest_CommonCreateState(argv, 0);
@ -121,7 +125,7 @@ int main(int argc, char **argv)
}
}
if (consumed <= 0) {
static const char *options[] = { "[driver_name]", NULL };
static const char *options[] = { "[device_name]", NULL };
SDLTest_CommonLogUsage(state, argv[0], options);
exit(1);
}
@ -145,19 +149,20 @@ int main(int argc, char **argv)
SDL_Log("Using audio driver: %s\n", SDL_GetCurrentAudioDriver());
devcount = SDL_GetNumAudioDevices(SDL_TRUE);
for (i = 0; i < devcount; i++) {
SDL_Log(" Capture device #%d: '%s'\n", i, SDL_GetAudioDeviceName(i, SDL_TRUE));
devices = SDL_GetAudioCaptureDevices(NULL);
for (i = 0; devices[i] != 0; i++) {
char *name = SDL_GetAudioDeviceName(devices[i]);
SDL_Log(" Capture device #%d: '%s'\n", i, name);
if (devname && (SDL_strcmp(devname, name) == 0)) {
want_device = devices[i];
}
SDL_free(name);
}
SDL_zero(wanted);
wanted.freq = 44100;
wanted.format = SDL_AUDIO_F32SYS;
wanted.channels = 1;
wanted.samples = 4096;
wanted.callback = NULL;
SDL_zero(spec);
if (devname && (want_device == SDL_AUDIO_DEVICE_DEFAULT_CAPTURE)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Didn't see a capture device named '%s', using the system default instead.\n", devname);
devname = NULL;
}
/* DirectSound can fail in some instances if you open the same hardware
for both capture and output and didn't open the output end first,
@ -166,24 +171,40 @@ int main(int argc, char **argv)
circumstances. */
SDL_Log("Opening default playback device...\n");
devid_out = SDL_OpenAudioDevice(NULL, SDL_FALSE, &wanted, &spec, SDL_AUDIO_ALLOW_ANY_CHANGE);
if (!devid_out) {
device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, NULL);
if (!device) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open an audio device for playback: %s!\n", SDL_GetError());
SDL_Quit();
exit(1);
}
SDL_PauseAudioDevice(device);
SDL_GetAudioDeviceFormat(device, &spec);
stream_out = SDL_CreateAndBindAudioStream(device, &spec);
if (!stream_out) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create an audio stream for playback: %s!\n", SDL_GetError());
SDL_Quit();
exit(1);
}
SDL_Log("Opening capture device %s%s%s...\n",
devname ? "'" : "",
devname ? devname : "[[default]]",
devname ? "'" : "");
devid_in = SDL_OpenAudioDevice(devname, SDL_TRUE, &spec, &spec, 0);
if (!devid_in) {
device = SDL_OpenAudioDevice(want_device, NULL);
if (!device) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open an audio device for capture: %s!\n", SDL_GetError());
SDL_Quit();
exit(1);
}
SDL_PauseAudioDevice(device);
SDL_GetAudioDeviceFormat(device, &spec);
stream_in = SDL_CreateAndBindAudioStream(device, &spec);
if (!stream_in) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create an audio stream for capture: %s!\n", SDL_GetError());
SDL_Quit();
exit(1);
}
SDL_Log("Ready! Hold down mouse or finger to record!\n");
@ -198,11 +219,7 @@ int main(int argc, char **argv)
}
#endif
/* SDL_DestroyRenderer(renderer); */
/* SDL_DestroyWindow(window); */
/* SDL_Quit(); */
/* SDLTest_CommonDestroyState(state); */
SDLTest_CommonDestroyState(state);
return 0;
}

View File

@ -31,9 +31,6 @@ static SDL_AudioSpec spec;
static Uint8 *sound = NULL; /* Pointer to wave data */
static Uint32 soundlen = 0; /* Length of wave data */
static int posindex = 0;
static Uint32 positions[64];
static SDLTest_CommonState *state;
/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
@ -48,31 +45,6 @@ quit(int rc)
}
}
static void SDLCALL
fillerup(void *_pos, Uint8 *stream, int len)
{
Uint32 pos = *((Uint32 *)_pos);
Uint8 *waveptr;
int waveleft;
/* Set up the pointers */
waveptr = sound + pos;
waveleft = soundlen - pos;
/* Go! */
while (waveleft <= len) {
SDL_memcpy(stream, waveptr, waveleft);
stream += waveleft;
len -= waveleft;
waveptr = sound;
waveleft = soundlen;
pos = 0;
}
SDL_memcpy(stream, waveptr, len);
pos += len;
*((Uint32 *)_pos) = pos;
}
static int done = 0;
static void poked(int sig)
@ -97,28 +69,35 @@ static void iteration(void)
done = 1;
}
} else if (e.type == SDL_EVENT_AUDIO_DEVICE_ADDED) {
int index = e.adevice.which;
int iscapture = e.adevice.iscapture;
const char *name = SDL_GetAudioDeviceName(index, iscapture);
const SDL_AudioDeviceID which = (SDL_AudioDeviceID ) e.adevice.which;
const SDL_bool iscapture = e.adevice.iscapture ? SDL_TRUE : SDL_FALSE;
char *name = SDL_GetAudioDeviceName(which);
if (name != NULL) {
SDL_Log("New %s audio device at index %u: %s\n", devtypestr(iscapture), (unsigned int)index, name);
SDL_Log("New %s audio device at id %u: %s", devtypestr(iscapture), (unsigned int)which, name);
} else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Got new %s device at index %u, but failed to get the name: %s\n",
devtypestr(iscapture), (unsigned int)index, SDL_GetError());
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Got new %s device, id %u, but failed to get the name: %s",
devtypestr(iscapture), (unsigned int)which, SDL_GetError());
continue;
}
if (!iscapture) {
positions[posindex] = 0;
spec.userdata = &positions[posindex++];
spec.callback = fillerup;
dev = SDL_OpenAudioDevice(name, 0, &spec, NULL, 0);
dev = SDL_OpenAudioDevice(which, &spec);
if (!dev) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open '%s': %s\n", name, SDL_GetError());
} else {
SDL_AudioStream *stream;
SDL_Log("Opened '%s' as %u\n", name, (unsigned int)dev);
SDL_PlayAudioDevice(dev);
stream = SDL_CreateAndBindAudioStream(dev, &spec);
if (!stream) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create/bind an audio stream to %u ('%s'): %s", (unsigned int) dev, name, SDL_GetError());
SDL_CloseAudioDevice(dev);
}
/* !!! FIXME: laziness, this used to loop the audio, but we'll just play it once for now on each connect. */
SDL_PutAudioStreamData(stream, sound, soundlen);
SDL_FlushAudioStream(stream);
/* !!! FIXME: this is leaking the stream for now. We'll wire it up to a dictionary or whatever later. */
}
}
SDL_free(name);
} else if (e.type == SDL_EVENT_AUDIO_DEVICE_REMOVED) {
dev = (SDL_AudioDeviceID)e.adevice.which;
SDL_Log("%s device %u removed.\n", devtypestr(e.adevice.iscapture), (unsigned int)dev);
@ -194,7 +173,7 @@ int main(int argc, char *argv[])
}
/* Load the wave file into memory */
if (SDL_LoadWAV(filename, &spec, &sound, &soundlen) == NULL) {
if (SDL_LoadWAV(filename, &spec, &sound, &soundlen) == -1) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename, SDL_GetError());
quit(1);
}

View File

@ -14,29 +14,30 @@
#include <SDL3/SDL_test.h>
static void
print_devices(int iscapture)
print_devices(SDL_bool iscapture)
{
SDL_AudioSpec spec;
const char *typestr = ((iscapture) ? "capture" : "output");
int n = SDL_GetNumAudioDevices(iscapture);
int n = 0;
SDL_AudioDeviceID *devices = iscapture ? SDL_GetAudioCaptureDevices(&n) : SDL_GetAudioOutputDevices(&n);
SDL_Log("Found %d %s device%s:\n", n, typestr, n != 1 ? "s" : "");
if (n == -1) {
SDL_Log(" Driver can't detect specific %s devices.\n\n", typestr);
if (devices == NULL) {
SDL_Log(" Driver failed to report %s devices: %s\n\n", typestr, SDL_GetError());
} else if (n == 0) {
SDL_Log(" No %s devices found.\n\n", typestr);
} else {
int i;
SDL_Log("Found %d %s device%s:\n", n, typestr, n != 1 ? "s" : "");
for (i = 0; i < n; i++) {
const char *name = SDL_GetAudioDeviceName(i, iscapture);
char *name = SDL_GetAudioDeviceName(devices[i]);
if (name != NULL) {
SDL_Log(" %d: %s\n", i, name);
SDL_free(name);
} else {
SDL_Log(" %d Error: %s\n", i, SDL_GetError());
}
if (SDL_GetAudioDeviceSpec(i, iscapture, &spec) == 0) {
if (SDL_GetAudioDeviceFormat(devices[i], &spec) == 0) {
SDL_Log(" Sample Rate: %d\n", spec.freq);
SDL_Log(" Channels: %d\n", spec.channels);
SDL_Log(" SDL_AudioFormat: %X\n", spec.format);
@ -44,11 +45,11 @@ print_devices(int iscapture)
}
SDL_Log("\n");
}
SDL_free(devices);
}
int main(int argc, char **argv)
{
char *deviceName = NULL;
SDL_AudioSpec spec;
int i;
int n;
@ -88,24 +89,22 @@ int main(int argc, char **argv)
SDL_Log("Using audio driver: %s\n\n", SDL_GetCurrentAudioDriver());
print_devices(0);
print_devices(1);
print_devices(SDL_FALSE);
print_devices(SDL_TRUE);
if (SDL_GetDefaultAudioInfo(&deviceName, &spec, 0) < 0) {
SDL_Log("Error when calling SDL_GetDefaultAudioInfo: %s\n", SDL_GetError());
if (SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec) < 0) {
SDL_Log("Error when calling SDL_GetAudioDeviceFormat(default output): %s\n", SDL_GetError());
} else {
SDL_Log("Default Output Name: %s\n", deviceName != NULL ? deviceName : "unknown");
SDL_free(deviceName);
SDL_Log("Default Output Device:\n");
SDL_Log("Sample Rate: %d\n", spec.freq);
SDL_Log("Channels: %d\n", spec.channels);
SDL_Log("SDL_AudioFormat: %X\n", spec.format);
}
if (SDL_GetDefaultAudioInfo(&deviceName, &spec, 1) < 0) {
SDL_Log("Error when calling SDL_GetDefaultAudioInfo: %s\n", SDL_GetError());
if (SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_CAPTURE, &spec) < 0) {
SDL_Log("Error when calling SDL_GetAudioDeviceFormat(default capture): %s\n", SDL_GetError());
} else {
SDL_Log("Default Capture Name: %s\n", deviceName != NULL ? deviceName : "unknown");
SDL_free(deviceName);
SDL_Log("Default Capture Device:\n");
SDL_Log("Sample Rate: %d\n", spec.freq);
SDL_Log("Channels: %d\n", spec.channels);
SDL_Log("SDL_AudioFormat: %X\n", spec.format);
@ -115,3 +114,4 @@ int main(int argc, char **argv)
SDLTest_CommonDestroyState(state);
return 0;
}

View File

@ -15,13 +15,7 @@
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_test.h>
static void SDLCALL audio_callback(void *userdata, Uint8 * stream, int len)
{
SDL_AudioStream *audiostream = (SDL_AudioStream *) userdata;
SDL_memset(stream, 0, len);
SDL_GetAudioStreamData(audiostream, stream, len);
}
#include "testutils.h"
int main(int argc, char *argv[])
{
@ -36,24 +30,35 @@ int main(int argc, char *argv[])
Uint32 audio_len = 0;
SDL_AudioStream *stream;
SDL_AudioDeviceID device;
const char *fname = "sample.wav";
char *path;
int rc;
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
window = SDL_CreateWindow("Drag the slider: Normal speed", 640, 480, 0);
renderer = SDL_CreateRenderer(window, NULL, 0);
SDL_LoadWAV("sample.wav", &spec, &audio_buf, &audio_len);
stream = SDL_CreateAudioStream(spec.format, spec.channels, spec.freq, spec.format, spec.channels, spec.freq);
path = GetNearbyFilename(fname);
rc = SDL_LoadWAV(path ? path : fname, &spec, &audio_buf, &audio_len);
SDL_free(path);
if (rc < 0) {
SDL_Log("Failed to load '%s': %s", fname, SDL_GetError());
SDL_Quit();
return 1;
}
stream = SDL_CreateAudioStream(&spec, &spec);
SDL_PutAudioStreamData(stream, audio_buf, audio_len);
spec.callback = audio_callback;
spec.userdata = stream;
device = SDL_OpenAudioDevice(NULL, SDL_FALSE, &spec, NULL, 0);
SDL_PlayAudioDevice(device);
device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec);
SDL_BindAudioStream(device, stream);
slider_fill_area.w /= 2;
while (!done) {
SDL_Event e;
int newmultiplier = multiplier;
while (SDL_PollEvent(&e)) {
if (e.type == SDL_EVENT_QUIT) {
done = 1;
@ -74,6 +79,7 @@ int main(int argc, char *argv[])
}
if (multiplier != newmultiplier) {
SDL_AudioSpec newspec;
char title[64];
int newfreq = spec.freq;
@ -94,13 +100,13 @@ int main(int argc, char *argv[])
newfreq = spec.freq + (int) (spec.freq * (multiplier / 100.0f));
}
/* SDL_Log("newfreq=%d multiplier=%d\n", newfreq, multiplier); */
SDL_LockAudioDevice(device);
SDL_SetAudioStreamFormat(stream, spec.format, spec.channels, newfreq, spec.format, spec.channels, spec.freq);
SDL_UnlockAudioDevice(device);
SDL_memcpy(&newspec, &spec, sizeof (spec));
newspec.freq = newfreq;
SDL_SetAudioStreamFormat(stream, &newspec, NULL);
}
/* keep it looping. */
if (SDL_GetAudioStreamAvailable(stream) < (1024 * 100)) {
if (SDL_GetAudioStreamAvailable(stream) < ((int) (audio_len / 2))) {
SDL_PutAudioStreamData(stream, audio_buf, audio_len);
}
@ -116,6 +122,7 @@ int main(int argc, char *argv[])
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_CloseAudioDevice(device);
SDL_DestroyAudioStream(stream);
SDL_free(audio_buf);
SDL_Quit();
return 0;

View File

@ -38,6 +38,7 @@ static void audioTearDown(void *arg)
SDLTest_AssertPass("Cleanup of test files completed");
}
#if 0 /* !!! FIXME: maybe update this? */
/* Global counter for callback invocation */
static int g_audio_testCallbackCounter;
@ -51,6 +52,7 @@ static void SDLCALL audio_testCallback(void *userdata, Uint8 *stream, int len)
g_audio_testCallbackCounter++;
g_audio_testCallbackLength += len;
}
#endif
static SDL_AudioDeviceID g_audio_id = -1;
@ -176,28 +178,22 @@ static int audio_initOpenCloseQuitAudio(void *arg)
desired.freq = 22050;
desired.format = SDL_AUDIO_S16SYS;
desired.channels = 2;
desired.samples = 4096;
desired.callback = audio_testCallback;
desired.userdata = NULL;
case 1:
/* Set custom desired spec */
desired.freq = 48000;
desired.format = SDL_AUDIO_F32SYS;
desired.channels = 2;
desired.samples = 2048;
desired.callback = audio_testCallback;
desired.userdata = NULL;
break;
}
/* Call Open (maybe multiple times) */
for (k = 0; k <= j; k++) {
result = SDL_OpenAudioDevice(NULL, 0, &desired, NULL, 0);
result = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &desired);
if (k == 0) {
g_audio_id = result;
}
SDLTest_AssertPass("Call to SDL_OpenAudioDevice(NULL, 0, desired_spec_%d, NULL, 0), call %d", j, k + 1);
SDLTest_AssertPass("Call to SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, desired_spec_%d), call %d", j, k + 1);
SDLTest_AssertCheck(result > 0, "Verify return value; expected: > 0, got: %d", result);
}
@ -230,11 +226,9 @@ static int audio_initOpenCloseQuitAudio(void *arg)
*/
static int audio_pauseUnpauseAudio(void *arg)
{
int iMax;
int i, j /*, k, l*/;
int result;
int i, iMax, j, k, l;
int totalDelay;
int pause_on;
int originalCounter;
const char *audioDriver;
SDL_AudioSpec desired;
@ -269,9 +263,6 @@ static int audio_pauseUnpauseAudio(void *arg)
desired.freq = 22050;
desired.format = SDL_AUDIO_S16SYS;
desired.channels = 2;
desired.samples = 4096;
desired.callback = audio_testCallback;
desired.userdata = NULL;
break;
case 1:
@ -279,18 +270,16 @@ static int audio_pauseUnpauseAudio(void *arg)
desired.freq = 48000;
desired.format = SDL_AUDIO_F32SYS;
desired.channels = 2;
desired.samples = 2048;
desired.callback = audio_testCallback;
desired.userdata = NULL;
break;
}
/* Call Open */
g_audio_id = SDL_OpenAudioDevice(NULL, 0, &desired, NULL, 0);
g_audio_id = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &desired);
result = g_audio_id;
SDLTest_AssertPass("Call to SDL_OpenAudioDevice(NULL, 0, desired_spec_%d, NULL, 0)", j);
SDLTest_AssertPass("Call to SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, desired_spec_%d)", j);
SDLTest_AssertCheck(result > 0, "Verify return value; expected > 0 got: %d", result);
#if 0 /* !!! FIXME: maybe update this? */
/* Start and stop audio multiple times */
for (l = 0; l < 3; l++) {
SDLTest_Log("Pause/Unpause iteration: %d", l + 1);
@ -300,14 +289,13 @@ static int audio_pauseUnpauseAudio(void *arg)
g_audio_testCallbackLength = 0;
/* Un-pause audio to start playing (maybe multiple times) */
pause_on = 0;
for (k = 0; k <= j; k++) {
SDL_PlayAudioDevice(g_audio_id);
SDLTest_AssertPass("Call to SDL_PlayAudioDevice(g_audio_id), call %d", k + 1);
}
/* Wait for callback */
totalDelay = 0;
int totalDelay = 0;
do {
SDL_Delay(10);
totalDelay += 10;
@ -317,7 +305,7 @@ static int audio_pauseUnpauseAudio(void *arg)
/* Pause audio to stop playing (maybe multiple times) */
for (k = 0; k <= j; k++) {
pause_on = (k == 0) ? 1 : SDLTest_RandomIntegerInRange(99, 9999);
const int pause_on = (k == 0) ? 1 : SDLTest_RandomIntegerInRange(99, 9999);
if (pause_on) {
SDL_PauseAudioDevice(g_audio_id);
SDLTest_AssertPass("Call to SDL_PauseAudioDevice(g_audio_id), call %d", k + 1);
@ -328,10 +316,11 @@ static int audio_pauseUnpauseAudio(void *arg)
}
/* Ensure callback is not called again */
originalCounter = g_audio_testCallbackCounter;
const int originalCounter = g_audio_testCallbackCounter;
SDL_Delay(totalDelay + 10);
SDLTest_AssertCheck(originalCounter == g_audio_testCallbackCounter, "Verify callback counter; expected: %d, got: %d", originalCounter, g_audio_testCallbackCounter);
}
#endif
/* Call Close */
SDL_CloseAudioDevice(g_audio_id);
@ -358,51 +347,34 @@ static int audio_pauseUnpauseAudio(void *arg)
*/
static int audio_enumerateAndNameAudioDevices(void *arg)
{
int t, tt;
int i, n, nn;
const char *name, *nameAgain;
int t;
int i, n;
char *name;
SDL_AudioDeviceID *devices = NULL;
/* Iterate over types: t=0 output device, t=1 input/capture device */
for (t = 0; t < 2; t++) {
/* Get number of devices. */
n = SDL_GetNumAudioDevices(t);
SDLTest_AssertPass("Call to SDL_GetNumAudioDevices(%i)", t);
devices = (t) ? SDL_GetAudioCaptureDevices(&n) : SDL_GetAudioOutputDevices(&n);
SDLTest_AssertPass("Call to SDL_GetAudio%sDevices(%i)", (t) ? "Capture" : "Output", t);
SDLTest_Log("Number of %s devices < 0, reported as %i", (t) ? "capture" : "output", n);
SDLTest_AssertCheck(n >= 0, "Validate result is >= 0, got: %i", n);
/* Variation of non-zero type */
if (t == 1) {
tt = t + SDLTest_RandomIntegerInRange(1, 10);
nn = SDL_GetNumAudioDevices(tt);
SDLTest_AssertCheck(n == nn, "Verify result from SDL_GetNumAudioDevices(%i), expected same number of audio devices %i, got %i", tt, n, nn);
nn = SDL_GetNumAudioDevices(-tt);
SDLTest_AssertCheck(n == nn, "Verify result from SDL_GetNumAudioDevices(%i), expected same number of audio devices %i, got %i", -tt, n, nn);
}
/* List devices. */
if (n > 0) {
SDLTest_AssertCheck(devices != NULL, "Validate devices is not NULL if n > 0");
for (i = 0; i < n; i++) {
name = SDL_GetAudioDeviceName(i, t);
SDLTest_AssertPass("Call to SDL_GetAudioDeviceName(%i, %i)", i, t);
SDLTest_AssertCheck(name != NULL, "Verify result from SDL_GetAudioDeviceName(%i, %i) is not NULL", i, t);
name = SDL_GetAudioDeviceName(devices[i]);
SDLTest_AssertPass("Call to SDL_GetAudioDeviceName(%i)", i);
SDLTest_AssertCheck(name != NULL, "Verify result from SDL_GetAudioDeviceName(%i) is not NULL", i);
if (name != NULL) {
SDLTest_AssertCheck(name[0] != '\0', "verify result from SDL_GetAudioDeviceName(%i, %i) is not empty, got: '%s'", i, t, name);
if (t == 1) {
/* Also try non-zero type */
tt = t + SDLTest_RandomIntegerInRange(1, 10);
nameAgain = SDL_GetAudioDeviceName(i, tt);
SDLTest_AssertCheck(nameAgain != NULL, "Verify result from SDL_GetAudioDeviceName(%i, %i) is not NULL", i, tt);
if (nameAgain != NULL) {
SDLTest_AssertCheck(nameAgain[0] != '\0', "Verify result from SDL_GetAudioDeviceName(%i, %i) is not empty, got: '%s'", i, tt, nameAgain);
SDLTest_AssertCheck(SDL_strcmp(name, nameAgain) == 0,
"Verify SDL_GetAudioDeviceName(%i, %i) and SDL_GetAudioDeviceName(%i %i) return the same string",
i, t, i, tt);
}
}
SDLTest_AssertCheck(name[0] != '\0', "verify result from SDL_GetAudioDeviceName(%i) is not empty, got: '%s'", i, name);
SDL_free(name);
}
}
}
SDL_free(devices);
}
return TEST_COMPLETED;
@ -416,42 +388,7 @@ static int audio_enumerateAndNameAudioDevices(void *arg)
*/
static int audio_enumerateAndNameAudioDevicesNegativeTests(void *arg)
{
int t;
int i, j, no, nc;
const char *name;
/* Get number of devices. */
no = SDL_GetNumAudioDevices(0);
SDLTest_AssertPass("Call to SDL_GetNumAudioDevices(0)");
nc = SDL_GetNumAudioDevices(1);
SDLTest_AssertPass("Call to SDL_GetNumAudioDevices(1)");
/* Invalid device index when getting name */
for (t = 0; t < 2; t++) {
/* Negative device index */
i = SDLTest_RandomIntegerInRange(-10, -1);
name = SDL_GetAudioDeviceName(i, t);
SDLTest_AssertPass("Call to SDL_GetAudioDeviceName(%i, %i)", i, t);
SDLTest_AssertCheck(name == NULL, "Check SDL_GetAudioDeviceName(%i, %i) result NULL, expected NULL, got: %s", i, t, (name == NULL) ? "NULL" : name);
/* Device index past range */
for (j = 0; j < 3; j++) {
i = (t) ? nc + j : no + j;
name = SDL_GetAudioDeviceName(i, t);
SDLTest_AssertPass("Call to SDL_GetAudioDeviceName(%i, %i)", i, t);
SDLTest_AssertCheck(name == NULL, "Check SDL_GetAudioDeviceName(%i, %i) result, expected: NULL, got: %s", i, t, (name == NULL) ? "NULL" : name);
}
/* Capture index past capture range but within output range */
if ((no > 0) && (no > nc) && (t == 1)) {
i = no - 1;
name = SDL_GetAudioDeviceName(i, t);
SDLTest_AssertPass("Call to SDL_GetAudioDeviceName(%i, %i)", i, t);
SDLTest_AssertCheck(name == NULL, "Check SDL_GetAudioDeviceName(%i, %i) result, expected: NULL, got: %s", i, t, (name == NULL) ? "NULL" : name);
}
}
return TEST_COMPLETED;
return TEST_COMPLETED; /* nothing in here atm since these interfaces changed in SDL3. */
}
/**
@ -532,8 +469,7 @@ static int audio_buildAudioStream(void *arg)
spec1.format = SDL_AUDIO_S16LSB;
spec1.channels = 2;
spec1.freq = 22050;
stream = SDL_CreateAudioStream(spec1.format, spec1.channels, spec1.freq,
spec1.format, spec1.channels, spec1.freq);
stream = SDL_CreateAudioStream(&spec1, &spec1);
SDLTest_AssertPass("Call to SDL_CreateAudioStream(spec1 ==> spec1)");
SDLTest_AssertCheck(stream != NULL, "Verify stream value; expected: != NULL, got: %p", (void *)stream);
SDL_DestroyAudioStream(stream);
@ -545,8 +481,7 @@ static int audio_buildAudioStream(void *arg)
spec2.format = SDL_AUDIO_S16LSB;
spec2.channels = 2;
spec2.freq = 44100;
stream = SDL_CreateAudioStream(spec1.format, spec1.channels, spec1.freq,
spec2.format, spec2.channels, spec2.freq);
stream = SDL_CreateAudioStream(&spec1, &spec2);
SDLTest_AssertPass("Call to SDL_CreateAudioStream(spec1 ==> spec2)");
SDLTest_AssertCheck(stream != NULL, "Verify stream value; expected: != NULL, got: %p", (void *)stream);
SDL_DestroyAudioStream(stream);
@ -564,8 +499,7 @@ static int audio_buildAudioStream(void *arg)
spec2.format = g_audioFormats[ii];
spec2.channels = g_audioChannels[jj];
spec2.freq = g_audioFrequencies[kk];
stream = SDL_CreateAudioStream(spec1.format, spec1.channels, spec1.freq,
spec2.format, spec2.channels, spec2.freq);
stream = SDL_CreateAudioStream(&spec1, &spec2);
SDLTest_AssertPass("Call to SDL_CreateAudioStream(format[%i]=%s(%i),channels[%i]=%i,freq[%i]=%i ==> format[%i]=%s(%i),channels[%i]=%i,freq[%i]=%i)",
i, g_audioFormatsVerbose[i], spec1.format, j, spec1.channels, k, spec1.freq, ii, g_audioFormatsVerbose[ii], spec2.format, jj, spec2.channels, kk, spec2.freq);
@ -646,8 +580,7 @@ static int audio_buildAudioStreamNegative(void *arg)
spec2.freq = 0;
}
SDLTest_Log("%s", message);
stream = SDL_CreateAudioStream(spec1.format, spec1.channels, spec1.freq,
spec2.format, spec2.channels, spec2.freq);
stream = SDL_CreateAudioStream(&spec1, &spec2);
SDLTest_AssertPass("Call to SDL_CreateAudioStream(spec1 ==> spec2)");
SDLTest_AssertCheck(stream == NULL, "Verify stream value; expected: NULL, got: %p", (void *)stream);
error = SDL_GetError();
@ -669,16 +602,7 @@ static int audio_buildAudioStreamNegative(void *arg)
*/
static int audio_getAudioStatus(void *arg)
{
SDL_AudioStatus result;
/* Check current audio status */
result = SDL_GetAudioDeviceStatus(g_audio_id);
SDLTest_AssertPass("Call to SDL_GetAudioDeviceStatus(g_audio_id)");
SDLTest_AssertCheck(result == SDL_AUDIO_STOPPED || result == SDL_AUDIO_PLAYING || result == SDL_AUDIO_PAUSED,
"Verify returned value; expected: STOPPED (%i) | PLAYING (%i) | PAUSED (%i), got: %i",
SDL_AUDIO_STOPPED, SDL_AUDIO_PLAYING, SDL_AUDIO_PAUSED, result);
return TEST_COMPLETED;
return TEST_COMPLETED; /* no longer a thing in SDL3. */
}
/**
@ -688,57 +612,7 @@ static int audio_getAudioStatus(void *arg)
*/
static int audio_openCloseAndGetAudioStatus(void *arg)
{
SDL_AudioStatus result;
int i;
int count;
const char *device;
SDL_AudioDeviceID id;
SDL_AudioSpec desired, obtained;
/* Get number of devices. */
count = SDL_GetNumAudioDevices(0);
SDLTest_AssertPass("Call to SDL_GetNumAudioDevices(0)");
if (count > 0) {
for (i = 0; i < count; i++) {
/* Get device name */
device = SDL_GetAudioDeviceName(i, 0);
SDLTest_AssertPass("SDL_GetAudioDeviceName(%i,0)", i);
SDLTest_AssertCheck(device != NULL, "Validate device name is not NULL; got: %s", (device != NULL) ? device : "NULL");
if (device == NULL) {
return TEST_ABORTED;
}
/* Set standard desired spec */
desired.freq = 22050;
desired.format = SDL_AUDIO_S16SYS;
desired.channels = 2;
desired.samples = 4096;
desired.callback = audio_testCallback;
desired.userdata = NULL;
/* Open device */
id = SDL_OpenAudioDevice(device, 0, &desired, &obtained, SDL_AUDIO_ALLOW_ANY_CHANGE);
SDLTest_AssertPass("SDL_OpenAudioDevice('%s',...)", device);
SDLTest_AssertCheck(id > 0, "Validate device ID; expected: > 0, got: %" SDL_PRIu32, id);
if (id > 0) {
/* Check device audio status */
result = SDL_GetAudioDeviceStatus(id);
SDLTest_AssertPass("Call to SDL_GetAudioDeviceStatus()");
SDLTest_AssertCheck(result == SDL_AUDIO_STOPPED || result == SDL_AUDIO_PLAYING || result == SDL_AUDIO_PAUSED,
"Verify returned value; expected: STOPPED (%i) | PLAYING (%i) | PAUSED (%i), got: %i",
SDL_AUDIO_STOPPED, SDL_AUDIO_PLAYING, SDL_AUDIO_PAUSED, result);
/* Close device again */
SDL_CloseAudioDevice(id);
SDLTest_AssertPass("Call to SDL_CloseAudioDevice()");
}
}
} else {
SDLTest_Log("No devices to test with");
}
return TEST_COMPLETED;
return TEST_COMPLETED; /* not a thing in SDL3. */
}
/**
@ -749,60 +623,7 @@ static int audio_openCloseAndGetAudioStatus(void *arg)
*/
static int audio_lockUnlockOpenAudioDevice(void *arg)
{
int i;
int count;
const char *device;
SDL_AudioDeviceID id;
SDL_AudioSpec desired, obtained;
/* Get number of devices. */
count = SDL_GetNumAudioDevices(0);
SDLTest_AssertPass("Call to SDL_GetNumAudioDevices(0)");
if (count > 0) {
for (i = 0; i < count; i++) {
/* Get device name */
device = SDL_GetAudioDeviceName(i, 0);
SDLTest_AssertPass("SDL_GetAudioDeviceName(%i,0)", i);
SDLTest_AssertCheck(device != NULL, "Validate device name is not NULL; got: %s", (device != NULL) ? device : "NULL");
if (device == NULL) {
return TEST_ABORTED;
}
/* Set standard desired spec */
desired.freq = 22050;
desired.format = SDL_AUDIO_S16SYS;
desired.channels = 2;
desired.samples = 4096;
desired.callback = audio_testCallback;
desired.userdata = NULL;
/* Open device */
id = SDL_OpenAudioDevice(device, 0, &desired, &obtained, SDL_AUDIO_ALLOW_ANY_CHANGE);
SDLTest_AssertPass("SDL_OpenAudioDevice('%s',...)", device);
SDLTest_AssertCheck(id > 1, "Validate device ID; expected: > 0, got: %" SDL_PRIu32, id);
if (id > 0) {
/* Lock to protect callback */
SDL_LockAudioDevice(id);
SDLTest_AssertPass("SDL_LockAudioDevice(%" SDL_PRIu32 ")", id);
/* Simulate callback processing */
SDL_Delay(10);
SDLTest_Log("Simulate callback processing - delay");
/* Unlock again */
SDL_UnlockAudioDevice(id);
SDLTest_AssertPass("SDL_UnlockAudioDevice(%" SDL_PRIu32 ")", id);
/* Close device again */
SDL_CloseAudioDevice(id);
SDLTest_AssertPass("Call to SDL_CloseAudioDevice()");
}
}
} else {
SDLTest_Log("No devices to test with");
}
return TEST_COMPLETED;
return TEST_COMPLETED; /* not a thing in SDL3 */
}
/**
@ -862,8 +683,7 @@ static int audio_convertAudio(void *arg)
spec2.channels = g_audioChannels[jj];
spec2.freq = g_audioFrequencies[kk];
stream = SDL_CreateAudioStream(spec1.format, spec1.channels, spec1.freq,
spec2.format, spec2.channels, spec2.freq);
stream = SDL_CreateAudioStream(&spec1, &spec2);
SDLTest_AssertPass("Call to SDL_CreateAudioStream(format[%i]=%s(%i),channels[%i]=%i,freq[%i]=%i ==> format[%i]=%s(%i),channels[%i]=%i,freq[%i]=%i)",
i, g_audioFormatsVerbose[i], spec1.format, j, spec1.channels, k, spec1.freq, ii, g_audioFormatsVerbose[ii], spec2.format, jj, spec2.channels, kk, spec2.freq);
SDLTest_AssertCheck(stream != NULL, "Verify stream value; expected: != NULL, got: %p", (void *)stream);
@ -936,59 +756,7 @@ static int audio_convertAudio(void *arg)
*/
static int audio_openCloseAudioDeviceConnected(void *arg)
{
int result = -1;
int i;
int count;
const char *device;
SDL_AudioDeviceID id;
SDL_AudioSpec desired, obtained;
/* Get number of devices. */
count = SDL_GetNumAudioDevices(0);
SDLTest_AssertPass("Call to SDL_GetNumAudioDevices(0)");
if (count > 0) {
for (i = 0; i < count; i++) {
/* Get device name */
device = SDL_GetAudioDeviceName(i, 0);
SDLTest_AssertPass("SDL_GetAudioDeviceName(%i,0)", i);
SDLTest_AssertCheck(device != NULL, "Validate device name is not NULL; got: %s", (device != NULL) ? device : "NULL");
if (device == NULL) {
return TEST_ABORTED;
}
/* Set standard desired spec */
desired.freq = 22050;
desired.format = SDL_AUDIO_S16SYS;
desired.channels = 2;
desired.samples = 4096;
desired.callback = audio_testCallback;
desired.userdata = NULL;
/* Open device */
id = SDL_OpenAudioDevice(device, 0, &desired, &obtained, SDL_AUDIO_ALLOW_ANY_CHANGE);
SDLTest_AssertPass("SDL_OpenAudioDevice('%s',...)", device);
SDLTest_AssertCheck(id > 0, "Validate device ID; expected: > 0, got: %" SDL_PRIu32, id);
if (id > 0) {
/* TODO: enable test code when function is available in SDL3 */
#ifdef AUDIODEVICECONNECTED_DEFINED
/* Get connected status */
result = SDL_AudioDeviceConnected(id);
SDLTest_AssertPass("Call to SDL_AudioDeviceConnected()");
#endif
SDLTest_AssertCheck(result == 1, "Verify returned value; expected: 1; got: %i", result);
/* Close device again */
SDL_CloseAudioDevice(id);
SDLTest_AssertPass("Call to SDL_CloseAudioDevice()");
}
}
} else {
SDLTest_Log("No devices to test with");
}
return TEST_COMPLETED;
return TEST_COMPLETED; /* not a thing in SDL3. */
}
static double sine_wave_sample(const Sint64 idx, const Sint64 rate, const Sint64 freq, const double phase)
@ -1040,6 +808,7 @@ static int audio_resampleLoss(void *arg)
const int len_in = frames_in * (int)sizeof(float);
const int len_target = frames_target * (int)sizeof(float);
SDL_AudioSpec tmpspec1, tmpspec2;
Uint64 tick_beg = 0;
Uint64 tick_end = 0;
int i = 0;
@ -1056,7 +825,13 @@ static int audio_resampleLoss(void *arg)
SDLTest_AssertPass("Test resampling of %i s %i Hz %f phase sine wave from sampling rate of %i Hz to %i Hz",
spec->time, spec->freq, spec->phase, spec->rate_in, spec->rate_out);
stream = SDL_CreateAudioStream(SDL_AUDIO_F32, 1, spec->rate_in, SDL_AUDIO_F32, 1, spec->rate_out);
tmpspec1.format = SDL_AUDIO_F32;
tmpspec1.channels = 1;
tmpspec1.freq = spec->rate_in;
tmpspec2.format = SDL_AUDIO_F32;
tmpspec2.channels = 1;
tmpspec2.freq = spec->rate_out;
stream = SDL_CreateAudioStream(&tmpspec1, &tmpspec2);
SDLTest_AssertPass("Call to SDL_CreateAudioStream(SDL_AUDIO_F32, 1, %i, SDL_AUDIO_F32, 1, %i)", spec->rate_in, spec->rate_out);
SDLTest_AssertCheck(stream != NULL, "Expected SDL_CreateAudioStream to succeed.");
if (stream == NULL) {

View File

@ -27,85 +27,57 @@ static SDL_AudioSpec spec;
static Uint8 *sound = NULL; /* Pointer to wave data */
static Uint32 soundlen = 0; /* Length of wave data */
typedef struct
{
SDL_AudioDeviceID dev;
int soundpos;
SDL_AtomicInt done;
} callback_data;
/* these have to be in globals so the Emscripten port can see them in the mainloop. :/ */
static SDL_AudioDeviceID device = 0;
static SDL_AudioStream *stream = NULL;
static callback_data cbd[64];
static void SDLCALL
play_through_once(void *arg, Uint8 *stream, int len)
{
callback_data *cbdata = (callback_data *)arg;
Uint8 *waveptr = sound + cbdata->soundpos;
int waveleft = soundlen - cbdata->soundpos;
int cpy = len;
if (cpy > waveleft) {
cpy = waveleft;
}
SDL_memcpy(stream, waveptr, cpy);
len -= cpy;
cbdata->soundpos += cpy;
if (len > 0) {
stream += cpy;
SDL_memset(stream, spec.silence, len);
SDL_AtomicSet(&cbdata->done, 1);
}
}
#ifdef __EMSCRIPTEN__
static void loop(void)
{
if (SDL_AtomicGet(&cbd[0].done)) {
emscripten_cancel_main_loop();
SDL_PauseAudioDevice(cbd[0].dev);
SDL_CloseAudioDevice(cbd[0].dev);
if (SDL_GetAudioStreamAvailable(stream) == 0) {
SDL_Log("done.");
SDL_CloseAudioDevice(device);
SDL_DestroyAudioStream(stream);
SDL_free(sound);
SDL_Quit();
emscripten_cancel_main_loop();
}
}
#endif
static void
test_multi_audio(int devcount)
test_multi_audio(SDL_AudioDeviceID *devices, int devcount)
{
int keep_going = 1;
SDL_AudioStream **streams = NULL;
int i;
#ifdef __ANDROID__
#ifdef __ANDROID__ /* !!! FIXME: maybe always create a window, in the SDLTest layer, so these #ifdefs don't have to be here? */
SDL_Event event;
/* Create a Window to get fully initialized event processing for testing pause on Android. */
SDL_CreateWindow("testmultiaudio", 320, 240, 0);
#endif
if (devcount > 64) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Too many devices (%d), clamping to 64...\n",
devcount);
devcount = 64;
}
spec.callback = play_through_once;
for (i = 0; i < devcount; i++) {
const char *devname = SDL_GetAudioDeviceName(i, 0);
SDL_Log("playing on device #%d: ('%s')...", i, devname);
char *devname = SDL_GetAudioDeviceName(devices[i]);
SDL_memset(&cbd[0], '\0', sizeof(callback_data));
spec.userdata = &cbd[0];
cbd[0].dev = SDL_OpenAudioDevice(devname, 0, &spec, NULL, 0);
if (cbd[0].dev == 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Open device failed: %s\n", SDL_GetError());
SDL_Log("Playing on device #%d of %d: id=%u, name='%s'...", i, devcount, (unsigned int) devices[i], devname);
device = SDL_OpenAudioDevice(devices[i], &spec);
if (device == 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Open device failed: %s", SDL_GetError());
} else if ((stream = SDL_CreateAndBindAudioStream(device, &spec)) == NULL) { /* we can reuse these, but we'll just make one each time for now. */
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Audio stream creation failed: %s", SDL_GetError());
SDL_CloseAudioDevice(device);
} else {
SDL_PlayAudioDevice(cbd[0].dev);
SDL_PutAudioStreamData(stream, sound, soundlen);
SDL_FlushAudioStream(stream);
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop(loop, 0, 1);
#else
while (!SDL_AtomicGet(&cbd[0].done)) {
while (SDL_GetAudioStreamAvailable(stream) > 0) {
#ifdef __ANDROID__
/* Empty queue, some application events would prevent pause. */
while (SDL_PollEvent(&event)) {
@ -113,61 +85,77 @@ test_multi_audio(int devcount)
#endif
SDL_Delay(100);
}
SDL_PauseAudioDevice(cbd[0].dev);
#endif
SDL_Log("done.\n");
SDL_CloseAudioDevice(cbd[0].dev);
SDL_Log("done.");
SDL_CloseAudioDevice(device);
SDL_DestroyAudioStream(stream);
}
SDL_free(devname);
stream = NULL;
}
SDL_memset(cbd, '\0', sizeof(cbd));
SDL_Log("playing on all devices...\n");
for (i = 0; i < devcount; i++) {
const char *devname = SDL_GetAudioDeviceName(i, 0);
spec.userdata = &cbd[i];
cbd[i].dev = SDL_OpenAudioDevice(devname, 0, &spec, NULL, 0);
if (cbd[i].dev == 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Open device %d failed: %s\n", i, SDL_GetError());
}
}
for (i = 0; i < devcount; i++) {
if (cbd[i].dev) {
SDL_PlayAudioDevice(cbd[i].dev);
}
}
while (keep_going) {
keep_going = 0;
/* note that Emscripten currently doesn't run this part (but maybe only has a single audio device anyhow?) */
SDL_Log("Playing on all devices...\n");
streams = (SDL_AudioStream **) SDL_calloc(devcount, sizeof (SDL_AudioStream *));
if (!streams) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!");
} else {
for (i = 0; i < devcount; i++) {
if ((cbd[i].dev) && (!SDL_AtomicGet(&cbd[i].done))) {
keep_going = 1;
char *devname = SDL_GetAudioDeviceName(devices[i]);
device = SDL_OpenAudioDevice(devices[i], &spec);
if (device == 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Open device %d of %d (id=%u, name='%s') failed: %s\n", i, devcount, (unsigned int) devices[i], devname, SDL_GetError());
}
SDL_free(devname);
devices[i] = device; /* just replace the physical device ID with the newly-opened logical device ID. */
if (device) {
SDL_PauseAudioDevice(device); /* hold while we set up all the streams. */
streams[i] = SDL_CreateAndBindAudioStream(device, &spec);
if (streams[i] == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Audio stream creation failed for device %d of %d: %s", i, devcount, SDL_GetError());
} else {
SDL_PutAudioStreamData(streams[i], sound, soundlen);
SDL_FlushAudioStream(streams[i]);
}
}
}
#ifdef __ANDROID__
/* Empty queue, some application events would prevent pause. */
while (SDL_PollEvent(&event)) {
/* try to start all the devices about the same time. SDL does not guarantee sync across physical devices. */
for (i = 0; i < devcount; i++) {
if (devices[i]) {
SDL_UnpauseAudioDevice(devices[i]);
}
}
while (keep_going) {
keep_going = 0;
for (i = 0; i < devcount; i++) {
if (streams[i] && (SDL_GetAudioStreamAvailable(streams[i]) > 0)) {
keep_going = 1;
}
}
#ifdef __ANDROID__
/* Empty queue, some application events would prevent pause. */
while (SDL_PollEvent(&event)) {}
#endif
SDL_Delay(100);
}
#ifndef __EMSCRIPTEN__
for (i = 0; i < devcount; i++) {
if (cbd[i].dev) {
SDL_PauseAudioDevice(cbd[i].dev);
SDL_CloseAudioDevice(cbd[i].dev);
SDL_Delay(100);
}
for (i = 0; i < devcount; i++) {
SDL_CloseAudioDevice(devices[i]);
SDL_DestroyAudioStream(streams[i]);
}
SDL_free(streams);
}
SDL_Log("All done!\n");
#endif
}
int main(int argc, char **argv)
{
SDL_AudioDeviceID *devices = NULL;
int devcount = 0;
int i;
char *filename = NULL;
@ -212,20 +200,21 @@ int main(int argc, char **argv)
filename = GetResourceFilename(filename, "sample.wav");
devcount = SDL_GetNumAudioDevices(0);
if (devcount < 1) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Don't see any specific audio devices!\n");
devices = SDL_GetAudioOutputDevices(&devcount);
if (devices == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Don't see any specific audio devices!");
} else {
/* Load the wave file into memory */
if (SDL_LoadWAV(filename, &spec, &sound, &soundlen) == NULL) {
if (SDL_LoadWAV(filename, &spec, &sound, &soundlen) == -1) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename,
SDL_GetError());
} else {
test_multi_audio(devcount);
test_multi_audio(devices, devcount);
SDL_free(sound);
}
}
SDL_free(devices);
SDL_free(filename);
SDL_Quit();
@ -233,3 +222,4 @@ int main(int argc, char **argv)
return 0;
}

View File

@ -22,12 +22,11 @@ static void log_usage(char *progname, SDLTest_CommonState *state) {
int main(int argc, char **argv)
{
SDL_AudioSpec spec;
SDL_AudioSpec cvtspec;
SDL_AudioStream *stream = NULL;
Uint8 *dst_buf = NULL;
Uint32 len = 0;
Uint8 *data = NULL;
int cvtfreq = 0;
int cvtchans = 0;
int bitsize = 0;
int blockalign = 0;
int avgbytes = 0;
@ -49,6 +48,8 @@ int main(int argc, char **argv)
/* Enable standard application logging */
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
SDL_zero(cvtspec);
/* Parse commandline */
for (i = 1; i < argc;) {
int consumed;
@ -65,14 +66,14 @@ int main(int argc, char **argv)
consumed = 1;
} else if (argpos == 2) {
char *endp;
cvtfreq = (int)SDL_strtoul(argv[i], &endp, 0);
cvtspec.freq = (int)SDL_strtoul(argv[i], &endp, 0);
if (endp != argv[i] && *endp == '\0') {
argpos++;
consumed = 1;
}
} else if (argpos == 3) {
char *endp;
cvtchans = (int)SDL_strtoul(argv[i], &endp, 0);
cvtspec.channels = (int)SDL_strtoul(argv[i], &endp, 0);
if (endp != argv[i] && *endp == '\0') {
argpos++;
consumed = 1;
@ -100,14 +101,14 @@ int main(int argc, char **argv)
goto end;
}
if (SDL_LoadWAV(file_in, &spec, &data, &len) == NULL) {
if (SDL_LoadWAV(file_in, &spec, &data, &len) == -1) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "failed to load %s: %s\n", file_in, SDL_GetError());
ret = 3;
goto end;
}
if (SDL_ConvertAudioSamples(spec.format, spec.channels, spec.freq, data, len,
spec.format, cvtchans, cvtfreq, &dst_buf, &dst_len) < 0) {
cvtspec.format = spec.format;
if (SDL_ConvertAudioSamples(&spec, data, len, &cvtspec, &dst_buf, &dst_len) < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "failed to convert samples: %s\n", SDL_GetError());
ret = 4;
goto end;
@ -122,8 +123,8 @@ int main(int argc, char **argv)
}
bitsize = SDL_AUDIO_BITSIZE(spec.format);
blockalign = (bitsize / 8) * cvtchans;
avgbytes = cvtfreq * blockalign;
blockalign = (bitsize / 8) * cvtspec.channels;
avgbytes = cvtspec.freq * blockalign;
SDL_WriteLE32(io, 0x46464952); /* RIFF */
SDL_WriteLE32(io, dst_len + 36);
@ -131,8 +132,8 @@ int main(int argc, char **argv)
SDL_WriteLE32(io, 0x20746D66); /* fmt */
SDL_WriteLE32(io, 16); /* chunk size */
SDL_WriteLE16(io, SDL_AUDIO_ISFLOAT(spec.format) ? 3 : 1); /* uncompressed */
SDL_WriteLE16(io, cvtchans); /* channels */
SDL_WriteLE32(io, cvtfreq); /* sample rate */
SDL_WriteLE16(io, cvtspec.channels); /* channels */
SDL_WriteLE32(io, cvtspec.freq); /* sample rate */
SDL_WriteLE32(io, avgbytes); /* average bytes per second */
SDL_WriteLE16(io, blockalign); /* block align */
SDL_WriteLE16(io, bitsize); /* significant bits per sample */

View File

@ -96,20 +96,23 @@ static SDL_bool is_lfe_channel(int channel_index, int channel_count)
return (channel_count == 3 && channel_index == 2) || (channel_count >= 6 && channel_index == 3);
}
static void SDLCALL fill_buffer(void *unused, Uint8 *stream, int len)
static void SDLCALL fill_buffer(SDL_AudioStream *stream, int len, void *unused)
{
Sint16 *buffer = (Sint16 *)stream;
int samples = len / sizeof(Sint16);
const int samples = len / sizeof(Sint16);
Sint16 *buffer = NULL;
static int total_samples = 0;
int i;
SDL_memset(stream, 0, len);
/* This can happen for a short time when switching devices */
if (active_channel == total_channels) {
return;
}
buffer = (Sint16 *) SDL_calloc(samples, sizeof(Sint16));
if (!buffer) {
return; /* oh well. */
}
/* Play a sine wave on the active channel only */
for (i = active_channel; i < samples; i += total_channels) {
float time = (float)total_samples++ / SAMPLE_RATE_HZ;
@ -134,12 +137,18 @@ static void SDLCALL fill_buffer(void *unused, Uint8 *stream, int len)
break;
}
}
SDL_PutAudioStreamData(stream, buffer, samples * sizeof (Sint16));
SDL_free(buffer);
}
int main(int argc, char *argv[])
{
int i;
SDL_AudioDeviceID *devices = NULL;
SDLTest_CommonState *state;
int devcount = 0;
int i;
/* Initialize test framework */
state = SDLTest_CommonCreateState(argv, 0);
@ -168,38 +177,58 @@ int main(int argc, char *argv[])
SDL_Log("Using audio driver: %s\n", SDL_GetCurrentAudioDriver());
for (i = 0; i < SDL_GetNumAudioDevices(0); i++) {
const char *devname = SDL_GetAudioDeviceName(i, 0);
devices = SDL_GetAudioOutputDevices(&devcount);
if (!devices) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GetAudioOutputDevices() failed: %s\n", SDL_GetError());
devcount = 0;
}
SDL_Log("Available audio devices:");
for (i = 0; i < devcount; i++) {
SDL_Log("%s", SDL_GetAudioDeviceName(devices[i]));
}
for (i = 0; i < devcount; i++) {
SDL_AudioStream *stream = NULL;
char *devname = SDL_GetAudioDeviceName(devices[i]);
int j;
SDL_AudioSpec spec;
SDL_AudioDeviceID dev;
if (SDL_GetAudioDeviceSpec(i, 0, &spec) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GetAudioSpec() failed: %s\n", SDL_GetError());
SDL_Log("Testing audio device: %s\n", devname);
SDL_free(devname);
if (SDL_GetAudioDeviceFormat(devices[i], &spec) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GetAudioDeviceFormat() failed: %s\n", SDL_GetError());
continue;
}
SDL_Log(" (%d channels)\n", spec.channels);
spec.freq = SAMPLE_RATE_HZ;
spec.format = SDL_AUDIO_S16SYS;
spec.samples = 4096;
spec.callback = fill_buffer;
dev = SDL_OpenAudioDevice(devname, 0, &spec, NULL, 0);
dev = SDL_OpenAudioDevice(devices[i], &spec);
if (dev == 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_OpenAudioDevice() failed: %s\n", SDL_GetError());
continue;
}
SDL_Log("Testing audio device: %s (%d channels)\n", devname, spec.channels);
stream = SDL_CreateAndBindAudioStream(dev, &spec);
if (stream == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_CreateAndBindAudioStream() failed: %s\n", SDL_GetError());
SDL_CloseAudioDevice(dev);
continue;
}
/* These are used by the fill_buffer callback */
total_channels = spec.channels;
active_channel = 0;
SDL_PlayAudioDevice(dev);
SDL_SetAudioStreamGetCallback(stream, fill_buffer, NULL);
for (j = 0; j < total_channels; j++) {
int sine_freq = is_lfe_channel(j, total_channels) ? LFE_SINE_FREQ_HZ : SINE_FREQ_HZ;
const int sine_freq = is_lfe_channel(j, total_channels) ? LFE_SINE_FREQ_HZ : SINE_FREQ_HZ;
SDL_Log("Playing %d Hz test tone on channel: %s\n", sine_freq, get_channel_name(j, total_channels));
@ -212,8 +241,11 @@ int main(int argc, char *argv[])
}
SDL_CloseAudioDevice(dev);
SDL_DestroyAudioStream(stream);
}
SDL_free(devices);
SDL_Quit();
return 0;
}

BIN
test/trashcan.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB