initial commit-moved from vulkan_guide
This commit is contained in:
651
third_party/SDL/src/SDL.c
vendored
Normal file
651
third_party/SDL/src/SDL.c
vendored
Normal file
@@ -0,0 +1,651 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "./SDL_internal.h"
|
||||
|
||||
#if defined(__WIN32__) || defined(__GDK__)
|
||||
#include "core/windows/SDL_windows.h"
|
||||
#elif defined(__OS2__)
|
||||
#include <stdlib.h> /* _exit() */
|
||||
#elif !defined(__WINRT__)
|
||||
#include <unistd.h> /* _exit(), etc. */
|
||||
#endif
|
||||
#if defined(__OS2__)
|
||||
#include "core/os2/SDL_os2.h"
|
||||
#if SDL_THREAD_OS2
|
||||
#include "thread/os2/SDL_systls_c.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* this checks for HAVE_DBUS_DBUS_H internally. */
|
||||
#include "core/linux/SDL_dbus.h"
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
/* Initialization code for SDL */
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SDL_bits.h"
|
||||
#include "SDL_revision.h"
|
||||
#include "SDL_assert_c.h"
|
||||
#include "SDL_log_c.h"
|
||||
#include "events/SDL_events_c.h"
|
||||
#include "haptic/SDL_haptic_c.h"
|
||||
#include "joystick/SDL_joystick_c.h"
|
||||
#include "sensor/SDL_sensor_c.h"
|
||||
|
||||
/* Initialization/Cleanup routines */
|
||||
#if !SDL_TIMERS_DISABLED
|
||||
#include "timer/SDL_timer_c.h"
|
||||
#endif
|
||||
#if SDL_VIDEO_DRIVER_WINDOWS
|
||||
extern int SDL_HelperWindowCreate(void);
|
||||
extern int SDL_HelperWindowDestroy(void);
|
||||
#endif
|
||||
|
||||
#ifdef SDL_BUILD_MAJOR_VERSION
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MAJOR_VERSION,
|
||||
SDL_MAJOR_VERSION == SDL_BUILD_MAJOR_VERSION);
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MINOR_VERSION,
|
||||
SDL_MINOR_VERSION == SDL_BUILD_MINOR_VERSION);
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MICRO_VERSION,
|
||||
SDL_PATCHLEVEL == SDL_BUILD_MICRO_VERSION);
|
||||
#endif
|
||||
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_MAJOR_VERSION_min, SDL_MAJOR_VERSION >= 0);
|
||||
/* Limited only by the need to fit in SDL_version */
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_MAJOR_VERSION_max, SDL_MAJOR_VERSION <= 255);
|
||||
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_MINOR_VERSION_min, SDL_MINOR_VERSION >= 0);
|
||||
/* Limited only by the need to fit in SDL_version */
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_MINOR_VERSION_max, SDL_MINOR_VERSION <= 255);
|
||||
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_PATCHLEVEL_min, SDL_PATCHLEVEL >= 0);
|
||||
/* Limited by its encoding in SDL_VERSIONNUM and in the ABI versions */
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_PATCHLEVEL_max, SDL_PATCHLEVEL <= 99);
|
||||
|
||||
/* This is not declared in any header, although it is shared between some
|
||||
parts of SDL, because we don't want anything calling it without an
|
||||
extremely good reason. */
|
||||
extern SDL_NORETURN void SDL_ExitProcess(int exitcode);
|
||||
SDL_NORETURN void SDL_ExitProcess(int exitcode)
|
||||
{
|
||||
#if defined(__WIN32__) || defined(__GDK__)
|
||||
/* "if you do not know the state of all threads in your process, it is
|
||||
better to call TerminateProcess than ExitProcess"
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/ms682658(v=vs.85).aspx */
|
||||
TerminateProcess(GetCurrentProcess(), exitcode);
|
||||
/* MingW doesn't have TerminateProcess marked as noreturn, so add an
|
||||
ExitProcess here that will never be reached but make MingW happy. */
|
||||
ExitProcess(exitcode);
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
emscripten_cancel_main_loop(); /* this should "kill" the app. */
|
||||
emscripten_force_exit(exitcode); /* this should "kill" the app. */
|
||||
exit(exitcode);
|
||||
#elif defined(__HAIKU__) /* Haiku has _Exit, but it's not marked noreturn. */
|
||||
_exit(exitcode);
|
||||
#elif defined(HAVE__EXIT) /* Upper case _Exit() */
|
||||
_Exit(exitcode);
|
||||
#else
|
||||
_exit(exitcode);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* The initialized subsystems */
|
||||
#ifdef SDL_MAIN_NEEDED
|
||||
static SDL_bool SDL_MainIsReady = SDL_FALSE;
|
||||
#else
|
||||
static SDL_bool SDL_MainIsReady = SDL_TRUE;
|
||||
#endif
|
||||
static SDL_bool SDL_bInMainQuit = SDL_FALSE;
|
||||
static Uint8 SDL_SubsystemRefCount[32];
|
||||
|
||||
/* Private helper to increment a subsystem's ref counter. */
|
||||
static void SDL_PrivateSubsystemRefCountIncr(Uint32 subsystem)
|
||||
{
|
||||
const int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
|
||||
SDL_assert((subsystem_index < 0) || (SDL_SubsystemRefCount[subsystem_index] < 255));
|
||||
if (subsystem_index >= 0) {
|
||||
++SDL_SubsystemRefCount[subsystem_index];
|
||||
}
|
||||
}
|
||||
|
||||
/* Private helper to decrement a subsystem's ref counter. */
|
||||
static void SDL_PrivateSubsystemRefCountDecr(Uint32 subsystem)
|
||||
{
|
||||
const int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
|
||||
if ((subsystem_index >= 0) && (SDL_SubsystemRefCount[subsystem_index] > 0)) {
|
||||
--SDL_SubsystemRefCount[subsystem_index];
|
||||
}
|
||||
}
|
||||
|
||||
/* Private helper to check if a system needs init. */
|
||||
static SDL_bool SDL_PrivateShouldInitSubsystem(Uint32 subsystem)
|
||||
{
|
||||
const int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
|
||||
SDL_assert((subsystem_index < 0) || (SDL_SubsystemRefCount[subsystem_index] < 255));
|
||||
return ((subsystem_index >= 0) && (SDL_SubsystemRefCount[subsystem_index] == 0)) ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Private helper to check if a system needs to be quit. */
|
||||
static SDL_bool SDL_PrivateShouldQuitSubsystem(Uint32 subsystem)
|
||||
{
|
||||
const int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
|
||||
if ((subsystem_index >= 0) && (SDL_SubsystemRefCount[subsystem_index] == 0)) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
/* If we're in SDL_Quit, we shut down every subsystem, even if refcount
|
||||
* isn't zero.
|
||||
*/
|
||||
return (((subsystem_index >= 0) && (SDL_SubsystemRefCount[subsystem_index] == 1)) || SDL_bInMainQuit) ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
|
||||
void SDL_SetMainReady(void)
|
||||
{
|
||||
SDL_MainIsReady = SDL_TRUE;
|
||||
}
|
||||
|
||||
int SDL_InitSubSystem(Uint32 flags)
|
||||
{
|
||||
Uint32 flags_initialized = 0;
|
||||
|
||||
if (!SDL_MainIsReady) {
|
||||
return SDL_SetError("Application didn't initialize properly, did you include SDL_main.h in the file containing your main() function?");
|
||||
}
|
||||
|
||||
SDL_LogInit();
|
||||
|
||||
/* Clear the error message */
|
||||
SDL_ClearError();
|
||||
|
||||
#if SDL_USE_LIBDBUS
|
||||
SDL_DBus_Init();
|
||||
#endif
|
||||
|
||||
if (flags & SDL_INIT_GAMECONTROLLER) {
|
||||
/* game controller implies joystick */
|
||||
flags |= SDL_INIT_JOYSTICK;
|
||||
}
|
||||
|
||||
if (flags & (SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO)) {
|
||||
/* video or joystick or audio implies events */
|
||||
flags |= SDL_INIT_EVENTS;
|
||||
}
|
||||
|
||||
#if SDL_THREAD_OS2
|
||||
SDL_OS2TLSAlloc(); /* thread/os2/SDL_systls.c */
|
||||
#endif
|
||||
|
||||
#if SDL_VIDEO_DRIVER_WINDOWS
|
||||
if (flags & (SDL_INIT_HAPTIC | SDL_INIT_JOYSTICK)) {
|
||||
if (SDL_HelperWindowCreate() < 0) {
|
||||
goto quit_and_error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !SDL_TIMERS_DISABLED
|
||||
SDL_TicksInit();
|
||||
#endif
|
||||
|
||||
/* Initialize the event subsystem */
|
||||
if (flags & SDL_INIT_EVENTS) {
|
||||
#if !SDL_EVENTS_DISABLED
|
||||
if (SDL_PrivateShouldInitSubsystem(SDL_INIT_EVENTS)) {
|
||||
if (SDL_EventsInit() < 0) {
|
||||
goto quit_and_error;
|
||||
}
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountIncr(SDL_INIT_EVENTS);
|
||||
flags_initialized |= SDL_INIT_EVENTS;
|
||||
#else
|
||||
SDL_SetError("SDL not built with events support");
|
||||
goto quit_and_error;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Initialize the timer subsystem */
|
||||
if (flags & SDL_INIT_TIMER) {
|
||||
#if !SDL_TIMERS_DISABLED && !SDL_TIMER_DUMMY
|
||||
if (SDL_PrivateShouldInitSubsystem(SDL_INIT_TIMER)) {
|
||||
if (SDL_TimerInit() < 0) {
|
||||
goto quit_and_error;
|
||||
}
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountIncr(SDL_INIT_TIMER);
|
||||
flags_initialized |= SDL_INIT_TIMER;
|
||||
#else
|
||||
SDL_SetError("SDL not built with timer support");
|
||||
goto quit_and_error;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Initialize the video subsystem */
|
||||
if (flags & SDL_INIT_VIDEO) {
|
||||
#if !SDL_VIDEO_DISABLED
|
||||
if (SDL_PrivateShouldInitSubsystem(SDL_INIT_VIDEO)) {
|
||||
if (SDL_VideoInit(NULL) < 0) {
|
||||
goto quit_and_error;
|
||||
}
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountIncr(SDL_INIT_VIDEO);
|
||||
flags_initialized |= SDL_INIT_VIDEO;
|
||||
#else
|
||||
SDL_SetError("SDL not built with video support");
|
||||
goto quit_and_error;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Initialize the audio subsystem */
|
||||
if (flags & SDL_INIT_AUDIO) {
|
||||
#if !SDL_AUDIO_DISABLED
|
||||
if (SDL_PrivateShouldInitSubsystem(SDL_INIT_AUDIO)) {
|
||||
if (SDL_AudioInit(NULL) < 0) {
|
||||
goto quit_and_error;
|
||||
}
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountIncr(SDL_INIT_AUDIO);
|
||||
flags_initialized |= SDL_INIT_AUDIO;
|
||||
#else
|
||||
SDL_SetError("SDL not built with audio support");
|
||||
goto quit_and_error;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Initialize the joystick subsystem */
|
||||
if (flags & SDL_INIT_JOYSTICK) {
|
||||
#if !SDL_JOYSTICK_DISABLED
|
||||
if (SDL_PrivateShouldInitSubsystem(SDL_INIT_JOYSTICK)) {
|
||||
if (SDL_JoystickInit() < 0) {
|
||||
goto quit_and_error;
|
||||
}
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountIncr(SDL_INIT_JOYSTICK);
|
||||
flags_initialized |= SDL_INIT_JOYSTICK;
|
||||
#else
|
||||
SDL_SetError("SDL not built with joystick support");
|
||||
goto quit_and_error;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (flags & SDL_INIT_GAMECONTROLLER) {
|
||||
#if !SDL_JOYSTICK_DISABLED
|
||||
if (SDL_PrivateShouldInitSubsystem(SDL_INIT_GAMECONTROLLER)) {
|
||||
if (SDL_GameControllerInit() < 0) {
|
||||
goto quit_and_error;
|
||||
}
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountIncr(SDL_INIT_GAMECONTROLLER);
|
||||
flags_initialized |= SDL_INIT_GAMECONTROLLER;
|
||||
#else
|
||||
SDL_SetError("SDL not built with joystick support");
|
||||
goto quit_and_error;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Initialize the haptic subsystem */
|
||||
if (flags & SDL_INIT_HAPTIC) {
|
||||
#if !SDL_HAPTIC_DISABLED
|
||||
if (SDL_PrivateShouldInitSubsystem(SDL_INIT_HAPTIC)) {
|
||||
if (SDL_HapticInit() < 0) {
|
||||
goto quit_and_error;
|
||||
}
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountIncr(SDL_INIT_HAPTIC);
|
||||
flags_initialized |= SDL_INIT_HAPTIC;
|
||||
#else
|
||||
SDL_SetError("SDL not built with haptic (force feedback) support");
|
||||
goto quit_and_error;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Initialize the sensor subsystem */
|
||||
if (flags & SDL_INIT_SENSOR) {
|
||||
#if !SDL_SENSOR_DISABLED
|
||||
if (SDL_PrivateShouldInitSubsystem(SDL_INIT_SENSOR)) {
|
||||
if (SDL_SensorInit() < 0) {
|
||||
goto quit_and_error;
|
||||
}
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountIncr(SDL_INIT_SENSOR);
|
||||
flags_initialized |= SDL_INIT_SENSOR;
|
||||
#else
|
||||
SDL_SetError("SDL not built with sensor support");
|
||||
goto quit_and_error;
|
||||
#endif
|
||||
}
|
||||
|
||||
(void)flags_initialized; /* make static analysis happy, since this only gets used in error cases. */
|
||||
|
||||
return 0;
|
||||
|
||||
quit_and_error:
|
||||
SDL_QuitSubSystem(flags_initialized);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int SDL_Init(Uint32 flags)
|
||||
{
|
||||
return SDL_InitSubSystem(flags);
|
||||
}
|
||||
|
||||
void SDL_QuitSubSystem(Uint32 flags)
|
||||
{
|
||||
#if defined(__OS2__)
|
||||
#if SDL_THREAD_OS2
|
||||
SDL_OS2TLSFree(); /* thread/os2/SDL_systls.c */
|
||||
#endif
|
||||
SDL_OS2Quit();
|
||||
#endif
|
||||
|
||||
/* Shut down requested initialized subsystems */
|
||||
#if !SDL_SENSOR_DISABLED
|
||||
if (flags & SDL_INIT_SENSOR) {
|
||||
if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_SENSOR)) {
|
||||
SDL_SensorQuit();
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountDecr(SDL_INIT_SENSOR);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !SDL_JOYSTICK_DISABLED
|
||||
if (flags & SDL_INIT_GAMECONTROLLER) {
|
||||
/* game controller implies joystick */
|
||||
flags |= SDL_INIT_JOYSTICK;
|
||||
|
||||
if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_GAMECONTROLLER)) {
|
||||
SDL_GameControllerQuit();
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountDecr(SDL_INIT_GAMECONTROLLER);
|
||||
}
|
||||
|
||||
if (flags & SDL_INIT_JOYSTICK) {
|
||||
/* joystick implies events */
|
||||
flags |= SDL_INIT_EVENTS;
|
||||
|
||||
if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_JOYSTICK)) {
|
||||
SDL_JoystickQuit();
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountDecr(SDL_INIT_JOYSTICK);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !SDL_HAPTIC_DISABLED
|
||||
if (flags & SDL_INIT_HAPTIC) {
|
||||
if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_HAPTIC)) {
|
||||
SDL_HapticQuit();
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountDecr(SDL_INIT_HAPTIC);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !SDL_AUDIO_DISABLED
|
||||
if (flags & SDL_INIT_AUDIO) {
|
||||
/* audio implies events */
|
||||
flags |= SDL_INIT_EVENTS;
|
||||
|
||||
if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_AUDIO)) {
|
||||
SDL_AudioQuit();
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountDecr(SDL_INIT_AUDIO);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !SDL_VIDEO_DISABLED
|
||||
if (flags & SDL_INIT_VIDEO) {
|
||||
/* video implies events */
|
||||
flags |= SDL_INIT_EVENTS;
|
||||
|
||||
if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_VIDEO)) {
|
||||
SDL_VideoQuit();
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountDecr(SDL_INIT_VIDEO);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !SDL_TIMERS_DISABLED && !SDL_TIMER_DUMMY
|
||||
if (flags & SDL_INIT_TIMER) {
|
||||
if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_TIMER)) {
|
||||
SDL_TimerQuit();
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountDecr(SDL_INIT_TIMER);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !SDL_EVENTS_DISABLED
|
||||
if (flags & SDL_INIT_EVENTS) {
|
||||
if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_EVENTS)) {
|
||||
SDL_EventsQuit();
|
||||
}
|
||||
SDL_PrivateSubsystemRefCountDecr(SDL_INIT_EVENTS);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Uint32 SDL_WasInit(Uint32 flags)
|
||||
{
|
||||
int i;
|
||||
int num_subsystems = SDL_arraysize(SDL_SubsystemRefCount);
|
||||
Uint32 initialized = 0;
|
||||
|
||||
/* Fast path for checking one flag */
|
||||
if (SDL_HasExactlyOneBitSet32(flags)) {
|
||||
int subsystem_index = SDL_MostSignificantBitIndex32(flags);
|
||||
return SDL_SubsystemRefCount[subsystem_index] ? flags : 0;
|
||||
}
|
||||
|
||||
if (!flags) {
|
||||
flags = SDL_INIT_EVERYTHING;
|
||||
}
|
||||
|
||||
num_subsystems = SDL_min(num_subsystems, SDL_MostSignificantBitIndex32(flags) + 1);
|
||||
|
||||
/* Iterate over each bit in flags, and check the matching subsystem. */
|
||||
for (i = 0; i < num_subsystems; ++i) {
|
||||
if ((flags & 1) && SDL_SubsystemRefCount[i] > 0) {
|
||||
initialized |= (1 << i);
|
||||
}
|
||||
|
||||
flags >>= 1;
|
||||
}
|
||||
|
||||
return initialized;
|
||||
}
|
||||
|
||||
void SDL_Quit(void)
|
||||
{
|
||||
SDL_bInMainQuit = SDL_TRUE;
|
||||
|
||||
/* Quit all subsystems */
|
||||
#if SDL_VIDEO_DRIVER_WINDOWS
|
||||
SDL_HelperWindowDestroy();
|
||||
#endif
|
||||
SDL_QuitSubSystem(SDL_INIT_EVERYTHING);
|
||||
|
||||
#if !SDL_TIMERS_DISABLED
|
||||
SDL_TicksQuit();
|
||||
#endif
|
||||
|
||||
SDL_ClearHints();
|
||||
SDL_AssertionsQuit();
|
||||
|
||||
#if SDL_USE_LIBDBUS
|
||||
SDL_DBus_Quit();
|
||||
#endif
|
||||
|
||||
SDL_LogQuit();
|
||||
|
||||
/* Now that every subsystem has been quit, we reset the subsystem refcount
|
||||
* and the list of initialized subsystems.
|
||||
*/
|
||||
SDL_memset(SDL_SubsystemRefCount, 0x0, sizeof(SDL_SubsystemRefCount));
|
||||
|
||||
SDL_TLSCleanup();
|
||||
|
||||
SDL_bInMainQuit = SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Get the library version number */
|
||||
void SDL_GetVersion(SDL_version *ver)
|
||||
{
|
||||
static SDL_bool check_hint = SDL_TRUE;
|
||||
static SDL_bool legacy_version = SDL_FALSE;
|
||||
|
||||
if (ver == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_VERSION(ver);
|
||||
|
||||
if (check_hint) {
|
||||
check_hint = SDL_FALSE;
|
||||
legacy_version = SDL_GetHintBoolean("SDL_LEGACY_VERSION", SDL_FALSE);
|
||||
}
|
||||
|
||||
if (legacy_version) {
|
||||
/* Prior to SDL 2.24.0, the patch version was incremented with every release */
|
||||
ver->patch = ver->minor;
|
||||
ver->minor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the library source revision */
|
||||
const char *SDL_GetRevision(void)
|
||||
{
|
||||
return SDL_REVISION;
|
||||
}
|
||||
|
||||
/* Get the library source revision number */
|
||||
int SDL_GetRevisionNumber(void)
|
||||
{
|
||||
return 0; /* doesn't make sense without Mercurial. */
|
||||
}
|
||||
|
||||
/* Get the name of the platform */
|
||||
const char *SDL_GetPlatform(void)
|
||||
{
|
||||
#if __AIX__
|
||||
return "AIX";
|
||||
#elif __ANDROID__
|
||||
return "Android";
|
||||
#elif __BSDI__
|
||||
return "BSDI";
|
||||
#elif __DREAMCAST__
|
||||
return "Dreamcast";
|
||||
#elif __EMSCRIPTEN__
|
||||
return "Emscripten";
|
||||
#elif __FREEBSD__
|
||||
return "FreeBSD";
|
||||
#elif __HAIKU__
|
||||
return "Haiku";
|
||||
#elif __HPUX__
|
||||
return "HP-UX";
|
||||
#elif __IRIX__
|
||||
return "Irix";
|
||||
#elif __LINUX__
|
||||
return "Linux";
|
||||
#elif __MINT__
|
||||
return "Atari MiNT";
|
||||
#elif __MACOS__
|
||||
return "MacOS Classic";
|
||||
#elif __MACOSX__
|
||||
return "Mac OS X";
|
||||
#elif __NACL__
|
||||
return "NaCl";
|
||||
#elif __NETBSD__
|
||||
return "NetBSD";
|
||||
#elif __OPENBSD__
|
||||
return "OpenBSD";
|
||||
#elif __OS2__
|
||||
return "OS/2";
|
||||
#elif __OSF__
|
||||
return "OSF/1";
|
||||
#elif __QNXNTO__
|
||||
return "QNX Neutrino";
|
||||
#elif __RISCOS__
|
||||
return "RISC OS";
|
||||
#elif __SOLARIS__
|
||||
return "Solaris";
|
||||
#elif __WIN32__
|
||||
return "Windows";
|
||||
#elif __WINRT__
|
||||
return "WinRT";
|
||||
#elif __WINGDK__
|
||||
return "WinGDK";
|
||||
#elif __XBOXONE__
|
||||
return "Xbox One";
|
||||
#elif __XBOXSERIES__
|
||||
return "Xbox Series X|S";
|
||||
#elif __TVOS__
|
||||
return "tvOS";
|
||||
#elif __IPHONEOS__
|
||||
return "iOS";
|
||||
#elif __PS2__
|
||||
return "PlayStation 2";
|
||||
#elif __PSP__
|
||||
return "PlayStation Portable";
|
||||
#elif __VITA__
|
||||
return "PlayStation Vita";
|
||||
#elif __NGAGE__
|
||||
return "Nokia N-Gage";
|
||||
#elif __3DS__
|
||||
return "Nintendo 3DS";
|
||||
#else
|
||||
return "Unknown (see SDL_platform.h)";
|
||||
#endif
|
||||
}
|
||||
|
||||
SDL_bool SDL_IsTablet(void)
|
||||
{
|
||||
#if __ANDROID__
|
||||
extern SDL_bool SDL_IsAndroidTablet(void);
|
||||
return SDL_IsAndroidTablet();
|
||||
#elif __IPHONEOS__
|
||||
extern SDL_bool SDL_IsIPad(void);
|
||||
return SDL_IsIPad();
|
||||
#else
|
||||
return SDL_FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__WIN32__)
|
||||
|
||||
#if (!defined(HAVE_LIBC) || defined(__WATCOMC__)) && !defined(SDL_STATIC_LIB)
|
||||
/* FIXME: Still need to include DllMain() on Watcom C ? */
|
||||
|
||||
BOOL APIENTRY MINGW32_FORCEALIGN _DllMainCRTStartup(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
#endif /* Building DLL */
|
||||
|
||||
#endif /* defined(__WIN32__) || defined(__GDK__) */
|
||||
|
||||
/* vi: set sts=4 ts=4 sw=4 expandtab: */
|
||||
463
third_party/SDL/src/SDL_assert.c
vendored
Normal file
463
third_party/SDL/src/SDL_assert.c
vendored
Normal file
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "./SDL_internal.h"
|
||||
|
||||
#if defined(__WIN32__) || defined(__GDK__)
|
||||
#include "core/windows/SDL_windows.h"
|
||||
#endif
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SDL_atomic.h"
|
||||
#include "SDL_messagebox.h"
|
||||
#include "SDL_video.h"
|
||||
#include "SDL_assert.h"
|
||||
#include "SDL_assert_c.h"
|
||||
#include "video/SDL_sysvideo.h"
|
||||
|
||||
#if defined(__WIN32__) || defined(__GDK__)
|
||||
#ifndef WS_OVERLAPPEDWINDOW
|
||||
#define WS_OVERLAPPEDWINDOW 0
|
||||
#endif
|
||||
#else /* fprintf, etc. */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <emscripten.h>
|
||||
/* older Emscriptens don't have this, but we need to for wasm64 compatibility. */
|
||||
#ifndef MAIN_THREAD_EM_ASM_PTR
|
||||
#ifdef __wasm64__
|
||||
#error You need to upgrade your Emscripten compiler to support wasm64
|
||||
#else
|
||||
#define MAIN_THREAD_EM_ASM_PTR MAIN_THREAD_EM_ASM_INT
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* The size of the stack buffer to use for rendering assert messages. */
|
||||
#define SDL_MAX_ASSERT_MESSAGE_STACK 256
|
||||
|
||||
static SDL_assert_state SDLCALL SDL_PromptAssertion(const SDL_assert_data *data, void *userdata);
|
||||
|
||||
/*
|
||||
* We keep all triggered assertions in a singly-linked list so we can
|
||||
* generate a report later.
|
||||
*/
|
||||
static SDL_assert_data *triggered_assertions = NULL;
|
||||
|
||||
#ifndef SDL_THREADS_DISABLED
|
||||
static SDL_mutex *assertion_mutex = NULL;
|
||||
#endif
|
||||
|
||||
static SDL_AssertionHandler assertion_handler = SDL_PromptAssertion;
|
||||
static void *assertion_userdata = NULL;
|
||||
|
||||
#ifdef __GNUC__
|
||||
static void debug_print(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
#endif
|
||||
|
||||
static void debug_print(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
SDL_LogMessageV(SDL_LOG_CATEGORY_ASSERT, SDL_LOG_PRIORITY_WARN, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void SDL_AddAssertionToReport(SDL_assert_data *data)
|
||||
{
|
||||
/* (data) is always a static struct defined with the assert macros, so
|
||||
we don't have to worry about copying or allocating them. */
|
||||
data->trigger_count++;
|
||||
if (data->trigger_count == 1) { /* not yet added? */
|
||||
data->next = triggered_assertions;
|
||||
triggered_assertions = data;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__WIN32__) || defined(__GDK__)
|
||||
#define ENDLINE "\r\n"
|
||||
#else
|
||||
#define ENDLINE "\n"
|
||||
#endif
|
||||
|
||||
static int SDL_RenderAssertMessage(char *buf, size_t buf_len, const SDL_assert_data *data)
|
||||
{
|
||||
return SDL_snprintf(buf, buf_len,
|
||||
"Assertion failure at %s (%s:%d), triggered %u %s:" ENDLINE " '%s'",
|
||||
data->function, data->filename, data->linenum,
|
||||
data->trigger_count, (data->trigger_count == 1) ? "time" : "times",
|
||||
data->condition);
|
||||
}
|
||||
|
||||
static void SDL_GenerateAssertionReport(void)
|
||||
{
|
||||
const SDL_assert_data *item = triggered_assertions;
|
||||
|
||||
/* only do this if the app hasn't assigned an assertion handler. */
|
||||
if ((item != NULL) && (assertion_handler != SDL_PromptAssertion)) {
|
||||
debug_print("\n\nSDL assertion report.\n");
|
||||
debug_print("All SDL assertions between last init/quit:\n\n");
|
||||
|
||||
while (item != NULL) {
|
||||
debug_print(
|
||||
"'%s'\n"
|
||||
" * %s (%s:%d)\n"
|
||||
" * triggered %u time%s.\n"
|
||||
" * always ignore: %s.\n",
|
||||
item->condition, item->function, item->filename,
|
||||
item->linenum, item->trigger_count,
|
||||
(item->trigger_count == 1) ? "" : "s",
|
||||
item->always_ignore ? "yes" : "no");
|
||||
item = item->next;
|
||||
}
|
||||
debug_print("\n");
|
||||
|
||||
SDL_ResetAssertionReport();
|
||||
}
|
||||
}
|
||||
|
||||
/* This is not declared in any header, although it is shared between some
|
||||
parts of SDL, because we don't want anything calling it without an
|
||||
extremely good reason. */
|
||||
#if defined(__WATCOMC__)
|
||||
extern void SDL_ExitProcess(int exitcode);
|
||||
#pragma aux SDL_ExitProcess aborts;
|
||||
#endif
|
||||
extern SDL_NORETURN void SDL_ExitProcess(int exitcode);
|
||||
|
||||
#if defined(__WATCOMC__)
|
||||
static void SDL_AbortAssertion(void);
|
||||
#pragma aux SDL_AbortAssertion aborts;
|
||||
#endif
|
||||
static SDL_NORETURN void SDL_AbortAssertion(void)
|
||||
{
|
||||
SDL_Quit();
|
||||
SDL_ExitProcess(42);
|
||||
}
|
||||
|
||||
static SDL_assert_state SDLCALL SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
|
||||
{
|
||||
const char *envr;
|
||||
SDL_assert_state state = SDL_ASSERTION_ABORT;
|
||||
SDL_Window *window;
|
||||
SDL_MessageBoxData messagebox;
|
||||
SDL_MessageBoxButtonData buttons[] = {
|
||||
{ 0, SDL_ASSERTION_RETRY, "Retry" },
|
||||
{ 0, SDL_ASSERTION_BREAK, "Break" },
|
||||
{ 0, SDL_ASSERTION_ABORT, "Abort" },
|
||||
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
|
||||
SDL_ASSERTION_IGNORE, "Ignore" },
|
||||
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
|
||||
SDL_ASSERTION_ALWAYS_IGNORE, "Always Ignore" }
|
||||
};
|
||||
int selected;
|
||||
|
||||
char stack_buf[SDL_MAX_ASSERT_MESSAGE_STACK];
|
||||
char *message = stack_buf;
|
||||
size_t buf_len = sizeof(stack_buf);
|
||||
int len;
|
||||
|
||||
(void)userdata; /* unused in default handler. */
|
||||
|
||||
/* Assume the output will fit... */
|
||||
len = SDL_RenderAssertMessage(message, buf_len, data);
|
||||
|
||||
/* .. and if it didn't, try to allocate as much room as we actually need. */
|
||||
if (len >= (int)buf_len) {
|
||||
if (SDL_size_add_overflow(len, 1, &buf_len) == 0) {
|
||||
message = (char *)SDL_malloc(buf_len);
|
||||
if (message) {
|
||||
len = SDL_RenderAssertMessage(message, buf_len, data);
|
||||
} else {
|
||||
message = stack_buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Something went very wrong */
|
||||
if (len < 0) {
|
||||
if (message != stack_buf) {
|
||||
SDL_free(message);
|
||||
}
|
||||
return SDL_ASSERTION_ABORT;
|
||||
}
|
||||
|
||||
debug_print("\n\n%s\n\n", message);
|
||||
|
||||
/* let env. variable override, so unit tests won't block in a GUI. */
|
||||
envr = SDL_getenv("SDL_ASSERT");
|
||||
if (envr != NULL) {
|
||||
if (message != stack_buf) {
|
||||
SDL_free(message);
|
||||
}
|
||||
|
||||
if (SDL_strcmp(envr, "abort") == 0) {
|
||||
return SDL_ASSERTION_ABORT;
|
||||
} else if (SDL_strcmp(envr, "break") == 0) {
|
||||
return SDL_ASSERTION_BREAK;
|
||||
} else if (SDL_strcmp(envr, "retry") == 0) {
|
||||
return SDL_ASSERTION_RETRY;
|
||||
} else if (SDL_strcmp(envr, "ignore") == 0) {
|
||||
return SDL_ASSERTION_IGNORE;
|
||||
} else if (SDL_strcmp(envr, "always_ignore") == 0) {
|
||||
return SDL_ASSERTION_ALWAYS_IGNORE;
|
||||
} else {
|
||||
return SDL_ASSERTION_ABORT; /* oh well. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Leave fullscreen mode, if possible (scary!) */
|
||||
window = SDL_GetFocusWindow();
|
||||
if (window) {
|
||||
if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
|
||||
SDL_MinimizeWindow(window);
|
||||
} else {
|
||||
/* !!! FIXME: ungrab the input if we're not fullscreen? */
|
||||
/* No need to mess with the window */
|
||||
window = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Show a messagebox if we can, otherwise fall back to stdio */
|
||||
SDL_zero(messagebox);
|
||||
messagebox.flags = SDL_MESSAGEBOX_WARNING;
|
||||
messagebox.window = window;
|
||||
messagebox.title = "Assertion Failed";
|
||||
messagebox.message = message;
|
||||
messagebox.numbuttons = SDL_arraysize(buttons);
|
||||
messagebox.buttons = buttons;
|
||||
|
||||
if (SDL_ShowMessageBox(&messagebox, &selected) == 0) {
|
||||
if (selected == -1) {
|
||||
state = SDL_ASSERTION_IGNORE;
|
||||
} else {
|
||||
state = (SDL_assert_state)selected;
|
||||
}
|
||||
} else {
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
/* This is nasty, but we can't block on a custom UI. */
|
||||
for (;;) {
|
||||
SDL_bool okay = SDL_TRUE;
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
char *buf = (char *) MAIN_THREAD_EM_ASM_PTR({
|
||||
var str =
|
||||
UTF8ToString($0) + '\n\n' +
|
||||
'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :';
|
||||
var reply = window.prompt(str, "i");
|
||||
if (reply === null) {
|
||||
reply = "i";
|
||||
}
|
||||
return allocate(intArrayFromString(reply), 'i8', ALLOC_NORMAL);
|
||||
}, message);
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
if (SDL_strcmp(buf, "a") == 0) {
|
||||
state = SDL_ASSERTION_ABORT;
|
||||
#if 0 /* (currently) no break functionality on Emscripten */
|
||||
} else if (SDL_strcmp(buf, "b") == 0) {
|
||||
state = SDL_ASSERTION_BREAK;
|
||||
#endif
|
||||
} else if (SDL_strcmp(buf, "r") == 0) {
|
||||
state = SDL_ASSERTION_RETRY;
|
||||
} else if (SDL_strcmp(buf, "i") == 0) {
|
||||
state = SDL_ASSERTION_IGNORE;
|
||||
} else if (SDL_strcmp(buf, "A") == 0) {
|
||||
state = SDL_ASSERTION_ALWAYS_IGNORE;
|
||||
} else {
|
||||
okay = SDL_FALSE;
|
||||
}
|
||||
free(buf);
|
||||
|
||||
if (okay) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#elif defined(HAVE_STDIO_H)
|
||||
/* this is a little hacky. */
|
||||
for (;;) {
|
||||
char buf[32];
|
||||
(void)fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
|
||||
(void)fflush(stderr);
|
||||
if (fgets(buf, sizeof(buf), stdin) == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (SDL_strncmp(buf, "a", 1) == 0) {
|
||||
state = SDL_ASSERTION_ABORT;
|
||||
break;
|
||||
} else if (SDL_strncmp(buf, "b", 1) == 0) {
|
||||
state = SDL_ASSERTION_BREAK;
|
||||
break;
|
||||
} else if (SDL_strncmp(buf, "r", 1) == 0) {
|
||||
state = SDL_ASSERTION_RETRY;
|
||||
break;
|
||||
} else if (SDL_strncmp(buf, "i", 1) == 0) {
|
||||
state = SDL_ASSERTION_IGNORE;
|
||||
break;
|
||||
} else if (SDL_strncmp(buf, "A", 1) == 0) {
|
||||
state = SDL_ASSERTION_ALWAYS_IGNORE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_STDIO_H */
|
||||
}
|
||||
|
||||
/* Re-enter fullscreen mode */
|
||||
if (window) {
|
||||
SDL_RestoreWindow(window);
|
||||
}
|
||||
|
||||
if (message != stack_buf) {
|
||||
SDL_free(message);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
SDL_assert_state SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file, int line)
|
||||
{
|
||||
SDL_assert_state state = SDL_ASSERTION_IGNORE;
|
||||
static int assertion_running = 0;
|
||||
|
||||
#ifndef SDL_THREADS_DISABLED
|
||||
static SDL_SpinLock spinlock = 0;
|
||||
SDL_AtomicLock(&spinlock);
|
||||
if (assertion_mutex == NULL) { /* never called SDL_Init()? */
|
||||
assertion_mutex = SDL_CreateMutex();
|
||||
if (assertion_mutex == NULL) {
|
||||
SDL_AtomicUnlock(&spinlock);
|
||||
return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
|
||||
}
|
||||
}
|
||||
SDL_AtomicUnlock(&spinlock);
|
||||
|
||||
SDL_LockMutex(assertion_mutex);
|
||||
#endif /* !SDL_THREADS_DISABLED */
|
||||
|
||||
/* doing this because Visual C is upset over assigning in the macro. */
|
||||
if (data->trigger_count == 0) {
|
||||
data->function = func;
|
||||
data->filename = file;
|
||||
data->linenum = line;
|
||||
}
|
||||
|
||||
SDL_AddAssertionToReport(data);
|
||||
|
||||
assertion_running++;
|
||||
if (assertion_running > 1) { /* assert during assert! Abort. */
|
||||
if (assertion_running == 2) {
|
||||
SDL_AbortAssertion();
|
||||
} else if (assertion_running == 3) { /* Abort asserted! */
|
||||
SDL_ExitProcess(42);
|
||||
} else {
|
||||
while (1) { /* do nothing but spin; what else can you do?! */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!data->always_ignore) {
|
||||
state = assertion_handler(data, assertion_userdata);
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case SDL_ASSERTION_ALWAYS_IGNORE:
|
||||
state = SDL_ASSERTION_IGNORE;
|
||||
data->always_ignore = 1;
|
||||
break;
|
||||
|
||||
case SDL_ASSERTION_IGNORE:
|
||||
case SDL_ASSERTION_RETRY:
|
||||
case SDL_ASSERTION_BREAK:
|
||||
break; /* macro handles these. */
|
||||
|
||||
case SDL_ASSERTION_ABORT:
|
||||
SDL_AbortAssertion();
|
||||
/*break; ...shouldn't return, but oh well. */
|
||||
}
|
||||
|
||||
assertion_running--;
|
||||
|
||||
#ifndef SDL_THREADS_DISABLED
|
||||
SDL_UnlockMutex(assertion_mutex);
|
||||
#endif
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void SDL_AssertionsQuit(void)
|
||||
{
|
||||
#if SDL_ASSERT_LEVEL > 0
|
||||
SDL_GenerateAssertionReport();
|
||||
#ifndef SDL_THREADS_DISABLED
|
||||
if (assertion_mutex != NULL) {
|
||||
SDL_DestroyMutex(assertion_mutex);
|
||||
assertion_mutex = NULL;
|
||||
}
|
||||
#endif
|
||||
#endif /* SDL_ASSERT_LEVEL > 0 */
|
||||
}
|
||||
|
||||
void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
|
||||
{
|
||||
if (handler != NULL) {
|
||||
assertion_handler = handler;
|
||||
assertion_userdata = userdata;
|
||||
} else {
|
||||
assertion_handler = SDL_PromptAssertion;
|
||||
assertion_userdata = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const SDL_assert_data *SDL_GetAssertionReport(void)
|
||||
{
|
||||
return triggered_assertions;
|
||||
}
|
||||
|
||||
void SDL_ResetAssertionReport(void)
|
||||
{
|
||||
SDL_assert_data *next = NULL;
|
||||
SDL_assert_data *item;
|
||||
for (item = triggered_assertions; item != NULL; item = next) {
|
||||
next = (SDL_assert_data *)item->next;
|
||||
item->always_ignore = SDL_FALSE;
|
||||
item->trigger_count = 0;
|
||||
item->next = NULL;
|
||||
}
|
||||
|
||||
triggered_assertions = NULL;
|
||||
}
|
||||
|
||||
SDL_AssertionHandler SDL_GetDefaultAssertionHandler(void)
|
||||
{
|
||||
return SDL_PromptAssertion;
|
||||
}
|
||||
|
||||
SDL_AssertionHandler SDL_GetAssertionHandler(void **userdata)
|
||||
{
|
||||
if (userdata != NULL) {
|
||||
*userdata = assertion_userdata;
|
||||
}
|
||||
return assertion_handler;
|
||||
}
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
29
third_party/SDL/src/SDL_assert_c.h
vendored
Normal file
29
third_party/SDL/src/SDL_assert_c.h
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_assert_c_h_
|
||||
#define SDL_assert_c_h_
|
||||
|
||||
extern void SDL_AssertionsQuit(void);
|
||||
|
||||
#endif /* SDL_assert_c_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
323
third_party/SDL/src/SDL_dataqueue.c
vendored
Normal file
323
third_party/SDL/src/SDL_dataqueue.c
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "./SDL_internal.h"
|
||||
|
||||
#include "SDL.h"
|
||||
#include "./SDL_dataqueue.h"
|
||||
|
||||
typedef struct SDL_DataQueuePacket
|
||||
{
|
||||
size_t datalen; /* bytes currently in use in this packet. */
|
||||
size_t startpos; /* bytes currently consumed in this packet. */
|
||||
struct SDL_DataQueuePacket *next; /* next item in linked list. */
|
||||
Uint8 data[SDL_VARIABLE_LENGTH_ARRAY]; /* packet data */
|
||||
} SDL_DataQueuePacket;
|
||||
|
||||
struct SDL_DataQueue
|
||||
{
|
||||
SDL_mutex *lock;
|
||||
SDL_DataQueuePacket *head; /* device fed from here. */
|
||||
SDL_DataQueuePacket *tail; /* queue fills to here. */
|
||||
SDL_DataQueuePacket *pool; /* these are unused packets. */
|
||||
size_t packet_size; /* size of new packets */
|
||||
size_t queued_bytes; /* number of bytes of data in the queue. */
|
||||
};
|
||||
|
||||
static void SDL_FreeDataQueueList(SDL_DataQueuePacket *packet)
|
||||
{
|
||||
while (packet) {
|
||||
SDL_DataQueuePacket *next = packet->next;
|
||||
SDL_free(packet);
|
||||
packet = next;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_DataQueue *SDL_NewDataQueue(const size_t _packetlen, const size_t initialslack)
|
||||
{
|
||||
SDL_DataQueue *queue = (SDL_DataQueue *)SDL_calloc(1, sizeof(SDL_DataQueue));
|
||||
|
||||
if (queue == NULL) {
|
||||
SDL_OutOfMemory();
|
||||
} else {
|
||||
const size_t packetlen = _packetlen ? _packetlen : 1024;
|
||||
const size_t wantpackets = (initialslack + (packetlen - 1)) / packetlen;
|
||||
size_t i;
|
||||
|
||||
queue->packet_size = packetlen;
|
||||
|
||||
queue->lock = SDL_CreateMutex();
|
||||
if (!queue->lock) {
|
||||
SDL_free(queue);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < wantpackets; i++) {
|
||||
SDL_DataQueuePacket *packet = (SDL_DataQueuePacket *)SDL_malloc(sizeof(SDL_DataQueuePacket) + packetlen);
|
||||
if (packet) { /* don't care if this fails, we'll deal later. */
|
||||
packet->datalen = 0;
|
||||
packet->startpos = 0;
|
||||
packet->next = queue->pool;
|
||||
queue->pool = packet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
void SDL_FreeDataQueue(SDL_DataQueue *queue)
|
||||
{
|
||||
if (queue) {
|
||||
SDL_FreeDataQueueList(queue->head);
|
||||
SDL_FreeDataQueueList(queue->pool);
|
||||
SDL_DestroyMutex(queue->lock);
|
||||
SDL_free(queue);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_ClearDataQueue(SDL_DataQueue *queue, const size_t slack)
|
||||
{
|
||||
const size_t packet_size = queue ? queue->packet_size : 1;
|
||||
const size_t slackpackets = (slack + (packet_size - 1)) / packet_size;
|
||||
SDL_DataQueuePacket *packet;
|
||||
SDL_DataQueuePacket *prev = NULL;
|
||||
size_t i;
|
||||
|
||||
if (queue == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_LockMutex(queue->lock);
|
||||
|
||||
packet = queue->head;
|
||||
|
||||
/* merge the available pool and the current queue into one list. */
|
||||
if (packet) {
|
||||
queue->tail->next = queue->pool;
|
||||
} else {
|
||||
packet = queue->pool;
|
||||
}
|
||||
|
||||
/* Remove the queued packets from the device. */
|
||||
queue->tail = NULL;
|
||||
queue->head = NULL;
|
||||
queue->queued_bytes = 0;
|
||||
queue->pool = packet;
|
||||
|
||||
/* Optionally keep some slack in the pool to reduce memory allocation pressure. */
|
||||
for (i = 0; packet && (i < slackpackets); i++) {
|
||||
prev = packet;
|
||||
packet = packet->next;
|
||||
}
|
||||
|
||||
if (prev) {
|
||||
prev->next = NULL;
|
||||
} else {
|
||||
queue->pool = NULL;
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
|
||||
SDL_FreeDataQueueList(packet); /* free extra packets */
|
||||
}
|
||||
|
||||
/* You must hold queue->lock before calling this! */
|
||||
static SDL_DataQueuePacket *AllocateDataQueuePacket(SDL_DataQueue *queue)
|
||||
{
|
||||
SDL_DataQueuePacket *packet;
|
||||
|
||||
SDL_assert(queue != NULL);
|
||||
|
||||
packet = queue->pool;
|
||||
if (packet != NULL) {
|
||||
/* we have one available in the pool. */
|
||||
queue->pool = packet->next;
|
||||
} else {
|
||||
/* Have to allocate a new one! */
|
||||
packet = (SDL_DataQueuePacket *)SDL_malloc(sizeof(SDL_DataQueuePacket) + queue->packet_size);
|
||||
if (packet == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
packet->datalen = 0;
|
||||
packet->startpos = 0;
|
||||
packet->next = NULL;
|
||||
|
||||
SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
|
||||
if (queue->tail == NULL) {
|
||||
queue->head = packet;
|
||||
} else {
|
||||
queue->tail->next = packet;
|
||||
}
|
||||
queue->tail = packet;
|
||||
return packet;
|
||||
}
|
||||
|
||||
int SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *_data, const size_t _len)
|
||||
{
|
||||
size_t len = _len;
|
||||
const Uint8 *data = (const Uint8 *)_data;
|
||||
const size_t packet_size = queue ? queue->packet_size : 0;
|
||||
SDL_DataQueuePacket *orighead;
|
||||
SDL_DataQueuePacket *origtail;
|
||||
size_t origlen;
|
||||
size_t datalen;
|
||||
|
||||
if (queue == NULL) {
|
||||
return SDL_InvalidParamError("queue");
|
||||
}
|
||||
|
||||
SDL_LockMutex(queue->lock);
|
||||
|
||||
orighead = queue->head;
|
||||
origtail = queue->tail;
|
||||
origlen = origtail ? origtail->datalen : 0;
|
||||
|
||||
while (len > 0) {
|
||||
SDL_DataQueuePacket *packet = queue->tail;
|
||||
SDL_assert(packet == NULL || (packet->datalen <= packet_size));
|
||||
if (packet == NULL || (packet->datalen >= packet_size)) {
|
||||
/* tail packet missing or completely full; we need a new packet. */
|
||||
packet = AllocateDataQueuePacket(queue);
|
||||
if (packet == NULL) {
|
||||
/* uhoh, reset so we've queued nothing new, free what we can. */
|
||||
if (origtail == NULL) {
|
||||
packet = queue->head; /* whole queue. */
|
||||
} else {
|
||||
packet = origtail->next; /* what we added to existing queue. */
|
||||
origtail->next = NULL;
|
||||
origtail->datalen = origlen;
|
||||
}
|
||||
queue->head = orighead;
|
||||
queue->tail = origtail;
|
||||
queue->pool = NULL;
|
||||
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
SDL_FreeDataQueueList(packet); /* give back what we can. */
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
}
|
||||
|
||||
datalen = SDL_min(len, packet_size - packet->datalen);
|
||||
SDL_memcpy(packet->data + packet->datalen, data, datalen);
|
||||
data += datalen;
|
||||
len -= datalen;
|
||||
packet->datalen += datalen;
|
||||
queue->queued_bytes += datalen;
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t
|
||||
SDL_PeekIntoDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len)
|
||||
{
|
||||
size_t len = _len;
|
||||
Uint8 *buf = (Uint8 *)_buf;
|
||||
Uint8 *ptr = buf;
|
||||
SDL_DataQueuePacket *packet;
|
||||
|
||||
if (queue == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_LockMutex(queue->lock);
|
||||
|
||||
for (packet = queue->head; len && packet; packet = packet->next) {
|
||||
const size_t avail = packet->datalen - packet->startpos;
|
||||
const size_t cpy = SDL_min(len, avail);
|
||||
SDL_assert(queue->queued_bytes >= avail);
|
||||
|
||||
SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
|
||||
ptr += cpy;
|
||||
len -= cpy;
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
|
||||
return (size_t)(ptr - buf);
|
||||
}
|
||||
|
||||
size_t
|
||||
SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len)
|
||||
{
|
||||
size_t len = _len;
|
||||
Uint8 *buf = (Uint8 *)_buf;
|
||||
Uint8 *ptr = buf;
|
||||
SDL_DataQueuePacket *packet;
|
||||
|
||||
if (queue == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_LockMutex(queue->lock);
|
||||
|
||||
while ((len > 0) && ((packet = queue->head) != NULL)) {
|
||||
const size_t avail = packet->datalen - packet->startpos;
|
||||
const size_t cpy = SDL_min(len, avail);
|
||||
SDL_assert(queue->queued_bytes >= avail);
|
||||
|
||||
SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
|
||||
packet->startpos += cpy;
|
||||
ptr += cpy;
|
||||
queue->queued_bytes -= cpy;
|
||||
len -= cpy;
|
||||
|
||||
if (packet->startpos == packet->datalen) { /* packet is done, put it in the pool. */
|
||||
queue->head = packet->next;
|
||||
SDL_assert((packet->next != NULL) || (packet == queue->tail));
|
||||
packet->next = queue->pool;
|
||||
queue->pool = packet;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
|
||||
|
||||
if (queue->head == NULL) {
|
||||
queue->tail = NULL; /* in case we drained the queue entirely. */
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
|
||||
return (size_t)(ptr - buf);
|
||||
}
|
||||
|
||||
size_t
|
||||
SDL_CountDataQueue(SDL_DataQueue *queue)
|
||||
{
|
||||
size_t retval = 0;
|
||||
if (queue) {
|
||||
SDL_LockMutex(queue->lock);
|
||||
retval = queue->queued_bytes;
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
SDL_mutex *SDL_GetDataQueueMutex(SDL_DataQueue *queue)
|
||||
{
|
||||
return queue ? queue->lock : NULL;
|
||||
}
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
40
third_party/SDL/src/SDL_dataqueue.h
vendored
Normal file
40
third_party/SDL/src/SDL_dataqueue.h
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#ifndef SDL_dataqueue_h_
|
||||
#define SDL_dataqueue_h_
|
||||
|
||||
/* this is not (currently) a public API. But maybe it should be! */
|
||||
|
||||
struct SDL_DataQueue;
|
||||
typedef struct SDL_DataQueue SDL_DataQueue;
|
||||
|
||||
SDL_DataQueue *SDL_NewDataQueue(const size_t packetlen, const size_t initialslack);
|
||||
void SDL_FreeDataQueue(SDL_DataQueue *queue);
|
||||
void SDL_ClearDataQueue(SDL_DataQueue *queue, const size_t slack);
|
||||
int SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *data, const size_t len);
|
||||
size_t SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *buf, const size_t len);
|
||||
size_t SDL_PeekIntoDataQueue(SDL_DataQueue *queue, void *buf, const size_t len);
|
||||
size_t SDL_CountDataQueue(SDL_DataQueue *queue);
|
||||
SDL_mutex *SDL_GetDataQueueMutex(SDL_DataQueue *queue); /* don't destroy this, obviously. */
|
||||
|
||||
#endif /* SDL_dataqueue_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
123
third_party/SDL/src/SDL_error.c
vendored
Normal file
123
third_party/SDL/src/SDL_error.c
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "./SDL_internal.h"
|
||||
|
||||
/* Simple error handling in SDL */
|
||||
|
||||
#include "SDL_error.h"
|
||||
#include "SDL_error_c.h"
|
||||
|
||||
int SDL_SetError(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
|
||||
{
|
||||
/* Ignore call if invalid format pointer was passed */
|
||||
if (fmt != NULL) {
|
||||
va_list ap;
|
||||
int result;
|
||||
SDL_error *error = SDL_GetErrBuf();
|
||||
|
||||
error->error = 1; /* mark error as valid */
|
||||
|
||||
va_start(ap, fmt);
|
||||
result = SDL_vsnprintf(error->str, error->len, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (result >= 0 && (size_t)result >= error->len && error->realloc_func) {
|
||||
size_t len = (size_t)result + 1;
|
||||
char *str = (char *)error->realloc_func(error->str, len);
|
||||
if (str) {
|
||||
error->str = str;
|
||||
error->len = len;
|
||||
va_start(ap, fmt);
|
||||
(void)SDL_vsnprintf(error->str, error->len, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL_LogGetPriority(SDL_LOG_CATEGORY_ERROR) <= SDL_LOG_PRIORITY_DEBUG) {
|
||||
/* If we are in debug mode, print out the error message */
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_ERROR, "%s", error->str);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Available for backwards compatibility */
|
||||
const char *SDL_GetError(void)
|
||||
{
|
||||
const SDL_error *error = SDL_GetErrBuf();
|
||||
return error->error ? error->str : "";
|
||||
}
|
||||
|
||||
void SDL_ClearError(void)
|
||||
{
|
||||
SDL_GetErrBuf()->error = 0;
|
||||
}
|
||||
|
||||
/* Very common errors go here */
|
||||
int SDL_Error(SDL_errorcode code)
|
||||
{
|
||||
switch (code) {
|
||||
case SDL_ENOMEM:
|
||||
return SDL_SetError("Out of memory");
|
||||
case SDL_EFREAD:
|
||||
return SDL_SetError("Error reading from datastream");
|
||||
case SDL_EFWRITE:
|
||||
return SDL_SetError("Error writing to datastream");
|
||||
case SDL_EFSEEK:
|
||||
return SDL_SetError("Error seeking in datastream");
|
||||
case SDL_UNSUPPORTED:
|
||||
return SDL_SetError("That operation is not supported");
|
||||
default:
|
||||
return SDL_SetError("Unknown SDL error");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TEST_ERROR
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char buffer[BUFSIZ + 1];
|
||||
|
||||
SDL_SetError("Hi there!");
|
||||
printf("Error 1: %s\n", SDL_GetError());
|
||||
SDL_ClearError();
|
||||
SDL_memset(buffer, '1', BUFSIZ);
|
||||
buffer[BUFSIZ] = 0;
|
||||
SDL_SetError("This is the error: %s (%f)", buffer, 1.0);
|
||||
printf("Error 2: %s\n", SDL_GetError());
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
char *SDL_GetErrorMsg(char *errstr, int maxlen)
|
||||
{
|
||||
const SDL_error *error = SDL_GetErrBuf();
|
||||
|
||||
if (error->error) {
|
||||
SDL_strlcpy(errstr, error->str, maxlen);
|
||||
} else {
|
||||
*errstr = '\0';
|
||||
}
|
||||
|
||||
return errstr;
|
||||
}
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
44
third_party/SDL/src/SDL_error_c.h
vendored
Normal file
44
third_party/SDL/src/SDL_error_c.h
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "./SDL_internal.h"
|
||||
|
||||
/* This file defines a structure that carries language-independent
|
||||
error messages
|
||||
*/
|
||||
|
||||
#ifndef SDL_error_c_h_
|
||||
#define SDL_error_c_h_
|
||||
|
||||
typedef struct SDL_error
|
||||
{
|
||||
int error; /* This is a numeric value corresponding to the current error */
|
||||
char *str;
|
||||
size_t len;
|
||||
SDL_realloc_func realloc_func;
|
||||
SDL_free_func free_func;
|
||||
} SDL_error;
|
||||
|
||||
/* Defined in SDL_thread.c */
|
||||
extern SDL_error *SDL_GetErrBuf(void);
|
||||
|
||||
#endif /* SDL_error_c_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
91
third_party/SDL/src/SDL_guid.c
vendored
Normal file
91
third_party/SDL/src/SDL_guid.c
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
|
||||
#include "SDL_guid.h"
|
||||
|
||||
/* convert the guid to a printable string */
|
||||
void SDL_GUIDToString(SDL_GUID guid, char *pszGUID, int cbGUID)
|
||||
{
|
||||
static const char k_rgchHexToASCII[] = "0123456789abcdef";
|
||||
int i;
|
||||
|
||||
if ((pszGUID == NULL) || (cbGUID <= 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(guid.data) && i < (cbGUID - 1) / 2; i++) {
|
||||
/* each input byte writes 2 ascii chars, and might write a null byte. */
|
||||
/* If we don't have room for next input byte, stop */
|
||||
unsigned char c = guid.data[i];
|
||||
|
||||
*pszGUID++ = k_rgchHexToASCII[c >> 4];
|
||||
*pszGUID++ = k_rgchHexToASCII[c & 0x0F];
|
||||
}
|
||||
*pszGUID = '\0';
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Purpose: Returns the 4 bit nibble for a hex character
|
||||
* Input : c -
|
||||
* Output : unsigned char
|
||||
*-----------------------------------------------------------------------------*/
|
||||
static unsigned char nibble(unsigned char c)
|
||||
{
|
||||
if ((c >= '0') && (c <= '9')) {
|
||||
return c - '0';
|
||||
}
|
||||
|
||||
if ((c >= 'A') && (c <= 'F')) {
|
||||
return c - 'A' + 0x0a;
|
||||
}
|
||||
|
||||
if ((c >= 'a') && (c <= 'f')) {
|
||||
return c - 'a' + 0x0a;
|
||||
}
|
||||
|
||||
/* received an invalid character, and no real way to return an error */
|
||||
/* AssertMsg1(false, "Q_nibble invalid hex character '%c' ", c); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* convert the string version of a guid to the struct */
|
||||
SDL_GUID SDL_GUIDFromString(const char *pchGUID)
|
||||
{
|
||||
SDL_GUID guid;
|
||||
int maxoutputbytes = sizeof(guid);
|
||||
size_t len = SDL_strlen(pchGUID);
|
||||
Uint8 *p;
|
||||
size_t i;
|
||||
|
||||
/* Make sure it's even */
|
||||
len = (len) & ~0x1;
|
||||
|
||||
SDL_memset(&guid, 0x00, sizeof(guid));
|
||||
|
||||
p = (Uint8 *)&guid;
|
||||
for (i = 0; (i < len) && ((p - (Uint8 *)&guid) < maxoutputbytes); i += 2, p++) {
|
||||
*p = (nibble((unsigned char)pchGUID[i]) << 4) | nibble((unsigned char)pchGUID[i + 1]);
|
||||
}
|
||||
|
||||
return guid;
|
||||
}
|
||||
299
third_party/SDL/src/SDL_hints.c
vendored
Normal file
299
third_party/SDL/src/SDL_hints.c
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "./SDL_internal.h"
|
||||
|
||||
#include "SDL_hints.h"
|
||||
#include "SDL_error.h"
|
||||
#include "SDL_hints_c.h"
|
||||
|
||||
/* Assuming there aren't many hints set and they aren't being queried in
|
||||
critical performance paths, we'll just use linked lists here.
|
||||
*/
|
||||
typedef struct SDL_HintWatch
|
||||
{
|
||||
SDL_HintCallback callback;
|
||||
void *userdata;
|
||||
struct SDL_HintWatch *next;
|
||||
} SDL_HintWatch;
|
||||
|
||||
typedef struct SDL_Hint
|
||||
{
|
||||
char *name;
|
||||
char *value;
|
||||
SDL_HintPriority priority;
|
||||
SDL_HintWatch *callbacks;
|
||||
struct SDL_Hint *next;
|
||||
} SDL_Hint;
|
||||
|
||||
static SDL_Hint *SDL_hints;
|
||||
|
||||
SDL_bool SDL_SetHintWithPriority(const char *name, const char *value, SDL_HintPriority priority)
|
||||
{
|
||||
const char *env;
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry;
|
||||
|
||||
if (name == NULL) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
env = SDL_getenv(name);
|
||||
if (env && priority < SDL_HINT_OVERRIDE) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
for (hint = SDL_hints; hint; hint = hint->next) {
|
||||
if (SDL_strcmp(name, hint->name) == 0) {
|
||||
if (priority < hint->priority) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
if (hint->value != value &&
|
||||
(value == NULL || !hint->value || SDL_strcmp(hint->value, value) != 0)) {
|
||||
for (entry = hint->callbacks; entry;) {
|
||||
/* Save the next entry in case this one is deleted */
|
||||
SDL_HintWatch *next = entry->next;
|
||||
entry->callback(entry->userdata, name, hint->value, value);
|
||||
entry = next;
|
||||
}
|
||||
SDL_free(hint->value);
|
||||
hint->value = value ? SDL_strdup(value) : NULL;
|
||||
}
|
||||
hint->priority = priority;
|
||||
return SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Couldn't find the hint, add a new one */
|
||||
hint = (SDL_Hint *)SDL_malloc(sizeof(*hint));
|
||||
if (hint == NULL) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
hint->name = SDL_strdup(name);
|
||||
hint->value = value ? SDL_strdup(value) : NULL;
|
||||
hint->priority = priority;
|
||||
hint->callbacks = NULL;
|
||||
hint->next = SDL_hints;
|
||||
SDL_hints = hint;
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
SDL_bool SDL_ResetHint(const char *name)
|
||||
{
|
||||
const char *env;
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry;
|
||||
|
||||
if (name == NULL) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
env = SDL_getenv(name);
|
||||
for (hint = SDL_hints; hint; hint = hint->next) {
|
||||
if (SDL_strcmp(name, hint->name) == 0) {
|
||||
if ((env == NULL && hint->value != NULL) ||
|
||||
(env != NULL && hint->value == NULL) ||
|
||||
(env != NULL && SDL_strcmp(env, hint->value) != 0)) {
|
||||
for (entry = hint->callbacks; entry;) {
|
||||
/* Save the next entry in case this one is deleted */
|
||||
SDL_HintWatch *next = entry->next;
|
||||
entry->callback(entry->userdata, name, hint->value, env);
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
SDL_free(hint->value);
|
||||
hint->value = NULL;
|
||||
hint->priority = SDL_HINT_DEFAULT;
|
||||
return SDL_TRUE;
|
||||
}
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
void SDL_ResetHints(void)
|
||||
{
|
||||
const char *env;
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry;
|
||||
|
||||
for (hint = SDL_hints; hint; hint = hint->next) {
|
||||
env = SDL_getenv(hint->name);
|
||||
if ((env == NULL && hint->value != NULL) ||
|
||||
(env != NULL && hint->value == NULL) ||
|
||||
(env != NULL && SDL_strcmp(env, hint->value) != 0)) {
|
||||
for (entry = hint->callbacks; entry;) {
|
||||
/* Save the next entry in case this one is deleted */
|
||||
SDL_HintWatch *next = entry->next;
|
||||
entry->callback(entry->userdata, hint->name, hint->value, env);
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
SDL_free(hint->value);
|
||||
hint->value = NULL;
|
||||
hint->priority = SDL_HINT_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_bool SDL_SetHint(const char *name, const char *value)
|
||||
{
|
||||
return SDL_SetHintWithPriority(name, value, SDL_HINT_NORMAL);
|
||||
}
|
||||
|
||||
const char *SDL_GetHint(const char *name)
|
||||
{
|
||||
const char *env;
|
||||
SDL_Hint *hint;
|
||||
|
||||
env = SDL_getenv(name);
|
||||
for (hint = SDL_hints; hint; hint = hint->next) {
|
||||
if (SDL_strcmp(name, hint->name) == 0) {
|
||||
if (env == NULL || hint->priority == SDL_HINT_OVERRIDE) {
|
||||
return hint->value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
SDL_bool SDL_GetStringBoolean(const char *value, SDL_bool default_value)
|
||||
{
|
||||
if (value == NULL || !*value) {
|
||||
return default_value;
|
||||
}
|
||||
if (*value == '0' || SDL_strcasecmp(value, "false") == 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
SDL_bool SDL_GetHintBoolean(const char *name, SDL_bool default_value)
|
||||
{
|
||||
const char *hint = SDL_GetHint(name);
|
||||
return SDL_GetStringBoolean(hint, default_value);
|
||||
}
|
||||
|
||||
void SDL_AddHintCallback(const char *name, SDL_HintCallback callback, void *userdata)
|
||||
{
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry;
|
||||
const char *value;
|
||||
|
||||
if (name == NULL || !*name) {
|
||||
SDL_InvalidParamError("name");
|
||||
return;
|
||||
}
|
||||
if (!callback) {
|
||||
SDL_InvalidParamError("callback");
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_DelHintCallback(name, callback, userdata);
|
||||
|
||||
entry = (SDL_HintWatch *)SDL_malloc(sizeof(*entry));
|
||||
if (entry == NULL) {
|
||||
SDL_OutOfMemory();
|
||||
return;
|
||||
}
|
||||
entry->callback = callback;
|
||||
entry->userdata = userdata;
|
||||
|
||||
for (hint = SDL_hints; hint; hint = hint->next) {
|
||||
if (SDL_strcmp(name, hint->name) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hint == NULL) {
|
||||
/* Need to add a hint entry for this watcher */
|
||||
hint = (SDL_Hint *)SDL_malloc(sizeof(*hint));
|
||||
if (hint == NULL) {
|
||||
SDL_OutOfMemory();
|
||||
SDL_free(entry);
|
||||
return;
|
||||
}
|
||||
hint->name = SDL_strdup(name);
|
||||
if (!hint->name) {
|
||||
SDL_free(entry);
|
||||
SDL_free(hint);
|
||||
SDL_OutOfMemory();
|
||||
return;
|
||||
}
|
||||
hint->value = NULL;
|
||||
hint->priority = SDL_HINT_DEFAULT;
|
||||
hint->callbacks = NULL;
|
||||
hint->next = SDL_hints;
|
||||
SDL_hints = hint;
|
||||
}
|
||||
|
||||
/* Add it to the callbacks for this hint */
|
||||
entry->next = hint->callbacks;
|
||||
hint->callbacks = entry;
|
||||
|
||||
/* Now call it with the current value */
|
||||
value = SDL_GetHint(name);
|
||||
callback(userdata, name, value, value);
|
||||
}
|
||||
|
||||
void SDL_DelHintCallback(const char *name, SDL_HintCallback callback, void *userdata)
|
||||
{
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry, *prev;
|
||||
|
||||
for (hint = SDL_hints; hint; hint = hint->next) {
|
||||
if (SDL_strcmp(name, hint->name) == 0) {
|
||||
prev = NULL;
|
||||
for (entry = hint->callbacks; entry; entry = entry->next) {
|
||||
if (callback == entry->callback && userdata == entry->userdata) {
|
||||
if (prev) {
|
||||
prev->next = entry->next;
|
||||
} else {
|
||||
hint->callbacks = entry->next;
|
||||
}
|
||||
SDL_free(entry);
|
||||
break;
|
||||
}
|
||||
prev = entry;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_ClearHints(void)
|
||||
{
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry;
|
||||
|
||||
while (SDL_hints) {
|
||||
hint = SDL_hints;
|
||||
SDL_hints = hint->next;
|
||||
|
||||
SDL_free(hint->name);
|
||||
SDL_free(hint->value);
|
||||
for (entry = hint->callbacks; entry;) {
|
||||
SDL_HintWatch *freeable = entry;
|
||||
entry = entry->next;
|
||||
SDL_free(freeable);
|
||||
}
|
||||
SDL_free(hint);
|
||||
}
|
||||
}
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
32
third_party/SDL/src/SDL_hints_c.h
vendored
Normal file
32
third_party/SDL/src/SDL_hints_c.h
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "./SDL_internal.h"
|
||||
|
||||
/* This file defines useful function for working with SDL hints */
|
||||
|
||||
#ifndef SDL_hints_c_h_
|
||||
#define SDL_hints_c_h_
|
||||
|
||||
extern SDL_bool SDL_GetStringBoolean(const char *value, SDL_bool default_value);
|
||||
|
||||
#endif /* SDL_hints_c_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
133
third_party/SDL/src/SDL_internal.h
vendored
Normal file
133
third_party/SDL/src/SDL_internal.h
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#ifndef SDL_internal_h_
|
||||
#define SDL_internal_h_
|
||||
|
||||
/* Many of SDL's features require _GNU_SOURCE on various platforms */
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
/* This is for a variable-length array at the end of a struct:
|
||||
struct x { int y; char z[SDL_VARIABLE_LENGTH_ARRAY]; };
|
||||
Use this because GCC 2 needs different magic than other compilers. */
|
||||
#if (defined(__GNUC__) && (__GNUC__ <= 2)) || defined(__CC_ARM) || defined(__cplusplus)
|
||||
#define SDL_VARIABLE_LENGTH_ARRAY 1
|
||||
#else
|
||||
#define SDL_VARIABLE_LENGTH_ARRAY
|
||||
#endif
|
||||
|
||||
#define SDL_MAX_SMALL_ALLOC_STACKSIZE 128
|
||||
#define SDL_small_alloc(type, count, pisstack) ((*(pisstack) = ((sizeof(type) * (count)) < SDL_MAX_SMALL_ALLOC_STACKSIZE)), (*(pisstack) ? SDL_stack_alloc(type, count) : (type *)SDL_malloc(sizeof(type) * (count))))
|
||||
#define SDL_small_free(ptr, isstack) \
|
||||
if ((isstack)) { \
|
||||
SDL_stack_free(ptr); \
|
||||
} else { \
|
||||
SDL_free(ptr); \
|
||||
}
|
||||
|
||||
#include "dynapi/SDL_dynapi.h"
|
||||
|
||||
#if SDL_DYNAMIC_API
|
||||
#include "dynapi/SDL_dynapi_overrides.h"
|
||||
/* force DECLSPEC off...it's all internal symbols now.
|
||||
These will have actual #defines during SDL_dynapi.c only */
|
||||
#define DECLSPEC
|
||||
#endif
|
||||
|
||||
#include "SDL_config.h"
|
||||
|
||||
/* If you run into a warning that O_CLOEXEC is redefined, update the SDL configuration header for your platform to add HAVE_O_CLOEXEC */
|
||||
#ifndef HAVE_O_CLOEXEC
|
||||
#define O_CLOEXEC 0
|
||||
#endif
|
||||
|
||||
/* A few #defines to reduce SDL2 footprint.
|
||||
Only effective when library is statically linked.
|
||||
You have to manually edit this file. */
|
||||
#ifndef SDL_LEAN_AND_MEAN
|
||||
#define SDL_LEAN_AND_MEAN 0
|
||||
#endif
|
||||
|
||||
/* Optimized functions from 'SDL_blit_0.c'
|
||||
- blit with source BitsPerPixel < 8, palette */
|
||||
#ifndef SDL_HAVE_BLIT_0
|
||||
#define SDL_HAVE_BLIT_0 !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* Optimized functions from 'SDL_blit_1.c'
|
||||
- blit with source BytesPerPixel == 1, palette */
|
||||
#ifndef SDL_HAVE_BLIT_1
|
||||
#define SDL_HAVE_BLIT_1 !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* Optimized functions from 'SDL_blit_A.c'
|
||||
- blit with 'SDL_BLENDMODE_BLEND' blending mode */
|
||||
#ifndef SDL_HAVE_BLIT_A
|
||||
#define SDL_HAVE_BLIT_A !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* Optimized functions from 'SDL_blit_N.c'
|
||||
- blit with COLORKEY mode, or nothing */
|
||||
#ifndef SDL_HAVE_BLIT_N
|
||||
#define SDL_HAVE_BLIT_N !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* Optimized functions from 'SDL_blit_N.c'
|
||||
- RGB565 conversion with Lookup tables */
|
||||
#ifndef SDL_HAVE_BLIT_N_RGB565
|
||||
#define SDL_HAVE_BLIT_N_RGB565 !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* Optimized functions from 'SDL_blit_AUTO.c'
|
||||
- blit with modulate color, modulate alpha, any blending mode
|
||||
- scaling or not */
|
||||
#ifndef SDL_HAVE_BLIT_AUTO
|
||||
#define SDL_HAVE_BLIT_AUTO !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* Run-Length-Encoding
|
||||
- SDL_SetColorKey() called with SDL_RLEACCEL flag */
|
||||
#ifndef SDL_HAVE_RLE
|
||||
#define SDL_HAVE_RLE !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* Software SDL_Renderer
|
||||
- creation of software renderer
|
||||
- *not* general blitting functions
|
||||
- {blend,draw}{fillrect,line,point} internal functions */
|
||||
#ifndef SDL_VIDEO_RENDER_SW
|
||||
#define SDL_VIDEO_RENDER_SW !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* YUV formats
|
||||
- handling of YUV surfaces
|
||||
- blitting and conversion functions */
|
||||
#ifndef SDL_HAVE_YUV
|
||||
#define SDL_HAVE_YUV !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include "SDL_assert.h"
|
||||
#include "SDL_log.h"
|
||||
|
||||
#endif /* SDL_internal_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
89
third_party/SDL/src/SDL_list.c
vendored
Normal file
89
third_party/SDL/src/SDL_list.c
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "./SDL_internal.h"
|
||||
|
||||
#include "SDL.h"
|
||||
#include "./SDL_list.h"
|
||||
|
||||
/* Push */
|
||||
int SDL_ListAdd(SDL_ListNode **head, void *ent)
|
||||
{
|
||||
SDL_ListNode *node = SDL_malloc(sizeof(*node));
|
||||
|
||||
if (node == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
node->entry = ent;
|
||||
node->next = *head;
|
||||
*head = node;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pop from end as a FIFO (if add with SDL_ListAdd) */
|
||||
void SDL_ListPop(SDL_ListNode **head, void **ent)
|
||||
{
|
||||
SDL_ListNode **ptr = head;
|
||||
|
||||
/* Invalid or empty */
|
||||
if (head == NULL || *head == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
while ((*ptr)->next) {
|
||||
ptr = &(*ptr)->next;
|
||||
}
|
||||
|
||||
if (ent) {
|
||||
*ent = (*ptr)->entry;
|
||||
}
|
||||
|
||||
SDL_free(*ptr);
|
||||
*ptr = NULL;
|
||||
}
|
||||
|
||||
void SDL_ListRemove(SDL_ListNode **head, void *ent)
|
||||
{
|
||||
SDL_ListNode **ptr = head;
|
||||
|
||||
while (*ptr) {
|
||||
if ((*ptr)->entry == ent) {
|
||||
SDL_ListNode *tmp = *ptr;
|
||||
*ptr = (*ptr)->next;
|
||||
SDL_free(tmp);
|
||||
return;
|
||||
}
|
||||
ptr = &(*ptr)->next;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_ListClear(SDL_ListNode **head)
|
||||
{
|
||||
SDL_ListNode *l = *head;
|
||||
*head = NULL;
|
||||
while (l) {
|
||||
SDL_ListNode *tmp = l;
|
||||
l = l->next;
|
||||
SDL_free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
38
third_party/SDL/src/SDL_list.h
vendored
Normal file
38
third_party/SDL/src/SDL_list.h
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_list_h_
|
||||
#define SDL_list_h_
|
||||
|
||||
typedef struct SDL_ListNode
|
||||
{
|
||||
void *entry;
|
||||
struct SDL_ListNode *next;
|
||||
} SDL_ListNode;
|
||||
|
||||
int SDL_ListAdd(SDL_ListNode **head, void *ent);
|
||||
void SDL_ListPop(SDL_ListNode **head, void **ent);
|
||||
void SDL_ListRemove(SDL_ListNode **head, void *ent);
|
||||
void SDL_ListClear(SDL_ListNode **head);
|
||||
|
||||
#endif /* SDL_list_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
505
third_party/SDL/src/SDL_log.c
vendored
Normal file
505
third_party/SDL/src/SDL_log.c
vendored
Normal file
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "./SDL_internal.h"
|
||||
|
||||
#if defined(__WIN32__) || defined(__WINRT__) || defined(__GDK__)
|
||||
#include "core/windows/SDL_windows.h"
|
||||
#endif
|
||||
|
||||
/* Simple log messages in SDL */
|
||||
|
||||
#include "SDL_error.h"
|
||||
#include "SDL_log.h"
|
||||
#include "SDL_mutex.h"
|
||||
#include "SDL_log_c.h"
|
||||
|
||||
#if HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <android/log.h>
|
||||
#endif
|
||||
|
||||
#include "stdlib/SDL_vacopy.h"
|
||||
|
||||
/* The size of the stack buffer to use for rendering log messages. */
|
||||
#define SDL_MAX_LOG_MESSAGE_STACK 256
|
||||
|
||||
#define DEFAULT_PRIORITY SDL_LOG_PRIORITY_CRITICAL
|
||||
#define DEFAULT_ASSERT_PRIORITY SDL_LOG_PRIORITY_WARN
|
||||
#define DEFAULT_APPLICATION_PRIORITY SDL_LOG_PRIORITY_INFO
|
||||
#define DEFAULT_TEST_PRIORITY SDL_LOG_PRIORITY_VERBOSE
|
||||
|
||||
typedef struct SDL_LogLevel
|
||||
{
|
||||
int category;
|
||||
SDL_LogPriority priority;
|
||||
struct SDL_LogLevel *next;
|
||||
} SDL_LogLevel;
|
||||
|
||||
/* The default log output function */
|
||||
static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, const char *message);
|
||||
|
||||
static SDL_LogLevel *SDL_loglevels;
|
||||
static SDL_LogPriority SDL_default_priority = DEFAULT_PRIORITY;
|
||||
static SDL_LogPriority SDL_assert_priority = DEFAULT_ASSERT_PRIORITY;
|
||||
static SDL_LogPriority SDL_application_priority = DEFAULT_APPLICATION_PRIORITY;
|
||||
static SDL_LogPriority SDL_test_priority = DEFAULT_TEST_PRIORITY;
|
||||
static SDL_LogOutputFunction SDL_log_function = SDL_LogOutput;
|
||||
static void *SDL_log_userdata = NULL;
|
||||
static SDL_mutex *log_function_mutex = NULL;
|
||||
|
||||
static const char *SDL_priority_prefixes[SDL_NUM_LOG_PRIORITIES] = {
|
||||
NULL,
|
||||
"VERBOSE",
|
||||
"DEBUG",
|
||||
"INFO",
|
||||
"WARN",
|
||||
"ERROR",
|
||||
"CRITICAL"
|
||||
};
|
||||
|
||||
#ifdef __ANDROID__
|
||||
static const char *SDL_category_prefixes[] = {
|
||||
"APP",
|
||||
"ERROR",
|
||||
"ASSERT",
|
||||
"SYSTEM",
|
||||
"AUDIO",
|
||||
"VIDEO",
|
||||
"RENDER",
|
||||
"INPUT",
|
||||
"TEST"
|
||||
};
|
||||
|
||||
SDL_COMPILE_TIME_ASSERT(category_prefixes_enum, SDL_TABLESIZE(SDL_category_prefixes) == SDL_LOG_CATEGORY_RESERVED1);
|
||||
|
||||
static int SDL_android_priority[SDL_NUM_LOG_PRIORITIES] = {
|
||||
ANDROID_LOG_UNKNOWN,
|
||||
ANDROID_LOG_VERBOSE,
|
||||
ANDROID_LOG_DEBUG,
|
||||
ANDROID_LOG_INFO,
|
||||
ANDROID_LOG_WARN,
|
||||
ANDROID_LOG_ERROR,
|
||||
ANDROID_LOG_FATAL
|
||||
};
|
||||
#endif /* __ANDROID__ */
|
||||
|
||||
void SDL_LogInit(void)
|
||||
{
|
||||
if (log_function_mutex == NULL) {
|
||||
/* if this fails we'll try to continue without it. */
|
||||
log_function_mutex = SDL_CreateMutex();
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_LogQuit(void)
|
||||
{
|
||||
SDL_LogResetPriorities();
|
||||
if (log_function_mutex) {
|
||||
SDL_DestroyMutex(log_function_mutex);
|
||||
log_function_mutex = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_LogSetAllPriority(SDL_LogPriority priority)
|
||||
{
|
||||
SDL_LogLevel *entry;
|
||||
|
||||
for (entry = SDL_loglevels; entry; entry = entry->next) {
|
||||
entry->priority = priority;
|
||||
}
|
||||
SDL_default_priority = priority;
|
||||
SDL_assert_priority = priority;
|
||||
SDL_application_priority = priority;
|
||||
}
|
||||
|
||||
void SDL_LogSetPriority(int category, SDL_LogPriority priority)
|
||||
{
|
||||
SDL_LogLevel *entry;
|
||||
|
||||
for (entry = SDL_loglevels; entry; entry = entry->next) {
|
||||
if (entry->category == category) {
|
||||
entry->priority = priority;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a new entry */
|
||||
entry = (SDL_LogLevel *)SDL_malloc(sizeof(*entry));
|
||||
if (entry) {
|
||||
entry->category = category;
|
||||
entry->priority = priority;
|
||||
entry->next = SDL_loglevels;
|
||||
SDL_loglevels = entry;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_LogPriority SDL_LogGetPriority(int category)
|
||||
{
|
||||
SDL_LogLevel *entry;
|
||||
|
||||
for (entry = SDL_loglevels; entry; entry = entry->next) {
|
||||
if (entry->category == category) {
|
||||
return entry->priority;
|
||||
}
|
||||
}
|
||||
|
||||
if (category == SDL_LOG_CATEGORY_TEST) {
|
||||
return SDL_test_priority;
|
||||
} else if (category == SDL_LOG_CATEGORY_APPLICATION) {
|
||||
return SDL_application_priority;
|
||||
} else if (category == SDL_LOG_CATEGORY_ASSERT) {
|
||||
return SDL_assert_priority;
|
||||
} else {
|
||||
return SDL_default_priority;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_LogResetPriorities(void)
|
||||
{
|
||||
SDL_LogLevel *entry;
|
||||
|
||||
while (SDL_loglevels) {
|
||||
entry = SDL_loglevels;
|
||||
SDL_loglevels = entry->next;
|
||||
SDL_free(entry);
|
||||
}
|
||||
|
||||
SDL_default_priority = DEFAULT_PRIORITY;
|
||||
SDL_assert_priority = DEFAULT_ASSERT_PRIORITY;
|
||||
SDL_application_priority = DEFAULT_APPLICATION_PRIORITY;
|
||||
SDL_test_priority = DEFAULT_TEST_PRIORITY;
|
||||
}
|
||||
|
||||
void SDL_Log(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void SDL_LogVerbose(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
SDL_LogMessageV(category, SDL_LOG_PRIORITY_VERBOSE, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void SDL_LogDebug(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
SDL_LogMessageV(category, SDL_LOG_PRIORITY_DEBUG, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void SDL_LogInfo(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
SDL_LogMessageV(category, SDL_LOG_PRIORITY_INFO, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void SDL_LogWarn(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
SDL_LogMessageV(category, SDL_LOG_PRIORITY_WARN, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void SDL_LogError(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
SDL_LogMessageV(category, SDL_LOG_PRIORITY_ERROR, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void SDL_LogCritical(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
SDL_LogMessageV(category, SDL_LOG_PRIORITY_CRITICAL, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void SDL_LogMessage(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
SDL_LogMessageV(category, priority, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
static const char *GetCategoryPrefix(int category)
|
||||
{
|
||||
if (category < SDL_LOG_CATEGORY_RESERVED1) {
|
||||
return SDL_category_prefixes[category];
|
||||
}
|
||||
if (category < SDL_LOG_CATEGORY_CUSTOM) {
|
||||
return "RESERVED";
|
||||
}
|
||||
return "CUSTOM";
|
||||
}
|
||||
#endif /* __ANDROID__ */
|
||||
|
||||
void SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va_list ap)
|
||||
{
|
||||
char *message = NULL;
|
||||
char stack_buf[SDL_MAX_LOG_MESSAGE_STACK];
|
||||
size_t len_plus_term;
|
||||
int len;
|
||||
va_list aq;
|
||||
|
||||
/* Nothing to do if we don't have an output function */
|
||||
if (!SDL_log_function) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make sure we don't exceed array bounds */
|
||||
if ((int)priority < 0 || priority >= SDL_NUM_LOG_PRIORITIES) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* See if we want to do anything with this message */
|
||||
if (priority < SDL_LogGetPriority(category)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (log_function_mutex == NULL) {
|
||||
/* this mutex creation can race if you log from two threads at startup. You should have called SDL_Init first! */
|
||||
log_function_mutex = SDL_CreateMutex();
|
||||
}
|
||||
|
||||
/* Render into stack buffer */
|
||||
va_copy(aq, ap);
|
||||
len = SDL_vsnprintf(stack_buf, sizeof(stack_buf), fmt, aq);
|
||||
va_end(aq);
|
||||
|
||||
if (len < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* If message truncated, allocate and re-render */
|
||||
if (len >= sizeof(stack_buf) && SDL_size_add_overflow(len, 1, &len_plus_term) == 0) {
|
||||
/* Allocate exactly what we need, including the zero-terminator */
|
||||
message = (char *)SDL_malloc(len_plus_term);
|
||||
if (message == NULL) {
|
||||
return;
|
||||
}
|
||||
va_copy(aq, ap);
|
||||
len = SDL_vsnprintf(message, len_plus_term, fmt, aq);
|
||||
va_end(aq);
|
||||
} else {
|
||||
message = stack_buf;
|
||||
}
|
||||
|
||||
/* Chop off final endline. */
|
||||
if ((len > 0) && (message[len - 1] == '\n')) {
|
||||
message[--len] = '\0';
|
||||
if ((len > 0) && (message[len - 1] == '\r')) { /* catch "\r\n", too. */
|
||||
message[--len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
SDL_LockMutex(log_function_mutex);
|
||||
SDL_log_function(SDL_log_userdata, category, priority, message);
|
||||
SDL_UnlockMutex(log_function_mutex);
|
||||
|
||||
/* Free only if dynamically allocated */
|
||||
if (message != stack_buf) {
|
||||
SDL_free(message);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__WIN32__) && !defined(HAVE_STDIO_H) && !defined(__WINRT__) && !defined(__GDK__)
|
||||
/* Flag tracking the attachment of the console: 0=unattached, 1=attached to a console, 2=attached to a file, -1=error */
|
||||
static int consoleAttached = 0;
|
||||
|
||||
/* Handle to stderr output of console. */
|
||||
static HANDLE stderrHandle = NULL;
|
||||
#endif
|
||||
|
||||
static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
|
||||
const char *message)
|
||||
{
|
||||
#if defined(__WIN32__) || defined(__WINRT__) || defined(__GDK__)
|
||||
/* Way too many allocations here, urgh */
|
||||
/* Note: One can't call SDL_SetError here, since that function itself logs. */
|
||||
{
|
||||
char *output;
|
||||
size_t length;
|
||||
LPTSTR tstr;
|
||||
SDL_bool isstack;
|
||||
|
||||
#if !defined(HAVE_STDIO_H) && !defined(__WINRT__) && !defined(__GDK__)
|
||||
BOOL attachResult;
|
||||
DWORD attachError;
|
||||
DWORD charsWritten;
|
||||
DWORD consoleMode;
|
||||
|
||||
/* Maybe attach console and get stderr handle */
|
||||
if (consoleAttached == 0) {
|
||||
attachResult = AttachConsole(ATTACH_PARENT_PROCESS);
|
||||
if (!attachResult) {
|
||||
attachError = GetLastError();
|
||||
if (attachError == ERROR_INVALID_HANDLE) {
|
||||
/* This is expected when running from Visual Studio */
|
||||
/*OutputDebugString(TEXT("Parent process has no console\r\n"));*/
|
||||
consoleAttached = -1;
|
||||
} else if (attachError == ERROR_GEN_FAILURE) {
|
||||
OutputDebugString(TEXT("Could not attach to console of parent process\r\n"));
|
||||
consoleAttached = -1;
|
||||
} else if (attachError == ERROR_ACCESS_DENIED) {
|
||||
/* Already attached */
|
||||
consoleAttached = 1;
|
||||
} else {
|
||||
OutputDebugString(TEXT("Error attaching console\r\n"));
|
||||
consoleAttached = -1;
|
||||
}
|
||||
} else {
|
||||
/* Newly attached */
|
||||
consoleAttached = 1;
|
||||
}
|
||||
|
||||
if (consoleAttached == 1) {
|
||||
stderrHandle = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
if (GetConsoleMode(stderrHandle, &consoleMode) == 0) {
|
||||
/* WriteConsole fails if the output is redirected to a file. Must use WriteFile instead. */
|
||||
consoleAttached = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) && !defined(__GDK__) */
|
||||
|
||||
length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1 + 1;
|
||||
output = SDL_small_alloc(char, length, &isstack);
|
||||
(void)SDL_snprintf(output, length, "%s: %s\r\n", SDL_priority_prefixes[priority], message);
|
||||
tstr = WIN_UTF8ToString(output);
|
||||
|
||||
/* Output to debugger */
|
||||
OutputDebugString(tstr);
|
||||
|
||||
#if !defined(HAVE_STDIO_H) && !defined(__WINRT__) && !defined(__GDK__)
|
||||
/* Screen output to stderr, if console was attached. */
|
||||
if (consoleAttached == 1) {
|
||||
if (!WriteConsole(stderrHandle, tstr, (DWORD)SDL_tcslen(tstr), &charsWritten, NULL)) {
|
||||
OutputDebugString(TEXT("Error calling WriteConsole\r\n"));
|
||||
if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
|
||||
OutputDebugString(TEXT("Insufficient heap memory to write message\r\n"));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (consoleAttached == 2) {
|
||||
if (!WriteFile(stderrHandle, output, (DWORD)SDL_strlen(output), &charsWritten, NULL)) {
|
||||
OutputDebugString(TEXT("Error calling WriteFile\r\n"));
|
||||
}
|
||||
}
|
||||
#endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) && !defined(__GDK__) */
|
||||
|
||||
SDL_free(tstr);
|
||||
SDL_small_free(output, isstack);
|
||||
}
|
||||
#elif defined(__ANDROID__)
|
||||
{
|
||||
char tag[32];
|
||||
|
||||
SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category));
|
||||
__android_log_write(SDL_android_priority[priority], tag, message);
|
||||
}
|
||||
#elif defined(__APPLE__) && (defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT))
|
||||
/* Technically we don't need Cocoa/UIKit, but that's where this function is defined for now.
|
||||
*/
|
||||
extern void SDL_NSLog(const char *prefix, const char *text);
|
||||
{
|
||||
SDL_NSLog(SDL_priority_prefixes[priority], message);
|
||||
return;
|
||||
}
|
||||
#elif defined(__PSP__) || defined(__PS2__)
|
||||
{
|
||||
FILE *pFile;
|
||||
pFile = fopen("SDL_Log.txt", "a");
|
||||
if (pFile != NULL) {
|
||||
(void)fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message);
|
||||
(void)fclose(pFile);
|
||||
}
|
||||
}
|
||||
#elif defined(__VITA__)
|
||||
{
|
||||
FILE *pFile;
|
||||
pFile = fopen("ux0:/data/SDL_Log.txt", "a");
|
||||
if (pFile != NULL) {
|
||||
(void)fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message);
|
||||
(void)fclose(pFile);
|
||||
}
|
||||
}
|
||||
#elif defined(__3DS__)
|
||||
{
|
||||
FILE *pFile;
|
||||
pFile = fopen("sdmc:/3ds/SDL_Log.txt", "a");
|
||||
if (pFile != NULL) {
|
||||
(void)fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message);
|
||||
(void)fclose(pFile);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if HAVE_STDIO_H && \
|
||||
!(defined(__APPLE__) && (defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT)))
|
||||
fprintf(stderr, "%s: %s\n", SDL_priority_prefixes[priority], message);
|
||||
#if __NACL__
|
||||
fflush(stderr);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata)
|
||||
{
|
||||
if (callback) {
|
||||
*callback = SDL_log_function;
|
||||
}
|
||||
if (userdata) {
|
||||
*userdata = SDL_log_userdata;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata)
|
||||
{
|
||||
SDL_log_function = callback;
|
||||
SDL_log_userdata = userdata;
|
||||
}
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
33
third_party/SDL/src/SDL_log_c.h
vendored
Normal file
33
third_party/SDL/src/SDL_log_c.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "./SDL_internal.h"
|
||||
|
||||
/* This file defines useful function for working with SDL logging */
|
||||
|
||||
#ifndef SDL_log_c_h_
|
||||
#define SDL_log_c_h_
|
||||
|
||||
extern void SDL_LogInit(void);
|
||||
extern void SDL_LogQuit(void);
|
||||
|
||||
#endif /* SDL_log_c_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
52
third_party/SDL/src/SDL_utils.c
vendored
Normal file
52
third_party/SDL/src/SDL_utils.c
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_utils_c.h"
|
||||
|
||||
/* Common utility functions that aren't in the public API */
|
||||
|
||||
int SDL_powerof2(int x)
|
||||
{
|
||||
int value;
|
||||
|
||||
if (x <= 0) {
|
||||
/* Return some sane value - we shouldn't hit this in our use cases */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This trick works for 32-bit values */
|
||||
{
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_powerof2, sizeof(x) == sizeof(Uint32));
|
||||
}
|
||||
value = x;
|
||||
value -= 1;
|
||||
value |= value >> 1;
|
||||
value |= value >> 2;
|
||||
value |= value >> 4;
|
||||
value |= value >> 8;
|
||||
value |= value >> 16;
|
||||
value += 1;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
32
third_party/SDL/src/SDL_utils_c.h
vendored
Normal file
32
third_party/SDL/src/SDL_utils_c.h
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_utils_h_
|
||||
#define SDL_utils_h_
|
||||
|
||||
/* Common utility functions that aren't in the public API */
|
||||
|
||||
/* Return the smallest power of 2 greater than or equal to 'x' */
|
||||
extern int SDL_powerof2(int x);
|
||||
|
||||
#endif /* SDL_utils_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
305
third_party/SDL/src/atomic/SDL_atomic.c
vendored
Normal file
305
third_party/SDL/src/atomic/SDL_atomic.c
vendored
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../SDL_internal.h"
|
||||
|
||||
#include "SDL_atomic.h"
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1500)
|
||||
#include <intrin.h>
|
||||
#define HAVE_MSC_ATOMICS 1
|
||||
#endif
|
||||
|
||||
#if defined(__MACOSX__) /* !!! FIXME: should we favor gcc atomics? */
|
||||
#include <libkern/OSAtomic.h>
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_GCC_ATOMICS) && defined(__SOLARIS__)
|
||||
#include <atomic.h>
|
||||
#endif
|
||||
|
||||
/* The __atomic_load_n() intrinsic showed up in different times for different compilers. */
|
||||
#if defined(__clang__)
|
||||
#if __has_builtin(__atomic_load_n) || defined(HAVE_GCC_ATOMICS)
|
||||
/* !!! FIXME: this advertises as available in the NDK but uses an external symbol we don't have.
|
||||
It might be in a later NDK or we might need an extra library? --ryan. */
|
||||
#if !defined(__ANDROID__)
|
||||
#define HAVE_ATOMIC_LOAD_N 1
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(__GNUC__)
|
||||
#if (__GNUC__ >= 5)
|
||||
#define HAVE_ATOMIC_LOAD_N 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
#if defined(__WATCOMC__) && defined(__386__)
|
||||
SDL_COMPILE_TIME_ASSERT(intsize, 4==sizeof(int));
|
||||
#define HAVE_WATCOM_ATOMICS
|
||||
extern __inline int _SDL_xchg_watcom(volatile int *a, int v);
|
||||
#pragma aux _SDL_xchg_watcom = \
|
||||
"lock xchg [ecx], eax" \
|
||||
parm [ecx] [eax] \
|
||||
value [eax] \
|
||||
modify exact [eax];
|
||||
|
||||
extern __inline unsigned char _SDL_cmpxchg_watcom(volatile int *a, int newval, int oldval);
|
||||
#pragma aux _SDL_cmpxchg_watcom = \
|
||||
"lock cmpxchg [edx], ecx" \
|
||||
"setz al" \
|
||||
parm [edx] [ecx] [eax] \
|
||||
value [al] \
|
||||
modify exact [eax];
|
||||
|
||||
extern __inline int _SDL_xadd_watcom(volatile int *a, int v);
|
||||
#pragma aux _SDL_xadd_watcom = \
|
||||
"lock xadd [ecx], eax" \
|
||||
parm [ecx] [eax] \
|
||||
value [eax] \
|
||||
modify exact [eax];
|
||||
|
||||
#endif /* __WATCOMC__ && __386__ */
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
/*
|
||||
If any of the operations are not provided then we must emulate some
|
||||
of them. That means we need a nice implementation of spin locks
|
||||
that avoids the "one big lock" problem. We use a vector of spin
|
||||
locks and pick which one to use based on the address of the operand
|
||||
of the function.
|
||||
|
||||
To generate the index of the lock we first shift by 3 bits to get
|
||||
rid on the zero bits that result from 32 and 64 bit allignment of
|
||||
data. We then mask off all but 5 bits and use those 5 bits as an
|
||||
index into the table.
|
||||
|
||||
Picking the lock this way insures that accesses to the same data at
|
||||
the same time will go to the same lock. OTOH, accesses to different
|
||||
data have only a 1/32 chance of hitting the same lock. That should
|
||||
pretty much eliminate the chances of several atomic operations on
|
||||
different data from waiting on the same "big lock". If it isn't
|
||||
then the table of locks can be expanded to a new size so long as
|
||||
the new size is a power of two.
|
||||
|
||||
Contributed by Bob Pendleton, bob@pendleton.com
|
||||
*/
|
||||
|
||||
#if !defined(HAVE_MSC_ATOMICS) && !defined(HAVE_GCC_ATOMICS) && !defined(__MACOSX__) && !defined(__SOLARIS__) && !defined(HAVE_WATCOM_ATOMICS)
|
||||
#define EMULATE_CAS 1
|
||||
#endif
|
||||
|
||||
#if EMULATE_CAS
|
||||
static SDL_SpinLock locks[32];
|
||||
|
||||
static SDL_INLINE void enterLock(void *a)
|
||||
{
|
||||
uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
|
||||
|
||||
SDL_AtomicLock(&locks[index]);
|
||||
}
|
||||
|
||||
static SDL_INLINE void leaveLock(void *a)
|
||||
{
|
||||
uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
|
||||
|
||||
SDL_AtomicUnlock(&locks[index]);
|
||||
}
|
||||
#endif
|
||||
|
||||
SDL_bool SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval)
|
||||
{
|
||||
#ifdef HAVE_MSC_ATOMICS
|
||||
SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(a->value));
|
||||
return _InterlockedCompareExchange((long *)&a->value, (long)newval, (long)oldval) == (long)oldval;
|
||||
#elif defined(HAVE_WATCOM_ATOMICS)
|
||||
return (SDL_bool)_SDL_cmpxchg_watcom(&a->value, newval, oldval);
|
||||
#elif defined(HAVE_GCC_ATOMICS)
|
||||
return (SDL_bool) __sync_bool_compare_and_swap(&a->value, oldval, newval);
|
||||
#elif defined(__MACOSX__) /* this is deprecated in 10.12 sdk; favor gcc atomics. */
|
||||
return (SDL_bool) OSAtomicCompareAndSwap32Barrier(oldval, newval, &a->value);
|
||||
#elif defined(__SOLARIS__)
|
||||
return (SDL_bool)((int)atomic_cas_uint((volatile uint_t *)&a->value, (uint_t)oldval, (uint_t)newval) == oldval);
|
||||
#elif EMULATE_CAS
|
||||
SDL_bool retval = SDL_FALSE;
|
||||
|
||||
enterLock(a);
|
||||
if (a->value == oldval) {
|
||||
a->value = newval;
|
||||
retval = SDL_TRUE;
|
||||
}
|
||||
leaveLock(a);
|
||||
|
||||
return retval;
|
||||
#else
|
||||
#error Please define your platform.
|
||||
#endif
|
||||
}
|
||||
|
||||
SDL_bool SDL_AtomicCASPtr(void **a, void *oldval, void *newval)
|
||||
{
|
||||
#if defined(HAVE_MSC_ATOMICS)
|
||||
return _InterlockedCompareExchangePointer(a, newval, oldval) == oldval;
|
||||
#elif defined(HAVE_WATCOM_ATOMICS)
|
||||
return (SDL_bool)_SDL_cmpxchg_watcom((int *)a, (long)newval, (long)oldval);
|
||||
#elif defined(HAVE_GCC_ATOMICS)
|
||||
return __sync_bool_compare_and_swap(a, oldval, newval);
|
||||
#elif defined(__MACOSX__) && defined(__LP64__) /* this is deprecated in 10.12 sdk; favor gcc atomics. */
|
||||
return (SDL_bool) OSAtomicCompareAndSwap64Barrier((int64_t)oldval, (int64_t)newval, (int64_t*) a);
|
||||
#elif defined(__MACOSX__) && !defined(__LP64__) /* this is deprecated in 10.12 sdk; favor gcc atomics. */
|
||||
return (SDL_bool) OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t*) a);
|
||||
#elif defined(__SOLARIS__)
|
||||
return (SDL_bool)(atomic_cas_ptr(a, oldval, newval) == oldval);
|
||||
#elif EMULATE_CAS
|
||||
SDL_bool retval = SDL_FALSE;
|
||||
|
||||
enterLock(a);
|
||||
if (*a == oldval) {
|
||||
*a = newval;
|
||||
retval = SDL_TRUE;
|
||||
}
|
||||
leaveLock(a);
|
||||
|
||||
return retval;
|
||||
#else
|
||||
#error Please define your platform.
|
||||
#endif
|
||||
}
|
||||
|
||||
int SDL_AtomicSet(SDL_atomic_t *a, int v)
|
||||
{
|
||||
#ifdef HAVE_MSC_ATOMICS
|
||||
SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value));
|
||||
return _InterlockedExchange((long *)&a->value, v);
|
||||
#elif defined(HAVE_WATCOM_ATOMICS)
|
||||
return _SDL_xchg_watcom(&a->value, v);
|
||||
#elif defined(HAVE_GCC_ATOMICS)
|
||||
return __sync_lock_test_and_set(&a->value, v);
|
||||
#elif defined(__SOLARIS__)
|
||||
return (int)atomic_swap_uint((volatile uint_t *)&a->value, v);
|
||||
#else
|
||||
int value;
|
||||
do {
|
||||
value = a->value;
|
||||
} while (!SDL_AtomicCAS(a, value, v));
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *SDL_AtomicSetPtr(void **a, void *v)
|
||||
{
|
||||
#if defined(HAVE_MSC_ATOMICS)
|
||||
return _InterlockedExchangePointer(a, v);
|
||||
#elif defined(HAVE_WATCOM_ATOMICS)
|
||||
return (void *)_SDL_xchg_watcom((int *)a, (long)v);
|
||||
#elif defined(HAVE_GCC_ATOMICS)
|
||||
return __sync_lock_test_and_set(a, v);
|
||||
#elif defined(__SOLARIS__)
|
||||
return atomic_swap_ptr(a, v);
|
||||
#else
|
||||
void *value;
|
||||
do {
|
||||
value = *a;
|
||||
} while (!SDL_AtomicCASPtr(a, value, v));
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
int SDL_AtomicAdd(SDL_atomic_t *a, int v)
|
||||
{
|
||||
#ifdef HAVE_MSC_ATOMICS
|
||||
SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(long) == sizeof(a->value));
|
||||
return _InterlockedExchangeAdd((long *)&a->value, v);
|
||||
#elif defined(HAVE_WATCOM_ATOMICS)
|
||||
return _SDL_xadd_watcom(&a->value, v);
|
||||
#elif defined(HAVE_GCC_ATOMICS)
|
||||
return __sync_fetch_and_add(&a->value, v);
|
||||
#elif defined(__SOLARIS__)
|
||||
int pv = a->value;
|
||||
membar_consumer();
|
||||
atomic_add_int((volatile uint_t *)&a->value, v);
|
||||
return pv;
|
||||
#else
|
||||
int value;
|
||||
do {
|
||||
value = a->value;
|
||||
} while (!SDL_AtomicCAS(a, value, (value + v)));
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
int SDL_AtomicGet(SDL_atomic_t *a)
|
||||
{
|
||||
#ifdef HAVE_ATOMIC_LOAD_N
|
||||
return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);
|
||||
#elif defined(HAVE_MSC_ATOMICS)
|
||||
SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value));
|
||||
return _InterlockedOr((long *)&a->value, 0);
|
||||
#elif defined(HAVE_WATCOM_ATOMICS)
|
||||
return _SDL_xadd_watcom(&a->value, 0);
|
||||
#elif defined(HAVE_GCC_ATOMICS)
|
||||
return __sync_or_and_fetch(&a->value, 0);
|
||||
#elif defined(__MACOSX__) /* this is deprecated in 10.12 sdk; favor gcc atomics. */
|
||||
return sizeof(a->value) == sizeof(uint32_t) ? OSAtomicOr32Barrier(0, (volatile uint32_t *)&a->value) : OSAtomicAdd64Barrier(0, (volatile int64_t *)&a->value);
|
||||
#elif defined(__SOLARIS__)
|
||||
return atomic_or_uint((volatile uint_t *)&a->value, 0);
|
||||
#else
|
||||
int value;
|
||||
do {
|
||||
value = a->value;
|
||||
} while (!SDL_AtomicCAS(a, value, value));
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *SDL_AtomicGetPtr(void **a)
|
||||
{
|
||||
#ifdef HAVE_ATOMIC_LOAD_N
|
||||
return __atomic_load_n(a, __ATOMIC_SEQ_CST);
|
||||
#elif defined(HAVE_MSC_ATOMICS)
|
||||
return _InterlockedCompareExchangePointer(a, NULL, NULL);
|
||||
#elif defined(HAVE_GCC_ATOMICS)
|
||||
return __sync_val_compare_and_swap(a, (void *)0, (void *)0);
|
||||
#elif defined(__SOLARIS__)
|
||||
return atomic_cas_ptr(a, (void *)0, (void *)0);
|
||||
#else
|
||||
void *value;
|
||||
do {
|
||||
value = *a;
|
||||
} while (!SDL_AtomicCASPtr(a, value, value));
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SDL_MEMORY_BARRIER_USES_FUNCTION
|
||||
#error This file should be built in arm mode so the mcr instruction is available for memory barriers
|
||||
#endif
|
||||
|
||||
void SDL_MemoryBarrierReleaseFunction(void)
|
||||
{
|
||||
SDL_MemoryBarrierRelease();
|
||||
}
|
||||
|
||||
void SDL_MemoryBarrierAcquireFunction(void)
|
||||
{
|
||||
SDL_MemoryBarrierAcquire();
|
||||
}
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
213
third_party/SDL/src/atomic/SDL_spinlock.c
vendored
Normal file
213
third_party/SDL/src/atomic/SDL_spinlock.c
vendored
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../SDL_internal.h"
|
||||
|
||||
#if defined(__WIN32__) || defined(__WINRT__) || defined(__GDK__)
|
||||
#include "../core/windows/SDL_windows.h"
|
||||
#endif
|
||||
|
||||
#include "SDL_atomic.h"
|
||||
#include "SDL_mutex.h"
|
||||
#include "SDL_timer.h"
|
||||
|
||||
#if !defined(HAVE_GCC_ATOMICS) && defined(__SOLARIS__)
|
||||
#include <atomic.h>
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_GCC_ATOMICS) && defined(__RISCOS__)
|
||||
#include <unixlib/local.h>
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#if defined(PS2)
|
||||
#include <kernel.h>
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_GCC_ATOMICS) && defined(__MACOSX__)
|
||||
#include <libkern/OSAtomic.h>
|
||||
#endif
|
||||
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
#if defined(__WATCOMC__) && defined(__386__)
|
||||
SDL_COMPILE_TIME_ASSERT(locksize, 4==sizeof(SDL_SpinLock));
|
||||
extern __inline int _SDL_xchg_watcom(volatile int *a, int v);
|
||||
#pragma aux _SDL_xchg_watcom = \
|
||||
"lock xchg [ecx], eax" \
|
||||
parm [ecx] [eax] \
|
||||
value [eax] \
|
||||
modify exact [eax];
|
||||
#endif /* __WATCOMC__ && __386__ */
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
/* This function is where all the magic happens... */
|
||||
SDL_bool SDL_AtomicTryLock(SDL_SpinLock *lock)
|
||||
{
|
||||
#if SDL_ATOMIC_DISABLED
|
||||
/* Terrible terrible damage */
|
||||
static SDL_mutex *_spinlock_mutex;
|
||||
|
||||
if (_spinlock_mutex == NULL) {
|
||||
/* Race condition on first lock... */
|
||||
_spinlock_mutex = SDL_CreateMutex();
|
||||
}
|
||||
SDL_LockMutex(_spinlock_mutex);
|
||||
if (*lock == 0) {
|
||||
*lock = 1;
|
||||
SDL_UnlockMutex(_spinlock_mutex);
|
||||
return SDL_TRUE;
|
||||
} else {
|
||||
SDL_UnlockMutex(_spinlock_mutex);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
#elif HAVE_GCC_ATOMICS || HAVE_GCC_SYNC_LOCK_TEST_AND_SET
|
||||
return __sync_lock_test_and_set(lock, 1) == 0;
|
||||
|
||||
#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
|
||||
return _InterlockedExchange_acq(lock, 1) == 0;
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
SDL_COMPILE_TIME_ASSERT(locksize, sizeof(*lock) == sizeof(long));
|
||||
return InterlockedExchange((long *)lock, 1) == 0;
|
||||
|
||||
#elif defined(__WATCOMC__) && defined(__386__)
|
||||
return _SDL_xchg_watcom(lock, 1) == 0;
|
||||
|
||||
#elif defined(__GNUC__) && defined(__arm__) && \
|
||||
(defined(__ARM_ARCH_3__) || defined(__ARM_ARCH_3M__) || \
|
||||
defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) || \
|
||||
defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5TE__) || \
|
||||
defined(__ARM_ARCH_5TEJ__))
|
||||
int result;
|
||||
|
||||
#if defined(__RISCOS__)
|
||||
if (__cpucap_have_rex()) {
|
||||
__asm__ __volatile__(
|
||||
"ldrex %0, [%2]\nteq %0, #0\nstrexeq %0, %1, [%2]"
|
||||
: "=&r"(result)
|
||||
: "r"(1), "r"(lock)
|
||||
: "cc", "memory");
|
||||
return result == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
__asm__ __volatile__(
|
||||
"swp %0, %1, [%2]\n"
|
||||
: "=&r,&r"(result)
|
||||
: "r,0"(1), "r,r"(lock)
|
||||
: "memory");
|
||||
return result == 0;
|
||||
|
||||
#elif defined(__GNUC__) && defined(__arm__)
|
||||
int result;
|
||||
__asm__ __volatile__(
|
||||
"ldrex %0, [%2]\nteq %0, #0\nstrexeq %0, %1, [%2]"
|
||||
: "=&r"(result)
|
||||
: "r"(1), "r"(lock)
|
||||
: "cc", "memory");
|
||||
return result == 0;
|
||||
|
||||
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
|
||||
int result;
|
||||
__asm__ __volatile__(
|
||||
"lock ; xchgl %0, (%1)\n"
|
||||
: "=r"(result)
|
||||
: "r"(lock), "0"(1)
|
||||
: "cc", "memory");
|
||||
return result == 0;
|
||||
|
||||
#elif defined(__MACOSX__) || defined(__IPHONEOS__)
|
||||
/* Maybe used for PowerPC, but the Intel asm or gcc atomics are favored. */
|
||||
return OSAtomicCompareAndSwap32Barrier(0, 1, lock);
|
||||
|
||||
#elif defined(__SOLARIS__) && defined(_LP64)
|
||||
/* Used for Solaris with non-gcc compilers. */
|
||||
return (SDL_bool)((int)atomic_cas_64((volatile uint64_t *)lock, 0, 1) == 0);
|
||||
|
||||
#elif defined(__SOLARIS__) && !defined(_LP64)
|
||||
/* Used for Solaris with non-gcc compilers. */
|
||||
return (SDL_bool)((int)atomic_cas_32((volatile uint32_t *)lock, 0, 1) == 0);
|
||||
#elif defined(PS2)
|
||||
uint32_t oldintr;
|
||||
SDL_bool res = SDL_FALSE;
|
||||
// disable interuption
|
||||
oldintr = DIntr();
|
||||
|
||||
if (*lock == 0) {
|
||||
*lock = 1;
|
||||
res = SDL_TRUE;
|
||||
}
|
||||
// enable interuption
|
||||
if (oldintr) {
|
||||
EIntr();
|
||||
}
|
||||
return res;
|
||||
#else
|
||||
#error Please implement for your platform.
|
||||
return SDL_FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SDL_AtomicLock(SDL_SpinLock *lock)
|
||||
{
|
||||
int iterations = 0;
|
||||
/* FIXME: Should we have an eventual timeout? */
|
||||
while (!SDL_AtomicTryLock(lock)) {
|
||||
if (iterations < 32) {
|
||||
iterations++;
|
||||
SDL_CPUPauseInstruction();
|
||||
} else {
|
||||
/* !!! FIXME: this doesn't definitely give up the current timeslice, it does different things on various platforms. */
|
||||
SDL_Delay(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_AtomicUnlock(SDL_SpinLock *lock)
|
||||
{
|
||||
#if HAVE_GCC_ATOMICS || HAVE_GCC_SYNC_LOCK_TEST_AND_SET
|
||||
__sync_lock_release(lock);
|
||||
|
||||
#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
|
||||
_InterlockedExchange_rel(lock, 0);
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
_ReadWriteBarrier();
|
||||
*lock = 0;
|
||||
|
||||
#elif defined(__WATCOMC__) && defined(__386__)
|
||||
SDL_CompilerBarrier();
|
||||
*lock = 0;
|
||||
|
||||
#elif defined(__SOLARIS__)
|
||||
/* Used for Solaris when not using gcc. */
|
||||
*lock = 0;
|
||||
membar_producer();
|
||||
|
||||
#else
|
||||
*lock = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
1749
third_party/SDL/src/audio/SDL_audio.c
vendored
Normal file
1749
third_party/SDL/src/audio/SDL_audio.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
76
third_party/SDL/src/audio/SDL_audio_c.h
vendored
Normal file
76
third_party/SDL/src/audio/SDL_audio_c.h
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_audio_c_h_
|
||||
#define SDL_audio_c_h_
|
||||
|
||||
#include "../SDL_internal.h"
|
||||
|
||||
#ifndef DEBUG_CONVERT
|
||||
#define DEBUG_CONVERT 0
|
||||
#endif
|
||||
|
||||
#if DEBUG_CONVERT
|
||||
#define LOG_DEBUG_CONVERT(from, to) SDL_Log("SDL_AUDIO_CONVERT: Converting %s to %s.\n", from, to);
|
||||
#else
|
||||
#define LOG_DEBUG_CONVERT(from, to)
|
||||
#endif
|
||||
|
||||
/* Functions and variables exported from SDL_audio.c for SDL_sysaudio.c */
|
||||
|
||||
#ifdef HAVE_LIBSAMPLERATE_H
|
||||
#include "samplerate.h"
|
||||
extern SDL_bool SRC_available;
|
||||
extern int SRC_converter;
|
||||
extern SRC_STATE *(*SRC_src_new)(int converter_type, int channels, int *error);
|
||||
extern int (*SRC_src_process)(SRC_STATE *state, SRC_DATA *data);
|
||||
extern int (*SRC_src_reset)(SRC_STATE *state);
|
||||
extern SRC_STATE *(*SRC_src_delete)(SRC_STATE *state);
|
||||
extern const char *(*SRC_src_strerror)(int error);
|
||||
extern int (*SRC_src_simple)(SRC_DATA *data, int converter_type, int channels);
|
||||
#endif
|
||||
|
||||
/* Functions to get a list of "close" audio formats */
|
||||
extern SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format);
|
||||
extern SDL_AudioFormat SDL_NextAudioFormat(void);
|
||||
|
||||
/* Function to calculate the size and silence for a SDL_AudioSpec */
|
||||
extern Uint8 SDL_SilenceValueForFormat(const SDL_AudioFormat format);
|
||||
extern void SDL_CalculateAudioSpec(SDL_AudioSpec *spec);
|
||||
|
||||
/* Choose the audio filter functions below */
|
||||
extern void SDL_ChooseAudioConverters(void);
|
||||
|
||||
/* These pointers get set during SDL_ChooseAudioConverters() to various SIMD implementations. */
|
||||
extern SDL_AudioFilter SDL_Convert_S8_to_F32;
|
||||
extern SDL_AudioFilter SDL_Convert_U8_to_F32;
|
||||
extern SDL_AudioFilter SDL_Convert_S16_to_F32;
|
||||
extern SDL_AudioFilter SDL_Convert_U16_to_F32;
|
||||
extern SDL_AudioFilter SDL_Convert_S32_to_F32;
|
||||
extern SDL_AudioFilter SDL_Convert_F32_to_S8;
|
||||
extern SDL_AudioFilter SDL_Convert_F32_to_U8;
|
||||
extern SDL_AudioFilter SDL_Convert_F32_to_S16;
|
||||
extern SDL_AudioFilter SDL_Convert_F32_to_U16;
|
||||
extern SDL_AudioFilter SDL_Convert_F32_to_S32;
|
||||
|
||||
#endif /* SDL_audio_c_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
1402
third_party/SDL/src/audio/SDL_audio_channel_converters.h
vendored
Normal file
1402
third_party/SDL/src/audio/SDL_audio_channel_converters.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1061
third_party/SDL/src/audio/SDL_audio_resampler_filter.h
vendored
Normal file
1061
third_party/SDL/src/audio/SDL_audio_resampler_filter.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1427
third_party/SDL/src/audio/SDL_audiocvt.c
vendored
Normal file
1427
third_party/SDL/src/audio/SDL_audiocvt.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
128
third_party/SDL/src/audio/SDL_audiodev.c
vendored
Normal file
128
third_party/SDL/src/audio/SDL_audiodev.c
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../SDL_internal.h"
|
||||
|
||||
/* Get the name of the audio device we use for output */
|
||||
|
||||
#if SDL_AUDIO_DRIVER_NETBSD || SDL_AUDIO_DRIVER_OSS || SDL_AUDIO_DRIVER_SUNAUDIO
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h> /* For close() */
|
||||
|
||||
#include "SDL_stdinc.h"
|
||||
#include "SDL_audiodev_c.h"
|
||||
|
||||
#ifndef _PATH_DEV_DSP
|
||||
#if defined(__NETBSD__) || defined(__OPENBSD__)
|
||||
#define _PATH_DEV_DSP "/dev/audio"
|
||||
#else
|
||||
#define _PATH_DEV_DSP "/dev/dsp"
|
||||
#endif
|
||||
#endif
|
||||
#ifndef _PATH_DEV_DSP24
|
||||
#define _PATH_DEV_DSP24 "/dev/sound/dsp"
|
||||
#endif
|
||||
#ifndef _PATH_DEV_AUDIO
|
||||
#define _PATH_DEV_AUDIO "/dev/audio"
|
||||
#endif
|
||||
|
||||
static void test_device(const int iscapture, const char *fname, int flags, int (*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);
|
||||
close(audio_fd);
|
||||
if (okay) {
|
||||
static size_t dummyhandle = 0;
|
||||
dummyhandle++;
|
||||
SDL_assert(dummyhandle != 0);
|
||||
|
||||
/* Note that spec is NULL; while we are opening the device
|
||||
* endpoint here, the endpoint does not provide any mix format
|
||||
* information, making this information inaccessible at
|
||||
* enumeration time
|
||||
*/
|
||||
SDL_AddAudioDevice(iscapture, fname, NULL, (void *)(uintptr_t)dummyhandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int test_stub(int fd)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void SDL_EnumUnixAudioDevices_Internal(const int iscapture, const int classic, int (*test)(int))
|
||||
{
|
||||
const int flags = iscapture ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT;
|
||||
const char *audiodev;
|
||||
char audiopath[1024];
|
||||
|
||||
if (test == NULL) {
|
||||
test = test_stub;
|
||||
}
|
||||
|
||||
/* Figure out what our audio device is */
|
||||
audiodev = SDL_getenv("SDL_PATH_DSP");
|
||||
if (audiodev == NULL) {
|
||||
audiodev = SDL_getenv("AUDIODEV");
|
||||
}
|
||||
if (audiodev == NULL) {
|
||||
if (classic) {
|
||||
audiodev = _PATH_DEV_AUDIO;
|
||||
} else {
|
||||
struct stat sb;
|
||||
|
||||
/* Added support for /dev/sound/\* in Linux 2.4 */
|
||||
if (((stat("/dev/sound", &sb) == 0) && S_ISDIR(sb.st_mode)) && ((stat(_PATH_DEV_DSP24, &sb) == 0) && S_ISCHR(sb.st_mode))) {
|
||||
audiodev = _PATH_DEV_DSP24;
|
||||
} else {
|
||||
audiodev = _PATH_DEV_DSP;
|
||||
}
|
||||
}
|
||||
}
|
||||
test_device(iscapture, audiodev, flags, test);
|
||||
|
||||
if (SDL_strlen(audiodev) < (sizeof(audiopath) - 3)) {
|
||||
int instance = 0;
|
||||
while (instance <= 64) {
|
||||
(void)SDL_snprintf(audiopath, SDL_arraysize(audiopath),
|
||||
"%s%d", audiodev, instance);
|
||||
instance++;
|
||||
test_device(iscapture, audiopath, flags, test);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_EnumUnixAudioDevices(const int classic, int (*test)(int))
|
||||
{
|
||||
SDL_EnumUnixAudioDevices_Internal(SDL_TRUE, classic, test);
|
||||
SDL_EnumUnixAudioDevices_Internal(SDL_FALSE, classic, test);
|
||||
}
|
||||
|
||||
#endif /* Audio driver selection */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
44
third_party/SDL/src/audio/SDL_audiodev_c.h
vendored
Normal file
44
third_party/SDL/src/audio/SDL_audiodev_c.h
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_audiodev_c_h_
|
||||
#define SDL_audiodev_c_h_
|
||||
|
||||
#include "SDL.h"
|
||||
#include "../SDL_internal.h"
|
||||
#include "SDL_sysaudio.h"
|
||||
|
||||
/* Open the audio device for playback, and don't block if busy */
|
||||
/* #define USE_BLOCKING_WRITES */
|
||||
|
||||
#ifdef USE_BLOCKING_WRITES
|
||||
#define OPEN_FLAGS_OUTPUT O_WRONLY
|
||||
#define OPEN_FLAGS_INPUT O_RDONLY
|
||||
#else
|
||||
#define OPEN_FLAGS_OUTPUT (O_WRONLY | O_NONBLOCK)
|
||||
#define OPEN_FLAGS_INPUT (O_RDONLY | O_NONBLOCK)
|
||||
#endif
|
||||
|
||||
extern void SDL_EnumUnixAudioDevices(const int classic, int (*test)(int));
|
||||
|
||||
#endif /* SDL_audiodev_c_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
1489
third_party/SDL/src/audio/SDL_audiotypecvt.c
vendored
Normal file
1489
third_party/SDL/src/audio/SDL_audiotypecvt.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
345
third_party/SDL/src/audio/SDL_mixer.c
vendored
Normal file
345
third_party/SDL/src/audio/SDL_mixer.c
vendored
Normal file
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../SDL_internal.h"
|
||||
|
||||
/* This provides the default mixing callback for the SDL audio routines */
|
||||
|
||||
#include "SDL_cpuinfo.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "SDL_sysaudio.h"
|
||||
|
||||
/* This table is used to add two sound values together and pin
|
||||
* the value to avoid overflow. (used with permission from ARDI)
|
||||
*/
|
||||
static const Uint8 mix8[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
|
||||
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
|
||||
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
|
||||
0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
|
||||
0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
|
||||
0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
|
||||
0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
|
||||
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B,
|
||||
0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
|
||||
0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
|
||||
0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C,
|
||||
0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92,
|
||||
0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
|
||||
0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
|
||||
0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3,
|
||||
0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE,
|
||||
0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9,
|
||||
0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4,
|
||||
0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
|
||||
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA,
|
||||
0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5,
|
||||
0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
/* The volume ranges from 0 - 128 */
|
||||
#define ADJUST_VOLUME(s, v) (s = (s * v) / SDL_MIX_MAXVOLUME)
|
||||
#define ADJUST_VOLUME_U8(s, v) (s = (((s - 128) * v) / SDL_MIX_MAXVOLUME) + 128)
|
||||
#define ADJUST_VOLUME_U16(s, v) (s = (((s - 32768) * v) / SDL_MIX_MAXVOLUME) + 32768)
|
||||
|
||||
void SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
|
||||
Uint32 len, int volume)
|
||||
{
|
||||
if (volume == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
|
||||
case AUDIO_U8:
|
||||
{
|
||||
Uint8 src_sample;
|
||||
|
||||
while (len--) {
|
||||
src_sample = *src;
|
||||
ADJUST_VOLUME_U8(src_sample, volume);
|
||||
*dst = mix8[*dst + src_sample];
|
||||
++dst;
|
||||
++src;
|
||||
}
|
||||
} break;
|
||||
|
||||
case AUDIO_S8:
|
||||
{
|
||||
Sint8 *dst8, *src8;
|
||||
Sint8 src_sample;
|
||||
int dst_sample;
|
||||
const int max_audioval = SDL_MAX_SINT8;
|
||||
const int min_audioval = SDL_MIN_SINT8;
|
||||
|
||||
src8 = (Sint8 *)src;
|
||||
dst8 = (Sint8 *)dst;
|
||||
while (len--) {
|
||||
src_sample = *src8;
|
||||
ADJUST_VOLUME(src_sample, volume);
|
||||
dst_sample = *dst8 + src_sample;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*dst8 = dst_sample;
|
||||
++dst8;
|
||||
++src8;
|
||||
}
|
||||
} break;
|
||||
|
||||
case AUDIO_S16LSB:
|
||||
{
|
||||
Sint16 src1, src2;
|
||||
int dst_sample;
|
||||
const int max_audioval = SDL_MAX_SINT16;
|
||||
const int min_audioval = SDL_MIN_SINT16;
|
||||
|
||||
len /= 2;
|
||||
while (len--) {
|
||||
src1 = SDL_SwapLE16(*(Sint16 *)src);
|
||||
ADJUST_VOLUME(src1, volume);
|
||||
src2 = SDL_SwapLE16(*(Sint16 *)dst);
|
||||
src += 2;
|
||||
dst_sample = src1 + src2;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(Sint16 *)dst = SDL_SwapLE16(dst_sample);
|
||||
dst += 2;
|
||||
}
|
||||
} break;
|
||||
|
||||
case AUDIO_S16MSB:
|
||||
{
|
||||
Sint16 src1, src2;
|
||||
int dst_sample;
|
||||
const int max_audioval = SDL_MAX_SINT16;
|
||||
const int min_audioval = SDL_MIN_SINT16;
|
||||
|
||||
len /= 2;
|
||||
while (len--) {
|
||||
src1 = SDL_SwapBE16(*(Sint16 *)src);
|
||||
ADJUST_VOLUME(src1, volume);
|
||||
src2 = SDL_SwapBE16(*(Sint16 *)dst);
|
||||
src += 2;
|
||||
dst_sample = src1 + src2;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(Sint16 *)dst = SDL_SwapBE16(dst_sample);
|
||||
dst += 2;
|
||||
}
|
||||
} break;
|
||||
|
||||
case AUDIO_U16LSB:
|
||||
{
|
||||
Uint16 src1, src2;
|
||||
int dst_sample;
|
||||
const int max_audioval = SDL_MAX_SINT16;
|
||||
const int min_audioval = SDL_MIN_SINT16;
|
||||
|
||||
len /= 2;
|
||||
while (len--) {
|
||||
src1 = SDL_SwapLE16(*(Uint16 *)src);
|
||||
ADJUST_VOLUME_U16(src1, volume);
|
||||
src2 = SDL_SwapLE16(*(Uint16 *)dst);
|
||||
src += 2;
|
||||
dst_sample = src1 + src2 - 32768 * 2;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
dst_sample += 32768;
|
||||
*(Uint16 *)dst = SDL_SwapLE16(dst_sample);
|
||||
dst += 2;
|
||||
}
|
||||
} break;
|
||||
|
||||
case AUDIO_U16MSB:
|
||||
{
|
||||
Uint16 src1, src2;
|
||||
int dst_sample;
|
||||
const int max_audioval = SDL_MAX_SINT16;
|
||||
const int min_audioval = SDL_MIN_SINT16;
|
||||
|
||||
len /= 2;
|
||||
while (len--) {
|
||||
src1 = SDL_SwapBE16(*(Uint16 *)src);
|
||||
ADJUST_VOLUME_U16(src1, volume);
|
||||
src2 = SDL_SwapBE16(*(Uint16 *)dst);
|
||||
src += 2;
|
||||
dst_sample = src1 + src2 - 32768 * 2;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
dst_sample += 32768;
|
||||
*(Uint16 *)dst = SDL_SwapBE16(dst_sample);
|
||||
dst += 2;
|
||||
}
|
||||
} break;
|
||||
|
||||
case AUDIO_S32LSB:
|
||||
{
|
||||
const Uint32 *src32 = (Uint32 *)src;
|
||||
Uint32 *dst32 = (Uint32 *)dst;
|
||||
Sint64 src1, src2;
|
||||
Sint64 dst_sample;
|
||||
const Sint64 max_audioval = SDL_MAX_SINT32;
|
||||
const Sint64 min_audioval = SDL_MIN_SINT32;
|
||||
|
||||
len /= 4;
|
||||
while (len--) {
|
||||
src1 = (Sint64)((Sint32)SDL_SwapLE32(*src32));
|
||||
src32++;
|
||||
ADJUST_VOLUME(src1, volume);
|
||||
src2 = (Sint64)((Sint32)SDL_SwapLE32(*dst32));
|
||||
dst_sample = src1 + src2;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(dst32++) = SDL_SwapLE32((Uint32)((Sint32)dst_sample));
|
||||
}
|
||||
} break;
|
||||
|
||||
case AUDIO_S32MSB:
|
||||
{
|
||||
const Uint32 *src32 = (Uint32 *)src;
|
||||
Uint32 *dst32 = (Uint32 *)dst;
|
||||
Sint64 src1, src2;
|
||||
Sint64 dst_sample;
|
||||
const Sint64 max_audioval = SDL_MAX_SINT32;
|
||||
const Sint64 min_audioval = SDL_MIN_SINT32;
|
||||
|
||||
len /= 4;
|
||||
while (len--) {
|
||||
src1 = (Sint64)((Sint32)SDL_SwapBE32(*src32));
|
||||
src32++;
|
||||
ADJUST_VOLUME(src1, volume);
|
||||
src2 = (Sint64)((Sint32)SDL_SwapBE32(*dst32));
|
||||
dst_sample = src1 + src2;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(dst32++) = SDL_SwapBE32((Uint32)((Sint32)dst_sample));
|
||||
}
|
||||
} break;
|
||||
|
||||
case AUDIO_F32LSB:
|
||||
{
|
||||
const float fmaxvolume = 1.0f / ((float)SDL_MIX_MAXVOLUME);
|
||||
const float fvolume = (float)volume;
|
||||
const float *src32 = (float *)src;
|
||||
float *dst32 = (float *)dst;
|
||||
float src1, src2;
|
||||
double dst_sample;
|
||||
/* !!! FIXME: are these right? */
|
||||
const double max_audioval = 3.402823466e+38F;
|
||||
const double min_audioval = -3.402823466e+38F;
|
||||
|
||||
len /= 4;
|
||||
while (len--) {
|
||||
src1 = ((SDL_SwapFloatLE(*src32) * fvolume) * fmaxvolume);
|
||||
src2 = SDL_SwapFloatLE(*dst32);
|
||||
src32++;
|
||||
|
||||
dst_sample = ((double)src1) + ((double)src2);
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(dst32++) = SDL_SwapFloatLE((float)dst_sample);
|
||||
}
|
||||
} break;
|
||||
|
||||
case AUDIO_F32MSB:
|
||||
{
|
||||
const float fmaxvolume = 1.0f / ((float)SDL_MIX_MAXVOLUME);
|
||||
const float fvolume = (float)volume;
|
||||
const float *src32 = (float *)src;
|
||||
float *dst32 = (float *)dst;
|
||||
float src1, src2;
|
||||
double dst_sample;
|
||||
/* !!! FIXME: are these right? */
|
||||
const double max_audioval = 3.402823466e+38F;
|
||||
const double min_audioval = -3.402823466e+38F;
|
||||
|
||||
len /= 4;
|
||||
while (len--) {
|
||||
src1 = ((SDL_SwapFloatBE(*src32) * fvolume) * fmaxvolume);
|
||||
src2 = SDL_SwapFloatBE(*dst32);
|
||||
src32++;
|
||||
|
||||
dst_sample = ((double)src1) + ((double)src2);
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(dst32++) = SDL_SwapFloatBE((float)dst_sample);
|
||||
}
|
||||
} break;
|
||||
|
||||
default: /* If this happens... FIXME! */
|
||||
SDL_SetError("SDL_MixAudioFormat(): unknown audio format");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
215
third_party/SDL/src/audio/SDL_sysaudio.h
vendored
Normal file
215
third_party/SDL/src/audio/SDL_sysaudio.h
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_sysaudio_h_
|
||||
#define SDL_sysaudio_h_
|
||||
|
||||
#include "SDL_mutex.h"
|
||||
#include "SDL_thread.h"
|
||||
#include "../SDL_dataqueue.h"
|
||||
#include "./SDL_audio_c.h"
|
||||
|
||||
/* !!! 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;
|
||||
#define _THIS SDL_AudioDevice *_this
|
||||
|
||||
/* Audio targets 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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)
|
||||
|
||||
typedef struct SDL_AudioDriverImpl
|
||||
{
|
||||
void (*DetectDevices)(void);
|
||||
int (*OpenDevice)(_THIS, const char *devname);
|
||||
void (*ThreadInit)(_THIS); /* Called by audio thread at start */
|
||||
void (*ThreadDeinit)(_THIS); /* Called by audio thread at end */
|
||||
void (*WaitDevice)(_THIS);
|
||||
void (*PlayDevice)(_THIS);
|
||||
Uint8 *(*GetDeviceBuf)(_THIS);
|
||||
int (*CaptureFromDevice)(_THIS, void *buffer, int buflen);
|
||||
void (*FlushCapture)(_THIS);
|
||||
void (*CloseDevice)(_THIS);
|
||||
void (*LockDevice)(_THIS);
|
||||
void (*UnlockDevice)(_THIS);
|
||||
void (*FreeDeviceHandle)(void *handle); /**< SDL is done with 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;
|
||||
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;
|
||||
} SDL_AudioDriver;
|
||||
|
||||
/* Define the SDL audio driver structure */
|
||||
struct SDL_AudioDevice
|
||||
{
|
||||
/* * * */
|
||||
/* Data common to all devices */
|
||||
SDL_AudioDeviceID id;
|
||||
|
||||
/* The device's current audio specification */
|
||||
SDL_AudioSpec spec;
|
||||
|
||||
/* The callback's expected audio specification (converted vs device's spec). */
|
||||
SDL_AudioSpec callbackspec;
|
||||
|
||||
/* Stream that converts and resamples. NULL if not needed. */
|
||||
SDL_AudioStream *stream;
|
||||
|
||||
/* Current state flags */
|
||||
SDL_atomic_t shutdown; /* true if we are signaling the play thread to end. */
|
||||
SDL_atomic_t enabled; /* true if device is functioning and connected. */
|
||||
SDL_atomic_t paused;
|
||||
SDL_bool iscapture;
|
||||
|
||||
/* Scratch buffer used in the bridge between SDL and the user callback. */
|
||||
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 */
|
||||
SDL_Thread *thread;
|
||||
SDL_threadID threadid;
|
||||
|
||||
/* Queued buffers (if app not using callback). */
|
||||
SDL_DataQueue *buffer_queue;
|
||||
|
||||
/* * * */
|
||||
/* Data private to this driver */
|
||||
struct SDL_PrivateAudioData *hidden;
|
||||
|
||||
void *handle;
|
||||
};
|
||||
#undef _THIS
|
||||
|
||||
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. */
|
||||
} AudioBootStrap;
|
||||
|
||||
/* 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;
|
||||
extern AudioBootStrap JACK_bootstrap;
|
||||
extern AudioBootStrap SNDIO_bootstrap;
|
||||
extern AudioBootStrap NETBSDAUDIO_bootstrap;
|
||||
extern AudioBootStrap DSP_bootstrap;
|
||||
extern AudioBootStrap QSAAUDIO_bootstrap;
|
||||
extern AudioBootStrap SUNAUDIO_bootstrap;
|
||||
extern AudioBootStrap ARTS_bootstrap;
|
||||
extern AudioBootStrap ESD_bootstrap;
|
||||
extern AudioBootStrap NACLAUDIO_bootstrap;
|
||||
extern AudioBootStrap NAS_bootstrap;
|
||||
extern AudioBootStrap WASAPI_bootstrap;
|
||||
extern AudioBootStrap DSOUND_bootstrap;
|
||||
extern AudioBootStrap WINMM_bootstrap;
|
||||
extern AudioBootStrap PAUDIO_bootstrap;
|
||||
extern AudioBootStrap HAIKUAUDIO_bootstrap;
|
||||
extern AudioBootStrap COREAUDIO_bootstrap;
|
||||
extern AudioBootStrap DISKAUDIO_bootstrap;
|
||||
extern AudioBootStrap DUMMYAUDIO_bootstrap;
|
||||
extern AudioBootStrap FUSIONSOUND_bootstrap;
|
||||
extern AudioBootStrap aaudio_bootstrap;
|
||||
extern AudioBootStrap openslES_bootstrap;
|
||||
extern AudioBootStrap ANDROIDAUDIO_bootstrap;
|
||||
extern AudioBootStrap PS2AUDIO_bootstrap;
|
||||
extern AudioBootStrap PSPAUDIO_bootstrap;
|
||||
extern AudioBootStrap VITAAUD_bootstrap;
|
||||
extern AudioBootStrap N3DSAUDIO_bootstrap;
|
||||
extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap;
|
||||
extern AudioBootStrap OS2AUDIO_bootstrap;
|
||||
|
||||
#endif /* SDL_sysaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
2137
third_party/SDL/src/audio/SDL_wave.c
vendored
Normal file
2137
third_party/SDL/src/audio/SDL_wave.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
153
third_party/SDL/src/audio/SDL_wave.h
vendored
Normal file
153
third_party/SDL/src/audio/SDL_wave.h
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../SDL_internal.h"
|
||||
|
||||
/* RIFF WAVE files are little-endian */
|
||||
|
||||
/*******************************************/
|
||||
/* Define values for Microsoft WAVE format */
|
||||
/*******************************************/
|
||||
/* FOURCC */
|
||||
#define RIFF 0x46464952 /* "RIFF" */
|
||||
#define WAVE 0x45564157 /* "WAVE" */
|
||||
#define FACT 0x74636166 /* "fact" */
|
||||
#define LIST 0x5453494c /* "LIST" */
|
||||
#define BEXT 0x74786562 /* "bext" */
|
||||
#define JUNK 0x4B4E554A /* "JUNK" */
|
||||
#define FMT 0x20746D66 /* "fmt " */
|
||||
#define DATA 0x61746164 /* "data" */
|
||||
/* Format tags */
|
||||
#define UNKNOWN_CODE 0x0000
|
||||
#define PCM_CODE 0x0001
|
||||
#define MS_ADPCM_CODE 0x0002
|
||||
#define IEEE_FLOAT_CODE 0x0003
|
||||
#define ALAW_CODE 0x0006
|
||||
#define MULAW_CODE 0x0007
|
||||
#define IMA_ADPCM_CODE 0x0011
|
||||
#define MPEG_CODE 0x0050
|
||||
#define MPEGLAYER3_CODE 0x0055
|
||||
#define EXTENSIBLE_CODE 0xFFFE
|
||||
|
||||
/* Stores the WAVE format information. */
|
||||
typedef struct WaveFormat
|
||||
{
|
||||
Uint16 formattag; /* Raw value of the first field in the fmt chunk data. */
|
||||
Uint16 encoding; /* Actual encoding, possibly from the extensible header. */
|
||||
Uint16 channels; /* Number of channels. */
|
||||
Uint32 frequency; /* Sampling rate in Hz. */
|
||||
Uint32 byterate; /* Average bytes per second. */
|
||||
Uint16 blockalign; /* Bytes per block. */
|
||||
Uint16 bitspersample; /* Currently supported are 8, 16, 24, 32, and 4 for ADPCM. */
|
||||
|
||||
/* Extra information size. Number of extra bytes starting at byte 18 in the
|
||||
* fmt chunk data. This is at least 22 for the extensible header.
|
||||
*/
|
||||
Uint16 extsize;
|
||||
|
||||
/* Extensible WAVE header fields */
|
||||
Uint16 validsamplebits;
|
||||
Uint32 samplesperblock; /* For compressed formats. Can be zero. Actually 16 bits in the header. */
|
||||
Uint32 channelmask;
|
||||
Uint8 subformat[16]; /* A format GUID. */
|
||||
} WaveFormat;
|
||||
|
||||
/* Stores information on the fact chunk. */
|
||||
typedef struct WaveFact
|
||||
{
|
||||
/* Represents the state of the fact chunk in the WAVE file.
|
||||
* Set to -1 if the fact chunk is invalid.
|
||||
* Set to 0 if the fact chunk is not present
|
||||
* Set to 1 if the fact chunk is present and valid.
|
||||
* Set to 2 if samplelength is going to be used as the number of sample frames.
|
||||
*/
|
||||
Sint32 status;
|
||||
|
||||
/* Version 1 of the RIFF specification calls the field in the fact chunk
|
||||
* dwFileSize. The Standards Update then calls it dwSampleLength and specifies
|
||||
* that it is 'the length of the data in samples'. WAVE files from Windows
|
||||
* with this chunk have it set to the samples per channel (sample frames).
|
||||
* This is useful to truncate compressed audio to a specific sample count
|
||||
* because a compressed block is usually decoded to a fixed number of
|
||||
* sample frames.
|
||||
*/
|
||||
Uint32 samplelength; /* Raw sample length value from the fact chunk. */
|
||||
} WaveFact;
|
||||
|
||||
/* Generic struct for the chunks in the WAVE file. */
|
||||
typedef struct WaveChunk
|
||||
{
|
||||
Uint32 fourcc; /* FOURCC of the chunk. */
|
||||
Uint32 length; /* Size of the chunk data. */
|
||||
Sint64 position; /* Position of the data in the stream. */
|
||||
Uint8 *data; /* When allocated, this points to the chunk data. length is used for the memory allocation size. */
|
||||
size_t size; /* Number of bytes in data that could be read from the stream. Can be smaller than length. */
|
||||
} WaveChunk;
|
||||
|
||||
/* Controls how the size of the RIFF chunk affects the loading of a WAVE file. */
|
||||
typedef enum WaveRiffSizeHint
|
||||
{
|
||||
RiffSizeNoHint,
|
||||
RiffSizeForce,
|
||||
RiffSizeIgnoreZero,
|
||||
RiffSizeIgnore,
|
||||
RiffSizeMaximum
|
||||
} WaveRiffSizeHint;
|
||||
|
||||
/* Controls how a truncated WAVE file is handled. */
|
||||
typedef enum WaveTruncationHint
|
||||
{
|
||||
TruncNoHint,
|
||||
TruncVeryStrict,
|
||||
TruncStrict,
|
||||
TruncDropFrame,
|
||||
TruncDropBlock
|
||||
} WaveTruncationHint;
|
||||
|
||||
/* Controls how the fact chunk affects the loading of a WAVE file. */
|
||||
typedef enum WaveFactChunkHint
|
||||
{
|
||||
FactNoHint,
|
||||
FactTruncate,
|
||||
FactStrict,
|
||||
FactIgnoreZero,
|
||||
FactIgnore
|
||||
} WaveFactChunkHint;
|
||||
|
||||
typedef struct WaveFile
|
||||
{
|
||||
WaveChunk chunk;
|
||||
WaveFormat format;
|
||||
WaveFact fact;
|
||||
|
||||
/* Number of sample frames that will be decoded. Calculated either with the
|
||||
* size of the data chunk or, if the appropriate hint is enabled, with the
|
||||
* sample length value from the fact chunk.
|
||||
*/
|
||||
Sint64 sampleframes;
|
||||
|
||||
void *decoderdata; /* Some decoders require extra data for a state. */
|
||||
|
||||
WaveRiffSizeHint riffhint;
|
||||
WaveTruncationHint trunchint;
|
||||
WaveFactChunkHint facthint;
|
||||
} WaveFile;
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
465
third_party/SDL/src/audio/aaudio/SDL_aaudio.c
vendored
Normal file
465
third_party/SDL/src/audio/aaudio/SDL_aaudio.c
vendored
Normal file
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_AAUDIO
|
||||
|
||||
#include "SDL_audio.h"
|
||||
#include "SDL_loadso.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../../core/android/SDL_android.h"
|
||||
#include "SDL_aaudio.h"
|
||||
|
||||
/* Debug */
|
||||
#if 0
|
||||
#define LOGI(...) SDL_Log(__VA_ARGS__);
|
||||
#else
|
||||
#define LOGI(...)
|
||||
#endif
|
||||
|
||||
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 SDL_AudioDevice *audioDevice = NULL;
|
||||
static SDL_AudioDevice *captureDevice = NULL;
|
||||
|
||||
static int aaudio_LoadFunctions(AAUDIO_Data *data)
|
||||
{
|
||||
#define SDL_PROC(ret, func, params) \
|
||||
do { \
|
||||
data->func = SDL_LoadFunction(data->handle, #func); \
|
||||
if (!data->func) { \
|
||||
return SDL_SetError("Couldn't load AAUDIO function %s: %s", #func, SDL_GetError()); \
|
||||
} \
|
||||
} 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)
|
||||
{
|
||||
LOGI("SDL aaudio_errorCallback: %d - %s", error, ctx.AAudio_convertResultToText(error));
|
||||
}
|
||||
|
||||
#define LIB_AAUDIO_SO "libaaudio.so"
|
||||
|
||||
static int aaudio_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
struct SDL_PrivateAudioData *private;
|
||||
SDL_bool iscapture = this->iscapture;
|
||||
aaudio_result_t res;
|
||||
LOGI(__func__);
|
||||
|
||||
SDL_assert((captureDevice == NULL) || !iscapture);
|
||||
SDL_assert((audioDevice == NULL) || iscapture);
|
||||
|
||||
if (iscapture) {
|
||||
if (!Android_JNI_RequestPermission("android.permission.RECORD_AUDIO")) {
|
||||
LOGI("This app doesn't have RECORD_AUDIO permission");
|
||||
return SDL_SetError("This app doesn't have RECORD_AUDIO permission");
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
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 == AUDIO_S16SYS) {
|
||||
format = AAUDIO_FORMAT_PCM_I16;
|
||||
} else if (this->spec.format == AUDIO_S16SYS) {
|
||||
format = AAUDIO_FORMAT_PCM_FLOAT;
|
||||
}
|
||||
ctx.AAudioStreamBuilder_setFormat(ctx.builder, format);
|
||||
}
|
||||
|
||||
ctx.AAudioStreamBuilder_setErrorCallback(ctx.builder, aaudio_errorCallback, private);
|
||||
|
||||
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);
|
||||
|
||||
res = ctx.AAudioStreamBuilder_openStream(ctx.builder, &private->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res);
|
||||
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 = AUDIO_S16SYS;
|
||||
} else if (fmt == AAUDIO_FORMAT_PCM_FLOAT) {
|
||||
this->spec.format = AUDIO_F32SYS;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
private->frame_size = this->spec.channels * (SDL_AUDIO_BITSIZE(this->spec.format) / 8);
|
||||
|
||||
res = ctx.AAudioStream_requestStart(private->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));
|
||||
}
|
||||
|
||||
LOGI("SDL AAudioStream_requestStart OK");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aaudio_CloseDevice(_THIS)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->iscapture) {
|
||||
SDL_assert(captureDevice == this);
|
||||
captureDevice = NULL;
|
||||
} else {
|
||||
SDL_assert(audioDevice == this);
|
||||
audioDevice = NULL;
|
||||
}
|
||||
|
||||
SDL_free(this->hidden->mixbuf);
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
static Uint8 *aaudio_GetDeviceBuf(_THIS)
|
||||
{
|
||||
struct SDL_PrivateAudioData *private = this->hidden;
|
||||
return private->mixbuf;
|
||||
}
|
||||
|
||||
static void aaudio_PlayDevice(_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(_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) {
|
||||
aaudio_result_t res;
|
||||
res = ctx.AAudioStreamBuilder_delete(ctx.builder);
|
||||
if (res != AAUDIO_OK) {
|
||||
SDL_SetError("Failed AAudioStreamBuilder_delete %s", ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
}
|
||||
SDL_UnloadObject(ctx.handle);
|
||||
}
|
||||
ctx.handle = NULL;
|
||||
ctx.builder = NULL;
|
||||
LOGI("End AAUDIO %s", SDL_GetError());
|
||||
}
|
||||
|
||||
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,
|
||||
* so don't use it until 8.1.
|
||||
*
|
||||
* See https://github.com/google/oboe/issues/40 for more information.
|
||||
*/
|
||||
if (SDL_GetAndroidSDKVersion() < 27) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
SDL_zero(ctx);
|
||||
|
||||
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)
|
||||
{
|
||||
/* TODO: Handle multiple devices? */
|
||||
struct SDL_PrivateAudioData *private;
|
||||
if (audioDevice != NULL && audioDevice->hidden != NULL) {
|
||||
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) {
|
||||
private = (struct SDL_PrivateAudioData *)captureDevice->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)
|
||||
{
|
||||
/* 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 (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) {
|
||||
private = (struct SDL_PrivateAudioData *)captureDevice->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)
|
||||
{
|
||||
struct SDL_PrivateAudioData *private;
|
||||
int64_t framePosition, timeNanoseconds;
|
||||
aaudio_result_t res;
|
||||
|
||||
if (audioDevice == NULL || !audioDevice->hidden) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
private = audioDevice->hidden;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_AAUDIO */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
52
third_party/SDL/src/audio/aaudio/SDL_aaudio.h
vendored
Normal file
52
third_party/SDL/src/audio/aaudio/SDL_aaudio.h
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef _SDL_aaudio_h
|
||||
#define _SDL_aaudio_h
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include <stdbool.h>
|
||||
#include <aaudio/AAudio.h>
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
AAudioStream *stream;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
int frame_size;
|
||||
|
||||
/* Resume device if it was paused automatically */
|
||||
int resume;
|
||||
};
|
||||
|
||||
void aaudio_ResumeDevices(void);
|
||||
void aaudio_PauseDevices(void);
|
||||
SDL_bool aaudio_DetectBrokenPlayState(void);
|
||||
|
||||
#endif /* _SDL_aaudio_h */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
79
third_party/SDL/src/audio/aaudio/SDL_aaudiofuncs.h
vendored
Normal file
79
third_party/SDL/src/audio/aaudio/SDL_aaudiofuncs.h
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright , (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#define SDL_PROC_UNUSED(ret, func, params)
|
||||
|
||||
SDL_PROC(const char *, AAudio_convertResultToText, (aaudio_result_t returnCode))
|
||||
SDL_PROC(const char *, AAudio_convertStreamStateToText, (aaudio_stream_state_t state))
|
||||
SDL_PROC(aaudio_result_t, AAudio_createStreamBuilder, (AAudioStreamBuilder * *builder))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setDeviceId, (AAudioStreamBuilder * builder, int32_t deviceId))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setSampleRate, (AAudioStreamBuilder * builder, int32_t sampleRate))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setChannelCount, (AAudioStreamBuilder * builder, int32_t channelCount))
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSamplesPerFrame, (AAudioStreamBuilder * builder, int32_t samplesPerFrame))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setFormat, (AAudioStreamBuilder * builder, aaudio_format_t format))
|
||||
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_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_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))
|
||||
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_release, (AAudioStream * stream)) /* API 30 */
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_close, (AAudioStream * stream))
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_requestStart, (AAudioStream * stream))
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_requestPause, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_requestFlush, (AAudioStream * stream))
|
||||
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_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_getSampleRate, (AAudioStream * stream))
|
||||
SDL_PROC(int32_t, AAudioStream_getChannelCount, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getSamplesPerFrame, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getDeviceId, (AAudioStream * stream))
|
||||
SDL_PROC(aaudio_format_t, AAudioStream_getFormat, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_sharing_mode_t, AAudioStream_getSharingMode, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_performance_mode_t, AAudioStream_getPerformanceMode, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_direction_t, AAudioStream_getDirection, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int64_t, AAudioStream_getFramesWritten, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int64_t, AAudioStream_getFramesRead, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_session_id_t, AAudioStream_getSessionId, (AAudioStream * stream)) /* API 28 */
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_getTimestamp, (AAudioStream * stream, clockid_t clockid, int64_t *framePosition, int64_t *timeNanoseconds))
|
||||
SDL_PROC_UNUSED(aaudio_usage_t, AAudioStream_getUsage, (AAudioStream * stream)) /* API 28 */
|
||||
SDL_PROC_UNUSED(aaudio_content_type_t, AAudioStream_getContentType, (AAudioStream * stream)) /* API 28 */
|
||||
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 */
|
||||
991
third_party/SDL/src/audio/alsa/SDL_alsa_audio.c
vendored
Normal file
991
third_party/SDL/src/audio/alsa/SDL_alsa_audio.c
vendored
Normal file
@@ -0,0 +1,991 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_ALSA
|
||||
|
||||
#ifndef SDL_ALSA_NON_BLOCKING
|
||||
#define SDL_ALSA_NON_BLOCKING 0
|
||||
#endif
|
||||
|
||||
/* without the thread, you will detect devices on startup, but will not get futher hotplug events. But that might be okay. */
|
||||
#ifndef SDL_ALSA_HOTPLUG_THREAD
|
||||
#define SDL_ALSA_HOTPLUG_THREAD 1
|
||||
#endif
|
||||
|
||||
/* Allow access to a raw mixing buffer */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <signal.h> /* For kill() */
|
||||
#include <string.h>
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_alsa_audio.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
|
||||
#include "SDL_loadso.h"
|
||||
#endif
|
||||
|
||||
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 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);
|
||||
static int (*ALSA_snd_pcm_prepare)(snd_pcm_t *);
|
||||
static int (*ALSA_snd_pcm_drain)(snd_pcm_t *);
|
||||
static const char *(*ALSA_snd_strerror)(int);
|
||||
static size_t (*ALSA_snd_pcm_hw_params_sizeof)(void);
|
||||
static size_t (*ALSA_snd_pcm_sw_params_sizeof)(void);
|
||||
static void (*ALSA_snd_pcm_hw_params_copy)(snd_pcm_hw_params_t *, const snd_pcm_hw_params_t *);
|
||||
static int (*ALSA_snd_pcm_hw_params_any)(snd_pcm_t *, snd_pcm_hw_params_t *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_access)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_format)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_channels)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int);
|
||||
static int (*ALSA_snd_pcm_hw_params_get_channels)(const snd_pcm_hw_params_t *, unsigned int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_rate_near)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_period_size_near)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_get_period_size)(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_periods_min)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_periods_first)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_get_periods)(const snd_pcm_hw_params_t *, unsigned int *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near)(snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
|
||||
static int (*ALSA_snd_pcm_hw_params_get_buffer_size)(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
|
||||
static int (*ALSA_snd_pcm_hw_params)(snd_pcm_t *, snd_pcm_hw_params_t *);
|
||||
static int (*ALSA_snd_pcm_sw_params_current)(snd_pcm_t *,
|
||||
snd_pcm_sw_params_t *);
|
||||
static int (*ALSA_snd_pcm_sw_params_set_start_threshold)(snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
|
||||
static int (*ALSA_snd_pcm_sw_params)(snd_pcm_t *, snd_pcm_sw_params_t *);
|
||||
static int (*ALSA_snd_pcm_nonblock)(snd_pcm_t *, int);
|
||||
static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int);
|
||||
static int (*ALSA_snd_pcm_sw_params_set_avail_min)(snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
|
||||
static int (*ALSA_snd_pcm_reset)(snd_pcm_t *);
|
||||
static int (*ALSA_snd_device_name_hint)(int, const char *, void ***);
|
||||
static char *(*ALSA_snd_device_name_get_hint)(const void *, const char *);
|
||||
static int (*ALSA_snd_device_name_free_hint)(void **);
|
||||
static snd_pcm_sframes_t (*ALSA_snd_pcm_avail)(snd_pcm_t *);
|
||||
#ifdef SND_CHMAP_API_VERSION
|
||||
static snd_pcm_chmap_t *(*ALSA_snd_pcm_get_chmap)(snd_pcm_t *);
|
||||
static int (*ALSA_snd_pcm_chmap_print)(const snd_pcm_chmap_t *map, size_t maxlen, char *buf);
|
||||
#endif
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
|
||||
#define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof
|
||||
#define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof
|
||||
|
||||
static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
|
||||
static void *alsa_handle = NULL;
|
||||
|
||||
static int load_alsa_sym(const char *fn, void **addr)
|
||||
{
|
||||
*addr = SDL_LoadFunction(alsa_handle, fn);
|
||||
if (*addr == NULL) {
|
||||
/* Don't call SDL_SetError(): SDL_LoadFunction already did. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
|
||||
#define SDL_ALSA_SYM(x) \
|
||||
if (!load_alsa_sym(#x, (void **)(char *)&ALSA_##x)) \
|
||||
return -1
|
||||
#else
|
||||
#define SDL_ALSA_SYM(x) ALSA_##x = x
|
||||
#endif
|
||||
|
||||
static int load_alsa_syms(void)
|
||||
{
|
||||
SDL_ALSA_SYM(snd_pcm_open);
|
||||
SDL_ALSA_SYM(snd_pcm_close);
|
||||
SDL_ALSA_SYM(snd_pcm_writei);
|
||||
SDL_ALSA_SYM(snd_pcm_readi);
|
||||
SDL_ALSA_SYM(snd_pcm_recover);
|
||||
SDL_ALSA_SYM(snd_pcm_prepare);
|
||||
SDL_ALSA_SYM(snd_pcm_drain);
|
||||
SDL_ALSA_SYM(snd_strerror);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_sizeof);
|
||||
SDL_ALSA_SYM(snd_pcm_sw_params_sizeof);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_copy);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_any);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_access);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_format);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_channels);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_get_channels);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_min);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_first);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_get_periods);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_buffer_size_near);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_get_buffer_size);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params);
|
||||
SDL_ALSA_SYM(snd_pcm_sw_params_current);
|
||||
SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold);
|
||||
SDL_ALSA_SYM(snd_pcm_sw_params);
|
||||
SDL_ALSA_SYM(snd_pcm_nonblock);
|
||||
SDL_ALSA_SYM(snd_pcm_wait);
|
||||
SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min);
|
||||
SDL_ALSA_SYM(snd_pcm_reset);
|
||||
SDL_ALSA_SYM(snd_device_name_hint);
|
||||
SDL_ALSA_SYM(snd_device_name_get_hint);
|
||||
SDL_ALSA_SYM(snd_device_name_free_hint);
|
||||
SDL_ALSA_SYM(snd_pcm_avail);
|
||||
#ifdef SND_CHMAP_API_VERSION
|
||||
SDL_ALSA_SYM(snd_pcm_get_chmap);
|
||||
SDL_ALSA_SYM(snd_pcm_chmap_print);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef SDL_ALSA_SYM
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
|
||||
|
||||
static void UnloadALSALibrary(void)
|
||||
{
|
||||
if (alsa_handle != NULL) {
|
||||
SDL_UnloadObject(alsa_handle);
|
||||
alsa_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadALSALibrary(void)
|
||||
{
|
||||
int retval = 0;
|
||||
if (alsa_handle == NULL) {
|
||||
alsa_handle = SDL_LoadObject(alsa_library);
|
||||
if (alsa_handle == NULL) {
|
||||
retval = -1;
|
||||
/* Don't call SDL_SetError(): SDL_LoadObject already did. */
|
||||
} else {
|
||||
retval = load_alsa_syms();
|
||||
if (retval < 0) {
|
||||
UnloadALSALibrary();
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void UnloadALSALibrary(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int LoadALSALibrary(void)
|
||||
{
|
||||
load_alsa_syms();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
|
||||
|
||||
static const char *get_audio_device(void *handle, const int channels)
|
||||
{
|
||||
const char *device;
|
||||
|
||||
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(_THIS)
|
||||
{
|
||||
#if SDL_ALSA_NON_BLOCKING
|
||||
const snd_pcm_sframes_t needed = (snd_pcm_sframes_t)this->spec.samples;
|
||||
while (SDL_AtomicGet(&this->enabled)) {
|
||||
const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(this->hidden->pcm_handle);
|
||||
if ((rc < 0) && (rc != -EAGAIN)) {
|
||||
/* Hmm, not much we can do - abort */
|
||||
fprintf(stderr, "ALSA snd_pcm_avail failed (unrecoverable): %s\n",
|
||||
ALSA_snd_strerror(rc));
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
return;
|
||||
} else if (rc < needed) {
|
||||
const Uint32 delay = ((needed - (SDL_max(rc, 0))) * 1000) / this->spec.freq;
|
||||
SDL_Delay(SDL_max(delay, 10));
|
||||
} else {
|
||||
break; /* ready to go! */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* !!! FIXME: is there a channel swizzler in alsalib instead? */
|
||||
/*
|
||||
* https://bugzilla.libsdl.org/show_bug.cgi?id=110
|
||||
* "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
|
||||
* and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
|
||||
*/
|
||||
#define SWIZ6(T) \
|
||||
static void swizzle_alsa_channels_6_##T(void *buffer, const Uint32 bufferlen) \
|
||||
{ \
|
||||
T *ptr = (T *)buffer; \
|
||||
Uint32 i; \
|
||||
for (i = 0; i < bufferlen; i++, ptr += 6) { \
|
||||
T tmp; \
|
||||
tmp = ptr[2]; \
|
||||
ptr[2] = ptr[4]; \
|
||||
ptr[4] = tmp; \
|
||||
tmp = ptr[3]; \
|
||||
ptr[3] = ptr[5]; \
|
||||
ptr[5] = tmp; \
|
||||
} \
|
||||
}
|
||||
|
||||
/* !!! FIXME: is there a channel swizzler in alsalib instead? */
|
||||
/* !!! FIXME: this screams for a SIMD shuffle operation. */
|
||||
/*
|
||||
* https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/mapping-stream-formats-to-speaker-configurations
|
||||
* For Linux ALSA, this appears to be FL-FR-RL-RR-C-LFE-SL-SR
|
||||
* and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-SL-SR-RL-RR"
|
||||
*/
|
||||
#define SWIZ8(T) \
|
||||
static void swizzle_alsa_channels_8_##T(void *buffer, const Uint32 bufferlen) \
|
||||
{ \
|
||||
T *ptr = (T *)buffer; \
|
||||
Uint32 i; \
|
||||
for (i = 0; i < bufferlen; i++, ptr += 6) { \
|
||||
const T center = ptr[2]; \
|
||||
const T subwoofer = ptr[3]; \
|
||||
const T side_left = ptr[4]; \
|
||||
const T side_right = ptr[5]; \
|
||||
const T rear_left = ptr[6]; \
|
||||
const T rear_right = ptr[7]; \
|
||||
ptr[2] = rear_left; \
|
||||
ptr[3] = rear_right; \
|
||||
ptr[4] = center; \
|
||||
ptr[5] = subwoofer; \
|
||||
ptr[6] = side_left; \
|
||||
ptr[7] = side_right; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CHANNEL_SWIZZLE(x) \
|
||||
x(Uint64) \
|
||||
x(Uint32) \
|
||||
x(Uint16) \
|
||||
x(Uint8)
|
||||
|
||||
CHANNEL_SWIZZLE(SWIZ6)
|
||||
CHANNEL_SWIZZLE(SWIZ8)
|
||||
|
||||
#undef CHANNEL_SWIZZLE
|
||||
#undef SWIZ6
|
||||
#undef SWIZ8
|
||||
|
||||
/*
|
||||
* Called right before feeding this->hidden->mixbuf to the hardware. Swizzle
|
||||
* channels from Windows/Mac order to the format alsalib will want.
|
||||
*/
|
||||
static void swizzle_alsa_channels(_THIS, void *buffer, Uint32 bufferlen)
|
||||
{
|
||||
switch (this->spec.channels) {
|
||||
#define CHANSWIZ(chans) \
|
||||
case chans: \
|
||||
switch ((this->spec.format & (0xFF))) { \
|
||||
case 8: \
|
||||
swizzle_alsa_channels_##chans##_Uint8(buffer, bufferlen); \
|
||||
break; \
|
||||
case 16: \
|
||||
swizzle_alsa_channels_##chans##_Uint16(buffer, bufferlen); \
|
||||
break; \
|
||||
case 32: \
|
||||
swizzle_alsa_channels_##chans##_Uint32(buffer, bufferlen); \
|
||||
break; \
|
||||
case 64: \
|
||||
swizzle_alsa_channels_##chans##_Uint64(buffer, bufferlen); \
|
||||
break; \
|
||||
default: \
|
||||
SDL_assert(!"unhandled bitsize"); \
|
||||
break; \
|
||||
} \
|
||||
return;
|
||||
|
||||
CHANSWIZ(6);
|
||||
CHANSWIZ(8);
|
||||
#undef CHANSWIZ
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SND_CHMAP_API_VERSION
|
||||
/* Some devices have the right channel map, no swizzling necessary */
|
||||
static void no_swizzle(_THIS, void *buffer, Uint32 bufferlen)
|
||||
{
|
||||
}
|
||||
#endif /* SND_CHMAP_API_VERSION */
|
||||
|
||||
static void ALSA_PlayDevice(_THIS)
|
||||
{
|
||||
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);
|
||||
|
||||
this->hidden->swizzle_func(this, this->hidden->mixbuf, frames_left);
|
||||
|
||||
while (frames_left > 0 && SDL_AtomicGet(&this->enabled)) {
|
||||
int status = ALSA_snd_pcm_writei(this->hidden->pcm_handle,
|
||||
sample_buf, frames_left);
|
||||
|
||||
if (status < 0) {
|
||||
if (status == -EAGAIN) {
|
||||
/* Apparently snd_pcm_recover() doesn't handle this case -
|
||||
does it assume snd_pcm_wait() above? */
|
||||
SDL_Delay(1);
|
||||
continue;
|
||||
}
|
||||
status = ALSA_snd_pcm_recover(this->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_snd_strerror(status));
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
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);
|
||||
}
|
||||
|
||||
sample_buf += status * frame_size;
|
||||
frames_left -= status;
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 *ALSA_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return this->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static int ALSA_CaptureFromDevice(_THIS, 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 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);
|
||||
|
||||
if (status == -EAGAIN) {
|
||||
ALSA_snd_pcm_wait(this->hidden->pcm_handle, wait_time);
|
||||
status = 0;
|
||||
} else if (status < 0) {
|
||||
/*printf("ALSA: capture error %d\n", status);*/
|
||||
status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
|
||||
if (status < 0) {
|
||||
/* Hmm, not much we can do - abort */
|
||||
SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
|
||||
"ALSA read failed (unrecoverable): %s\n",
|
||||
ALSA_snd_strerror(status));
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/*printf("ALSA: captured %d bytes\n", status * frame_size);*/
|
||||
sample_buf += status * frame_size;
|
||||
frames_left -= status;
|
||||
}
|
||||
|
||||
this->hidden->swizzle_func(this, buffer, total_frames - frames_left);
|
||||
|
||||
return (total_frames - frames_left) * frame_size;
|
||||
}
|
||||
|
||||
static void ALSA_FlushCapture(_THIS)
|
||||
{
|
||||
ALSA_snd_pcm_reset(this->hidden->pcm_handle);
|
||||
}
|
||||
|
||||
static void ALSA_CloseDevice(_THIS)
|
||||
{
|
||||
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);
|
||||
}
|
||||
SDL_free(this->hidden->mixbuf);
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
static int ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params)
|
||||
{
|
||||
int status;
|
||||
snd_pcm_hw_params_t *hwparams;
|
||||
snd_pcm_uframes_t persize;
|
||||
unsigned int periods;
|
||||
|
||||
/* Copy the hardware parameters for this setup */
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
ALSA_snd_pcm_hw_params_copy(hwparams, params);
|
||||
|
||||
/* Attempt to match the period size to the requested buffer size */
|
||||
persize = this->spec.samples;
|
||||
status = ALSA_snd_pcm_hw_params_set_period_size_near(
|
||||
this->hidden->pcm_handle, hwparams, &persize, NULL);
|
||||
if (status < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Need to at least double buffer */
|
||||
periods = 2;
|
||||
status = ALSA_snd_pcm_hw_params_set_periods_min(
|
||||
this->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);
|
||||
if (status < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* "set" the hardware with the desired parameters */
|
||||
status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams);
|
||||
if (status < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
this->spec.samples = persize;
|
||||
|
||||
/* This is useful for debugging */
|
||||
if (SDL_getenv("SDL_AUDIO_ALSA_DEBUG")) {
|
||||
snd_pcm_uframes_t bufsize;
|
||||
|
||||
ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize);
|
||||
|
||||
SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
|
||||
"ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
|
||||
persize, periods, bufsize);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ALSA_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
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;
|
||||
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) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(this->hidden);
|
||||
|
||||
/* Open the audio device */
|
||||
/* Name of device should depend on # channels in spec */
|
||||
status = ALSA_snd_pcm_open(&pcm_handle,
|
||||
get_audio_device(this->handle, this->spec.channels),
|
||||
iscapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
|
||||
SND_PCM_NONBLOCK);
|
||||
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't open audio device: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
|
||||
this->hidden->pcm_handle = pcm_handle;
|
||||
|
||||
/* Figure out what the hardware is capable of */
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't get hardware config: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
|
||||
/* SDL only uses interleaved sample output */
|
||||
status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't set interleaved access: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
|
||||
/* Try for a closest match on audio format */
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
switch (test_format) {
|
||||
case AUDIO_U8:
|
||||
format = SND_PCM_FORMAT_U8;
|
||||
break;
|
||||
case AUDIO_S8:
|
||||
format = SND_PCM_FORMAT_S8;
|
||||
break;
|
||||
case AUDIO_S16LSB:
|
||||
format = SND_PCM_FORMAT_S16_LE;
|
||||
break;
|
||||
case AUDIO_S16MSB:
|
||||
format = SND_PCM_FORMAT_S16_BE;
|
||||
break;
|
||||
case AUDIO_U16LSB:
|
||||
format = SND_PCM_FORMAT_U16_LE;
|
||||
break;
|
||||
case AUDIO_U16MSB:
|
||||
format = SND_PCM_FORMAT_U16_BE;
|
||||
break;
|
||||
case AUDIO_S32LSB:
|
||||
format = SND_PCM_FORMAT_S32_LE;
|
||||
break;
|
||||
case AUDIO_S32MSB:
|
||||
format = SND_PCM_FORMAT_S32_BE;
|
||||
break;
|
||||
case AUDIO_F32LSB:
|
||||
format = SND_PCM_FORMAT_FLOAT_LE;
|
||||
break;
|
||||
case AUDIO_F32MSB:
|
||||
format = SND_PCM_FORMAT_FLOAT_BE;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
if (ALSA_snd_pcm_hw_params_set_format(pcm_handle, hwparams, format) >= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "alsa");
|
||||
}
|
||||
this->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;
|
||||
#ifdef SND_CHMAP_API_VERSION
|
||||
chmap = ALSA_snd_pcm_get_chmap(pcm_handle);
|
||||
if (chmap) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
free(chmap);
|
||||
}
|
||||
#endif /* SND_CHMAP_API_VERSION */
|
||||
|
||||
/* Set the number of channels */
|
||||
status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
|
||||
this->spec.channels);
|
||||
channels = this->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;
|
||||
}
|
||||
|
||||
/* Set the audio rate */
|
||||
rate = this->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;
|
||||
|
||||
/* Set the buffer size, in samples */
|
||||
status = ALSA_set_buffer_size(this, 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_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);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("Couldn't set minimum available samples: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
status =
|
||||
ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't set start threshold: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
status = ALSA_snd_pcm_sw_params(pcm_handle, swparams);
|
||||
if (status < 0) {
|
||||
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);
|
||||
|
||||
/* 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) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
|
||||
}
|
||||
|
||||
#if !SDL_ALSA_NON_BLOCKING
|
||||
if (!iscapture) {
|
||||
ALSA_snd_pcm_nonblock(pcm_handle, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct ALSA_Device
|
||||
{
|
||||
char *name;
|
||||
SDL_bool iscapture;
|
||||
struct ALSA_Device *next;
|
||||
} ALSA_Device;
|
||||
|
||||
static void add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSeen)
|
||||
{
|
||||
ALSA_Device *dev = SDL_malloc(sizeof(ALSA_Device));
|
||||
char *desc;
|
||||
char *handle = NULL;
|
||||
char *ptr;
|
||||
|
||||
if (dev == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Not all alsa devices are enumerable via snd_device_name_get_hint
|
||||
(i.e. bluetooth devices). Therefore if hint is passed in to this
|
||||
function as NULL, assume name contains desc.
|
||||
Make sure not to free the storage associated with desc in this case */
|
||||
if (hint) {
|
||||
desc = ALSA_snd_device_name_get_hint(hint, "DESC");
|
||||
if (desc == NULL) {
|
||||
SDL_free(dev);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
desc = (char *)name;
|
||||
}
|
||||
|
||||
SDL_assert(name != NULL);
|
||||
|
||||
/* some strings have newlines, like "HDA NVidia, HDMI 0\nHDMI Audio Output".
|
||||
just chop the extra lines off, this seems to get a reasonable device
|
||||
name without extra details. */
|
||||
ptr = SDL_strchr(desc, '\n');
|
||||
if (ptr != NULL) {
|
||||
*ptr = '\0';
|
||||
}
|
||||
|
||||
/*printf("ALSA: adding %s device '%s' (%s)\n", iscapture ? "capture" : "output", name, desc);*/
|
||||
|
||||
handle = SDL_strdup(name);
|
||||
if (handle == NULL) {
|
||||
if (hint) {
|
||||
free(desc);
|
||||
}
|
||||
SDL_free(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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(iscapture, desc, NULL, handle);
|
||||
if (hint) {
|
||||
free(desc);
|
||||
}
|
||||
dev->name = handle;
|
||||
dev->iscapture = iscapture;
|
||||
dev->next = *pSeen;
|
||||
*pSeen = dev;
|
||||
}
|
||||
|
||||
static ALSA_Device *hotplug_devices = NULL;
|
||||
|
||||
static void ALSA_HotplugIteration(void)
|
||||
{
|
||||
void **hints = NULL;
|
||||
ALSA_Device *dev;
|
||||
ALSA_Device *unseen;
|
||||
ALSA_Device *seen;
|
||||
ALSA_Device *next;
|
||||
ALSA_Device *prev;
|
||||
|
||||
if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) {
|
||||
int i, j;
|
||||
const char *match = NULL;
|
||||
int bestmatch = 0xFFFF;
|
||||
size_t match_len = 0;
|
||||
int defaultdev = -1;
|
||||
static const char *const prefixes[] = {
|
||||
"hw:", "sysdefault:", "default:", NULL
|
||||
};
|
||||
|
||||
unseen = hotplug_devices;
|
||||
seen = NULL;
|
||||
|
||||
/* Apparently there are several different ways that ALSA lists
|
||||
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++) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(name);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
if (!isoutput && !isinput) {
|
||||
free(name);
|
||||
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;
|
||||
} else {
|
||||
unseen = next;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
free(name);
|
||||
}
|
||||
|
||||
ALSA_snd_device_name_free_hint(hints);
|
||||
|
||||
hotplug_devices = seen; /* now we have a known-good list of attached devices. */
|
||||
|
||||
/* report anything still in unseen as removed. */
|
||||
for (dev = unseen; dev; dev = next) {
|
||||
/*printf("ALSA: removing usb %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
|
||||
next = dev->next;
|
||||
SDL_RemoveAudioDevice(dev->iscapture, dev->name);
|
||||
SDL_free(dev->name);
|
||||
SDL_free(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if SDL_ALSA_HOTPLUG_THREAD
|
||||
static SDL_atomic_t ALSA_hotplug_shutdown;
|
||||
static SDL_Thread *ALSA_hotplug_thread;
|
||||
|
||||
static int SDLCALL ALSA_HotplugThread(void *arg)
|
||||
{
|
||||
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
|
||||
|
||||
while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) {
|
||||
/* Block awhile before checking again, unless we're told to stop. */
|
||||
const Uint32 ticks = SDL_GetTicks() + 5000;
|
||||
while (!SDL_AtomicGet(&ALSA_hotplug_shutdown) && !SDL_TICKS_PASSED(SDL_GetTicks(), ticks)) {
|
||||
SDL_Delay(100);
|
||||
}
|
||||
|
||||
ALSA_HotplugIteration(); /* run the check. */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ALSA_DetectDevices(void)
|
||||
{
|
||||
ALSA_HotplugIteration(); /* run once now before a thread continues to check. */
|
||||
|
||||
#if SDL_ALSA_HOTPLUG_THREAD
|
||||
SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
|
||||
ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", NULL);
|
||||
/* if the thread doesn't spin, oh well, you just don't get further hotplug events. */
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ALSA_Deinitialize(void)
|
||||
{
|
||||
ALSA_Device *dev;
|
||||
ALSA_Device *next;
|
||||
|
||||
#if SDL_ALSA_HOTPLUG_THREAD
|
||||
if (ALSA_hotplug_thread != NULL) {
|
||||
SDL_AtomicSet(&ALSA_hotplug_shutdown, 1);
|
||||
SDL_WaitThread(ALSA_hotplug_thread, NULL);
|
||||
ALSA_hotplug_thread = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Shutting down! Clean up any data we've gathered. */
|
||||
for (dev = hotplug_devices; dev; dev = next) {
|
||||
/*printf("ALSA: at shutdown, removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
|
||||
next = dev->next;
|
||||
SDL_free(dev->name);
|
||||
SDL_free(dev);
|
||||
}
|
||||
hotplug_devices = NULL;
|
||||
|
||||
UnloadALSALibrary();
|
||||
}
|
||||
|
||||
static SDL_bool ALSA_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (LoadALSALibrary() < 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = ALSA_DetectDevices;
|
||||
impl->OpenDevice = ALSA_OpenDevice;
|
||||
impl->WaitDevice = ALSA_WaitDevice;
|
||||
impl->GetDeviceBuf = ALSA_GetDeviceBuf;
|
||||
impl->PlayDevice = ALSA_PlayDevice;
|
||||
impl->CloseDevice = ALSA_CloseDevice;
|
||||
impl->Deinitialize = ALSA_Deinitialize;
|
||||
impl->CaptureFromDevice = ALSA_CaptureFromDevice;
|
||||
impl->FlushCapture = ALSA_FlushCapture;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->SupportsNonPow2Samples = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap ALSA_bootstrap = {
|
||||
"alsa", "ALSA PCM audio", ALSA_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_ALSA */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
48
third_party/SDL/src/audio/alsa/SDL_alsa_audio.h
vendored
Normal file
48
third_party/SDL/src/audio/alsa/SDL_alsa_audio.h
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_ALSA_audio_h_
|
||||
#define SDL_ALSA_audio_h_
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The audio device handle */
|
||||
snd_pcm_t *pcm_handle;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
|
||||
/* swizzle function */
|
||||
void (*swizzle_func)(_THIS, void *buffer, Uint32 bufferlen);
|
||||
};
|
||||
|
||||
#endif /* SDL_ALSA_audio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
208
third_party/SDL/src/audio/android/SDL_androidaudio.c
vendored
Normal file
208
third_party/SDL/src/audio/android/SDL_androidaudio.c
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_ANDROID
|
||||
|
||||
/* Output audio to Android */
|
||||
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_androidaudio.h"
|
||||
|
||||
#include "../../core/android/SDL_android.h"
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
static SDL_AudioDevice *audioDevice = NULL;
|
||||
static SDL_AudioDevice *captureDevice = NULL;
|
||||
|
||||
|
||||
static int ANDROIDAUDIO_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
SDL_AudioFormat test_format;
|
||||
SDL_bool iscapture = this->iscapture;
|
||||
|
||||
SDL_assert((captureDevice == NULL) || !iscapture);
|
||||
SDL_assert((audioDevice == NULL) || iscapture);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
if ((test_format == AUDIO_U8) ||
|
||||
(test_format == AUDIO_S16) ||
|
||||
(test_format == AUDIO_F32)) {
|
||||
this->spec.format = test_format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
/* Didn't find a compatible format :( */
|
||||
return SDL_SetError("%s: Unsupported audio format", "android");
|
||||
}
|
||||
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ANDROIDAUDIO_PlayDevice(_THIS)
|
||||
{
|
||||
Android_JNI_WriteAudioBuffer();
|
||||
}
|
||||
|
||||
static Uint8 *ANDROIDAUDIO_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return Android_JNI_GetAudioBuffer();
|
||||
}
|
||||
|
||||
static int ANDROIDAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
|
||||
{
|
||||
return Android_JNI_CaptureAudioBuffer(buffer, buflen);
|
||||
}
|
||||
|
||||
static void ANDROIDAUDIO_FlushCapture(_THIS)
|
||||
{
|
||||
Android_JNI_FlushCapturedAudio();
|
||||
}
|
||||
|
||||
static void ANDROIDAUDIO_CloseDevice(_THIS)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
static SDL_bool ANDROIDAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = Android_DetectDevices;
|
||||
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. */
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void ANDROIDAUDIO_ResumeDevices(void) {}
|
||||
void ANDROIDAUDIO_PauseDevices(void) {}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_ANDROID */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
42
third_party/SDL/src/audio/android/SDL_androidaudio.h
vendored
Normal file
42
third_party/SDL/src/audio/android/SDL_androidaudio.h
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_androidaudio_h_
|
||||
#define SDL_androidaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* Resume device if it was paused automatically */
|
||||
int resume;
|
||||
};
|
||||
|
||||
void ANDROIDAUDIO_ResumeDevices(void);
|
||||
void ANDROIDAUDIO_PauseDevices(void);
|
||||
|
||||
#endif /* SDL_androidaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
344
third_party/SDL/src/audio/arts/SDL_artsaudio.c
vendored
Normal file
344
third_party/SDL/src/audio/arts/SDL_artsaudio.c
vendored
Normal file
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_ARTS
|
||||
|
||||
/* Allow access to a raw mixing buffer */
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
#include <signal.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_artsaudio.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
|
||||
#include "SDL_name.h"
|
||||
#include "SDL_loadso.h"
|
||||
#else
|
||||
#define SDL_NAME(X) X
|
||||
#endif
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
|
||||
|
||||
static const char *arts_library = SDL_AUDIO_DRIVER_ARTS_DYNAMIC;
|
||||
static void *arts_handle = NULL;
|
||||
|
||||
/* !!! FIXME: I hate this SDL_NAME clutter...it makes everything so messy! */
|
||||
static int (*SDL_NAME(arts_init)) (void);
|
||||
static void (*SDL_NAME(arts_free)) (void);
|
||||
static arts_stream_t(*SDL_NAME(arts_play_stream)) (int rate, int bits,
|
||||
int channels,
|
||||
const char *name);
|
||||
static int (*SDL_NAME(arts_stream_set)) (arts_stream_t s,
|
||||
arts_parameter_t param, int value);
|
||||
static int (*SDL_NAME(arts_stream_get)) (arts_stream_t s,
|
||||
arts_parameter_t param);
|
||||
static int (*SDL_NAME(arts_write)) (arts_stream_t s, const void *buffer,
|
||||
int count);
|
||||
static void (*SDL_NAME(arts_close_stream)) (arts_stream_t s);
|
||||
static int (*SDL_NAME(arts_suspend))(void);
|
||||
static int (*SDL_NAME(arts_suspended)) (void);
|
||||
static const char *(*SDL_NAME(arts_error_text)) (int errorcode);
|
||||
|
||||
#define SDL_ARTS_SYM(x) { #x, (void **) (char *) &SDL_NAME(x) }
|
||||
static struct
|
||||
{
|
||||
const char *name;
|
||||
void **func;
|
||||
} arts_functions[] = {
|
||||
/* *INDENT-OFF* */
|
||||
SDL_ARTS_SYM(arts_init),
|
||||
SDL_ARTS_SYM(arts_free),
|
||||
SDL_ARTS_SYM(arts_play_stream),
|
||||
SDL_ARTS_SYM(arts_stream_set),
|
||||
SDL_ARTS_SYM(arts_stream_get),
|
||||
SDL_ARTS_SYM(arts_write),
|
||||
SDL_ARTS_SYM(arts_close_stream),
|
||||
SDL_ARTS_SYM(arts_suspend),
|
||||
SDL_ARTS_SYM(arts_suspended),
|
||||
SDL_ARTS_SYM(arts_error_text),
|
||||
/* *INDENT-ON* */
|
||||
};
|
||||
|
||||
#undef SDL_ARTS_SYM
|
||||
|
||||
static void UnloadARTSLibrary()
|
||||
{
|
||||
if (arts_handle != NULL) {
|
||||
SDL_UnloadObject(arts_handle);
|
||||
arts_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadARTSLibrary(void)
|
||||
{
|
||||
int i, retval = -1;
|
||||
|
||||
if (arts_handle == NULL) {
|
||||
arts_handle = SDL_LoadObject(arts_library);
|
||||
if (arts_handle != NULL) {
|
||||
retval = 0;
|
||||
for (i = 0; i < SDL_arraysize(arts_functions); ++i) {
|
||||
*arts_functions[i].func =
|
||||
SDL_LoadFunction(arts_handle, arts_functions[i].name);
|
||||
if (!*arts_functions[i].func) {
|
||||
retval = -1;
|
||||
UnloadARTSLibrary();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void UnloadARTSLibrary()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static int LoadARTSLibrary(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_ARTS_DYNAMIC */
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void ARTS_WaitDevice(_THIS)
|
||||
{
|
||||
Sint32 ticks;
|
||||
|
||||
/* Check to see if the thread-parent process is still alive */
|
||||
{
|
||||
static int cnt = 0;
|
||||
/* Note that this only works with thread implementations
|
||||
that use a different process id for each thread.
|
||||
*/
|
||||
/* Check every 10 loops */
|
||||
if (this->hidden->parent && (((++cnt) % 10) == 0)) {
|
||||
if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) {
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Use timer for general audio synchronization */
|
||||
ticks =
|
||||
((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
|
||||
if (ticks > 0) {
|
||||
SDL_Delay(ticks);
|
||||
}
|
||||
}
|
||||
|
||||
static void ARTS_PlayDevice(_THIS)
|
||||
{
|
||||
/* Write the audio data */
|
||||
int written = SDL_NAME(arts_write) (this->hidden->stream,
|
||||
this->hidden->mixbuf,
|
||||
this->hidden->mixlen);
|
||||
|
||||
/* If timer synchronization is enabled, set the next write frame */
|
||||
if (this->hidden->frame_ticks) {
|
||||
this->hidden->next_frame += this->hidden->frame_ticks;
|
||||
}
|
||||
|
||||
/* If we couldn't write, assume fatal error for now */
|
||||
if (written < 0) {
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||
#endif
|
||||
}
|
||||
|
||||
static Uint8 *ARTS_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return (this->hidden->mixbuf);
|
||||
}
|
||||
|
||||
|
||||
static void ARTS_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->stream) {
|
||||
SDL_NAME(arts_close_stream) (this->hidden->stream);
|
||||
}
|
||||
SDL_NAME(arts_free) ();
|
||||
SDL_free(this->hidden->mixbuf);
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
static int ARTS_Suspend(void)
|
||||
{
|
||||
const Uint32 abortms = SDL_GetTicks() + 3000; /* give up after 3 secs */
|
||||
while ( (!SDL_NAME(arts_suspended)()) && !SDL_TICKS_PASSED(SDL_GetTicks(), abortms) ) {
|
||||
if ( SDL_NAME(arts_suspend)() ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SDL_NAME(arts_suspended)();
|
||||
}
|
||||
|
||||
static int ARTS_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
int rc = 0;
|
||||
int bits, frag_spec = 0;
|
||||
SDL_AudioFormat test_format = 0;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*this->hidden));
|
||||
if (this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(this->hidden);
|
||||
|
||||
/* Try for a closest match on audio format */
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
|
||||
#endif
|
||||
switch (test_format) {
|
||||
case AUDIO_U8:
|
||||
case AUDIO_S16LSB:
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "arts");
|
||||
}
|
||||
this->spec.format = test_format;
|
||||
bits = SDL_AUDIO_BITSIZE(test_format);
|
||||
|
||||
if ((rc = SDL_NAME(arts_init) ()) != 0) {
|
||||
return SDL_SetError("Unable to initialize ARTS: %s",
|
||||
SDL_NAME(arts_error_text) (rc));
|
||||
}
|
||||
|
||||
if (!ARTS_Suspend()) {
|
||||
return SDL_SetError("ARTS can not open audio device");
|
||||
}
|
||||
|
||||
this->hidden->stream = SDL_NAME(arts_play_stream) (this->spec.freq,
|
||||
bits,
|
||||
this->spec.channels,
|
||||
"SDL");
|
||||
|
||||
/* Play nothing so we have at least one write (server bug workaround). */
|
||||
SDL_NAME(arts_write) (this->hidden->stream, "", 0);
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
/* Determine the power of two of the fragment size */
|
||||
for (frag_spec = 0; (0x01 << frag_spec) < this->spec.size; ++frag_spec);
|
||||
if ((0x01 << frag_spec) != this->spec.size) {
|
||||
return SDL_SetError("Fragment size must be a power of two");
|
||||
}
|
||||
frag_spec |= 0x00020000; /* two fragments, for low latency */
|
||||
|
||||
#ifdef ARTS_P_PACKET_SETTINGS
|
||||
SDL_NAME(arts_stream_set) (this->hidden->stream,
|
||||
ARTS_P_PACKET_SETTINGS, frag_spec);
|
||||
#else
|
||||
SDL_NAME(arts_stream_set) (this->hidden->stream, ARTS_P_PACKET_SIZE,
|
||||
frag_spec & 0xffff);
|
||||
SDL_NAME(arts_stream_set) (this->hidden->stream, ARTS_P_PACKET_COUNT,
|
||||
frag_spec >> 16);
|
||||
#endif
|
||||
this->spec.size = SDL_NAME(arts_stream_get) (this->hidden->stream,
|
||||
ARTS_P_PACKET_SIZE);
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
this->hidden->mixlen = this->spec.size;
|
||||
this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
|
||||
if (this->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
|
||||
|
||||
/* Get the parent process id (we're the parent of the audio thread) */
|
||||
this->hidden->parent = getpid();
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void ARTS_Deinitialize(void)
|
||||
{
|
||||
UnloadARTSLibrary();
|
||||
}
|
||||
|
||||
|
||||
static SDL_bool ARTS_Init(SDL_AudioDriverImpl * impl)
|
||||
{
|
||||
if (LoadARTSLibrary() < 0) {
|
||||
return SDL_FALSE;
|
||||
} else {
|
||||
if (SDL_NAME(arts_init) () != NULL) {
|
||||
UnloadARTSLibrary();
|
||||
SDL_SetError("ARTS: arts_init failed (no audio server?)");
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Play a stream so aRts doesn't crash */
|
||||
if (ARTS_Suspend()) {
|
||||
arts_stream_t stream;
|
||||
stream = SDL_NAME(arts_play_stream) (44100, 16, 2, "SDL");
|
||||
SDL_NAME(arts_write) (stream, "", 0);
|
||||
SDL_NAME(arts_close_stream) (stream);
|
||||
}
|
||||
|
||||
SDL_NAME(arts_free) ();
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = ARTS_OpenDevice;
|
||||
impl->PlayDevice = ARTS_PlayDevice;
|
||||
impl->WaitDevice = ARTS_WaitDevice;
|
||||
impl->GetDeviceBuf = ARTS_GetDeviceBuf;
|
||||
impl->CloseDevice = ARTS_CloseDevice;
|
||||
impl->Deinitialize = ARTS_Deinitialize;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
|
||||
AudioBootStrap ARTS_bootstrap = {
|
||||
"arts", "Analog RealTime Synthesizer", ARTS_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_ARTS */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
53
third_party/SDL/src/audio/arts/SDL_artsaudio.h
vendored
Normal file
53
third_party/SDL/src/audio/arts/SDL_artsaudio.h
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_artsaudio_h_
|
||||
#define SDL_artsaudio_h_
|
||||
|
||||
#include <artsc.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The stream descriptor for the audio device */
|
||||
arts_stream_t stream;
|
||||
|
||||
/* The parent process id, to detect when application quits */
|
||||
pid_t parent;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
|
||||
/* Support for audio timing using a timer, in addition to SDL_IOReady() */
|
||||
float frame_ticks;
|
||||
float next_frame;
|
||||
};
|
||||
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
|
||||
|
||||
#endif /* SDL_artsaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
76
third_party/SDL/src/audio/coreaudio/SDL_coreaudio.h
vendored
Normal file
76
third_party/SDL/src/audio/coreaudio/SDL_coreaudio.h
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_coreaudio_h_
|
||||
#define SDL_coreaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#if !defined(__IPHONEOS__)
|
||||
#define MACOSX_COREAUDIO 1
|
||||
#endif
|
||||
|
||||
#if MACOSX_COREAUDIO
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#else
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <UIKit/UIApplication.h>
|
||||
#endif
|
||||
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
|
||||
/* Things named "Master" were renamed to "Main" in macOS 12.0's SDK. */
|
||||
#if MACOSX_COREAUDIO
|
||||
#include <AvailabilityMacros.h>
|
||||
#ifndef MAC_OS_VERSION_12_0
|
||||
#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
SDL_Thread *thread;
|
||||
AudioQueueRef audioQueue;
|
||||
int numAudioBuffers;
|
||||
AudioQueueBufferRef *audioBuffer;
|
||||
void *buffer;
|
||||
UInt32 bufferOffset;
|
||||
UInt32 bufferSize;
|
||||
AudioStreamBasicDescription strdesc;
|
||||
SDL_sem *ready_semaphore;
|
||||
char *thread_error;
|
||||
#if MACOSX_COREAUDIO
|
||||
AudioDeviceID deviceID;
|
||||
SDL_atomic_t device_change_flag;
|
||||
#else
|
||||
SDL_bool interrupted;
|
||||
CFTypeRef interruption_listener;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* SDL_coreaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
1311
third_party/SDL/src/audio/coreaudio/SDL_coreaudio.m
vendored
Normal file
1311
third_party/SDL/src/audio/coreaudio/SDL_coreaudio.m
vendored
Normal file
File diff suppressed because it is too large
Load Diff
659
third_party/SDL/src/audio/directsound/SDL_directsound.c
vendored
Normal file
659
third_party/SDL/src/audio/directsound/SDL_directsound.c
vendored
Normal file
@@ -0,0 +1,659 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_DSOUND
|
||||
|
||||
/* Allow access to a raw mixing buffer */
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_loadso.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_directsound.h"
|
||||
#include <mmreg.h>
|
||||
#if HAVE_MMDEVICEAPI_H
|
||||
#include "../../core/windows/SDL_immdevice.h"
|
||||
#endif /* HAVE_MMDEVICEAPI_H */
|
||||
|
||||
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
||||
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
|
||||
#endif
|
||||
|
||||
/* For Vista+, we can enumerate DSound devices with IMMDevice */
|
||||
#if HAVE_MMDEVICEAPI_H
|
||||
static SDL_bool SupportsIMMDevice = SDL_FALSE;
|
||||
#endif /* HAVE_MMDEVICEAPI_H */
|
||||
|
||||
/* 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);
|
||||
static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL;
|
||||
static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL;
|
||||
static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = NULL;
|
||||
static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = 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 } };
|
||||
|
||||
static void DSOUND_Unload(void)
|
||||
{
|
||||
pDirectSoundCreate8 = NULL;
|
||||
pDirectSoundEnumerateW = NULL;
|
||||
pDirectSoundCaptureCreate8 = NULL;
|
||||
pDirectSoundCaptureEnumerateW = NULL;
|
||||
|
||||
if (DSoundDLL != NULL) {
|
||||
SDL_UnloadObject(DSoundDLL);
|
||||
DSoundDLL = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int DSOUND_Load(void)
|
||||
{
|
||||
int loaded = 0;
|
||||
|
||||
DSOUND_Unload();
|
||||
|
||||
DSoundDLL = SDL_LoadObject("DSOUND.DLL");
|
||||
if (DSoundDLL == NULL) {
|
||||
SDL_SetError("DirectSound: failed to load DSOUND.DLL");
|
||||
} else {
|
||||
/* 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. */
|
||||
DSOUNDLOAD(DirectSoundCreate8);
|
||||
DSOUNDLOAD(DirectSoundEnumerateW);
|
||||
DSOUNDLOAD(DirectSoundCaptureCreate8);
|
||||
DSOUNDLOAD(DirectSoundCaptureEnumerateW);
|
||||
#undef DSOUNDLOAD
|
||||
|
||||
if (!loaded) {
|
||||
SDL_SetError("DirectSound: System doesn't appear to have DX8.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
DSOUND_Unload();
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
static int SetDSerror(const char *function, int code)
|
||||
{
|
||||
const char *error;
|
||||
|
||||
switch (code) {
|
||||
case E_NOINTERFACE:
|
||||
error = "Unsupported interface -- Is DirectX 8.0 or later installed?";
|
||||
break;
|
||||
case DSERR_ALLOCATED:
|
||||
error = "Audio device in use";
|
||||
break;
|
||||
case DSERR_BADFORMAT:
|
||||
error = "Unsupported audio format";
|
||||
break;
|
||||
case DSERR_BUFFERLOST:
|
||||
error = "Mixing buffer was lost";
|
||||
break;
|
||||
case DSERR_CONTROLUNAVAIL:
|
||||
error = "Control requested is not available";
|
||||
break;
|
||||
case DSERR_INVALIDCALL:
|
||||
error = "Invalid call for the current state";
|
||||
break;
|
||||
case DSERR_INVALIDPARAM:
|
||||
error = "Invalid parameter";
|
||||
break;
|
||||
case DSERR_NODRIVER:
|
||||
error = "No audio device found";
|
||||
break;
|
||||
case DSERR_OUTOFMEMORY:
|
||||
error = "Out of memory";
|
||||
break;
|
||||
case DSERR_PRIOLEVELNEEDED:
|
||||
error = "Caller doesn't have priority";
|
||||
break;
|
||||
case DSERR_UNSUPPORTED:
|
||||
error = "Function not supported";
|
||||
break;
|
||||
default:
|
||||
error = "Unknown DirectSound error";
|
||||
break;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
#if HAVE_MMDEVICEAPI_H
|
||||
if (SupportsIMMDevice) {
|
||||
return SDL_IMMDevice_GetDefaultAudioInfo(name, spec, iscapture);
|
||||
}
|
||||
#endif /* HAVE_MMDEVICEAPI_H */
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static BOOL CALLBACK FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID data)
|
||||
{
|
||||
const int iscapture = (int)((size_t)data);
|
||||
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));
|
||||
|
||||
/* 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. */
|
||||
}
|
||||
}
|
||||
return TRUE; /* keep enumerating. */
|
||||
}
|
||||
|
||||
static void DSOUND_DetectDevices(void)
|
||||
{
|
||||
#if 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));
|
||||
#if HAVE_MMDEVICEAPI_H
|
||||
}
|
||||
#endif /* HAVE_MMDEVICEAPI_H*/
|
||||
}
|
||||
|
||||
static void DSOUND_WaitDevice(_THIS)
|
||||
{
|
||||
DWORD status = 0;
|
||||
DWORD cursor = 0;
|
||||
DWORD junk = 0;
|
||||
HRESULT result = DS_OK;
|
||||
|
||||
/* 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,
|
||||
&junk, &cursor);
|
||||
if (result != DS_OK) {
|
||||
if (result == DSERR_BUFFERLOST) {
|
||||
IDirectSoundBuffer_Restore(this->hidden->mixbuf);
|
||||
}
|
||||
#ifdef DEBUG_SOUND
|
||||
SetDSerror("DirectSound GetCurrentPosition", result);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
while ((cursor / this->spec.size) == this->hidden->lastchunk) {
|
||||
/* FIXME: find out how much time is left and sleep that long */
|
||||
SDL_Delay(1);
|
||||
|
||||
/* Try to restore a lost sound buffer */
|
||||
IDirectSoundBuffer_GetStatus(this->hidden->mixbuf, &status);
|
||||
if (status & DSBSTATUS_BUFFERLOST) {
|
||||
IDirectSoundBuffer_Restore(this->hidden->mixbuf);
|
||||
IDirectSoundBuffer_GetStatus(this->hidden->mixbuf, &status);
|
||||
if (status & DSBSTATUS_BUFFERLOST) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(status & DSBSTATUS_PLAYING)) {
|
||||
result = IDirectSoundBuffer_Play(this->hidden->mixbuf, 0, 0,
|
||||
DSBPLAY_LOOPING);
|
||||
if (result == DS_OK) {
|
||||
continue;
|
||||
}
|
||||
#ifdef DEBUG_SOUND
|
||||
SetDSerror("DirectSound Play", result);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find out where we are playing */
|
||||
result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf,
|
||||
&junk, &cursor);
|
||||
if (result != DS_OK) {
|
||||
SetDSerror("DirectSound GetCurrentPosition", result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DSOUND_PlayDevice(_THIS)
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 *DSOUND_GetDeviceBuf(_THIS)
|
||||
{
|
||||
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,
|
||||
&junk, &cursor);
|
||||
if (result == DSERR_BUFFERLOST) {
|
||||
IDirectSoundBuffer_Restore(this->hidden->mixbuf);
|
||||
result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf,
|
||||
&junk, &cursor);
|
||||
}
|
||||
if (result != DS_OK) {
|
||||
SetDSerror("DirectSound GetCurrentPosition", result);
|
||||
return NULL;
|
||||
}
|
||||
cursor /= this->spec.size;
|
||||
#ifdef DEBUG_SOUND
|
||||
/* Detect audio dropouts */
|
||||
{
|
||||
DWORD spot = cursor;
|
||||
if (spot < this->hidden->lastchunk) {
|
||||
spot += this->hidden->num_buffers;
|
||||
}
|
||||
if (spot > this->hidden->lastchunk + 1) {
|
||||
fprintf(stderr, "Audio dropout, missed %d fragments\n",
|
||||
(spot - (this->hidden->lastchunk + 1)));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
this->hidden->lastchunk = cursor;
|
||||
cursor = (cursor + 1) % this->hidden->num_buffers;
|
||||
cursor *= this->spec.size;
|
||||
|
||||
/* Lock the audio buffer */
|
||||
result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor,
|
||||
this->spec.size,
|
||||
(LPVOID *)&this->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,
|
||||
&junk, 0);
|
||||
}
|
||||
if (result != DS_OK) {
|
||||
SetDSerror("DirectSound Lock", result);
|
||||
return NULL;
|
||||
}
|
||||
return this->hidden->locked_buf;
|
||||
}
|
||||
|
||||
static int DSOUND_CaptureFromDevice(_THIS, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = this->hidden;
|
||||
DWORD junk, cursor, ptr1len, ptr2len;
|
||||
VOID *ptr1, *ptr2;
|
||||
|
||||
SDL_assert(buflen == this->spec.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) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_assert(ptr1len == this->spec.size);
|
||||
SDL_assert(ptr2 == NULL);
|
||||
SDL_assert(ptr2len == 0);
|
||||
|
||||
SDL_memcpy(buffer, ptr1, ptr1len);
|
||||
|
||||
if (IDirectSoundCaptureBuffer_Unlock(h->capturebuf, ptr1, ptr1len, ptr2, ptr2len) != DS_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
h->lastchunk = (h->lastchunk + 1) % h->num_buffers;
|
||||
|
||||
return ptr1len;
|
||||
}
|
||||
|
||||
static void DSOUND_FlushCapture(_THIS)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = this->hidden;
|
||||
DWORD junk, cursor;
|
||||
if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) == DS_OK) {
|
||||
h->lastchunk = cursor / this->spec.size;
|
||||
}
|
||||
}
|
||||
|
||||
static void DSOUND_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->mixbuf != NULL) {
|
||||
IDirectSoundBuffer_Stop(this->hidden->mixbuf);
|
||||
IDirectSoundBuffer_Release(this->hidden->mixbuf);
|
||||
}
|
||||
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(_THIS, const DWORD bufsize, WAVEFORMATEX *wfmt)
|
||||
{
|
||||
LPDIRECTSOUND sndObj = this->hidden->sound;
|
||||
LPDIRECTSOUNDBUFFER *sndbuf = &this->hidden->mixbuf;
|
||||
HRESULT result = DS_OK;
|
||||
DSBUFFERDESC format;
|
||||
LPVOID pvAudioPtr1, pvAudioPtr2;
|
||||
DWORD dwAudioBytes1, dwAudioBytes2;
|
||||
|
||||
/* Try to create the secondary buffer */
|
||||
SDL_zero(format);
|
||||
format.dwSize = sizeof(format);
|
||||
format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
|
||||
format.dwFlags |= DSBCAPS_GLOBALFOCUS;
|
||||
format.dwBufferBytes = bufsize;
|
||||
format.lpwfxFormat = wfmt;
|
||||
result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL);
|
||||
if (result != DS_OK) {
|
||||
return SetDSerror("DirectSound CreateSoundBuffer", result);
|
||||
}
|
||||
IDirectSoundBuffer_SetFormat(*sndbuf, wfmt);
|
||||
|
||||
/* 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);
|
||||
IDirectSoundBuffer_Unlock(*sndbuf,
|
||||
(LPVOID)pvAudioPtr1, dwAudioBytes1,
|
||||
(LPVOID)pvAudioPtr2, dwAudioBytes2);
|
||||
}
|
||||
|
||||
/* We're ready to go */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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(_THIS, const DWORD bufsize, WAVEFORMATEX *wfmt)
|
||||
{
|
||||
LPDIRECTSOUNDCAPTURE capture = this->hidden->capture;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &this->hidden->capturebuf;
|
||||
DSCBUFFERDESC format;
|
||||
HRESULT result;
|
||||
|
||||
SDL_zero(format);
|
||||
format.dwSize = sizeof(format);
|
||||
format.dwFlags = DSCBCAPS_WAVEMAPPED;
|
||||
format.dwBufferBytes = bufsize;
|
||||
format.lpwfxFormat = wfmt;
|
||||
|
||||
result = IDirectSoundCapture_CreateCaptureBuffer(capture, &format, capturebuf, NULL);
|
||||
if (result != DS_OK) {
|
||||
return SetDSerror("DirectSound CreateCaptureBuffer", result);
|
||||
}
|
||||
|
||||
result = IDirectSoundCaptureBuffer_Start(*capturebuf, DSCBSTART_LOOPING);
|
||||
if (result != DS_OK) {
|
||||
IDirectSoundCaptureBuffer_Release(*capturebuf);
|
||||
return SetDSerror("DirectSound Start", result);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* presumably this starts at zero, but just in case... */
|
||||
result = IDirectSoundCaptureBuffer_GetCurrentPosition(*capturebuf, &junk, &cursor);
|
||||
if (result != DS_OK) {
|
||||
IDirectSoundCaptureBuffer_Stop(*capturebuf);
|
||||
IDirectSoundCaptureBuffer_Release(*capturebuf);
|
||||
return SetDSerror("DirectSound GetCurrentPosition", result);
|
||||
}
|
||||
|
||||
this->hidden->lastchunk = cursor / this->spec.size;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DSOUND_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
const DWORD numchunks = 8;
|
||||
HRESULT result;
|
||||
SDL_bool tried_format = SDL_FALSE;
|
||||
SDL_bool iscapture = this->iscapture;
|
||||
SDL_AudioFormat test_format;
|
||||
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) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(this->hidden);
|
||||
|
||||
/* Open the audio device */
|
||||
if (iscapture) {
|
||||
result = pDirectSoundCaptureCreate8(guid, &this->hidden->capture, NULL);
|
||||
if (result != DS_OK) {
|
||||
return SetDSerror("DirectSoundCaptureCreate8", result);
|
||||
}
|
||||
} else {
|
||||
result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL);
|
||||
if (result != DS_OK) {
|
||||
return SetDSerror("DirectSoundCreate8", result);
|
||||
}
|
||||
result = IDirectSound_SetCooperativeLevel(this->hidden->sound,
|
||||
GetDesktopWindow(),
|
||||
DSSCL_NORMAL);
|
||||
if (result != DS_OK) {
|
||||
return SetDSerror("DirectSound SetCooperativeLevel", result);
|
||||
}
|
||||
}
|
||||
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
switch (test_format) {
|
||||
case AUDIO_U8:
|
||||
case AUDIO_S16:
|
||||
case AUDIO_S32:
|
||||
case AUDIO_F32:
|
||||
tried_format = SDL_TRUE;
|
||||
|
||||
this->spec.format = test_format;
|
||||
|
||||
/* Update the fragment size as size in bytes */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
bufsize = numchunks * this->spec.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) {
|
||||
wfmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
wfmt.Format.cbSize = sizeof(wfmt) - sizeof(WAVEFORMATEX);
|
||||
|
||||
if (SDL_AUDIO_ISFLOAT(this->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);
|
||||
|
||||
switch (this->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 */
|
||||
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
||||
break;
|
||||
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 */
|
||||
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 */
|
||||
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 */
|
||||
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)) {
|
||||
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.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);
|
||||
if (rc == 0) {
|
||||
this->hidden->num_buffers = numchunks;
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
if (tried_format) {
|
||||
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() */
|
||||
|
||||
return 0; /* good to go. */
|
||||
}
|
||||
|
||||
static void DSOUND_Deinitialize(void)
|
||||
{
|
||||
#if HAVE_MMDEVICEAPI_H
|
||||
if (SupportsIMMDevice) {
|
||||
SDL_IMMDevice_Quit();
|
||||
SupportsIMMDevice = SDL_FALSE;
|
||||
}
|
||||
#endif /* HAVE_MMDEVICEAPI_H */
|
||||
DSOUND_Unload();
|
||||
}
|
||||
|
||||
static SDL_bool DSOUND_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (!DSOUND_Load()) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
#if HAVE_MMDEVICEAPI_H
|
||||
SupportsIMMDevice = !(SDL_IMMDevice_Init() < 0);
|
||||
#endif /* HAVE_MMDEVICEAPI_H */
|
||||
|
||||
/* 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->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. */
|
||||
}
|
||||
|
||||
AudioBootStrap DSOUND_bootstrap = {
|
||||
"directsound", "DirectSound", DSOUND_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_DSOUND */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
47
third_party/SDL/src/audio/directsound/SDL_directsound.h
vendored
Normal file
47
third_party/SDL/src/audio/directsound/SDL_directsound.h
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_directsound_h_
|
||||
#define SDL_directsound_h_
|
||||
|
||||
#include "../../core/windows/SDL_directx.h"
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
/* The DirectSound objects */
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
LPDIRECTSOUND sound;
|
||||
LPDIRECTSOUNDBUFFER mixbuf;
|
||||
LPDIRECTSOUNDCAPTURE capture;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER capturebuf;
|
||||
int num_buffers;
|
||||
DWORD lastchunk;
|
||||
Uint8 *locked_buf;
|
||||
};
|
||||
|
||||
#endif /* SDL_directsound_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
197
third_party/SDL/src/audio/disk/SDL_diskaudio.c
vendored
Normal file
197
third_party/SDL/src/audio/disk/SDL_diskaudio.c
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_DISK
|
||||
|
||||
/* Output raw audio data to a file. */
|
||||
|
||||
#if HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include "SDL_rwops.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_diskaudio.h"
|
||||
|
||||
/* !!! 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(_THIS)
|
||||
{
|
||||
SDL_Delay(_this->hidden->io_delay);
|
||||
}
|
||||
|
||||
static void DISKAUDIO_PlayDevice(_THIS)
|
||||
{
|
||||
const size_t written = SDL_RWwrite(_this->hidden->io,
|
||||
_this->hidden->mixbuf,
|
||||
1, _this->spec.size);
|
||||
|
||||
/* If we couldn't write, assume fatal error for now */
|
||||
if (written != _this->spec.size) {
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||
#endif
|
||||
}
|
||||
|
||||
static Uint8 *DISKAUDIO_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return _this->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static int DISKAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = _this->hidden;
|
||||
const int origbuflen = buflen;
|
||||
|
||||
SDL_Delay(h->io_delay);
|
||||
|
||||
if (h->io) {
|
||||
const size_t br = SDL_RWread(h->io, buffer, 1, buflen);
|
||||
buflen -= (int)br;
|
||||
buffer = ((Uint8 *)buffer) + br;
|
||||
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);
|
||||
|
||||
return origbuflen;
|
||||
}
|
||||
|
||||
static void DISKAUDIO_FlushCapture(_THIS)
|
||||
{
|
||||
/* no op...we don't advance the file pointer or anything. */
|
||||
}
|
||||
|
||||
static void DISKAUDIO_CloseDevice(_THIS)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
return devname;
|
||||
}
|
||||
|
||||
static int DISKAUDIO_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
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);
|
||||
const char *envr = SDL_getenv(DISKENVR_IODELAY);
|
||||
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)
|
||||
SDL_malloc(sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(_this->hidden);
|
||||
|
||||
if (envr != NULL) {
|
||||
_this->hidden->io_delay = SDL_atoi(envr);
|
||||
} else {
|
||||
_this->hidden->io_delay = ((_this->spec.samples * 1000) / _this->spec.freq);
|
||||
}
|
||||
|
||||
/* Open the audio device */
|
||||
_this->hidden->io = SDL_RWFromFile(fname, iscapture ? "rb" : "wb");
|
||||
if (_this->hidden->io == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
if (!iscapture) {
|
||||
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->spec.size);
|
||||
if (_this->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->spec.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);
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void DISKAUDIO_DetectDevices(void)
|
||||
{
|
||||
SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *)0x1);
|
||||
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->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. */
|
||||
}
|
||||
|
||||
AudioBootStrap DISKAUDIO_bootstrap = {
|
||||
"disk", "direct-to-disk audio", DISKAUDIO_Init, SDL_TRUE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_DISK */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
41
third_party/SDL/src/audio/disk/SDL_diskaudio.h
vendored
Normal file
41
third_party/SDL/src/audio/disk/SDL_diskaudio.h
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_diskaudio_h_
|
||||
#define SDL_diskaudio_h_
|
||||
|
||||
#include "SDL_rwops.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *_this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The file descriptor for the audio device */
|
||||
SDL_RWops *io;
|
||||
Uint32 io_delay;
|
||||
Uint8 *mixbuf;
|
||||
};
|
||||
|
||||
#endif /* SDL_diskaudio_h_ */
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
318
third_party/SDL/src/audio/dsp/SDL_dspaudio.c
vendored
Normal file
318
third_party/SDL/src/audio/dsp/SDL_dspaudio.c
vendored
Normal file
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_OSS
|
||||
|
||||
/* Allow access to a raw mixing buffer */
|
||||
|
||||
#include <stdio.h> /* For perror() */
|
||||
#include <string.h> /* For strerror() */
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <sys/soundcard.h>
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_audiodev_c.h"
|
||||
#include "SDL_dspaudio.h"
|
||||
|
||||
static void DSP_DetectDevices(void)
|
||||
{
|
||||
SDL_EnumUnixAudioDevices(0, NULL);
|
||||
}
|
||||
|
||||
static void DSP_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->audio_fd >= 0) {
|
||||
close(this->hidden->audio_fd);
|
||||
}
|
||||
SDL_free(this->hidden->mixbuf);
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
static int DSP_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
SDL_bool iscapture = this->iscapture;
|
||||
const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
|
||||
int format;
|
||||
int value;
|
||||
int frag_spec;
|
||||
SDL_AudioFormat test_format;
|
||||
|
||||
/* We don't care what the devname is...we'll try to open anything. */
|
||||
/* ...but default to first name in the list... */
|
||||
if (devname == NULL) {
|
||||
devname = SDL_GetAudioDeviceName(0, iscapture);
|
||||
if (devname == NULL) {
|
||||
return SDL_SetError("No such audio device");
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure fragment size stays a power of 2, or OSS fails. */
|
||||
/* I don't know which of these are actually legal values, though... */
|
||||
if (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) {
|
||||
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));
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
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) {
|
||||
perror("SNDCTL_DSP_GETFMTS");
|
||||
return SDL_SetError("Couldn't get audio format list");
|
||||
}
|
||||
|
||||
/* Try for a closest match on audio format */
|
||||
format = 0;
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format);
|
||||
!format && test_format;) {
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
|
||||
#endif
|
||||
switch (test_format) {
|
||||
case AUDIO_U8:
|
||||
if (value & AFMT_U8) {
|
||||
format = AFMT_U8;
|
||||
}
|
||||
break;
|
||||
case AUDIO_S16LSB:
|
||||
if (value & AFMT_S16_LE) {
|
||||
format = AFMT_S16_LE;
|
||||
}
|
||||
break;
|
||||
case AUDIO_S16MSB:
|
||||
if (value & AFMT_S16_BE) {
|
||||
format = AFMT_S16_BE;
|
||||
}
|
||||
break;
|
||||
#if 0
|
||||
/*
|
||||
* These formats are not used by any real life systems so they are not
|
||||
* needed here.
|
||||
*/
|
||||
case AUDIO_S8:
|
||||
if (value & AFMT_S8) {
|
||||
format = AFMT_S8;
|
||||
}
|
||||
break;
|
||||
case AUDIO_U16LSB:
|
||||
if (value & AFMT_U16_LE) {
|
||||
format = AFMT_U16_LE;
|
||||
}
|
||||
break;
|
||||
case AUDIO_U16MSB:
|
||||
if (value & AFMT_U16_BE) {
|
||||
format = AFMT_U16_BE;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
format = 0;
|
||||
break;
|
||||
}
|
||||
if (!format) {
|
||||
test_format = SDL_NextAudioFormat();
|
||||
}
|
||||
}
|
||||
if (format == 0) {
|
||||
return SDL_SetError("Couldn't find any hardware audio formats");
|
||||
}
|
||||
this->spec.format = test_format;
|
||||
|
||||
/* Set the audio format */
|
||||
value = format;
|
||||
if ((ioctl(this->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) {
|
||||
perror("SNDCTL_DSP_CHANNELS");
|
||||
return SDL_SetError("Cannot set the number of channels");
|
||||
}
|
||||
this->spec.channels = value;
|
||||
|
||||
/* Set the DSP frequency */
|
||||
value = this->spec.freq;
|
||||
if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
|
||||
perror("SNDCTL_DSP_SPEED");
|
||||
return SDL_SetError("Couldn't set audio frequency");
|
||||
}
|
||||
this->spec.freq = value;
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
/* 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");
|
||||
}
|
||||
frag_spec |= 0x00020000; /* two fragments, for low latency */
|
||||
|
||||
/* Set the audio buffering parameters */
|
||||
#ifdef DEBUG_AUDIO
|
||||
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) {
|
||||
perror("SNDCTL_DSP_SETFRAGMENT");
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
{
|
||||
audio_buf_info info;
|
||||
ioctl(this->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);
|
||||
fprintf(stderr, "bytes = %d\n", info.bytes);
|
||||
}
|
||||
#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) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
|
||||
}
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void DSP_PlayDevice(_THIS)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = this->hidden;
|
||||
if (write(h->audio_fd, h->mixbuf, h->mixlen) == -1) {
|
||||
perror("Audio write");
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", h->mixlen);
|
||||
#endif
|
||||
}
|
||||
|
||||
static Uint8 *DSP_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return this->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static int DSP_CaptureFromDevice(_THIS, void *buffer, int buflen)
|
||||
{
|
||||
return (int)read(this->hidden->audio_fd, buffer, buflen);
|
||||
}
|
||||
|
||||
static void DSP_FlushCapture(_THIS)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = this->hidden;
|
||||
audio_buf_info info;
|
||||
if (ioctl(h->audio_fd, SNDCTL_DSP_GETISPACE, &info) == 0) {
|
||||
while (info.bytes > 0) {
|
||||
char buf[512];
|
||||
const size_t len = SDL_min(sizeof(buf), info.bytes);
|
||||
const ssize_t br = read(h->audio_fd, buf, len);
|
||||
if (br <= 0) {
|
||||
break;
|
||||
}
|
||||
info.bytes -= br;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_bool InitTimeDevicesExist = SDL_FALSE;
|
||||
static int 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;
|
||||
}
|
||||
|
||||
static SDL_bool DSP_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
InitTimeDevicesExist = SDL_FALSE;
|
||||
SDL_EnumUnixAudioDevices(0, look_for_devices_test);
|
||||
if (!InitTimeDevicesExist) {
|
||||
SDL_SetError("dsp: No such audio device");
|
||||
return SDL_FALSE; /* maybe try a different backend. */
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = DSP_DetectDevices;
|
||||
impl->OpenDevice = DSP_OpenDevice;
|
||||
impl->PlayDevice = DSP_PlayDevice;
|
||||
impl->GetDeviceBuf = DSP_GetDeviceBuf;
|
||||
impl->CloseDevice = DSP_CloseDevice;
|
||||
impl->CaptureFromDevice = DSP_CaptureFromDevice;
|
||||
impl->FlushCapture = DSP_FlushCapture;
|
||||
|
||||
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap DSP_bootstrap = {
|
||||
"dsp", "OSS /dev/dsp standard audio", DSP_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_OSS */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
43
third_party/SDL/src/audio/dsp/SDL_dspaudio.h
vendored
Normal file
43
third_party/SDL/src/audio/dsp/SDL_dspaudio.h
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_dspaudio_h_
|
||||
#define SDL_dspaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The file descriptor for the audio device */
|
||||
int audio_fd;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
};
|
||||
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
|
||||
|
||||
#endif /* SDL_dspaudio_h_ */
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
64
third_party/SDL/src/audio/dummy/SDL_dummyaudio.c
vendored
Normal file
64
third_party/SDL/src/audio/dummy/SDL_dummyaudio.c
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
/* Output audio to nowhere... */
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_dummyaudio.h"
|
||||
|
||||
static int DUMMYAUDIO_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
_this->hidden = (void *)0x1; /* just something non-NULL */
|
||||
|
||||
return 0; /* always succeeds. */
|
||||
}
|
||||
|
||||
static int DUMMYAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
|
||||
{
|
||||
/* Delay to make this sort of simulate real audio input. */
|
||||
SDL_Delay((_this->spec.samples * 1000) / _this->spec.freq);
|
||||
|
||||
/* always return a full buffer of silence. */
|
||||
SDL_memset(buffer, _this->spec.silence, buflen);
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static SDL_bool DUMMYAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = DUMMYAUDIO_OpenDevice;
|
||||
impl->CaptureFromDevice = DUMMYAUDIO_CaptureFromDevice;
|
||||
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap DUMMYAUDIO_bootstrap = {
|
||||
"dummy", "SDL dummy audio driver", DUMMYAUDIO_Init, SDL_TRUE
|
||||
};
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
41
third_party/SDL/src/audio/dummy/SDL_dummyaudio.h
vendored
Normal file
41
third_party/SDL/src/audio/dummy/SDL_dummyaudio.h
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_dummyaudio_h_
|
||||
#define SDL_dummyaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *_this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The file descriptor for the audio device */
|
||||
Uint8 *mixbuf;
|
||||
Uint32 mixlen;
|
||||
Uint32 write_delay;
|
||||
Uint32 initial_calls;
|
||||
};
|
||||
|
||||
#endif /* SDL_dummyaudio_h_ */
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
410
third_party/SDL/src/audio/emscripten/SDL_emscriptenaudio.c
vendored
Normal file
410
third_party/SDL/src/audio/emscripten/SDL_emscriptenaudio.c
vendored
Normal file
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_EMSCRIPTEN
|
||||
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_emscriptenaudio.h"
|
||||
|
||||
#include <emscripten/emscripten.h>
|
||||
|
||||
/* !!! FIXME: this currently expects that the audio callback runs in the main thread,
|
||||
!!! FIXME: in intervals when the application isn't running, but that may not be
|
||||
!!! FIXME: true always once pthread support becomes widespread. Revisit this code
|
||||
!!! FIXME: at some point and see what needs to be done for that! */
|
||||
|
||||
static void FeedAudioDevice(_THIS, const void *buf, const int buflen)
|
||||
{
|
||||
const int framelen = (SDL_AUDIO_BITSIZE(this->spec.format) / 8) * this->spec.channels;
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL2 = Module['SDL2'];
|
||||
var numChannels = SDL2.audio.currentOutputBuffer['numberOfChannels'];
|
||||
for (var c = 0; c < numChannels; ++c) {
|
||||
var channelData = SDL2.audio.currentOutputBuffer['getChannelData'](c);
|
||||
if (channelData.length != $1) {
|
||||
throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!';
|
||||
}
|
||||
|
||||
for (var j = 0; j < $1; ++j) {
|
||||
channelData[j] = HEAPF32[$0 + ((j*numChannels + c) << 2) >> 2]; /* !!! FIXME: why are these shifts here? */
|
||||
}
|
||||
}
|
||||
}, buf, buflen / framelen);
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
}
|
||||
|
||||
static void HandleAudioProcess(_THIS)
|
||||
{
|
||||
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_AudioStreamClear(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_AudioStreamAvailable(this->stream) < ((int)this->spec.size)) {
|
||||
callback(this->callbackspec.userdata, this->work_buffer, stream_len);
|
||||
if (SDL_AudioStreamPut(this->stream, this->work_buffer, stream_len) == -1) {
|
||||
SDL_AudioStreamClear(this->stream);
|
||||
SDL_AtomicSet(&this->enabled, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
got = SDL_AudioStreamGet(this->stream, this->work_buffer, this->spec.size);
|
||||
SDL_assert((got < 0) || (got == this->spec.size));
|
||||
if (got != this->spec.size) {
|
||||
SDL_memset(this->work_buffer, this->spec.silence, this->spec.size);
|
||||
}
|
||||
}
|
||||
|
||||
FeedAudioDevice(this, this->work_buffer, this->spec.size);
|
||||
}
|
||||
|
||||
static void HandleCaptureProcess(_THIS)
|
||||
{
|
||||
SDL_AudioCallback callback = this->callbackspec.callback;
|
||||
const int stream_len = this->callbackspec.size;
|
||||
|
||||
/* Only do something if audio is enabled */
|
||||
if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
|
||||
SDL_AudioStreamClear(this->stream);
|
||||
return;
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL2 = Module['SDL2'];
|
||||
var numChannels = SDL2.capture.currentCaptureBuffer.numberOfChannels;
|
||||
for (var c = 0; c < numChannels; ++c) {
|
||||
var channelData = SDL2.capture.currentCaptureBuffer.getChannelData(c);
|
||||
if (channelData.length != $1) {
|
||||
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. */
|
||||
for (var j = 0; j < $1; ++j) {
|
||||
setValue($0 + (j * 4), channelData[j], 'float');
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < $1; ++j) {
|
||||
setValue($0 + (((j * numChannels) + c) * 4), channelData[j], 'float');
|
||||
}
|
||||
}
|
||||
}
|
||||
}, this->work_buffer, (this->spec.size / sizeof(float)) / this->spec.channels);
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
/* 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_AudioStreamPut(this->stream, this->work_buffer, this->spec.size) == -1) {
|
||||
SDL_AtomicSet(&this->enabled, 0);
|
||||
}
|
||||
|
||||
while (SDL_AudioStreamAvailable(this->stream) >= stream_len) {
|
||||
const int got = SDL_AudioStreamGet(this->stream, this->work_buffer, stream_len);
|
||||
SDL_assert((got < 0) || (got == stream_len));
|
||||
if (got != stream_len) {
|
||||
SDL_memset(this->work_buffer, this->callbackspec.silence, stream_len);
|
||||
}
|
||||
callback(this->callbackspec.userdata, this->work_buffer, stream_len); /* Send it to the app. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void EMSCRIPTENAUDIO_CloseDevice(_THIS)
|
||||
{
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL2 = Module['SDL2'];
|
||||
if ($0) {
|
||||
if (SDL2.capture.silenceTimer !== undefined) {
|
||||
clearTimeout(SDL2.capture.silenceTimer);
|
||||
}
|
||||
if (SDL2.capture.stream !== undefined) {
|
||||
var tracks = SDL2.capture.stream.getAudioTracks();
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
SDL2.capture.stream.removeTrack(tracks[i]);
|
||||
}
|
||||
SDL2.capture.stream = undefined;
|
||||
}
|
||||
if (SDL2.capture.scriptProcessorNode !== undefined) {
|
||||
SDL2.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) {};
|
||||
SDL2.capture.scriptProcessorNode.disconnect();
|
||||
SDL2.capture.scriptProcessorNode = undefined;
|
||||
}
|
||||
if (SDL2.capture.mediaStreamNode !== undefined) {
|
||||
SDL2.capture.mediaStreamNode.disconnect();
|
||||
SDL2.capture.mediaStreamNode = undefined;
|
||||
}
|
||||
if (SDL2.capture.silenceBuffer !== undefined) {
|
||||
SDL2.capture.silenceBuffer = undefined
|
||||
}
|
||||
SDL2.capture = undefined;
|
||||
} else {
|
||||
if (SDL2.audio.scriptProcessorNode != undefined) {
|
||||
SDL2.audio.scriptProcessorNode.disconnect();
|
||||
SDL2.audio.scriptProcessorNode = undefined;
|
||||
}
|
||||
SDL2.audio = undefined;
|
||||
}
|
||||
if ((SDL2.audioContext !== undefined) && (SDL2.audio === undefined) && (SDL2.capture === undefined)) {
|
||||
SDL2.audioContext.close();
|
||||
SDL2.audioContext = undefined;
|
||||
}
|
||||
}, this->iscapture);
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
#if 0 /* !!! FIXME: currently not used. Can we move some stuff off the SDL2 namespace? --ryan. */
|
||||
SDL_free(this->hidden);
|
||||
#endif
|
||||
}
|
||||
|
||||
EM_JS_DEPS(sdlaudio, "$autoResumeAudioContext,$dynCall");
|
||||
|
||||
static int EMSCRIPTENAUDIO_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
SDL_AudioFormat test_format;
|
||||
SDL_bool iscapture = this->iscapture;
|
||||
int result;
|
||||
|
||||
/* based on parts of library_sdl.js */
|
||||
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
/* create context */
|
||||
result = MAIN_THREAD_EM_ASM_INT({
|
||||
if(typeof(Module['SDL2']) === 'undefined') {
|
||||
Module['SDL2'] = {};
|
||||
}
|
||||
var SDL2 = Module['SDL2'];
|
||||
if (!$0) {
|
||||
SDL2.audio = {};
|
||||
} else {
|
||||
SDL2.capture = {};
|
||||
}
|
||||
|
||||
if (!SDL2.audioContext) {
|
||||
if (typeof(AudioContext) !== 'undefined') {
|
||||
SDL2.audioContext = new AudioContext();
|
||||
} else if (typeof(webkitAudioContext) !== 'undefined') {
|
||||
SDL2.audioContext = new webkitAudioContext();
|
||||
}
|
||||
if (SDL2.audioContext) {
|
||||
autoResumeAudioContext(SDL2.audioContext);
|
||||
}
|
||||
}
|
||||
return SDL2.audioContext === undefined ? -1 : 0;
|
||||
}, iscapture);
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
if (result < 0) {
|
||||
return SDL_SetError("Web Audio API is not available!");
|
||||
}
|
||||
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
switch (test_format) {
|
||||
case AUDIO_F32: /* web audio only supports floats */
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
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 SDL2 namespace? --ryan. */
|
||||
this->hidden = (struct SDL_PrivateAudioData *)
|
||||
SDL_malloc(sizeof(*this->hidden));
|
||||
if (this->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 SDL2 = Module['SDL2'];
|
||||
return SDL2.audioContext.sampleRate;
|
||||
});
|
||||
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
if (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. */
|
||||
|
||||
/* 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
|
||||
that the audiocontext uselessly mixes into any output. On the
|
||||
upside, both of those things are not only run in native code in
|
||||
the browser, they're probably SIMD code, too. MediaRecorder
|
||||
feels like it's a pretty inefficient tapdance in similar ways,
|
||||
to be honest. */
|
||||
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL2 = Module['SDL2'];
|
||||
var have_microphone = function(stream) {
|
||||
//console.log('SDL audio capture: we have a microphone! Replacing silence callback.');
|
||||
if (SDL2.capture.silenceTimer !== undefined) {
|
||||
clearTimeout(SDL2.capture.silenceTimer);
|
||||
SDL2.capture.silenceTimer = undefined;
|
||||
}
|
||||
SDL2.capture.mediaStreamNode = SDL2.audioContext.createMediaStreamSource(stream);
|
||||
SDL2.capture.scriptProcessorNode = SDL2.audioContext.createScriptProcessor($1, $0, 1);
|
||||
SDL2.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) {
|
||||
if ((SDL2 === undefined) || (SDL2.capture === undefined)) { return; }
|
||||
audioProcessingEvent.outputBuffer.getChannelData(0).fill(0.0);
|
||||
SDL2.capture.currentCaptureBuffer = audioProcessingEvent.inputBuffer;
|
||||
dynCall('vi', $2, [$3]);
|
||||
};
|
||||
SDL2.capture.mediaStreamNode.connect(SDL2.capture.scriptProcessorNode);
|
||||
SDL2.capture.scriptProcessorNode.connect(SDL2.audioContext.destination);
|
||||
SDL2.capture.stream = stream;
|
||||
};
|
||||
|
||||
var no_microphone = function(error) {
|
||||
//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). */
|
||||
SDL2.capture.silenceBuffer = SDL2.audioContext.createBuffer($0, $1, SDL2.audioContext.sampleRate);
|
||||
SDL2.capture.silenceBuffer.getChannelData(0).fill(0.0);
|
||||
var silence_callback = function() {
|
||||
SDL2.capture.currentCaptureBuffer = SDL2.capture.silenceBuffer;
|
||||
dynCall('vi', $2, [$3]);
|
||||
};
|
||||
|
||||
SDL2.capture.silenceTimer = setTimeout(silence_callback, ($1 / SDL2.audioContext.sampleRate) * 1000);
|
||||
|
||||
if ((navigator.mediaDevices !== undefined) && (navigator.mediaDevices.getUserMedia !== undefined)) {
|
||||
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(have_microphone).catch(no_microphone);
|
||||
} else if (navigator.webkitGetUserMedia !== undefined) {
|
||||
navigator.webkitGetUserMedia({ audio: true, video: false }, have_microphone, no_microphone);
|
||||
}
|
||||
}, this->spec.channels, this->spec.samples, HandleCaptureProcess, this);
|
||||
} else {
|
||||
/* setup a ScriptProcessorNode */
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL2 = Module['SDL2'];
|
||||
SDL2.audio.scriptProcessorNode = SDL2.audioContext['createScriptProcessor']($1, 0, $0);
|
||||
SDL2.audio.scriptProcessorNode['onaudioprocess'] = function (e) {
|
||||
if ((SDL2 === undefined) || (SDL2.audio === undefined)) { return; }
|
||||
SDL2.audio.currentOutputBuffer = e['outputBuffer'];
|
||||
dynCall('vi', $2, [$3]);
|
||||
};
|
||||
SDL2.audio.scriptProcessorNode['connect'](SDL2.audioContext['destination']);
|
||||
}, this->spec.channels, this->spec.samples, HandleAudioProcess, this);
|
||||
}
|
||||
/* *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->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
|
||||
/* no threads here */
|
||||
impl->LockDevice = impl->UnlockDevice = EMSCRIPTENAUDIO_LockOrUnlockDeviceWithNoMixerLock;
|
||||
impl->ProvidesOwnCallbackThread = SDL_TRUE;
|
||||
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
/* check availability */
|
||||
available = MAIN_THREAD_EM_ASM_INT({
|
||||
if (typeof(AudioContext) !== 'undefined') {
|
||||
return true;
|
||||
} else if (typeof(webkitAudioContext) !== 'undefined') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
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;
|
||||
} else if (typeof(navigator.webkitGetUserMedia) !== 'undefined') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
impl->HasCaptureSupport = capture_available ? SDL_TRUE : SDL_FALSE;
|
||||
impl->OnlyHasDefaultCaptureDevice = capture_available ? SDL_TRUE : SDL_FALSE;
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
AudioBootStrap EMSCRIPTENAUDIO_bootstrap = {
|
||||
"emscripten", "SDL emscripten audio driver", EMSCRIPTENAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_EMSCRIPTEN */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
38
third_party/SDL/src/audio/emscripten/SDL_emscriptenaudio.h
vendored
Normal file
38
third_party/SDL/src/audio/emscripten/SDL_emscriptenaudio.h
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_emscriptenaudio_h_
|
||||
#define SDL_emscriptenaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
int unused;
|
||||
};
|
||||
|
||||
#endif /* SDL_emscriptenaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
322
third_party/SDL/src/audio/esd/SDL_esdaudio.c
vendored
Normal file
322
third_party/SDL/src/audio/esd/SDL_esdaudio.c
vendored
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_ESD
|
||||
|
||||
/* Allow access to an ESD network stream mixing buffer */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <esd.h>
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_esdaudio.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ESD_DYNAMIC
|
||||
#include "SDL_name.h"
|
||||
#include "SDL_loadso.h"
|
||||
#else
|
||||
#define SDL_NAME(X) X
|
||||
#endif
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ESD_DYNAMIC
|
||||
|
||||
static const char *esd_library = SDL_AUDIO_DRIVER_ESD_DYNAMIC;
|
||||
static void *esd_handle = NULL;
|
||||
|
||||
static int (*SDL_NAME(esd_open_sound)) (const char *host);
|
||||
static int (*SDL_NAME(esd_close)) (int esd);
|
||||
static int (*SDL_NAME(esd_play_stream)) (esd_format_t format, int rate,
|
||||
const char *host, const char *name);
|
||||
|
||||
#define SDL_ESD_SYM(x) { #x, (void **) (char *) &SDL_NAME(x) }
|
||||
static struct
|
||||
{
|
||||
const char *name;
|
||||
void **func;
|
||||
} const esd_functions[] = {
|
||||
SDL_ESD_SYM(esd_open_sound),
|
||||
SDL_ESD_SYM(esd_close), SDL_ESD_SYM(esd_play_stream),
|
||||
};
|
||||
|
||||
#undef SDL_ESD_SYM
|
||||
|
||||
static void UnloadESDLibrary()
|
||||
{
|
||||
if (esd_handle != NULL) {
|
||||
SDL_UnloadObject(esd_handle);
|
||||
esd_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadESDLibrary(void)
|
||||
{
|
||||
int i, retval = -1;
|
||||
|
||||
if (esd_handle == NULL) {
|
||||
esd_handle = SDL_LoadObject(esd_library);
|
||||
if (esd_handle) {
|
||||
retval = 0;
|
||||
for (i = 0; i < SDL_arraysize(esd_functions); ++i) {
|
||||
*esd_functions[i].func =
|
||||
SDL_LoadFunction(esd_handle, esd_functions[i].name);
|
||||
if (!*esd_functions[i].func) {
|
||||
retval = -1;
|
||||
UnloadESDLibrary();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void UnloadESDLibrary()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static int LoadESDLibrary(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_ESD_DYNAMIC */
|
||||
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void ESD_WaitDevice(_THIS)
|
||||
{
|
||||
Sint32 ticks;
|
||||
|
||||
/* Check to see if the thread-parent process is still alive */
|
||||
{
|
||||
static int cnt = 0;
|
||||
/* Note that this only works with thread implementations
|
||||
that use a different process id for each thread.
|
||||
*/
|
||||
/* Check every 10 loops */
|
||||
if (this->hidden->parent && (((++cnt) % 10) == 0)) {
|
||||
if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) {
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Use timer for general audio synchronization */
|
||||
ticks = ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
|
||||
if (ticks > 0) {
|
||||
SDL_Delay(ticks);
|
||||
}
|
||||
}
|
||||
|
||||
static void ESD_PlayDevice(_THIS)
|
||||
{
|
||||
int written = 0;
|
||||
|
||||
/* Write the audio data, checking for EAGAIN on broken audio drivers */
|
||||
do {
|
||||
written = write(this->hidden->audio_fd,
|
||||
this->hidden->mixbuf, this->hidden->mixlen);
|
||||
if ((written < 0) && ((errno == 0) || (errno == EAGAIN))) {
|
||||
SDL_Delay(1); /* Let a little CPU time go by */
|
||||
}
|
||||
} while ((written < 0) &&
|
||||
((errno == 0) || (errno == EAGAIN) || (errno == EINTR)));
|
||||
|
||||
/* Set the next write frame */
|
||||
this->hidden->next_frame += this->hidden->frame_ticks;
|
||||
|
||||
/* If we couldn't write, assume fatal error for now */
|
||||
if (written < 0) {
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 *ESD_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return (this->hidden->mixbuf);
|
||||
}
|
||||
|
||||
static void ESD_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->audio_fd >= 0) {
|
||||
SDL_NAME(esd_close) (this->hidden->audio_fd);
|
||||
}
|
||||
SDL_free(this->hidden->mixbuf);
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
/* Try to get the name of the program */
|
||||
static char *get_progname(void)
|
||||
{
|
||||
char *progname = NULL;
|
||||
#ifdef __LINUX__
|
||||
FILE *fp;
|
||||
static char temp[BUFSIZ];
|
||||
|
||||
SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid());
|
||||
fp = fopen(temp, "r");
|
||||
if (fp != NULL) {
|
||||
if (fgets(temp, sizeof(temp) - 1, fp)) {
|
||||
progname = SDL_strrchr(temp, '/');
|
||||
if (progname == NULL) {
|
||||
progname = temp;
|
||||
} else {
|
||||
progname = progname + 1;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
#endif
|
||||
return (progname);
|
||||
}
|
||||
|
||||
|
||||
static int ESD_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
esd_format_t format = (ESD_STREAM | ESD_PLAY);
|
||||
SDL_AudioFormat test_format = 0;
|
||||
int found = 0;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*this->hidden));
|
||||
if (this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(this->hidden);
|
||||
this->hidden->audio_fd = -1;
|
||||
|
||||
/* Convert audio spec to the ESD audio format */
|
||||
/* Try for a closest match on audio format */
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format);
|
||||
!found && test_format; test_format = SDL_NextAudioFormat()) {
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
|
||||
#endif
|
||||
found = 1;
|
||||
switch (test_format) {
|
||||
case AUDIO_U8:
|
||||
format |= ESD_BITS8;
|
||||
break;
|
||||
case AUDIO_S16SYS:
|
||||
format |= ESD_BITS16;
|
||||
break;
|
||||
default:
|
||||
found = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return SDL_SetError("Couldn't find any hardware audio formats");
|
||||
}
|
||||
|
||||
if (this->spec.channels == 1) {
|
||||
format |= ESD_MONO;
|
||||
} else {
|
||||
format |= ESD_STEREO;
|
||||
}
|
||||
#if 0
|
||||
this->spec.samples = ESD_BUF_SIZE; /* Darn, no way to change this yet */
|
||||
#endif
|
||||
|
||||
/* Open a connection to the ESD audio server */
|
||||
this->hidden->audio_fd =
|
||||
SDL_NAME(esd_play_stream) (format, this->spec.freq, NULL,
|
||||
get_progname());
|
||||
|
||||
if (this->hidden->audio_fd < 0) {
|
||||
return SDL_SetError("Couldn't open ESD connection");
|
||||
}
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
this->hidden->frame_ticks =
|
||||
(float) (this->spec.samples * 1000) / this->spec.freq;
|
||||
this->hidden->next_frame = SDL_GetTicks() + this->hidden->frame_ticks;
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
this->hidden->mixlen = this->spec.size;
|
||||
this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
|
||||
if (this->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
|
||||
|
||||
/* Get the parent process id (we're the parent of the audio thread) */
|
||||
this->hidden->parent = getpid();
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ESD_Deinitialize(void)
|
||||
{
|
||||
UnloadESDLibrary();
|
||||
}
|
||||
|
||||
static SDL_bool ESD_Init(SDL_AudioDriverImpl * impl)
|
||||
{
|
||||
if (LoadESDLibrary() < 0) {
|
||||
return SDL_FALSE;
|
||||
} else {
|
||||
int connection = 0;
|
||||
|
||||
/* Don't start ESD if it's not running */
|
||||
SDL_setenv("ESD_NO_SPAWN", "1", 0);
|
||||
|
||||
connection = SDL_NAME(esd_open_sound) (NULL);
|
||||
if (connection < 0) {
|
||||
UnloadESDLibrary();
|
||||
SDL_SetError("ESD: esd_open_sound failed (no audio server?)");
|
||||
return SDL_FALSE;
|
||||
}
|
||||
SDL_NAME(esd_close) (connection);
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = ESD_OpenDevice;
|
||||
impl->PlayDevice = ESD_PlayDevice;
|
||||
impl->WaitDevice = ESD_WaitDevice;
|
||||
impl->GetDeviceBuf = ESD_GetDeviceBuf;
|
||||
impl->CloseDevice = ESD_CloseDevice;
|
||||
impl->Deinitialize = ESD_Deinitialize;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
|
||||
AudioBootStrap ESD_bootstrap = {
|
||||
"esd", "Enlightened Sound Daemon", ESD_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_ESD */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
51
third_party/SDL/src/audio/esd/SDL_esdaudio.h
vendored
Normal file
51
third_party/SDL/src/audio/esd/SDL_esdaudio.h
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_esdaudio_h_
|
||||
#define SDL_esdaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The file descriptor for the audio device */
|
||||
int audio_fd;
|
||||
|
||||
/* The parent process id, to detect when application quits */
|
||||
pid_t parent;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
|
||||
/* Support for audio timing using a timer */
|
||||
float frame_ticks;
|
||||
float next_frame;
|
||||
};
|
||||
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
|
||||
|
||||
#endif /* SDL_esdaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
305
third_party/SDL/src/audio/fusionsound/SDL_fsaudio.c
vendored
Normal file
305
third_party/SDL/src/audio/fusionsound/SDL_fsaudio.c
vendored
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_FUSIONSOUND
|
||||
|
||||
/* !!! FIXME: why is this is SDL_FS_* instead of FUSIONSOUND_*? */
|
||||
|
||||
/* Allow access to a raw mixing buffer */
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
#include <signal.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_fsaudio.h"
|
||||
|
||||
#include <fusionsound/fusionsound_version.h>
|
||||
|
||||
/* #define SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC "libfusionsound.so" */
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC
|
||||
#include "SDL_name.h"
|
||||
#include "SDL_loadso.h"
|
||||
#else
|
||||
#define SDL_NAME(X) X
|
||||
#endif
|
||||
|
||||
#if (FUSIONSOUND_MAJOR_VERSION == 1) && (FUSIONSOUND_MINOR_VERSION < 1)
|
||||
typedef DFBResult DirectResult;
|
||||
#endif
|
||||
|
||||
/* Buffers to use - more than 2 gives a lot of latency */
|
||||
#define FUSION_BUFFERS (2)
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC
|
||||
|
||||
static const char *fs_library = SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC;
|
||||
static void *fs_handle = NULL;
|
||||
|
||||
static DirectResult (*SDL_NAME(FusionSoundInit)) (int *argc, char *(*argv[]));
|
||||
static DirectResult (*SDL_NAME(FusionSoundCreate)) (IFusionSound **
|
||||
ret_interface);
|
||||
|
||||
#define SDL_FS_SYM(x) { #x, (void **) (char *) &SDL_NAME(x) }
|
||||
static struct
|
||||
{
|
||||
const char *name;
|
||||
void **func;
|
||||
} fs_functions[] = {
|
||||
/* *INDENT-OFF* */
|
||||
SDL_FS_SYM(FusionSoundInit),
|
||||
SDL_FS_SYM(FusionSoundCreate),
|
||||
/* *INDENT-ON* */
|
||||
};
|
||||
|
||||
#undef SDL_FS_SYM
|
||||
|
||||
static void UnloadFusionSoundLibrary()
|
||||
{
|
||||
if (fs_handle != NULL) {
|
||||
SDL_UnloadObject(fs_handle);
|
||||
fs_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadFusionSoundLibrary(void)
|
||||
{
|
||||
int i, retval = -1;
|
||||
|
||||
if (fs_handle == NULL) {
|
||||
fs_handle = SDL_LoadObject(fs_library);
|
||||
if (fs_handle != NULL) {
|
||||
retval = 0;
|
||||
for (i = 0; i < SDL_arraysize(fs_functions); ++i) {
|
||||
*fs_functions[i].func =
|
||||
SDL_LoadFunction(fs_handle, fs_functions[i].name);
|
||||
if (!*fs_functions[i].func) {
|
||||
retval = -1;
|
||||
UnloadFusionSoundLibrary();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void UnloadFusionSoundLibrary()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static int LoadFusionSoundLibrary(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC */
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void SDL_FS_WaitDevice(_THIS)
|
||||
{
|
||||
this->hidden->stream->Wait(this->hidden->stream,
|
||||
this->hidden->mixsamples);
|
||||
}
|
||||
|
||||
static void SDL_FS_PlayDevice(_THIS)
|
||||
{
|
||||
DirectResult ret;
|
||||
|
||||
ret = this->hidden->stream->Write(this->hidden->stream,
|
||||
this->hidden->mixbuf,
|
||||
this->hidden->mixsamples);
|
||||
/* If we couldn't write, assume fatal error for now */
|
||||
if (ret) {
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static Uint8 *SDL_FS_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return (this->hidden->mixbuf);
|
||||
}
|
||||
|
||||
|
||||
static void SDL_FS_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->stream) {
|
||||
this->hidden->stream->Release(this->hidden->stream);
|
||||
}
|
||||
if (this->hidden->fs) {
|
||||
this->hidden->fs->Release(this->hidden->fs);
|
||||
}
|
||||
SDL_free(this->hidden->mixbuf);
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
|
||||
static int SDL_FS_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
int bytes;
|
||||
SDL_AudioFormat test_format;
|
||||
FSSampleFormat fs_format;
|
||||
FSStreamDescription desc;
|
||||
DirectResult ret;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*this->hidden));
|
||||
if (this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(this->hidden);
|
||||
|
||||
/* Try for a closest match on audio format */
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
|
||||
#endif
|
||||
switch (test_format) {
|
||||
case AUDIO_U8:
|
||||
fs_format = FSSF_U8;
|
||||
break;
|
||||
case AUDIO_S16SYS:
|
||||
fs_format = FSSF_S16;
|
||||
break;
|
||||
case AUDIO_S32SYS:
|
||||
fs_format = FSSF_S32;
|
||||
break;
|
||||
case AUDIO_F32SYS:
|
||||
fs_format = FSSF_FLOAT;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "fusionsound");
|
||||
}
|
||||
this->spec.format = test_format;
|
||||
bytes = SDL_AUDIO_BITSIZE(test_format) / 8;
|
||||
|
||||
/* Retrieve the main sound interface. */
|
||||
ret = SDL_NAME(FusionSoundCreate) (&this->hidden->fs);
|
||||
if (ret) {
|
||||
return SDL_SetError("Unable to initialize FusionSound: %d", ret);
|
||||
}
|
||||
|
||||
this->hidden->mixsamples = this->spec.size / bytes / this->spec.channels;
|
||||
|
||||
/* Fill stream description. */
|
||||
desc.flags = FSSDF_SAMPLERATE | FSSDF_BUFFERSIZE |
|
||||
FSSDF_CHANNELS | FSSDF_SAMPLEFORMAT | FSSDF_PREBUFFER;
|
||||
desc.samplerate = this->spec.freq;
|
||||
desc.buffersize = this->spec.size * FUSION_BUFFERS;
|
||||
desc.channels = this->spec.channels;
|
||||
desc.prebuffer = 10;
|
||||
desc.sampleformat = fs_format;
|
||||
|
||||
ret =
|
||||
this->hidden->fs->CreateStream(this->hidden->fs, &desc,
|
||||
&this->hidden->stream);
|
||||
if (ret) {
|
||||
return SDL_SetError("Unable to create FusionSoundStream: %d", ret);
|
||||
}
|
||||
|
||||
/* See what we got */
|
||||
desc.flags = FSSDF_SAMPLERATE | FSSDF_BUFFERSIZE |
|
||||
FSSDF_CHANNELS | FSSDF_SAMPLEFORMAT;
|
||||
ret = this->hidden->stream->GetDescription(this->hidden->stream, &desc);
|
||||
|
||||
this->spec.freq = desc.samplerate;
|
||||
this->spec.size =
|
||||
desc.buffersize / FUSION_BUFFERS * bytes * desc.channels;
|
||||
this->spec.channels = desc.channels;
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
this->hidden->mixlen = this->spec.size;
|
||||
this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
|
||||
if (this->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void SDL_FS_Deinitialize(void)
|
||||
{
|
||||
UnloadFusionSoundLibrary();
|
||||
}
|
||||
|
||||
|
||||
static SDL_bool SDL_FS_Init(SDL_AudioDriverImpl * impl)
|
||||
{
|
||||
if (LoadFusionSoundLibrary() < 0) {
|
||||
return SDL_FALSE;
|
||||
} else {
|
||||
DirectResult ret;
|
||||
|
||||
ret = SDL_NAME(FusionSoundInit) (NULL, NULL);
|
||||
if (ret) {
|
||||
UnloadFusionSoundLibrary();
|
||||
SDL_SetError
|
||||
("FusionSound: SDL_FS_init failed (FusionSoundInit: %d)",
|
||||
ret);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = SDL_FS_OpenDevice;
|
||||
impl->PlayDevice = SDL_FS_PlayDevice;
|
||||
impl->WaitDevice = SDL_FS_WaitDevice;
|
||||
impl->GetDeviceBuf = SDL_FS_GetDeviceBuf;
|
||||
impl->CloseDevice = SDL_FS_CloseDevice;
|
||||
impl->Deinitialize = SDL_FS_Deinitialize;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
|
||||
AudioBootStrap FUSIONSOUND_bootstrap = {
|
||||
"fusionsound", "FusionSound", SDL_FS_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_FUSIONSOUND */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
50
third_party/SDL/src/audio/fusionsound/SDL_fsaudio.h
vendored
Normal file
50
third_party/SDL/src/audio/fusionsound/SDL_fsaudio.h
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_fsaudio_h_
|
||||
#define SDL_fsaudio_h_
|
||||
|
||||
#include <fusionsound/fusionsound.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* Interface */
|
||||
IFusionSound *fs;
|
||||
|
||||
/* The stream interface for the audio device */
|
||||
IFusionSoundStream *stream;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
int mixsamples;
|
||||
|
||||
};
|
||||
|
||||
#endif /* SDL_fsaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
238
third_party/SDL/src/audio/haiku/SDL_haikuaudio.cc
vendored
Normal file
238
third_party/SDL/src/audio/haiku/SDL_haikuaudio.cc
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_HAIKU
|
||||
|
||||
/* Allow access to the audio stream on Haiku */
|
||||
|
||||
#include <SoundPlayer.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "../../main/haiku/SDL_BeApp.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_haikuaudio.h"
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* !!! 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)
|
||||
{
|
||||
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_AudioStreamClear(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_AudioStreamAvailable(audio->stream) < ilen) {
|
||||
callback(audio->callbackspec.userdata, audio->work_buffer, stream_len);
|
||||
if (SDL_AudioStreamPut(audio->stream, audio->work_buffer, stream_len) == -1) {
|
||||
SDL_AudioStreamClear(audio->stream);
|
||||
SDL_AtomicSet(&audio->enabled, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const int got = SDL_AudioStreamGet(audio->stream, stream, ilen);
|
||||
SDL_assert((got < 0) || (got == ilen));
|
||||
if (got != ilen) {
|
||||
SDL_memset(stream, audio->spec.silence, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(audio->mixer_lock);
|
||||
}
|
||||
|
||||
static void HAIKUAUDIO_CloseDevice(_THIS)
|
||||
{
|
||||
if (_this->hidden->audio_obj) {
|
||||
_this->hidden->audio_obj->Stop();
|
||||
delete _this->hidden->audio_obj;
|
||||
}
|
||||
delete _this->hidden;
|
||||
}
|
||||
|
||||
|
||||
static const int sig_list[] = {
|
||||
SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM, SIGTERM, SIGWINCH, 0
|
||||
};
|
||||
|
||||
static inline void MaskSignals(sigset_t * omask)
|
||||
{
|
||||
sigset_t mask;
|
||||
int i;
|
||||
|
||||
sigemptyset(&mask);
|
||||
for (i = 0; sig_list[i]; ++i) {
|
||||
sigaddset(&mask, sig_list[i]);
|
||||
}
|
||||
sigprocmask(SIG_BLOCK, &mask, omask);
|
||||
}
|
||||
|
||||
static inline void UnmaskSignals(sigset_t * omask)
|
||||
{
|
||||
sigprocmask(SIG_SETMASK, omask, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int HAIKUAUDIO_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
media_raw_audio_format format;
|
||||
SDL_AudioFormat test_format;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
_this->hidden = new SDL_PrivateAudioData;
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(_this->hidden);
|
||||
|
||||
/* Parse the audio format and fill the Be raw audio 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? */
|
||||
for (test_format = SDL_FirstAudioFormat(_this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
switch (test_format) {
|
||||
case AUDIO_S8:
|
||||
format.format = media_raw_audio_format::B_AUDIO_CHAR;
|
||||
break;
|
||||
|
||||
case AUDIO_U8:
|
||||
format.format = media_raw_audio_format::B_AUDIO_UCHAR;
|
||||
break;
|
||||
|
||||
case AUDIO_S16LSB:
|
||||
format.format = media_raw_audio_format::B_AUDIO_SHORT;
|
||||
break;
|
||||
|
||||
case AUDIO_S16MSB:
|
||||
format.format = media_raw_audio_format::B_AUDIO_SHORT;
|
||||
format.byte_order = B_MEDIA_BIG_ENDIAN;
|
||||
break;
|
||||
|
||||
case AUDIO_S32LSB:
|
||||
format.format = media_raw_audio_format::B_AUDIO_INT;
|
||||
break;
|
||||
|
||||
case AUDIO_S32MSB:
|
||||
format.format = media_raw_audio_format::B_AUDIO_INT;
|
||||
format.byte_order = B_MEDIA_BIG_ENDIAN;
|
||||
break;
|
||||
|
||||
case AUDIO_F32LSB:
|
||||
format.format = media_raw_audio_format::B_AUDIO_FLOAT;
|
||||
break;
|
||||
|
||||
case AUDIO_F32MSB:
|
||||
format.format = media_raw_audio_format::B_AUDIO_FLOAT;
|
||||
format.byte_order = B_MEDIA_BIG_ENDIAN;
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!test_format) { /* shouldn't happen, but just in case... */
|
||||
return SDL_SetError("%s: Unsupported audio format", "haiku");
|
||||
}
|
||||
_this->spec.format = test_format;
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
format.buffer_size = _this->spec.size;
|
||||
|
||||
/* 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);
|
||||
UnmaskSignals(&omask);
|
||||
|
||||
if (_this->hidden->audio_obj->Start() == B_NO_ERROR) {
|
||||
_this->hidden->audio_obj->SetHasData(true);
|
||||
} else {
|
||||
return SDL_SetError("Unable to start Be audio");
|
||||
}
|
||||
|
||||
/* We're running! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void HAIKUAUDIO_Deinitialize(void)
|
||||
{
|
||||
SDL_QuitBeApp();
|
||||
}
|
||||
|
||||
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 */
|
||||
impl->OpenDevice = HAIKUAUDIO_OpenDevice;
|
||||
impl->CloseDevice = HAIKUAUDIO_CloseDevice;
|
||||
impl->Deinitialize = HAIKUAUDIO_Deinitialize;
|
||||
impl->ProvidesOwnCallbackThread = SDL_TRUE;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
extern AudioBootStrap HAIKUAUDIO_bootstrap;
|
||||
}
|
||||
AudioBootStrap HAIKUAUDIO_bootstrap = {
|
||||
"haiku", "Haiku BSoundPlayer", HAIKUAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_HAIKU */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
38
third_party/SDL/src/audio/haiku/SDL_haikuaudio.h
vendored
Normal file
38
third_party/SDL/src/audio/haiku/SDL_haikuaudio.h
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_haikuaudio_h_
|
||||
#define SDL_haikuaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *_this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
BSoundPlayer *audio_obj;
|
||||
};
|
||||
|
||||
#endif /* SDL_haikuaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
431
third_party/SDL/src/audio/jack/SDL_jackaudio.c
vendored
Normal file
431
third_party/SDL/src/audio/jack/SDL_jackaudio.c
vendored
Normal file
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_JACK
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_jackaudio.h"
|
||||
#include "SDL_loadso.h"
|
||||
#include "../../thread/SDL_systhread.h"
|
||||
|
||||
static jack_client_t *(*JACK_jack_client_open)(const char *, jack_options_t, jack_status_t *, ...);
|
||||
static int (*JACK_jack_client_close)(jack_client_t *);
|
||||
static void (*JACK_jack_on_shutdown)(jack_client_t *, JackShutdownCallback, void *);
|
||||
static int (*JACK_jack_activate)(jack_client_t *);
|
||||
static int (*JACK_jack_deactivate)(jack_client_t *);
|
||||
static void *(*JACK_jack_port_get_buffer)(jack_port_t *, jack_nframes_t);
|
||||
static int (*JACK_jack_port_unregister)(jack_client_t *, jack_port_t *);
|
||||
static void (*JACK_jack_free)(void *);
|
||||
static const char **(*JACK_jack_get_ports)(jack_client_t *, const char *, const char *, unsigned long);
|
||||
static jack_nframes_t (*JACK_jack_get_sample_rate)(jack_client_t *);
|
||||
static jack_nframes_t (*JACK_jack_get_buffer_size)(jack_client_t *);
|
||||
static jack_port_t *(*JACK_jack_port_register)(jack_client_t *, const char *, const char *, unsigned long, unsigned long);
|
||||
static jack_port_t *(*JACK_jack_port_by_name)(jack_client_t *, const char *);
|
||||
static const char *(*JACK_jack_port_name)(const jack_port_t *);
|
||||
static const char *(*JACK_jack_port_type)(const jack_port_t *);
|
||||
static int (*JACK_jack_connect)(jack_client_t *, const char *, const char *);
|
||||
static int (*JACK_jack_set_process_callback)(jack_client_t *, JackProcessCallback, void *);
|
||||
|
||||
static int load_jack_syms(void);
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_JACK_DYNAMIC
|
||||
|
||||
static const char *jack_library = SDL_AUDIO_DRIVER_JACK_DYNAMIC;
|
||||
static void *jack_handle = NULL;
|
||||
|
||||
/* !!! FIXME: this is copy/pasted in several places now */
|
||||
static int load_jack_sym(const char *fn, void **addr)
|
||||
{
|
||||
*addr = SDL_LoadFunction(jack_handle, fn);
|
||||
if (*addr == NULL) {
|
||||
/* Don't call SDL_SetError(): SDL_LoadFunction already did. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
|
||||
#define SDL_JACK_SYM(x) \
|
||||
if (!load_jack_sym(#x, (void **)(char *)&JACK_##x)) \
|
||||
return -1
|
||||
|
||||
static void UnloadJackLibrary(void)
|
||||
{
|
||||
if (jack_handle != NULL) {
|
||||
SDL_UnloadObject(jack_handle);
|
||||
jack_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadJackLibrary(void)
|
||||
{
|
||||
int retval = 0;
|
||||
if (jack_handle == NULL) {
|
||||
jack_handle = SDL_LoadObject(jack_library);
|
||||
if (jack_handle == NULL) {
|
||||
retval = -1;
|
||||
/* Don't call SDL_SetError(): SDL_LoadObject already did. */
|
||||
} else {
|
||||
retval = load_jack_syms();
|
||||
if (retval < 0) {
|
||||
UnloadJackLibrary();
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define SDL_JACK_SYM(x) JACK_##x = x
|
||||
|
||||
static void UnloadJackLibrary(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int LoadJackLibrary(void)
|
||||
{
|
||||
load_jack_syms();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_JACK_DYNAMIC */
|
||||
|
||||
static int load_jack_syms(void)
|
||||
{
|
||||
SDL_JACK_SYM(jack_client_open);
|
||||
SDL_JACK_SYM(jack_client_close);
|
||||
SDL_JACK_SYM(jack_on_shutdown);
|
||||
SDL_JACK_SYM(jack_activate);
|
||||
SDL_JACK_SYM(jack_deactivate);
|
||||
SDL_JACK_SYM(jack_port_get_buffer);
|
||||
SDL_JACK_SYM(jack_port_unregister);
|
||||
SDL_JACK_SYM(jack_free);
|
||||
SDL_JACK_SYM(jack_get_ports);
|
||||
SDL_JACK_SYM(jack_get_sample_rate);
|
||||
SDL_JACK_SYM(jack_get_buffer_size);
|
||||
SDL_JACK_SYM(jack_port_register);
|
||||
SDL_JACK_SYM(jack_port_by_name);
|
||||
SDL_JACK_SYM(jack_port_name);
|
||||
SDL_JACK_SYM(jack_port_type);
|
||||
SDL_JACK_SYM(jack_connect);
|
||||
SDL_JACK_SYM(jack_set_process_callback);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jackShutdownCallback(void *arg) /* JACK went away; device is lost. */
|
||||
{
|
||||
SDL_AudioDevice *this = (SDL_AudioDevice *)arg;
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
SDL_SemPost(this->hidden->iosem); /* unblock the SDL thread. */
|
||||
}
|
||||
|
||||
// !!! FIXME: implement and register these!
|
||||
// typedef int(* JackSampleRateCallback)(jack_nframes_t nframes, void *arg)
|
||||
// typedef int(* JackBufferSizeCallback)(jack_nframes_t nframes, void *arg)
|
||||
|
||||
static int jackProcessPlaybackCallback(jack_nframes_t nframes, void *arg)
|
||||
{
|
||||
SDL_AudioDevice *this = (SDL_AudioDevice *)arg;
|
||||
jack_port_t **ports = this->hidden->sdlports;
|
||||
const int total_channels = this->spec.channels;
|
||||
const int total_frames = this->spec.samples;
|
||||
int channelsi;
|
||||
|
||||
if (!SDL_AtomicGet(&this->enabled)) {
|
||||
/* silence the buffer to avoid repeats and corruption. */
|
||||
SDL_memset(this->hidden->iobuffer, '\0', this->spec.size);
|
||||
}
|
||||
|
||||
for (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++) {
|
||||
*(dst++) = *src;
|
||||
src += total_channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_SemPost(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(_THIS)
|
||||
{
|
||||
if (SDL_AtomicGet(&this->enabled)) {
|
||||
if (SDL_SemWait(this->hidden->iosem) == -1) {
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 *JACK_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return (Uint8 *)this->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_SemPost(this->hidden->iosem); /* tell SDL thread we're done; new buffer is ready! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int JACK_CaptureFromDevice(_THIS, void *buffer, int buflen)
|
||||
{
|
||||
SDL_assert(buflen == this->spec.size); /* we always fill a full buffer. */
|
||||
|
||||
/* Wait for JACK to fill the iobuffer */
|
||||
if (SDL_SemWait(this->hidden->iosem) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_memcpy(buffer, this->hidden->iobuffer, buflen);
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static void JACK_FlushCapture(_THIS)
|
||||
{
|
||||
SDL_SemWait(this->hidden->iosem);
|
||||
}
|
||||
|
||||
static void JACK_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->client) {
|
||||
JACK_jack_deactivate(this->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]);
|
||||
}
|
||||
SDL_free(this->hidden->sdlports);
|
||||
}
|
||||
|
||||
JACK_jack_client_close(this->hidden->client);
|
||||
}
|
||||
|
||||
if (this->hidden->iosem) {
|
||||
SDL_DestroySemaphore(this->hidden->iosem);
|
||||
}
|
||||
|
||||
SDL_free(this->hidden->iobuffer);
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
static int JACK_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
/* 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;
|
||||
const unsigned long sysportflags = iscapture ? JackPortIsOutput : JackPortIsInput;
|
||||
const unsigned long sdlportflags = iscapture ? JackPortIsInput : JackPortIsOutput;
|
||||
const JackProcessCallback callback = iscapture ? jackProcessCaptureCallback : jackProcessPlaybackCallback;
|
||||
const char *sdlportstr = iscapture ? "input" : "output";
|
||||
const char **devports = NULL;
|
||||
int *audio_ports;
|
||||
jack_client_t *client = NULL;
|
||||
jack_status_t status;
|
||||
int channels = 0;
|
||||
int ports = 0;
|
||||
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) {
|
||||
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;
|
||||
if (client == NULL) {
|
||||
return SDL_SetError("Can't open JACK client");
|
||||
}
|
||||
|
||||
devports = JACK_jack_get_ports(client, NULL, NULL, JackPortIsPhysical | sysportflags);
|
||||
if (devports == NULL || !devports[0]) {
|
||||
return SDL_SetError("No physical JACK ports available");
|
||||
}
|
||||
|
||||
while (devports[++ports]) {
|
||||
/* spin to count devports */
|
||||
}
|
||||
|
||||
/* Filter out non-audio ports */
|
||||
audio_ports = SDL_calloc(ports, sizeof(*audio_ports));
|
||||
for (i = 0; i < ports; i++) {
|
||||
const jack_port_t *dport = JACK_jack_port_by_name(client, devports[i]);
|
||||
const char *type = JACK_jack_port_type(dport);
|
||||
const int len = SDL_strlen(type);
|
||||
/* See if type ends with "audio" */
|
||||
if (len >= 5 && !SDL_memcmp(type + len - 5, "audio", 5)) {
|
||||
audio_ports[channels++] = i;
|
||||
}
|
||||
}
|
||||
if (channels == 0) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("No physical JACK ports available");
|
||||
}
|
||||
|
||||
/* !!! 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 = AUDIO_F32SYS;
|
||||
this->spec.freq = JACK_jack_get_sample_rate(client);
|
||||
this->spec.channels = channels;
|
||||
this->spec.samples = JACK_jack_get_buffer_size(client);
|
||||
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
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) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("jack_port_register failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (JACK_jack_set_process_callback(client, callback, this) != 0) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("JACK: Couldn't set process callback");
|
||||
}
|
||||
|
||||
JACK_jack_on_shutdown(client, jackShutdownCallback, this);
|
||||
|
||||
if (JACK_jack_activate(client) != 0) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("Failed to activate JACK client");
|
||||
}
|
||||
|
||||
/* 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 *srcport = iscapture ? devports[audio_ports[i]] : sdlport;
|
||||
const char *dstport = iscapture ? sdlport : devports[audio_ports[i]];
|
||||
if (JACK_jack_connect(client, srcport, dstport) != 0) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("Couldn't connect JACK ports: %s => %s", srcport, dstport);
|
||||
}
|
||||
}
|
||||
|
||||
/* don't need these anymore. */
|
||||
JACK_jack_free(devports);
|
||||
SDL_free(audio_ports);
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void JACK_Deinitialize(void)
|
||||
{
|
||||
UnloadJackLibrary();
|
||||
}
|
||||
|
||||
static SDL_bool JACK_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (LoadJackLibrary() < 0) {
|
||||
return SDL_FALSE;
|
||||
} else {
|
||||
/* Make sure a JACK server is running and available. */
|
||||
jack_status_t status;
|
||||
jack_client_t *client = JACK_jack_client_open("SDL", JackNoStartServer, &status, NULL);
|
||||
if (client == NULL) {
|
||||
UnloadJackLibrary();
|
||||
return SDL_FALSE;
|
||||
}
|
||||
JACK_jack_client_close(client);
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = JACK_OpenDevice;
|
||||
impl->WaitDevice = JACK_WaitDevice;
|
||||
impl->GetDeviceBuf = JACK_GetDeviceBuf;
|
||||
impl->CloseDevice = JACK_CloseDevice;
|
||||
impl->Deinitialize = JACK_Deinitialize;
|
||||
impl->CaptureFromDevice = JACK_CaptureFromDevice;
|
||||
impl->FlushCapture = JACK_FlushCapture;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap JACK_bootstrap = {
|
||||
"jack", "JACK Audio Connection Kit", JACK_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_JACK */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
41
third_party/SDL/src/audio/jack/SDL_jackaudio.h
vendored
Normal file
41
third_party/SDL/src/audio/jack/SDL_jackaudio.h
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#ifndef SDL_jackaudio_h_
|
||||
#define SDL_jackaudio_h_
|
||||
|
||||
#include <jack/jack.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
jack_client_t *client;
|
||||
SDL_sem *iosem;
|
||||
float *iobuffer;
|
||||
jack_port_t **sdlports;
|
||||
};
|
||||
|
||||
#endif /* SDL_jackaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
347
third_party/SDL/src/audio/n3ds/SDL_n3dsaudio.c
vendored
Normal file
347
third_party/SDL/src/audio/n3ds/SDL_n3dsaudio.c
vendored
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_N3DS
|
||||
|
||||
#include "SDL_audio.h"
|
||||
|
||||
/* N3DS Audio driver */
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_n3dsaudio.h"
|
||||
#include "SDL_timer.h"
|
||||
|
||||
#define N3DSAUDIO_DRIVER_NAME "n3ds"
|
||||
|
||||
static dspHookCookie dsp_hook;
|
||||
static SDL_AudioDevice *audio_device;
|
||||
|
||||
static void FreePrivateData(_THIS);
|
||||
static int FindAudioFormat(_THIS);
|
||||
|
||||
static SDL_INLINE void contextLock(_THIS)
|
||||
{
|
||||
LightLock_Lock(&this->hidden->lock);
|
||||
}
|
||||
|
||||
static SDL_INLINE void contextUnlock(_THIS)
|
||||
{
|
||||
LightLock_Unlock(&this->hidden->lock);
|
||||
}
|
||||
|
||||
static void N3DSAUD_LockAudio(_THIS)
|
||||
{
|
||||
contextLock(this);
|
||||
}
|
||||
|
||||
static void N3DSAUD_UnlockAudio(_THIS)
|
||||
{
|
||||
contextUnlock(this);
|
||||
}
|
||||
|
||||
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);
|
||||
CondVar_Broadcast(&audio_device->hidden->cv);
|
||||
contextUnlock(audio_device);
|
||||
}
|
||||
}
|
||||
|
||||
static void AudioFrameFinished(void *device)
|
||||
{
|
||||
bool shouldBroadcast = false;
|
||||
unsigned i;
|
||||
SDL_AudioDevice *this = (SDL_AudioDevice *)device;
|
||||
|
||||
contextLock(this);
|
||||
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
if (this->hidden->waveBuf[i].status == NDSP_WBUF_DONE) {
|
||||
this->hidden->waveBuf[i].status = NDSP_WBUF_FREE;
|
||||
shouldBroadcast = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldBroadcast) {
|
||||
CondVar_Broadcast(&this->hidden->cv);
|
||||
}
|
||||
|
||||
contextUnlock(this);
|
||||
}
|
||||
|
||||
static int N3DSAUDIO_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
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) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
/* 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)) {
|
||||
SDL_SetError("DSP init failed: dspfirm.cdc missing!");
|
||||
} else {
|
||||
SDL_SetError("DSP init failed. Error code: 0x%lX", ndsp_init_res);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialise internal state */
|
||||
LightLock_Init(&this->hidden->lock);
|
||||
CondVar_Init(&this->hidden->cv);
|
||||
|
||||
if (this->spec.channels > 2) {
|
||||
this->spec.channels = 2;
|
||||
}
|
||||
|
||||
/* Should not happen but better be safe. */
|
||||
if (FindAudioFormat(this) < 0) {
|
||||
return SDL_SetError("No supported audio format found.");
|
||||
}
|
||||
|
||||
/* Update the fragment size as size in bytes */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
if (this->spec.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) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
|
||||
|
||||
data_vaddr = (Uint8 *)linearAlloc(this->hidden->mixlen * 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);
|
||||
|
||||
this->hidden->nextbuf = 0;
|
||||
this->hidden->channels = this->spec.channels;
|
||||
this->hidden->samplerate = this->spec.freq;
|
||||
|
||||
ndspChnReset(0);
|
||||
|
||||
ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
|
||||
ndspChnSetRate(0, this->spec.freq);
|
||||
ndspChnSetFormat(0, this->hidden->format);
|
||||
|
||||
SDL_memset(mix, 0, sizeof(mix));
|
||||
mix[0] = 1.0;
|
||||
mix[1] = 1.0;
|
||||
ndspChnSetMix(0, mix);
|
||||
|
||||
SDL_memset(this->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Setup callback */
|
||||
audio_device = this;
|
||||
ndspSetCallback(AudioFrameFinished, this);
|
||||
dspHook(&dsp_hook, N3DSAUD_DspHook);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int N3DSAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
|
||||
{
|
||||
/* Delay to make this sort of simulate real audio input. */
|
||||
SDL_Delay((this->spec.samples * 1000) / this->spec.freq);
|
||||
|
||||
/* always return a full buffer of silence. */
|
||||
SDL_memset(buffer, this->spec.silence, buflen);
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static void N3DSAUDIO_PlayDevice(_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);
|
||||
return;
|
||||
}
|
||||
|
||||
this->hidden->nextbuf = (nextbuf + 1) % NUM_BUFFERS;
|
||||
|
||||
contextUnlock(this);
|
||||
|
||||
memcpy((void *)this->hidden->waveBuf[nextbuf].data_vaddr,
|
||||
this->hidden->mixbuf, sampleLen);
|
||||
DSP_FlushDataCache(this->hidden->waveBuf[nextbuf].data_vaddr, sampleLen);
|
||||
|
||||
ndspChnWaveBufAdd(0, &this->hidden->waveBuf[nextbuf]);
|
||||
}
|
||||
|
||||
static void N3DSAUDIO_WaitDevice(_THIS)
|
||||
{
|
||||
contextLock(this);
|
||||
while (!this->hidden->isCancelled &&
|
||||
this->hidden->waveBuf[this->hidden->nextbuf].status != NDSP_WBUF_FREE) {
|
||||
CondVar_Wait(&this->hidden->cv, &this->hidden->lock);
|
||||
}
|
||||
contextUnlock(this);
|
||||
}
|
||||
|
||||
static Uint8 *N3DSAUDIO_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return this->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static void N3DSAUDIO_CloseDevice(_THIS)
|
||||
{
|
||||
contextLock(this);
|
||||
|
||||
dspUnhook(&dsp_hook);
|
||||
ndspSetCallback(NULL, NULL);
|
||||
|
||||
if (!this->hidden->isCancelled) {
|
||||
ndspChnReset(0);
|
||||
memset(this->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS);
|
||||
CondVar_Broadcast(&this->hidden->cv);
|
||||
}
|
||||
|
||||
contextUnlock(this);
|
||||
|
||||
ndspExit();
|
||||
|
||||
FreePrivateData(this);
|
||||
}
|
||||
|
||||
static void N3DSAUDIO_ThreadInit(_THIS)
|
||||
{
|
||||
s32 current_priority;
|
||||
svcGetThreadPriority(¤t_priority, CUR_THREAD_HANDLE);
|
||||
current_priority--;
|
||||
/* 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 */
|
||||
impl->HasCaptureSupport = SDL_FALSE;
|
||||
impl->CaptureFromDevice = N3DSAUDIO_CaptureFromDevice;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap N3DSAUDIO_bootstrap = {
|
||||
N3DSAUDIO_DRIVER_NAME,
|
||||
"SDL N3DS audio driver",
|
||||
N3DSAUDIO_Init,
|
||||
0
|
||||
};
|
||||
|
||||
/**
|
||||
* Cleans up all allocated memory, safe to call with null pointers
|
||||
*/
|
||||
static void FreePrivateData(_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(_THIS)
|
||||
{
|
||||
SDL_bool found_valid_format = SDL_FALSE;
|
||||
Uint16 test_format = SDL_FirstAudioFormat(this->spec.format);
|
||||
|
||||
while (!found_valid_format && test_format) {
|
||||
this->spec.format = test_format;
|
||||
switch (test_format) {
|
||||
case 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;
|
||||
found_valid_format = SDL_TRUE;
|
||||
break;
|
||||
case 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;
|
||||
found_valid_format = SDL_TRUE;
|
||||
break;
|
||||
default:
|
||||
test_format = SDL_NextAudioFormat();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return found_valid_format ? 0 : -1;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_N3DS */
|
||||
|
||||
/* vi: set sts=4 ts=4 sw=4 expandtab: */
|
||||
50
third_party/SDL/src/audio/n3ds/SDL_n3dsaudio.h
vendored
Normal file
50
third_party/SDL/src/audio/n3ds/SDL_n3dsaudio.h
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef _SDL_n3dsaudio_h_
|
||||
#define _SDL_n3dsaudio_h_
|
||||
|
||||
#include <3ds.h>
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
#define NUM_BUFFERS 2 /* -- Don't lower this! */
|
||||
|
||||
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;
|
||||
CondVar cv;
|
||||
SDL_bool isCancelled;
|
||||
};
|
||||
|
||||
#endif /* _SDL_n3dsaudio_h_ */
|
||||
/* vi: set sts=4 ts=4 sw=4 expandtab: */
|
||||
161
third_party/SDL/src/audio/nacl/SDL_naclaudio.c
vendored
Normal file
161
third_party/SDL/src/audio/nacl/SDL_naclaudio.c
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_NACL
|
||||
|
||||
#include "SDL_naclaudio.h"
|
||||
|
||||
#include "SDL_audio.h"
|
||||
#include "SDL_mutex.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_audiodev_c.h"
|
||||
|
||||
#include "ppapi/c/pp_errors.h"
|
||||
#include "ppapi/c/pp_instance.h"
|
||||
#include "ppapi_simple/ps.h"
|
||||
#include "ppapi_simple/ps_interface.h"
|
||||
#include "ppapi_simple/ps_event.h"
|
||||
|
||||
/* The tag name used by NACL audio */
|
||||
#define NACLAUDIO_DRIVER_NAME "nacl"
|
||||
|
||||
#define SAMPLE_FRAME_COUNT 4096
|
||||
|
||||
/* Audio driver functions */
|
||||
static void nacl_audio_callback(void* samples, uint32_t buffer_size, PP_TimeDelta latency, void* data);
|
||||
|
||||
/* FIXME: Make use of latency if needed */
|
||||
static void nacl_audio_callback(void* stream, uint32_t buffer_size, PP_TimeDelta latency, void* data)
|
||||
{
|
||||
const int len = (int) buffer_size;
|
||||
SDL_AudioDevice* _this = (SDL_AudioDevice*) data;
|
||||
SDL_AudioCallback callback = _this->callbackspec.callback;
|
||||
|
||||
SDL_LockMutex(_this->mixer_lock);
|
||||
|
||||
/* Only do something if audio is enabled */
|
||||
if (!SDL_AtomicGet(&_this->enabled) || SDL_AtomicGet(&_this->paused)) {
|
||||
if (_this->stream) {
|
||||
SDL_AudioStreamClear(_this->stream);
|
||||
}
|
||||
SDL_memset(stream, _this->spec.silence, len);
|
||||
} else {
|
||||
SDL_assert(_this->spec.size == len);
|
||||
|
||||
if (_this->stream == NULL) { /* no conversion necessary. */
|
||||
callback(_this->callbackspec.userdata, stream, len);
|
||||
} else { /* streaming/converting */
|
||||
const int stream_len = _this->callbackspec.size;
|
||||
while (SDL_AudioStreamAvailable(_this->stream) < len) {
|
||||
callback(_this->callbackspec.userdata, _this->work_buffer, stream_len);
|
||||
if (SDL_AudioStreamPut(_this->stream, _this->work_buffer, stream_len) == -1) {
|
||||
SDL_AudioStreamClear(_this->stream);
|
||||
SDL_AtomicSet(&_this->enabled, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const int got = SDL_AudioStreamGet(_this->stream, stream, len);
|
||||
SDL_assert((got < 0) || (got == len));
|
||||
if (got != len) {
|
||||
SDL_memset(stream, _this->spec.silence, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(_this->mixer_lock);
|
||||
}
|
||||
|
||||
static void NACLAUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
const PPB_Core *core = PSInterfaceCore();
|
||||
const PPB_Audio *ppb_audio = PSInterfaceAudio();
|
||||
SDL_PrivateAudioData *hidden = (SDL_PrivateAudioData *) device->hidden;
|
||||
|
||||
ppb_audio->StopPlayback(hidden->audio);
|
||||
core->ReleaseResource(hidden->audio);
|
||||
}
|
||||
|
||||
static int NACLAUDIO_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
PP_Instance instance = PSGetInstanceId();
|
||||
const PPB_Audio *ppb_audio = PSInterfaceAudio();
|
||||
const PPB_AudioConfig *ppb_audiocfg = PSInterfaceAudioConfig();
|
||||
|
||||
private = (SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*private));
|
||||
if (private == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
_this->spec.freq = 44100;
|
||||
_this->spec.format = AUDIO_S16LSB;
|
||||
_this->spec.channels = 2;
|
||||
_this->spec.samples = ppb_audiocfg->RecommendSampleFrameCount(
|
||||
instance,
|
||||
PP_AUDIOSAMPLERATE_44100,
|
||||
SAMPLE_FRAME_COUNT);
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
private->audio = ppb_audio->Create(
|
||||
instance,
|
||||
ppb_audiocfg->CreateStereo16Bit(instance, PP_AUDIOSAMPLERATE_44100, _this->spec.samples),
|
||||
nacl_audio_callback,
|
||||
_this);
|
||||
|
||||
/* Start audio playback while we are still on the main thread. */
|
||||
ppb_audio->StartPlayback(private->audio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SDL_bool NACLAUDIO_Init(SDL_AudioDriverImpl * impl)
|
||||
{
|
||||
if (PSGetInstanceId() == 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = NACLAUDIO_OpenDevice;
|
||||
impl->CloseDevice = NACLAUDIO_CloseDevice;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->ProvidesOwnCallbackThread = SDL_TRUE;
|
||||
/*
|
||||
* impl->WaitDevice = NACLAUDIO_WaitDevice;
|
||||
* impl->GetDeviceBuf = NACLAUDIO_GetDeviceBuf;
|
||||
* impl->PlayDevice = NACLAUDIO_PlayDevice;
|
||||
* impl->Deinitialize = NACLAUDIO_Deinitialize;
|
||||
*/
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap NACLAUDIO_bootstrap = {
|
||||
NACLAUDIO_DRIVER_NAME, "SDL NaCl Audio Driver",
|
||||
NACLAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_NACL */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
42
third_party/SDL/src/audio/nacl/SDL_naclaudio.h
vendored
Normal file
42
third_party/SDL/src/audio/nacl/SDL_naclaudio.h
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_naclaudio_h_
|
||||
#define SDL_naclaudio_h_
|
||||
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_mutex.h"
|
||||
|
||||
#include "ppapi/c/ppb_audio.h"
|
||||
|
||||
#define _THIS SDL_AudioDevice *_this
|
||||
#define private _this->hidden
|
||||
|
||||
typedef struct SDL_PrivateAudioData {
|
||||
PP_Resource audio;
|
||||
} SDL_PrivateAudioData;
|
||||
|
||||
#endif /* SDL_naclaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
443
third_party/SDL/src/audio/nas/SDL_nasaudio.c
vendored
Normal file
443
third_party/SDL/src/audio/nas/SDL_nasaudio.c
vendored
Normal file
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_NAS
|
||||
|
||||
/* Allow access to a raw mixing buffer */
|
||||
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "SDL_loadso.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_nasaudio.h"
|
||||
|
||||
static void (*NAS_AuCloseServer) (AuServer *);
|
||||
static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *);
|
||||
static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *);
|
||||
static void (*NAS_AuHandleEvents) (AuServer *);
|
||||
static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *);
|
||||
static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *);
|
||||
static void (*NAS_AuSetElements)
|
||||
(AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *);
|
||||
static void (*NAS_AuWriteElement)
|
||||
(AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *);
|
||||
static AuUint32 (*NAS_AuReadElement)
|
||||
(AuServer *, AuFlowID, int, AuUint32, AuPointer, AuStatus *);
|
||||
static AuServer *(*NAS_AuOpenServer)
|
||||
(_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **);
|
||||
static AuEventHandlerRec *(*NAS_AuRegisterEventHandler)
|
||||
(AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer);
|
||||
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
|
||||
|
||||
static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC;
|
||||
static void *nas_handle = NULL;
|
||||
|
||||
static int load_nas_sym(const char *fn, void **addr)
|
||||
{
|
||||
*addr = SDL_LoadFunction(nas_handle, fn);
|
||||
if (*addr == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
|
||||
#define SDL_NAS_SYM(x) \
|
||||
if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1
|
||||
#else
|
||||
#define SDL_NAS_SYM(x) NAS_##x = x
|
||||
#endif
|
||||
|
||||
static int load_nas_syms(void)
|
||||
{
|
||||
SDL_NAS_SYM(AuCloseServer);
|
||||
SDL_NAS_SYM(AuNextEvent);
|
||||
SDL_NAS_SYM(AuDispatchEvent);
|
||||
SDL_NAS_SYM(AuHandleEvents);
|
||||
SDL_NAS_SYM(AuCreateFlow);
|
||||
SDL_NAS_SYM(AuStartFlow);
|
||||
SDL_NAS_SYM(AuSetElements);
|
||||
SDL_NAS_SYM(AuWriteElement);
|
||||
SDL_NAS_SYM(AuReadElement);
|
||||
SDL_NAS_SYM(AuOpenServer);
|
||||
SDL_NAS_SYM(AuRegisterEventHandler);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef SDL_NAS_SYM
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
|
||||
|
||||
static void UnloadNASLibrary(void)
|
||||
{
|
||||
if (nas_handle != NULL) {
|
||||
SDL_UnloadObject(nas_handle);
|
||||
nas_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadNASLibrary(void)
|
||||
{
|
||||
int retval = 0;
|
||||
if (nas_handle == NULL) {
|
||||
nas_handle = SDL_LoadObject(nas_library);
|
||||
if (nas_handle == NULL) {
|
||||
/* Copy error string so we can use it in a new SDL_SetError(). */
|
||||
const char *origerr = SDL_GetError();
|
||||
const size_t len = SDL_strlen(origerr) + 1;
|
||||
char *err = SDL_stack_alloc(char, len);
|
||||
SDL_strlcpy(err, origerr, len);
|
||||
SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s", nas_library, err);
|
||||
SDL_stack_free(err);
|
||||
retval = -1;
|
||||
} else {
|
||||
retval = load_nas_syms();
|
||||
if (retval < 0) {
|
||||
UnloadNASLibrary();
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void UnloadNASLibrary(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int LoadNASLibrary(void)
|
||||
{
|
||||
load_nas_syms();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void NAS_WaitDevice(_THIS)
|
||||
{
|
||||
while (this->hidden->buf_free < this->hidden->mixlen) {
|
||||
AuEvent ev;
|
||||
NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
|
||||
NAS_AuDispatchEvent(this->hidden->aud, &ev);
|
||||
}
|
||||
}
|
||||
|
||||
static void NAS_PlayDevice(_THIS)
|
||||
{
|
||||
while (this->hidden->mixlen > this->hidden->buf_free) {
|
||||
/*
|
||||
* We think the buffer is full? Yikes! Ask the server for events,
|
||||
* in the hope that some of them is LowWater events telling us more
|
||||
* of the buffer is free now than what we think.
|
||||
*/
|
||||
AuEvent ev;
|
||||
NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
|
||||
NAS_AuDispatchEvent(this->hidden->aud, &ev);
|
||||
}
|
||||
this->hidden->buf_free -= this->hidden->mixlen;
|
||||
|
||||
/* Write the audio data */
|
||||
NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
|
||||
this->hidden->mixlen, this->hidden->mixbuf, AuFalse,
|
||||
NULL);
|
||||
|
||||
this->hidden->written += this->hidden->mixlen;
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
|
||||
#endif
|
||||
}
|
||||
|
||||
static Uint8 *NAS_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return (this->hidden->mixbuf);
|
||||
}
|
||||
|
||||
static int NAS_CaptureFromDevice(_THIS, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = this->hidden;
|
||||
int retval;
|
||||
|
||||
while (SDL_TRUE) {
|
||||
/* just keep the event queue moving and the server chattering. */
|
||||
NAS_AuHandleEvents(h->aud);
|
||||
|
||||
retval = (int) NAS_AuReadElement(h->aud, h->flow, 1, buflen, buffer, NULL);
|
||||
/*printf("read %d capture bytes\n", (int) retval);*/
|
||||
if (retval == 0) {
|
||||
SDL_Delay(10); /* don't burn the CPU if we're waiting for data. */
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void NAS_FlushCapture(_THIS)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = this->hidden;
|
||||
AuUint32 total = 0;
|
||||
AuUint32 br;
|
||||
Uint8 buf[512];
|
||||
|
||||
do {
|
||||
/* just keep the event queue moving and the server chattering. */
|
||||
NAS_AuHandleEvents(h->aud);
|
||||
br = NAS_AuReadElement(h->aud, h->flow, 1, sizeof(buf), buf, NULL);
|
||||
/*printf("flushed %d capture bytes\n", (int) br);*/
|
||||
total += br;
|
||||
} while ((br == sizeof(buf)) && (total < this->spec.size));
|
||||
}
|
||||
|
||||
static void NAS_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->aud) {
|
||||
NAS_AuCloseServer(this->hidden->aud);
|
||||
}
|
||||
SDL_free(this->hidden->mixbuf);
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
static AuBool event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
|
||||
{
|
||||
SDL_AudioDevice *this = (SDL_AudioDevice *) hnd->data;
|
||||
struct SDL_PrivateAudioData *h = this->hidden;
|
||||
if (this->iscapture) {
|
||||
return AuTrue; /* we don't (currently) care about any of this for capture devices */
|
||||
}
|
||||
|
||||
switch (ev->type) {
|
||||
case AuEventTypeElementNotify:
|
||||
{
|
||||
AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
|
||||
|
||||
switch (event->kind) {
|
||||
case AuElementNotifyKindLowWater:
|
||||
if (h->buf_free >= 0) {
|
||||
h->really += event->num_bytes;
|
||||
gettimeofday(&h->last_tv, 0);
|
||||
h->buf_free += event->num_bytes;
|
||||
} else {
|
||||
h->buf_free = event->num_bytes;
|
||||
}
|
||||
break;
|
||||
case AuElementNotifyKindState:
|
||||
switch (event->cur_state) {
|
||||
case AuStatePause:
|
||||
if (event->reason != AuReasonUser) {
|
||||
if (h->buf_free >= 0) {
|
||||
h->really += event->num_bytes;
|
||||
gettimeofday(&h->last_tv, 0);
|
||||
h->buf_free += event->num_bytes;
|
||||
} else {
|
||||
h->buf_free = event->num_bytes;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return AuTrue;
|
||||
}
|
||||
|
||||
static AuDeviceID find_device(_THIS)
|
||||
{
|
||||
/* These "Au" things are all macros, not functions... */
|
||||
struct SDL_PrivateAudioData *h = this->hidden;
|
||||
const unsigned int devicekind = this->iscapture ? AuComponentKindPhysicalInput : AuComponentKindPhysicalOutput;
|
||||
const int numdevs = AuServerNumDevices(h->aud);
|
||||
const int nch = this->spec.channels;
|
||||
int i;
|
||||
|
||||
/* Try to find exact match on channels first... */
|
||||
for (i = 0; i < numdevs; i++) {
|
||||
const AuDeviceAttributes *dev = AuServerDevice(h->aud, i);
|
||||
if ((AuDeviceKind(dev) == devicekind) && (AuDeviceNumTracks(dev) == nch)) {
|
||||
return AuDeviceIdentifier(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/* Take anything, then... */
|
||||
for (i = 0; i < numdevs; i++) {
|
||||
const AuDeviceAttributes *dev = AuServerDevice(h->aud, i);
|
||||
if (AuDeviceKind(dev) == devicekind) {
|
||||
this->spec.channels = AuDeviceNumTracks(dev);
|
||||
return AuDeviceIdentifier(dev);
|
||||
}
|
||||
}
|
||||
return AuNone;
|
||||
}
|
||||
|
||||
static int NAS_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
AuElement elms[3];
|
||||
int buffer_size;
|
||||
SDL_bool iscapture = this->iscapture;
|
||||
SDL_AudioFormat test_format, format = 0;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*this->hidden));
|
||||
if (this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(this->hidden);
|
||||
|
||||
/* Try for a closest match on audio format */
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
switch (test_format) {
|
||||
case AUDIO_U8:
|
||||
format = AuFormatLinearUnsigned8;
|
||||
break;
|
||||
case AUDIO_S8:
|
||||
format = AuFormatLinearSigned8;
|
||||
break;
|
||||
case AUDIO_U16LSB:
|
||||
format = AuFormatLinearUnsigned16LSB;
|
||||
break;
|
||||
case AUDIO_U16MSB:
|
||||
format = AuFormatLinearUnsigned16MSB;
|
||||
break;
|
||||
case AUDIO_S16LSB:
|
||||
format = AuFormatLinearSigned16LSB;
|
||||
break;
|
||||
case AUDIO_S16MSB:
|
||||
format = AuFormatLinearSigned16MSB;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "nas");
|
||||
}
|
||||
this->spec.format = test_format;
|
||||
|
||||
this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
|
||||
if (this->hidden->aud == 0) {
|
||||
return SDL_SetError("NAS: Couldn't open connection to NAS server");
|
||||
}
|
||||
|
||||
this->hidden->dev = find_device(this);
|
||||
if ((this->hidden->dev == AuNone)
|
||||
|| (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
|
||||
return SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
|
||||
}
|
||||
|
||||
buffer_size = this->spec.freq;
|
||||
if (buffer_size < 4096)
|
||||
buffer_size = 4096;
|
||||
|
||||
if (buffer_size > 32768)
|
||||
buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
if (iscapture) {
|
||||
AuMakeElementImportDevice(elms, this->spec.freq, this->hidden->dev,
|
||||
AuUnlimitedSamples, 0, NULL);
|
||||
AuMakeElementExportClient(elms + 1, 0, this->spec.freq, format,
|
||||
this->spec.channels, AuTrue, buffer_size,
|
||||
buffer_size, 0, NULL);
|
||||
} else {
|
||||
AuMakeElementImportClient(elms, this->spec.freq, format,
|
||||
this->spec.channels, AuTrue, buffer_size,
|
||||
buffer_size / 4, 0, NULL);
|
||||
AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
|
||||
AuUnlimitedSamples, 0, NULL);
|
||||
}
|
||||
|
||||
NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue,
|
||||
2, elms, NULL);
|
||||
|
||||
NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
|
||||
this->hidden->flow, event_handler,
|
||||
(AuPointer) this);
|
||||
|
||||
NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
|
||||
|
||||
/* 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) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
|
||||
}
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void NAS_Deinitialize(void)
|
||||
{
|
||||
UnloadNASLibrary();
|
||||
}
|
||||
|
||||
static SDL_bool NAS_Init(SDL_AudioDriverImpl * impl)
|
||||
{
|
||||
if (LoadNASLibrary() < 0) {
|
||||
return SDL_FALSE;
|
||||
} else {
|
||||
AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
|
||||
if (aud == NULL) {
|
||||
SDL_SetError("NAS: AuOpenServer() failed (no audio server?)");
|
||||
return SDL_FALSE;
|
||||
}
|
||||
NAS_AuCloseServer(aud);
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = NAS_OpenDevice;
|
||||
impl->PlayDevice = NAS_PlayDevice;
|
||||
impl->WaitDevice = NAS_WaitDevice;
|
||||
impl->GetDeviceBuf = NAS_GetDeviceBuf;
|
||||
impl->CaptureFromDevice = NAS_CaptureFromDevice;
|
||||
impl->FlushCapture = NAS_FlushCapture;
|
||||
impl->CloseDevice = NAS_CloseDevice;
|
||||
impl->Deinitialize = NAS_Deinitialize;
|
||||
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap NAS_bootstrap = {
|
||||
"nas", "Network Audio System", NAS_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_NAS */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
56
third_party/SDL/src/audio/nas/SDL_nasaudio.h
vendored
Normal file
56
third_party/SDL/src/audio/nas/SDL_nasaudio.h
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_nasaudio_h_
|
||||
#define SDL_nasaudio_h_
|
||||
|
||||
#ifdef __sgi
|
||||
#include <nas/audiolib.h>
|
||||
#else
|
||||
#include <audio/audiolib.h>
|
||||
#endif
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
AuServer *aud;
|
||||
AuFlowID flow;
|
||||
AuDeviceID dev;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
|
||||
int written;
|
||||
int really;
|
||||
int bps;
|
||||
struct timeval last_tv;
|
||||
int buf_free;
|
||||
};
|
||||
#endif /* SDL_nasaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
334
third_party/SDL/src/audio/netbsd/SDL_netbsdaudio.c
vendored
Normal file
334
third_party/SDL/src/audio/netbsd/SDL_netbsdaudio.c
vendored
Normal file
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_NETBSD
|
||||
|
||||
/*
|
||||
* Driver for native NetBSD audio(4).
|
||||
* nia@NetBSD.org
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/audioio.h>
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "../../core/unix/SDL_poll.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_audiodev_c.h"
|
||||
#include "SDL_netbsdaudio.h"
|
||||
|
||||
/* #define DEBUG_AUDIO */
|
||||
|
||||
static void NETBSDAUDIO_DetectDevices(void)
|
||||
{
|
||||
SDL_EnumUnixAudioDevices(0, NULL);
|
||||
}
|
||||
|
||||
static void NETBSDAUDIO_Status(_THIS)
|
||||
{
|
||||
#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) {
|
||||
fprintf(stderr, "AUDIO_GETINFO failed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
prinfo = this->iscapture ? &info.record : &info.play;
|
||||
|
||||
fprintf(stderr, "\n"
|
||||
"[%s info]\n"
|
||||
"buffer size : %d bytes\n"
|
||||
"sample rate : %i Hz\n"
|
||||
"channels : %i\n"
|
||||
"precision : %i-bit\n"
|
||||
"encoding : 0x%x\n"
|
||||
"seek : %i\n"
|
||||
"sample count : %i\n"
|
||||
"EOF count : %i\n"
|
||||
"paused : %s\n"
|
||||
"error occured : %s\n"
|
||||
"waiting : %s\n"
|
||||
"active : %s\n"
|
||||
"",
|
||||
this->iscapture ? "record" : "play",
|
||||
prinfo->buffer_size,
|
||||
prinfo->sample_rate,
|
||||
prinfo->channels,
|
||||
prinfo->precision,
|
||||
prinfo->encoding,
|
||||
prinfo->seek,
|
||||
prinfo->samples,
|
||||
prinfo->eof,
|
||||
prinfo->pause ? "yes" : "no",
|
||||
prinfo->error ? "yes" : "no",
|
||||
prinfo->waiting ? "yes" : "no",
|
||||
prinfo->active ? "yes" : "no");
|
||||
|
||||
fprintf(stderr, "\n"
|
||||
"[audio info]\n"
|
||||
"monitor_gain : %i\n"
|
||||
"hw block size : %d bytes\n"
|
||||
"hi watermark : %i\n"
|
||||
"lo watermark : %i\n"
|
||||
"audio mode : %s\n"
|
||||
"",
|
||||
info.monitor_gain,
|
||||
info.blocksize,
|
||||
info.hiwat, info.lowat,
|
||||
(info.mode == AUMODE_PLAY) ? "PLAY"
|
||||
: (info.mode = AUMODE_RECORD) ? "RECORD"
|
||||
: (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?"));
|
||||
|
||||
fprintf(stderr, "\n"
|
||||
"[audio spec]\n"
|
||||
"format : 0x%x\n"
|
||||
"size : %u\n"
|
||||
"",
|
||||
this->spec.format,
|
||||
this->spec.size);
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
#endif /* DEBUG_AUDIO */
|
||||
}
|
||||
|
||||
static void NETBSDAUDIO_PlayDevice(_THIS)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = this->hidden;
|
||||
int written;
|
||||
|
||||
/* Write the audio data */
|
||||
written = write(h->audio_fd, h->mixbuf, h->mixlen);
|
||||
if (written == -1) {
|
||||
/* Non recoverable error has occurred. It should be reported!!! */
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
perror("audio");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||
#endif
|
||||
}
|
||||
|
||||
static Uint8 *NETBSDAUDIO_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return this->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static int NETBSDAUDIO_CaptureFromDevice(_THIS, void *_buffer, int buflen)
|
||||
{
|
||||
Uint8 *buffer = (Uint8 *)_buffer;
|
||||
int br;
|
||||
|
||||
br = read(this->hidden->audio_fd, buffer, buflen);
|
||||
if (br == -1) {
|
||||
/* Non recoverable error has occurred. It should be reported!!! */
|
||||
perror("audio");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Captured %d bytes of audio data\n", br);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void NETBSDAUDIO_FlushCapture(_THIS)
|
||||
{
|
||||
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. */
|
||||
}
|
||||
remain -= br;
|
||||
}
|
||||
}
|
||||
|
||||
static void NETBSDAUDIO_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->audio_fd >= 0) {
|
||||
close(this->hidden->audio_fd);
|
||||
}
|
||||
SDL_free(this->hidden->mixbuf);
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
static int NETBSDAUDIO_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
SDL_bool iscapture = this->iscapture;
|
||||
SDL_AudioFormat test_format;
|
||||
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) {
|
||||
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));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
prinfo->sample_rate = this->spec.freq;
|
||||
prinfo->channels = this->spec.channels;
|
||||
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
switch (test_format) {
|
||||
case AUDIO_U8:
|
||||
encoding = AUDIO_ENCODING_ULINEAR;
|
||||
break;
|
||||
case AUDIO_S8:
|
||||
encoding = AUDIO_ENCODING_SLINEAR;
|
||||
break;
|
||||
case AUDIO_S16LSB:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_LE;
|
||||
break;
|
||||
case AUDIO_S16MSB:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_BE;
|
||||
break;
|
||||
case AUDIO_U16LSB:
|
||||
encoding = AUDIO_ENCODING_ULINEAR_LE;
|
||||
break;
|
||||
case AUDIO_U16MSB:
|
||||
encoding = AUDIO_ENCODING_ULINEAR_BE;
|
||||
break;
|
||||
case AUDIO_S32LSB:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_LE;
|
||||
break;
|
||||
case AUDIO_S32MSB:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_BE;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "netbsd");
|
||||
}
|
||||
prinfo->encoding = encoding;
|
||||
prinfo->precision = SDL_AUDIO_BITSIZE(test_format);
|
||||
|
||||
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(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
|
||||
return SDL_SetError("AUDIO_GETINFO failed for %s: %s", devname, strerror(errno));
|
||||
}
|
||||
|
||||
/* Final spec used for the device. */
|
||||
this->spec.format = test_format;
|
||||
this->spec.freq = prinfo->sample_rate;
|
||||
this->spec.channels = prinfo->channels;
|
||||
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
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) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
|
||||
}
|
||||
|
||||
NETBSDAUDIO_Status(this);
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SDL_bool NETBSDAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = NETBSDAUDIO_DetectDevices;
|
||||
impl->OpenDevice = NETBSDAUDIO_OpenDevice;
|
||||
impl->PlayDevice = NETBSDAUDIO_PlayDevice;
|
||||
impl->GetDeviceBuf = NETBSDAUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = NETBSDAUDIO_CloseDevice;
|
||||
impl->CaptureFromDevice = NETBSDAUDIO_CaptureFromDevice;
|
||||
impl->FlushCapture = NETBSDAUDIO_FlushCapture;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap NETBSDAUDIO_bootstrap = {
|
||||
"netbsd", "NetBSD audio", NETBSDAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_NETBSD */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
48
third_party/SDL/src/audio/netbsd/SDL_netbsdaudio.h
vendored
Normal file
48
third_party/SDL/src/audio/netbsd/SDL_netbsdaudio.h
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_netbsdaudio_h_
|
||||
#define SDL_netbsdaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The file descriptor for the audio device */
|
||||
int audio_fd;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
|
||||
/* Support for audio timing using a timer, in addition to SDL_IOReady() */
|
||||
float frame_ticks;
|
||||
float next_frame;
|
||||
};
|
||||
|
||||
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
|
||||
|
||||
#endif /* SDL_netbsdaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
775
third_party/SDL/src/audio/openslES/SDL_openslES.c
vendored
Normal file
775
third_party/SDL/src/audio/openslES/SDL_openslES.c
vendored
Normal file
@@ -0,0 +1,775 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if 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
|
||||
*/
|
||||
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../../core/android/SDL_android.h"
|
||||
#include "SDL_openslES.h"
|
||||
|
||||
/* for native audio */
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#if 0
|
||||
#define LOG_TAG "SDL_openslES"
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
//#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGV(...)
|
||||
#else
|
||||
#define LOGE(...)
|
||||
#define LOGI(...)
|
||||
#define LOGV(...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
#define SL_SPEAKER_FRONT_LEFT ((SLuint32) 0x00000001)
|
||||
#define SL_SPEAKER_FRONT_RIGHT ((SLuint32) 0x00000002)
|
||||
#define SL_SPEAKER_FRONT_CENTER ((SLuint32) 0x00000004)
|
||||
#define SL_SPEAKER_LOW_FREQUENCY ((SLuint32) 0x00000008)
|
||||
#define SL_SPEAKER_BACK_LEFT ((SLuint32) 0x00000010)
|
||||
#define SL_SPEAKER_BACK_RIGHT ((SLuint32) 0x00000020)
|
||||
#define SL_SPEAKER_FRONT_LEFT_OF_CENTER ((SLuint32) 0x00000040)
|
||||
#define SL_SPEAKER_FRONT_RIGHT_OF_CENTER ((SLuint32) 0x00000080)
|
||||
#define SL_SPEAKER_BACK_CENTER ((SLuint32) 0x00000100)
|
||||
#define SL_SPEAKER_SIDE_LEFT ((SLuint32) 0x00000200)
|
||||
#define SL_SPEAKER_SIDE_RIGHT ((SLuint32) 0x00000400)
|
||||
#define SL_SPEAKER_TOP_CENTER ((SLuint32) 0x00000800)
|
||||
#define SL_SPEAKER_TOP_FRONT_LEFT ((SLuint32) 0x00001000)
|
||||
#define SL_SPEAKER_TOP_FRONT_CENTER ((SLuint32) 0x00002000)
|
||||
#define SL_SPEAKER_TOP_FRONT_RIGHT ((SLuint32) 0x00004000)
|
||||
#define SL_SPEAKER_TOP_BACK_LEFT ((SLuint32) 0x00008000)
|
||||
#define SL_SPEAKER_TOP_BACK_CENTER ((SLuint32) 0x00010000)
|
||||
#define SL_SPEAKER_TOP_BACK_RIGHT ((SLuint32) 0x00020000)
|
||||
*/
|
||||
#define SL_ANDROID_SPEAKER_STEREO (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT)
|
||||
#define SL_ANDROID_SPEAKER_QUAD (SL_ANDROID_SPEAKER_STEREO | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT)
|
||||
#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 */
|
||||
static SLObjectItf engineObject = NULL;
|
||||
static SLEngineItf engineEngine = NULL;
|
||||
|
||||
/* output mix interfaces */
|
||||
static SLObjectItf outputMixObject = NULL;
|
||||
|
||||
/* buffer queue player interfaces */
|
||||
static SLObjectItf bqPlayerObject = NULL;
|
||||
static SLPlayItf bqPlayerPlay = NULL;
|
||||
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
|
||||
#if 0
|
||||
static SLVolumeItf bqPlayerVolume;
|
||||
#endif
|
||||
|
||||
/* recorder interfaces */
|
||||
static SLObjectItf recorderObject = NULL;
|
||||
static SLRecordItf recorderRecord = NULL;
|
||||
static SLAndroidSimpleBufferQueueItf recorderBufferQueue = NULL;
|
||||
|
||||
#if 0
|
||||
static const char *sldevaudiorecorderstr = "SLES Audio Recorder";
|
||||
static const char *sldevaudioplayerstr = "SLES Audio Player";
|
||||
|
||||
#define SLES_DEV_AUDIO_RECORDER sldevaudiorecorderstr
|
||||
#define SLES_DEV_AUDIO_PLAYER sldevaudioplayerstr
|
||||
static void openslES_DetectDevices( int iscapture )
|
||||
{
|
||||
LOGI( "openSLES_DetectDevices()" );
|
||||
if ( iscapture )
|
||||
addfn( SLES_DEV_AUDIO_RECORDER );
|
||||
else
|
||||
addfn( SLES_DEV_AUDIO_PLAYER );
|
||||
}
|
||||
#endif
|
||||
|
||||
static void openslES_DestroyEngine(void)
|
||||
{
|
||||
LOGI("openslES_DestroyEngine()");
|
||||
|
||||
/* 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 */
|
||||
if (engineObject != NULL) {
|
||||
(*engineObject)->Destroy(engineObject);
|
||||
engineObject = NULL;
|
||||
engineEngine = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int openslES_CreateEngine(void)
|
||||
{
|
||||
const SLInterfaceID ids[1] = { SL_IID_VOLUME };
|
||||
const SLboolean req[1] = { SL_BOOLEAN_FALSE };
|
||||
SLresult result;
|
||||
|
||||
LOGI("openSLES_CreateEngine()");
|
||||
|
||||
/* create engine */
|
||||
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("slCreateEngine failed: %d", result);
|
||||
goto error;
|
||||
}
|
||||
LOGI("slCreateEngine OK");
|
||||
|
||||
/* realize the engine */
|
||||
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeEngine failed: %d", result);
|
||||
goto error;
|
||||
}
|
||||
LOGI("RealizeEngine OK");
|
||||
|
||||
/* 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);
|
||||
goto error;
|
||||
}
|
||||
LOGI("EngineGetInterface OK");
|
||||
|
||||
/* create output mix */
|
||||
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("CreateOutputMix failed: %d", result);
|
||||
goto error;
|
||||
}
|
||||
LOGI("CreateOutputMix OK");
|
||||
|
||||
/* realize the output mix */
|
||||
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeOutputMix failed: %d", result);
|
||||
goto error;
|
||||
}
|
||||
return 1;
|
||||
|
||||
error:
|
||||
openslES_DestroyEngine();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
LOGV("SLES: Recording Callback");
|
||||
SDL_SemPost(audiodata->playsem);
|
||||
}
|
||||
|
||||
static void openslES_DestroyPCMRecorder(_THIS)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = this->hidden;
|
||||
SLresult result;
|
||||
|
||||
/* stop recording */
|
||||
if (recorderRecord != NULL) {
|
||||
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SetRecordState stopped: %d", result);
|
||||
}
|
||||
}
|
||||
|
||||
/* destroy audio recorder object, and invalidate all associated interfaces */
|
||||
if (recorderObject != NULL) {
|
||||
(*recorderObject)->Destroy(recorderObject);
|
||||
recorderObject = NULL;
|
||||
recorderRecord = NULL;
|
||||
recorderBufferQueue = NULL;
|
||||
}
|
||||
|
||||
if (audiodata->playsem) {
|
||||
SDL_DestroySemaphore(audiodata->playsem);
|
||||
audiodata->playsem = NULL;
|
||||
}
|
||||
|
||||
if (audiodata->mixbuff) {
|
||||
SDL_free(audiodata->mixbuff);
|
||||
}
|
||||
}
|
||||
|
||||
static int openslES_CreatePCMRecorder(_THIS)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = this->hidden;
|
||||
SLDataFormat_PCM format_pcm;
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
|
||||
SLDataSink audioSnk;
|
||||
SLDataLocator_IODevice loc_dev;
|
||||
SLDataSource audioSrc;
|
||||
const SLInterfaceID ids[1] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
|
||||
const SLboolean req[1] = { SL_BOOLEAN_TRUE };
|
||||
SLresult result;
|
||||
int i;
|
||||
|
||||
if (!Android_JNI_RequestPermission("android.permission.RECORD_AUDIO")) {
|
||||
LOGE("This app doesn't have RECORD_AUDIO permission");
|
||||
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 = AUDIO_S16SYS;
|
||||
this->spec.channels = 1;
|
||||
/*this->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/
|
||||
|
||||
/* Update the fragment size as size in bytes */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
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);
|
||||
|
||||
/* configure audio source */
|
||||
loc_dev.locatorType = SL_DATALOCATOR_IODEVICE;
|
||||
loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT;
|
||||
loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
|
||||
loc_dev.device = NULL;
|
||||
audioSrc.pLocator = &loc_dev;
|
||||
audioSrc.pFormat = NULL;
|
||||
|
||||
/* 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.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) */
|
||||
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 */
|
||||
result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeAudioPlayer failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
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);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RegisterCallback failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[i], this->spec.size);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Record enqueue buffers failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
/* start recording */
|
||||
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Record set state failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
return SDL_SetError("Open device failed!");
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
LOGV("SLES: Playback Callback");
|
||||
SDL_SemPost(audiodata->playsem);
|
||||
}
|
||||
|
||||
static void openslES_DestroyPCMPlayer(_THIS)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = this->hidden;
|
||||
SLresult result;
|
||||
|
||||
/* set the player's state to 'stopped' */
|
||||
if (bqPlayerPlay != NULL) {
|
||||
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 */
|
||||
if (bqPlayerObject != NULL) {
|
||||
(*bqPlayerObject)->Destroy(bqPlayerObject);
|
||||
|
||||
bqPlayerObject = NULL;
|
||||
bqPlayerPlay = NULL;
|
||||
bqPlayerBufferQueue = NULL;
|
||||
}
|
||||
|
||||
if (audiodata->playsem) {
|
||||
SDL_DestroySemaphore(audiodata->playsem);
|
||||
audiodata->playsem = NULL;
|
||||
}
|
||||
|
||||
if (audiodata->mixbuff) {
|
||||
SDL_free(audiodata->mixbuff);
|
||||
}
|
||||
}
|
||||
|
||||
static int openslES_CreatePCMPlayer(_THIS)
|
||||
{
|
||||
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) {
|
||||
SDL_AudioFormat test_format;
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
if (SDL_AUDIO_ISSIGNED(test_format)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
/* Didn't find a compatible format : */
|
||||
LOGI("No compatible audio format, using signed 16-bit audio");
|
||||
test_format = AUDIO_S16SYS;
|
||||
}
|
||||
this->spec.format = test_format;
|
||||
} else {
|
||||
/* Just go with signed 16-bit audio as it's the most compatible */
|
||||
this->spec.format = AUDIO_S16SYS;
|
||||
}
|
||||
|
||||
/* Update the fragment size as size in bytes */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
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);
|
||||
|
||||
/* configure audio source */
|
||||
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);
|
||||
|
||||
if (SDL_AUDIO_ISBIGENDIAN(this->spec.format)) {
|
||||
format_pcm.endianness = SL_BYTEORDER_BIGENDIAN;
|
||||
} else {
|
||||
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
|
||||
}
|
||||
|
||||
switch (this->spec.channels) {
|
||||
case 1:
|
||||
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT;
|
||||
break;
|
||||
case 2:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_STEREO;
|
||||
break;
|
||||
case 3:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_STEREO | SL_SPEAKER_FRONT_CENTER;
|
||||
break;
|
||||
case 4:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_QUAD;
|
||||
break;
|
||||
case 5:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER;
|
||||
break;
|
||||
case 6:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_5DOT1;
|
||||
break;
|
||||
case 7:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_BACK_CENTER;
|
||||
break;
|
||||
case 8:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_7DOT1;
|
||||
break;
|
||||
default:
|
||||
/* Unknown number of channels, fall back to stereo */
|
||||
this->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 */
|
||||
format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
|
||||
format_pcm_ex.endianness = format_pcm.endianness;
|
||||
format_pcm_ex.channelMask = format_pcm.channelMask;
|
||||
format_pcm_ex.numChannels = format_pcm.numChannels;
|
||||
format_pcm_ex.sampleRate = format_pcm.samplesPerSec;
|
||||
format_pcm_ex.bitsPerSample = format_pcm.bitsPerSample;
|
||||
format_pcm_ex.containerSize = format_pcm.containerSize;
|
||||
format_pcm_ex.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
|
||||
}
|
||||
|
||||
audioSrc.pLocator = &loc_bufq;
|
||||
audioSrc.pFormat = SDL_AUDIO_ISFLOAT(this->spec.format) ? (void *)&format_pcm_ex : (void *)&format_pcm;
|
||||
|
||||
/* configure audio sink */
|
||||
loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
|
||||
loc_outmix.outputMix = outputMixObject;
|
||||
audioSnk.pLocator = &loc_outmix;
|
||||
audioSnk.pFormat = NULL;
|
||||
|
||||
/* create audio player */
|
||||
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 */
|
||||
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeAudioPlayer failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
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);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RegisterCallback failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* 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; */
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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);
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int openslES_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*this->hidden));
|
||||
if (this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
if (this->iscapture) {
|
||||
LOGI("openslES_OpenDevice() %s for capture", devname);
|
||||
return openslES_CreatePCMRecorder(this);
|
||||
} else {
|
||||
int ret;
|
||||
LOGI("openslES_OpenDevice() %s for playing", devname);
|
||||
ret = openslES_CreatePCMPlayer(this);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return SDL_SetError("Open device failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void openslES_WaitDevice(_THIS)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = this->hidden;
|
||||
|
||||
LOGV("openslES_WaitDevice()");
|
||||
|
||||
/* Wait for an audio chunk to finish */
|
||||
SDL_SemWait(audiodata->playsem);
|
||||
}
|
||||
|
||||
static void openslES_PlayDevice(_THIS)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = this->hidden;
|
||||
SLresult result;
|
||||
|
||||
LOGV("======openslES_PlayDevice()======");
|
||||
|
||||
/* Queue it up */
|
||||
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], this->spec.size);
|
||||
|
||||
audiodata->next_buffer++;
|
||||
if (audiodata->next_buffer >= NUM_BUFFERS) {
|
||||
audiodata->next_buffer = 0;
|
||||
}
|
||||
|
||||
/* If Enqueue fails, callback won't be called.
|
||||
* Post the semphore, not to run out of buffer */
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
SDL_SemPost(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.. */
|
||||
|
||||
static Uint8 *openslES_GetDeviceBuf(_THIS)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = this->hidden;
|
||||
|
||||
LOGV("openslES_GetDeviceBuf()");
|
||||
return audiodata->pmixbuff[audiodata->next_buffer];
|
||||
}
|
||||
|
||||
static int openslES_CaptureFromDevice(_THIS, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = this->hidden;
|
||||
SLresult result;
|
||||
|
||||
/* Wait for new recorded data */
|
||||
SDL_SemWait(audiodata->playsem);
|
||||
|
||||
/* 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);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Record enqueue buffers failed: %d", result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
audiodata->next_buffer++;
|
||||
if (audiodata->next_buffer >= NUM_BUFFERS) {
|
||||
audiodata->next_buffer = 0;
|
||||
}
|
||||
|
||||
return this->spec.size;
|
||||
}
|
||||
|
||||
static void openslES_CloseDevice(_THIS)
|
||||
{
|
||||
/* struct SDL_PrivateAudioData *audiodata = this->hidden; */
|
||||
|
||||
if (this->iscapture) {
|
||||
LOGI("openslES_CloseDevice() for capture");
|
||||
openslES_DestroyPCMRecorder(this);
|
||||
} else {
|
||||
LOGI("openslES_CloseDevice() for playing");
|
||||
openslES_DestroyPCMPlayer(this);
|
||||
}
|
||||
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
static SDL_bool openslES_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
LOGI("openslES_Init() called");
|
||||
|
||||
if (!openslES_CreateEngine()) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
LOGI("openslES_Init() - set pointers");
|
||||
|
||||
/* Set the function pointers */
|
||||
/* impl->DetectDevices = openslES_DetectDevices; */
|
||||
impl->OpenDevice = openslES_OpenDevice;
|
||||
impl->WaitDevice = openslES_WaitDevice;
|
||||
impl->PlayDevice = openslES_PlayDevice;
|
||||
impl->GetDeviceBuf = openslES_GetDeviceBuf;
|
||||
impl->CaptureFromDevice = openslES_CaptureFromDevice;
|
||||
impl->CloseDevice = openslES_CloseDevice;
|
||||
impl->Deinitialize = openslES_DestroyEngine;
|
||||
|
||||
/* and the capabilities */
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
|
||||
LOGI("openslES_Init() - success");
|
||||
|
||||
/* this audio target is available. */
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap openslES_bootstrap = {
|
||||
"openslES", "opensl ES audio driver", openslES_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
void openslES_ResumeDevices(void)
|
||||
{
|
||||
if (bqPlayerPlay != NULL) {
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void openslES_PauseDevices(void)
|
||||
{
|
||||
if (bqPlayerPlay != NULL) {
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_OPENSLES */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
46
third_party/SDL/src/audio/openslES/SDL_openslES.h
vendored
Normal file
46
third_party/SDL/src/audio/openslES/SDL_openslES.h
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef _SDL_openslesaudio_h
|
||||
#define _SDL_openslesaudio_h
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
#define NUM_BUFFERS 2 /* -- Don't lower this! */
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
Uint8 *mixbuff;
|
||||
int next_buffer;
|
||||
Uint8 *pmixbuff[NUM_BUFFERS];
|
||||
SDL_sem *playsem;
|
||||
};
|
||||
|
||||
void openslES_ResumeDevices(void);
|
||||
void openslES_PauseDevices(void);
|
||||
|
||||
#endif /* _SDL_openslesaudio_h */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
601
third_party/SDL/src/audio/os2/SDL_os2audio.c
vendored
Normal file
601
third_party/SDL/src/audio/os2/SDL_os2audio.c
vendored
Normal file
@@ -0,0 +1,601 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_OS2
|
||||
|
||||
/* Allow access to a raw mixing buffer */
|
||||
|
||||
#include "../../core/os2/SDL_os2.h"
|
||||
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_os2audio.h"
|
||||
|
||||
static PMCI_MIX_BUFFER _getNextBuffer(SDL_PrivateAudioData *pAData, PMCI_MIX_BUFFER pBuffer)
|
||||
{
|
||||
PMCI_MIX_BUFFER pFirstBuffer = &pAData->aMixBuffers[0];
|
||||
PMCI_MIX_BUFFER pLastBuffer = &pAData->aMixBuffers[pAData->cMixBuffers -1];
|
||||
return (pBuffer == pLastBuffer ? pFirstBuffer : pBuffer+1);
|
||||
}
|
||||
|
||||
static ULONG _getEnvULong(const char *name, ULONG ulMax, ULONG ulDefault)
|
||||
{
|
||||
ULONG ulValue;
|
||||
char* end;
|
||||
char* envval = SDL_getenv(name);
|
||||
|
||||
if (envval == NULL)
|
||||
return ulDefault;
|
||||
|
||||
ulValue = SDL_strtoul(envval, &end, 10);
|
||||
return (end == envval) || (ulValue > ulMax)? ulDefault : ulMax;
|
||||
}
|
||||
|
||||
static int _MCIError(const char *func, ULONG ulResult)
|
||||
{
|
||||
CHAR acBuf[128];
|
||||
mciGetErrorString(ulResult, acBuf, sizeof(acBuf));
|
||||
return SDL_SetError("[%s] %s", func, acBuf);
|
||||
}
|
||||
|
||||
static void _mixIOError(const char *function, ULONG ulRC)
|
||||
{
|
||||
debug_os2("%s() - failed, rc = 0x%lX (%s)",
|
||||
function, ulRC,
|
||||
(ulRC == MCIERR_INVALID_MODE) ? "Mixer mode does not match request" :
|
||||
(ulRC == MCIERR_INVALID_BUFFER) ? "Caller sent an invalid buffer" : "unknown");
|
||||
}
|
||||
|
||||
static LONG APIENTRY cbAudioWriteEvent(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer,
|
||||
ULONG ulFlags)
|
||||
{
|
||||
SDL_AudioDevice *_this = (SDL_AudioDevice *)pBuffer->ulUserParm;
|
||||
SDL_PrivateAudioData *pAData = (SDL_PrivateAudioData *)_this->hidden;
|
||||
ULONG ulRC;
|
||||
|
||||
debug_os2("cbAudioWriteEvent: ulStatus = %lu, pBuffer = %p, ulFlags = %#lX",ulStatus,pBuffer,ulFlags);
|
||||
|
||||
if (pAData->ulState == 2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ulFlags != MIX_WRITE_COMPLETE) {
|
||||
debug_os2("flags = 0x%lX", ulFlags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pAData->pDrainBuffer = pBuffer;
|
||||
ulRC = pAData->stMCIMixSetup.pmixWrite(pAData->stMCIMixSetup.ulMixHandle,
|
||||
pAData->pDrainBuffer, 1);
|
||||
if (ulRC != MCIERR_SUCCESS) {
|
||||
_mixIOError("pmixWrite", ulRC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulRC = DosPostEventSem(pAData->hevBuf);
|
||||
if (ulRC != NO_ERROR && ulRC != ERROR_ALREADY_POSTED) {
|
||||
debug_os2("DosPostEventSem(), rc = %lu", ulRC);
|
||||
}
|
||||
|
||||
return 1; /* return value doesn't seem to matter. */
|
||||
}
|
||||
|
||||
static LONG APIENTRY cbAudioReadEvent(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer,
|
||||
ULONG ulFlags)
|
||||
{
|
||||
SDL_AudioDevice *_this = (SDL_AudioDevice *)pBuffer->ulUserParm;
|
||||
SDL_PrivateAudioData *pAData = (SDL_PrivateAudioData *)_this->hidden;
|
||||
ULONG ulRC;
|
||||
|
||||
debug_os2("cbAudioReadEvent: ulStatus = %lu, pBuffer = %p, ulFlags = %#lX",ulStatus,pBuffer,ulFlags);
|
||||
|
||||
if (pAData->ulState == 2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ulFlags != MIX_READ_COMPLETE) {
|
||||
debug_os2("flags = 0x%lX", ulFlags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pAData->pFillBuffer = pBuffer;
|
||||
if (pAData->pFillBuffer == pAData->aMixBuffers)
|
||||
{
|
||||
ulRC = pAData->stMCIMixSetup.pmixRead(pAData->stMCIMixSetup.ulMixHandle,
|
||||
pAData->pFillBuffer, pAData->cMixBuffers);
|
||||
if (ulRC != MCIERR_SUCCESS) {
|
||||
_mixIOError("pmixRead", ulRC);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ulRC = DosPostEventSem(pAData->hevBuf);
|
||||
if (ulRC != NO_ERROR && ulRC != ERROR_ALREADY_POSTED) {
|
||||
debug_os2("DosPostEventSem(), rc = %lu", ulRC);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void OS2_DetectDevices(void)
|
||||
{
|
||||
MCI_SYSINFO_PARMS stMCISysInfo;
|
||||
CHAR acBuf[256];
|
||||
ULONG ulDevicesNum;
|
||||
MCI_SYSINFO_LOGDEVICE stLogDevice;
|
||||
MCI_SYSINFO_PARMS stSysInfoParams;
|
||||
ULONG ulRC;
|
||||
ULONG ulNumber;
|
||||
MCI_GETDEVCAPS_PARMS stDevCapsParams;
|
||||
MCI_OPEN_PARMS stMCIOpen;
|
||||
MCI_GENERIC_PARMS stMCIGenericParams;
|
||||
|
||||
SDL_memset(&stMCISysInfo, 0, sizeof(stMCISysInfo));
|
||||
acBuf[0] = '\0';
|
||||
stMCISysInfo.pszReturn = acBuf;
|
||||
stMCISysInfo.ulRetSize = sizeof(acBuf);
|
||||
stMCISysInfo.usDeviceType = MCI_DEVTYPE_AUDIO_AMPMIX;
|
||||
ulRC = mciSendCommand(0, MCI_SYSINFO, MCI_WAIT | MCI_SYSINFO_QUANTITY,
|
||||
&stMCISysInfo, 0);
|
||||
if (LOUSHORT(ulRC) != MCIERR_SUCCESS) {
|
||||
debug_os2("MCI_SYSINFO, MCI_SYSINFO_QUANTITY - failed, rc = 0x%hX", LOUSHORT(ulRC));
|
||||
return;
|
||||
}
|
||||
|
||||
ulDevicesNum = SDL_strtoul(stMCISysInfo.pszReturn, NULL, 10);
|
||||
|
||||
for (ulNumber = 1; ulNumber <= ulDevicesNum;
|
||||
ulNumber++) {
|
||||
/* Get device install name. */
|
||||
stSysInfoParams.ulNumber = ulNumber;
|
||||
stSysInfoParams.pszReturn = acBuf;
|
||||
stSysInfoParams.ulRetSize = sizeof(acBuf);
|
||||
stSysInfoParams.usDeviceType = MCI_DEVTYPE_AUDIO_AMPMIX;
|
||||
ulRC = mciSendCommand(0, MCI_SYSINFO, MCI_WAIT | MCI_SYSINFO_INSTALLNAME,
|
||||
&stSysInfoParams, 0);
|
||||
if (LOUSHORT(ulRC) != MCIERR_SUCCESS) {
|
||||
debug_os2("MCI_SYSINFO, MCI_SYSINFO_INSTALLNAME - failed, rc = 0x%hX", LOUSHORT(ulRC));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get textual product description. */
|
||||
stSysInfoParams.ulItem = MCI_SYSINFO_QUERY_DRIVER;
|
||||
stSysInfoParams.pSysInfoParm = &stLogDevice;
|
||||
SDL_strlcpy(stLogDevice.szInstallName, stSysInfoParams.pszReturn, MAX_DEVICE_NAME);
|
||||
ulRC = mciSendCommand(0, MCI_SYSINFO, MCI_WAIT | MCI_SYSINFO_ITEM,
|
||||
&stSysInfoParams, 0);
|
||||
if (LOUSHORT(ulRC) != MCIERR_SUCCESS) {
|
||||
debug_os2("MCI_SYSINFO, MCI_SYSINFO_ITEM - failed, rc = 0x%hX", LOUSHORT(ulRC));
|
||||
continue;
|
||||
}
|
||||
|
||||
SDL_AddAudioDevice(0, stLogDevice.szProductInfo, NULL, (void *)ulNumber);
|
||||
|
||||
/* Open audio device for querying its capabilities */
|
||||
/* at this point we HAVE TO OPEN the waveaudio device and not the ampmix device */
|
||||
/* because only the waveaudio device (tied to the ampmix device) supports querying for playback/record capability */
|
||||
SDL_memset(&stMCIOpen, 0, sizeof(stMCIOpen));
|
||||
stMCIOpen.pszDeviceType = (PSZ)MAKEULONG(MCI_DEVTYPE_WAVEFORM_AUDIO,LOUSHORT(ulNumber));
|
||||
ulRC = mciSendCommand(0, MCI_OPEN,MCI_WAIT | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE,&stMCIOpen, 0);
|
||||
if (LOUSHORT(ulRC) != MCIERR_SUCCESS) {
|
||||
debug_os2("MCI_OPEN (getDevCaps) - failed");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check for recording capability */
|
||||
SDL_memset(&stDevCapsParams, 0, sizeof(stDevCapsParams));
|
||||
stDevCapsParams.ulItem = MCI_GETDEVCAPS_CAN_RECORD;
|
||||
ulRC = mciSendCommand(stMCIOpen.usDeviceID, MCI_GETDEVCAPS, MCI_WAIT | MCI_GETDEVCAPS_ITEM,
|
||||
&stDevCapsParams, 0);
|
||||
if (LOUSHORT(ulRC) != MCIERR_SUCCESS) {
|
||||
debug_os2("MCI_GETDEVCAPS, MCI_GETDEVCAPS_ITEM - failed, rc = 0x%hX", LOUSHORT(ulRC));
|
||||
}
|
||||
else {
|
||||
if (stDevCapsParams.ulReturn) {
|
||||
SDL_AddAudioDevice(1, stLogDevice.szProductInfo, NULL, (void *)(ulNumber | 0x80000000));
|
||||
}
|
||||
}
|
||||
|
||||
/* close the audio device, we are done querying its capabilities */
|
||||
SDL_memset(&stMCIGenericParams, 0, sizeof(stMCIGenericParams));
|
||||
ulRC = mciSendCommand(stMCIOpen.usDeviceID, MCI_CLOSE, MCI_WAIT,
|
||||
&stMCIGenericParams, 0);
|
||||
if (LOUSHORT(ulRC) != MCIERR_SUCCESS) {
|
||||
debug_os2("MCI_CLOSE (getDevCaps) - failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OS2_WaitDevice(_THIS)
|
||||
{
|
||||
SDL_PrivateAudioData *pAData = (SDL_PrivateAudioData *)_this->hidden;
|
||||
ULONG ulRC;
|
||||
|
||||
debug_os2("Enter");
|
||||
|
||||
/* Wait for an audio chunk to finish */
|
||||
ulRC = DosWaitEventSem(pAData->hevBuf, 5000);
|
||||
if (ulRC != NO_ERROR) {
|
||||
debug_os2("DosWaitEventSem(), rc = %lu", ulRC);
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 *OS2_GetDeviceBuf(_THIS)
|
||||
{
|
||||
SDL_PrivateAudioData *pAData = (SDL_PrivateAudioData *)_this->hidden;
|
||||
|
||||
debug_os2("Enter");
|
||||
|
||||
return (Uint8 *) pAData->pFillBuffer->pBuffer;
|
||||
}
|
||||
|
||||
static void OS2_PlayDevice(_THIS)
|
||||
{
|
||||
SDL_PrivateAudioData *pAData = (SDL_PrivateAudioData *)_this->hidden;
|
||||
ULONG ulRC;
|
||||
PMCI_MIX_BUFFER pMixBuffer = NULL;
|
||||
|
||||
debug_os2("Enter");
|
||||
|
||||
pMixBuffer = pAData->pDrainBuffer;
|
||||
pAData->pFillBuffer = _getNextBuffer(pAData, pAData->pFillBuffer);
|
||||
if (!pAData->ulState && pAData->pFillBuffer != pMixBuffer)
|
||||
{
|
||||
/*
|
||||
* this buffer was filled but we have not yet filled all buffers
|
||||
* so just signal event sem so that OS2_WaitDevice does not need
|
||||
* to block
|
||||
*/
|
||||
ulRC = DosPostEventSem(pAData->hevBuf);
|
||||
}
|
||||
|
||||
if (!pAData->ulState && (pAData->pFillBuffer == pMixBuffer) )
|
||||
{
|
||||
debug_os2("!hasStarted");
|
||||
pAData->ulState = 1;
|
||||
|
||||
/* Write buffers to kick off the amp mixer */
|
||||
ulRC = pAData->stMCIMixSetup.pmixWrite(pAData->stMCIMixSetup.ulMixHandle,
|
||||
pMixBuffer, pAData->cMixBuffers);
|
||||
|
||||
if (ulRC != MCIERR_SUCCESS) {
|
||||
_mixIOError("pmixWrite", ulRC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int OS2_CaptureFromDevice(_THIS,void *buffer,int buflen)
|
||||
{
|
||||
SDL_PrivateAudioData *pAData = (SDL_PrivateAudioData *)_this->hidden;
|
||||
ULONG ulRC;
|
||||
PMCI_MIX_BUFFER pMixBuffer = NULL;
|
||||
int len = 0;
|
||||
|
||||
if (!pAData->ulState)
|
||||
{
|
||||
pAData->ulState = 1;
|
||||
ulRC = pAData->stMCIMixSetup.pmixRead(pAData->stMCIMixSetup.ulMixHandle,
|
||||
pAData->aMixBuffers, pAData->cMixBuffers);
|
||||
if (ulRC != MCIERR_SUCCESS) {
|
||||
_mixIOError("pmixRead", ulRC);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for an audio chunk to finish */
|
||||
ulRC = DosWaitEventSem(pAData->hevBuf, 5000);
|
||||
if (ulRC != NO_ERROR)
|
||||
{
|
||||
debug_os2("DosWaitEventSem(), rc = %lu", ulRC);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pMixBuffer = pAData->pDrainBuffer;
|
||||
len = SDL_min((int)pMixBuffer->ulBufferLength, buflen);
|
||||
SDL_memcpy(buffer,pMixBuffer->pBuffer, len);
|
||||
pAData->pDrainBuffer = _getNextBuffer(pAData, pMixBuffer);
|
||||
|
||||
debug_os2("buflen = %u, ulBufferLength = %lu",buflen,pMixBuffer->ulBufferLength);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void OS2_FlushCapture(_THIS)
|
||||
{
|
||||
SDL_PrivateAudioData *pAData = (SDL_PrivateAudioData *)_this->hidden;
|
||||
ULONG ulIdx;
|
||||
|
||||
debug_os2("Enter");
|
||||
|
||||
/* Fill all device buffers with data */
|
||||
for (ulIdx = 0; ulIdx < pAData->cMixBuffers; ulIdx++) {
|
||||
pAData->aMixBuffers[ulIdx].ulFlags = 0;
|
||||
pAData->aMixBuffers[ulIdx].ulBufferLength = _this->spec.size;
|
||||
pAData->aMixBuffers[ulIdx].ulUserParm = (ULONG)_this;
|
||||
|
||||
SDL_memset(((PMCI_MIX_BUFFER)pAData->aMixBuffers)[ulIdx].pBuffer,
|
||||
_this->spec.silence, _this->spec.size);
|
||||
}
|
||||
pAData->pFillBuffer = pAData->aMixBuffers;
|
||||
pAData->pDrainBuffer = pAData->aMixBuffers;
|
||||
}
|
||||
|
||||
|
||||
static void OS2_CloseDevice(_THIS)
|
||||
{
|
||||
SDL_PrivateAudioData *pAData = (SDL_PrivateAudioData *)_this->hidden;
|
||||
MCI_GENERIC_PARMS sMCIGenericParms;
|
||||
ULONG ulRC;
|
||||
|
||||
debug_os2("Enter");
|
||||
|
||||
if (pAData == NULL)
|
||||
return;
|
||||
|
||||
pAData->ulState = 2;
|
||||
|
||||
/* Close up audio */
|
||||
if (pAData->usDeviceId != (USHORT)~0) { /* Device is open. */
|
||||
SDL_zero(sMCIGenericParms);
|
||||
|
||||
ulRC = mciSendCommand(pAData->usDeviceId, MCI_STOP,
|
||||
MCI_WAIT,
|
||||
&sMCIGenericParms, 0);
|
||||
if (LOUSHORT(ulRC) != MCIERR_SUCCESS) {
|
||||
debug_os2("MCI_STOP - failed" );
|
||||
}
|
||||
|
||||
if (pAData->stMCIMixSetup.ulBitsPerSample != 0) { /* Mixer was initialized. */
|
||||
ulRC = mciSendCommand(pAData->usDeviceId, MCI_MIXSETUP,
|
||||
MCI_WAIT | MCI_MIXSETUP_DEINIT,
|
||||
&pAData->stMCIMixSetup, 0);
|
||||
if (LOUSHORT(ulRC) != MCIERR_SUCCESS) {
|
||||
debug_os2("MCI_MIXSETUP, MCI_MIXSETUP_DEINIT - failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (pAData->cMixBuffers != 0) { /* Buffers was allocated. */
|
||||
MCI_BUFFER_PARMS stMCIBuffer;
|
||||
|
||||
stMCIBuffer.ulBufferSize = pAData->aMixBuffers[0].ulBufferLength;
|
||||
stMCIBuffer.ulNumBuffers = pAData->cMixBuffers;
|
||||
stMCIBuffer.pBufList = pAData->aMixBuffers;
|
||||
|
||||
ulRC = mciSendCommand(pAData->usDeviceId, MCI_BUFFER,
|
||||
MCI_WAIT | MCI_DEALLOCATE_MEMORY, &stMCIBuffer, 0);
|
||||
if (LOUSHORT(ulRC) != MCIERR_SUCCESS) {
|
||||
debug_os2("MCI_BUFFER, MCI_DEALLOCATE_MEMORY - failed");
|
||||
}
|
||||
}
|
||||
|
||||
ulRC = mciSendCommand(pAData->usDeviceId, MCI_CLOSE, MCI_WAIT,
|
||||
&sMCIGenericParms, 0);
|
||||
if (LOUSHORT(ulRC) != MCIERR_SUCCESS) {
|
||||
debug_os2("MCI_CLOSE - failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (pAData->hevBuf != NULLHANDLE)
|
||||
DosCloseEventSem(pAData->hevBuf);
|
||||
|
||||
SDL_free(pAData);
|
||||
}
|
||||
|
||||
static int OS2_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
SDL_PrivateAudioData *pAData;
|
||||
SDL_AudioFormat test_format;
|
||||
MCI_AMP_OPEN_PARMS stMCIAmpOpen;
|
||||
MCI_BUFFER_PARMS stMCIBuffer;
|
||||
ULONG ulRC;
|
||||
ULONG ulIdx;
|
||||
BOOL new_freq;
|
||||
ULONG ulHandle = (ULONG)_this->handle;
|
||||
SDL_bool iscapture = _this->iscapture;
|
||||
|
||||
new_freq = FALSE;
|
||||
SDL_zero(stMCIAmpOpen);
|
||||
SDL_zero(stMCIBuffer);
|
||||
|
||||
for (test_format = SDL_FirstAudioFormat(_this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
if (test_format == AUDIO_U8 || test_format == AUDIO_S16)
|
||||
break;
|
||||
}
|
||||
if (!test_format) {
|
||||
debug_os2("Unsupported audio format, AUDIO_S16 used");
|
||||
test_format = AUDIO_S16;
|
||||
}
|
||||
|
||||
pAData = (SDL_PrivateAudioData *) SDL_calloc(1, sizeof(struct SDL_PrivateAudioData));
|
||||
if (pAData == NULL)
|
||||
return SDL_OutOfMemory();
|
||||
_this->hidden = pAData;
|
||||
|
||||
ulRC = DosCreateEventSem(NULL, &pAData->hevBuf, DCE_AUTORESET, TRUE);
|
||||
if (ulRC != NO_ERROR) {
|
||||
debug_os2("DosCreateEventSem() failed, rc = %lu", ulRC);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Open audio device */
|
||||
stMCIAmpOpen.usDeviceID = 0;
|
||||
stMCIAmpOpen.pszDeviceType = (PSZ)MAKEULONG(MCI_DEVTYPE_AUDIO_AMPMIX,LOUSHORT(ulHandle));
|
||||
ulRC = mciSendCommand(0, MCI_OPEN,
|
||||
(_getEnvULong("SDL_AUDIO_SHARE", 1, 0) != 0)?
|
||||
MCI_WAIT | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE :
|
||||
MCI_WAIT | MCI_OPEN_TYPE_ID,
|
||||
&stMCIAmpOpen, 0);
|
||||
if (LOUSHORT(ulRC) != MCIERR_SUCCESS) {
|
||||
DosCloseEventSem(pAData->hevBuf);
|
||||
pAData->usDeviceId = (USHORT)~0;
|
||||
return _MCIError("MCI_OPEN", ulRC);
|
||||
}
|
||||
pAData->usDeviceId = stMCIAmpOpen.usDeviceID;
|
||||
|
||||
if (iscapture) {
|
||||
MCI_CONNECTOR_PARMS stMCIConnector;
|
||||
MCI_AMP_SET_PARMS stMCIAmpSet;
|
||||
BOOL fLineIn = _getEnvULong("SDL_AUDIO_LINEIN", 1, 0);
|
||||
|
||||
/* Set particular connector. */
|
||||
SDL_zero(stMCIConnector);
|
||||
stMCIConnector.ulConnectorType = (fLineIn)? MCI_LINE_IN_CONNECTOR :
|
||||
MCI_MICROPHONE_CONNECTOR;
|
||||
mciSendCommand(stMCIAmpOpen.usDeviceID, MCI_CONNECTOR,
|
||||
MCI_WAIT | MCI_ENABLE_CONNECTOR |
|
||||
MCI_CONNECTOR_TYPE, &stMCIConnector, 0);
|
||||
|
||||
/* Disable monitor. */
|
||||
SDL_zero(stMCIAmpSet);
|
||||
stMCIAmpSet.ulItem = MCI_AMP_SET_MONITOR;
|
||||
mciSendCommand(stMCIAmpOpen.usDeviceID, MCI_SET,
|
||||
MCI_WAIT | MCI_SET_OFF | MCI_SET_ITEM,
|
||||
&stMCIAmpSet, 0);
|
||||
|
||||
/* Set record volume. */
|
||||
stMCIAmpSet.ulLevel = _getEnvULong("SDL_AUDIO_RECVOL", 100, 90);
|
||||
stMCIAmpSet.ulItem = MCI_AMP_SET_AUDIO;
|
||||
stMCIAmpSet.ulAudio = MCI_SET_AUDIO_ALL; /* Both cnannels. */
|
||||
stMCIAmpSet.ulValue = (fLineIn) ? MCI_LINE_IN_CONNECTOR :
|
||||
MCI_MICROPHONE_CONNECTOR ;
|
||||
|
||||
mciSendCommand(stMCIAmpOpen.usDeviceID, MCI_SET,
|
||||
MCI_WAIT | MCI_SET_AUDIO | MCI_AMP_SET_GAIN,
|
||||
&stMCIAmpSet, 0);
|
||||
}
|
||||
|
||||
_this->spec.format = test_format;
|
||||
_this->spec.channels = _this->spec.channels > 1 ? 2 : 1;
|
||||
if (_this->spec.freq < 8000) {
|
||||
_this->spec.freq = 8000;
|
||||
new_freq = TRUE;
|
||||
} else if (_this->spec.freq > 48000) {
|
||||
_this->spec.freq = 48000;
|
||||
new_freq = TRUE;
|
||||
}
|
||||
|
||||
/* Setup mixer. */
|
||||
pAData->stMCIMixSetup.ulFormatTag = MCI_WAVE_FORMAT_PCM;
|
||||
pAData->stMCIMixSetup.ulBitsPerSample = SDL_AUDIO_BITSIZE(test_format);
|
||||
pAData->stMCIMixSetup.ulSamplesPerSec = _this->spec.freq;
|
||||
pAData->stMCIMixSetup.ulChannels = _this->spec.channels;
|
||||
pAData->stMCIMixSetup.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
|
||||
if (!iscapture) {
|
||||
pAData->stMCIMixSetup.ulFormatMode= MCI_PLAY;
|
||||
pAData->stMCIMixSetup.pmixEvent = cbAudioWriteEvent;
|
||||
} else {
|
||||
pAData->stMCIMixSetup.ulFormatMode= MCI_RECORD;
|
||||
pAData->stMCIMixSetup.pmixEvent = cbAudioReadEvent;
|
||||
}
|
||||
|
||||
ulRC = mciSendCommand(pAData->usDeviceId, MCI_MIXSETUP,
|
||||
MCI_WAIT | MCI_MIXSETUP_INIT, &pAData->stMCIMixSetup, 0);
|
||||
if (LOUSHORT(ulRC) != MCIERR_SUCCESS && _this->spec.freq > 44100) {
|
||||
new_freq = TRUE;
|
||||
pAData->stMCIMixSetup.ulSamplesPerSec = 44100;
|
||||
_this->spec.freq = 44100;
|
||||
ulRC = mciSendCommand(pAData->usDeviceId, MCI_MIXSETUP,
|
||||
MCI_WAIT | MCI_MIXSETUP_INIT, &pAData->stMCIMixSetup, 0);
|
||||
}
|
||||
|
||||
debug_os2("Setup mixer [BPS: %lu, Freq.: %lu, Channels: %lu]: %s",
|
||||
pAData->stMCIMixSetup.ulBitsPerSample,
|
||||
pAData->stMCIMixSetup.ulSamplesPerSec,
|
||||
pAData->stMCIMixSetup.ulChannels,
|
||||
(ulRC == MCIERR_SUCCESS)? "SUCCESS" : "FAIL");
|
||||
|
||||
if (ulRC != MCIERR_SUCCESS) {
|
||||
pAData->stMCIMixSetup.ulBitsPerSample = 0;
|
||||
return _MCIError("MCI_MIXSETUP", ulRC);
|
||||
}
|
||||
|
||||
if (_this->spec.samples == 0 || new_freq == TRUE) {
|
||||
/* also see SDL_audio.c:prepare_audiospec() */
|
||||
/* Pick a default of ~46 ms at desired frequency */
|
||||
Uint32 samples = (_this->spec.freq / 1000) * 46;
|
||||
Uint32 power2 = 1;
|
||||
while (power2 < samples) {
|
||||
power2 <<= 1;
|
||||
}
|
||||
_this->spec.samples = power2;
|
||||
}
|
||||
/* Update the fragment size as size in bytes */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
/* Allocate memory buffers */
|
||||
stMCIBuffer.ulBufferSize = _this->spec.size;/* (_this->spec.freq / 1000) * 100 */
|
||||
stMCIBuffer.ulNumBuffers = NUM_BUFFERS;
|
||||
stMCIBuffer.pBufList = pAData->aMixBuffers;
|
||||
|
||||
ulRC = mciSendCommand(pAData->usDeviceId, MCI_BUFFER,
|
||||
MCI_WAIT | MCI_ALLOCATE_MEMORY, &stMCIBuffer, 0);
|
||||
if (LOUSHORT(ulRC) != MCIERR_SUCCESS) {
|
||||
return _MCIError("MCI_BUFFER", ulRC);
|
||||
}
|
||||
pAData->cMixBuffers = stMCIBuffer.ulNumBuffers;
|
||||
_this->spec.size = stMCIBuffer.ulBufferSize;
|
||||
|
||||
debug_os2("%s, number of mix buffers: %lu",iscapture ? "capture": "play",pAData->cMixBuffers);
|
||||
|
||||
/* Fill all device buffers with data */
|
||||
for (ulIdx = 0; ulIdx < stMCIBuffer.ulNumBuffers; ulIdx++) {
|
||||
pAData->aMixBuffers[ulIdx].ulFlags = 0;
|
||||
pAData->aMixBuffers[ulIdx].ulBufferLength = stMCIBuffer.ulBufferSize;
|
||||
pAData->aMixBuffers[ulIdx].ulUserParm = (ULONG)_this;
|
||||
|
||||
SDL_memset(((PMCI_MIX_BUFFER)stMCIBuffer.pBufList)[ulIdx].pBuffer,
|
||||
_this->spec.silence, stMCIBuffer.ulBufferSize);
|
||||
}
|
||||
pAData->pFillBuffer = pAData->aMixBuffers;
|
||||
pAData->pDrainBuffer = pAData->aMixBuffers;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static SDL_bool OS2_Init(SDL_AudioDriverImpl * impl)
|
||||
{
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = OS2_DetectDevices;
|
||||
impl->OpenDevice = OS2_OpenDevice;
|
||||
impl->PlayDevice = OS2_PlayDevice;
|
||||
impl->WaitDevice = OS2_WaitDevice;
|
||||
impl->GetDeviceBuf = OS2_GetDeviceBuf;
|
||||
impl->CloseDevice = OS2_CloseDevice;
|
||||
impl->CaptureFromDevice = OS2_CaptureFromDevice ;
|
||||
impl->FlushCapture = OS2_FlushCapture;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
|
||||
AudioBootStrap OS2AUDIO_bootstrap = {
|
||||
"DART", "OS/2 DART", OS2_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_OS2 */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
55
third_party/SDL/src/audio/os2/SDL_os2audio.h
vendored
Normal file
55
third_party/SDL/src/audio/os2/SDL_os2audio.h
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_os2mm_h_
|
||||
#define SDL_os2mm_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#define INCL_OS2MM
|
||||
#define INCL_PM
|
||||
#define INCL_DOS
|
||||
#define INCL_DOSERRORS
|
||||
#include <os2.h>
|
||||
#include <os2me.h>
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *_this
|
||||
|
||||
#define NUM_BUFFERS 3
|
||||
|
||||
typedef struct SDL_PrivateAudioData
|
||||
{
|
||||
USHORT usDeviceId;
|
||||
BYTE _pad[2];
|
||||
MCI_MIXSETUP_PARMS stMCIMixSetup;
|
||||
HEV hevBuf;
|
||||
PMCI_MIX_BUFFER pFillBuffer;
|
||||
PMCI_MIX_BUFFER pDrainBuffer;
|
||||
ULONG ulState;
|
||||
ULONG cMixBuffers;
|
||||
MCI_MIX_BUFFER aMixBuffers[NUM_BUFFERS];
|
||||
} SDL_PrivateAudioData;
|
||||
|
||||
#endif /* SDL_os2mm_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
485
third_party/SDL/src/audio/paudio/SDL_paudio.c
vendored
Normal file
485
third_party/SDL/src/audio/paudio/SDL_paudio.c
vendored
Normal file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_PAUDIO
|
||||
|
||||
/* Allow access to a raw mixing buffer */
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "SDL_stdinc.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../../core/unix/SDL_poll.h"
|
||||
#include "SDL_paudio.h"
|
||||
|
||||
/* #define DEBUG_AUDIO */
|
||||
|
||||
/* A conflict within AIX 4.3.3 <sys/> headers and probably others as well.
|
||||
* I guess nobody ever uses audio... Shame over AIX header files. */
|
||||
#include <sys/machine.h>
|
||||
#undef BIG_ENDIAN
|
||||
#include <sys/audio.h>
|
||||
|
||||
/* Open the audio device for playback, and don't block if busy */
|
||||
/* #define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) */
|
||||
#define OPEN_FLAGS O_WRONLY
|
||||
|
||||
/* Get the name of the audio device we use for output */
|
||||
|
||||
#ifndef _PATH_DEV_DSP
|
||||
#define _PATH_DEV_DSP "/dev/%caud%c/%c"
|
||||
#endif
|
||||
|
||||
static char devsettings[][3] = {
|
||||
{'p', '0', '1'}, {'p', '0', '2'}, {'p', '0', '3'}, {'p', '0', '4'},
|
||||
{'p', '1', '1'}, {'p', '1', '2'}, {'p', '1', '3'}, {'p', '1', '4'},
|
||||
{'p', '2', '1'}, {'p', '2', '2'}, {'p', '2', '3'}, {'p', '2', '4'},
|
||||
{'p', '3', '1'}, {'p', '3', '2'}, {'p', '3', '3'}, {'p', '3', '4'},
|
||||
{'b', '0', '1'}, {'b', '0', '2'}, {'b', '0', '3'}, {'b', '0', '4'},
|
||||
{'b', '1', '1'}, {'b', '1', '2'}, {'b', '1', '3'}, {'b', '1', '4'},
|
||||
{'b', '2', '1'}, {'b', '2', '2'}, {'b', '2', '3'}, {'b', '2', '4'},
|
||||
{'b', '3', '1'}, {'b', '3', '2'}, {'b', '3', '3'}, {'b', '3', '4'},
|
||||
{'\0', '\0', '\0'}
|
||||
};
|
||||
|
||||
static int OpenUserDefinedDevice(char *path, int maxlen, int flags)
|
||||
{
|
||||
const char *audiodev;
|
||||
int fd;
|
||||
|
||||
/* Figure out what our audio device is */
|
||||
if ((audiodev = SDL_getenv("SDL_PATH_DSP")) == NULL) {
|
||||
audiodev = SDL_getenv("AUDIODEV");
|
||||
}
|
||||
if (audiodev == NULL) {
|
||||
return -1;
|
||||
}
|
||||
fd = open(audiodev, flags, 0);
|
||||
if (path != NULL) {
|
||||
SDL_strlcpy(path, audiodev, maxlen);
|
||||
path[maxlen - 1] = '\0';
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int OpenAudioPath(char *path, int maxlen, int flags, int classic)
|
||||
{
|
||||
struct stat sb;
|
||||
int cycle = 0;
|
||||
int fd = OpenUserDefinedDevice(path, maxlen, flags);
|
||||
|
||||
if (fd != -1) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* !!! FIXME: do we really need a table here? */
|
||||
while (devsettings[cycle][0] != '\0') {
|
||||
char audiopath[1024];
|
||||
SDL_snprintf(audiopath, SDL_arraysize(audiopath),
|
||||
_PATH_DEV_DSP,
|
||||
devsettings[cycle][0],
|
||||
devsettings[cycle][1], devsettings[cycle][2]);
|
||||
|
||||
if (stat(audiopath, &sb) == 0) {
|
||||
fd = open(audiopath, flags, 0);
|
||||
if (fd >= 0) {
|
||||
if (path != NULL) {
|
||||
SDL_strlcpy(path, audiopath, maxlen);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void PAUDIO_WaitDevice(_THIS)
|
||||
{
|
||||
fd_set fdset;
|
||||
|
||||
/* See if we need to use timed audio synchronization */
|
||||
if (this->hidden->frame_ticks) {
|
||||
/* Use timer for general audio synchronization */
|
||||
Sint32 ticks;
|
||||
|
||||
ticks = ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
|
||||
if (ticks > 0) {
|
||||
SDL_Delay(ticks);
|
||||
}
|
||||
} else {
|
||||
int timeoutMS;
|
||||
audio_buffer paud_bufinfo;
|
||||
|
||||
if (ioctl(this->hidden->audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0) {
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Couldn't get audio buffer information\n");
|
||||
#endif
|
||||
timeoutMS = 10 * 1000;
|
||||
} else {
|
||||
timeoutMS = paud_bufinfo.write_buf_time;
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Waiting for write_buf_time=%d ms\n", timeoutMS);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Waiting for audio to get ready\n");
|
||||
#endif
|
||||
if (SDL_IOReady(this->hidden->audio_fd, SDL_IOR_WRITE, timeoutMS) <= 0) {
|
||||
/*
|
||||
* In general we should never print to the screen,
|
||||
* but in this case we have no other way of letting
|
||||
* the user know what happened.
|
||||
*/
|
||||
fprintf(stderr, "SDL: %s - Audio timeout - buggy audio driver? (disabled)\n", strerror(errno));
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
/* Don't try to close - may hang */
|
||||
this->hidden->audio_fd = -1;
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Done disabling audio\n");
|
||||
#endif
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Ready!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void PAUDIO_PlayDevice(_THIS)
|
||||
{
|
||||
int written = 0;
|
||||
const Uint8 *mixbuf = this->hidden->mixbuf;
|
||||
const size_t mixlen = this->hidden->mixlen;
|
||||
|
||||
/* Write the audio data, checking for EAGAIN on broken audio drivers */
|
||||
do {
|
||||
written = write(this->hidden->audio_fd, mixbuf, mixlen);
|
||||
if ((written < 0) && ((errno == 0) || (errno == EAGAIN))) {
|
||||
SDL_Delay(1); /* Let a little CPU time go by */
|
||||
}
|
||||
} while ((written < 0) &&
|
||||
((errno == 0) || (errno == EAGAIN) || (errno == EINTR)));
|
||||
|
||||
/* If timer synchronization is enabled, set the next write frame */
|
||||
if (this->hidden->frame_ticks) {
|
||||
this->hidden->next_frame += this->hidden->frame_ticks;
|
||||
}
|
||||
|
||||
/* If we couldn't write, assume fatal error for now */
|
||||
if (written < 0) {
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||
#endif
|
||||
}
|
||||
|
||||
static Uint8 *PAUDIO_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return this->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static void PAUDIO_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->audio_fd >= 0) {
|
||||
close(this->hidden->audio_fd);
|
||||
}
|
||||
SDL_free(this->hidden->mixbuf);
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
static int PAUDIO_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
const char *workaround = SDL_getenv("SDL_DSP_NOSELECT");
|
||||
char audiodev[1024];
|
||||
const char *err = NULL;
|
||||
int flags;
|
||||
int bytes_per_sample;
|
||||
SDL_AudioFormat test_format;
|
||||
audio_init paud_init;
|
||||
audio_buffer paud_bufinfo;
|
||||
audio_control paud_control;
|
||||
audio_change paud_change;
|
||||
int fd = -1;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*this->hidden));
|
||||
if (this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(this->hidden);
|
||||
|
||||
/* Open the audio device */
|
||||
fd = OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
|
||||
this->hidden->audio_fd = fd;
|
||||
if (fd < 0) {
|
||||
return SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't set the buffer size - just ask the device for the maximum
|
||||
* that we can have.
|
||||
*/
|
||||
if (ioctl(fd, AUDIO_BUFFER, &paud_bufinfo) < 0) {
|
||||
return SDL_SetError("Couldn't get audio buffer information");
|
||||
}
|
||||
|
||||
if (this->spec.channels > 1)
|
||||
this->spec.channels = 2;
|
||||
else
|
||||
this->spec.channels = 1;
|
||||
|
||||
/*
|
||||
* Fields in the audio_init structure:
|
||||
*
|
||||
* Ignored by us:
|
||||
*
|
||||
* paud.loadpath[LOAD_PATH]; * DSP code to load, MWave chip only?
|
||||
* paud.slot_number; * slot number of the adapter
|
||||
* paud.device_id; * adapter identification number
|
||||
*
|
||||
* Input:
|
||||
*
|
||||
* paud.srate; * the sampling rate in Hz
|
||||
* paud.bits_per_sample; * 8, 16, 32, ...
|
||||
* paud.bsize; * block size for this rate
|
||||
* paud.mode; * ADPCM, PCM, MU_LAW, A_LAW, SOURCE_MIX
|
||||
* paud.channels; * 1=mono, 2=stereo
|
||||
* paud.flags; * FIXED - fixed length data
|
||||
* * LEFT_ALIGNED, RIGHT_ALIGNED (var len only)
|
||||
* * TWOS_COMPLEMENT - 2's complement data
|
||||
* * SIGNED - signed? comment seems wrong in sys/audio.h
|
||||
* * BIG_ENDIAN
|
||||
* paud.operation; * PLAY, RECORD
|
||||
*
|
||||
* Output:
|
||||
*
|
||||
* paud.flags; * PITCH - pitch is supported
|
||||
* * INPUT - input is supported
|
||||
* * OUTPUT - output is supported
|
||||
* * MONITOR - monitor is supported
|
||||
* * VOLUME - volume is supported
|
||||
* * VOLUME_DELAY - volume delay is supported
|
||||
* * BALANCE - balance is supported
|
||||
* * BALANCE_DELAY - balance delay is supported
|
||||
* * TREBLE - treble control is supported
|
||||
* * BASS - bass control is supported
|
||||
* * BESTFIT_PROVIDED - best fit returned
|
||||
* * LOAD_CODE - DSP load needed
|
||||
* paud.rc; * NO_PLAY - DSP code can't do play requests
|
||||
* * NO_RECORD - DSP code can't do record requests
|
||||
* * INVALID_REQUEST - request was invalid
|
||||
* * CONFLICT - conflict with open's flags
|
||||
* * OVERLOADED - out of DSP MIPS or memory
|
||||
* paud.position_resolution; * smallest increment for position
|
||||
*/
|
||||
|
||||
paud_init.srate = this->spec.freq;
|
||||
paud_init.mode = PCM;
|
||||
paud_init.operation = PLAY;
|
||||
paud_init.channels = this->spec.channels;
|
||||
|
||||
/* Try for a closest match on audio format */
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
|
||||
#endif
|
||||
switch (test_format) {
|
||||
case AUDIO_U8:
|
||||
flags = TWOS_COMPLEMENT | FIXED;
|
||||
break;
|
||||
case AUDIO_S8:
|
||||
flags = SIGNED | TWOS_COMPLEMENT | FIXED;
|
||||
break;
|
||||
case AUDIO_S16LSB:
|
||||
flags = SIGNED | TWOS_COMPLEMENT | FIXED;
|
||||
break;
|
||||
case AUDIO_S16MSB:
|
||||
flags = BIG_ENDIAN | SIGNED | TWOS_COMPLEMENT | FIXED;
|
||||
break;
|
||||
case AUDIO_U16LSB:
|
||||
flags = TWOS_COMPLEMENT | FIXED;
|
||||
break;
|
||||
case AUDIO_U16MSB:
|
||||
flags = BIG_ENDIAN | TWOS_COMPLEMENT | FIXED;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!test_format) {
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Couldn't find any hardware audio formats\n");
|
||||
#endif
|
||||
return SDL_SetError("%s: Unsupported audio format", "paud");
|
||||
}
|
||||
this->spec.format = test_format;
|
||||
paud_init.bits_per_sample = SDL_AUDIO_BITSIZE(test_format);
|
||||
bytes_per_sample = SDL_AUDIO_BITSIZE(test_format) / 8;
|
||||
paud_init.flags = flags;
|
||||
|
||||
/*
|
||||
* We know the buffer size and the max number of subsequent writes
|
||||
* that can be pending. If more than one can pend, allow the application
|
||||
* to do something like double buffering between our write buffer and
|
||||
* the device's own buffer that we are filling with write() anyway.
|
||||
*
|
||||
* We calculate this->spec.samples like this because
|
||||
* SDL_CalculateAudioSpec() will give put paud_bufinfo.write_buf_cap
|
||||
* (or paud_bufinfo.write_buf_cap/2) into this->spec.size in return.
|
||||
*/
|
||||
if (paud_bufinfo.request_buf_cap == 1) {
|
||||
this->spec.samples = paud_bufinfo.write_buf_cap
|
||||
/ bytes_per_sample / this->spec.channels;
|
||||
} else {
|
||||
this->spec.samples = paud_bufinfo.write_buf_cap
|
||||
/ bytes_per_sample / this->spec.channels / 2;
|
||||
}
|
||||
paud_init.bsize = bytes_per_sample * this->spec.channels;
|
||||
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
/*
|
||||
* The AIX paud device init can't modify the values of the audio_init
|
||||
* structure that we pass to it. So we don't need any recalculation
|
||||
* of this stuff and no reinit call as in linux dsp code.
|
||||
*
|
||||
* /dev/paud supports all of the encoding formats, so we don't need
|
||||
* to do anything like reopening the device, either.
|
||||
*/
|
||||
if (ioctl(fd, AUDIO_INIT, &paud_init) < 0) {
|
||||
switch (paud_init.rc) {
|
||||
case 1:
|
||||
err = "DSP can't do play requests";
|
||||
break;
|
||||
case 2:
|
||||
err = "DSP can't do record requests";
|
||||
break;
|
||||
case 4:
|
||||
err = "request was invalid";
|
||||
break;
|
||||
case 5:
|
||||
err = "conflict with open's flags";
|
||||
break;
|
||||
case 6:
|
||||
err = "out of DSP MIPS or memory";
|
||||
break;
|
||||
default:
|
||||
err = "not documented in sys/audio.h";
|
||||
break;
|
||||
}
|
||||
return SDL_SetError("paud: Couldn't set audio format (%s)", err);
|
||||
}
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
this->hidden->mixlen = this->spec.size;
|
||||
this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
|
||||
if (this->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
|
||||
|
||||
/*
|
||||
* Set some paramters: full volume, first speaker that we can find.
|
||||
* Ignore the other settings for now.
|
||||
*/
|
||||
paud_change.input = AUDIO_IGNORE; /* the new input source */
|
||||
paud_change.output = OUTPUT_1; /* EXTERNAL_SPEAKER,INTERNAL_SPEAKER,OUTPUT_1 */
|
||||
paud_change.monitor = AUDIO_IGNORE; /* the new monitor state */
|
||||
paud_change.volume = 0x7fffffff; /* volume level [0-0x7fffffff] */
|
||||
paud_change.volume_delay = AUDIO_IGNORE; /* the new volume delay */
|
||||
paud_change.balance = 0x3fffffff; /* the new balance */
|
||||
paud_change.balance_delay = AUDIO_IGNORE; /* the new balance delay */
|
||||
paud_change.treble = AUDIO_IGNORE; /* the new treble state */
|
||||
paud_change.bass = AUDIO_IGNORE; /* the new bass state */
|
||||
paud_change.pitch = AUDIO_IGNORE; /* the new pitch state */
|
||||
|
||||
paud_control.ioctl_request = AUDIO_CHANGE;
|
||||
paud_control.request_info = (char *) &paud_change;
|
||||
if (ioctl(fd, AUDIO_CONTROL, &paud_control) < 0) {
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Can't change audio display settings\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Tell the device to expect data. Actual start will wait for
|
||||
* the first write() call.
|
||||
*/
|
||||
paud_control.ioctl_request = AUDIO_START;
|
||||
paud_control.position = 0;
|
||||
if (ioctl(fd, AUDIO_CONTROL, &paud_control) < 0) {
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Can't start audio play\n");
|
||||
#endif
|
||||
return SDL_SetError("Can't start audio play");
|
||||
}
|
||||
|
||||
/* Check to see if we need to use SDL_IOReady() workaround */
|
||||
if (workaround != NULL) {
|
||||
this->hidden->frame_ticks = (float) (this->spec.samples * 1000) /
|
||||
this->spec.freq;
|
||||
this->hidden->next_frame = SDL_GetTicks() + this->hidden->frame_ticks;
|
||||
}
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SDL_bool PAUDIO_Init(SDL_AudioDriverImpl * impl)
|
||||
{
|
||||
/* !!! FIXME: not right for device enum? */
|
||||
int fd = OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
|
||||
if (fd < 0) {
|
||||
SDL_SetError("PAUDIO: Couldn't open audio device");
|
||||
return SDL_FALSE;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = PAUDIO_OpenDevice;
|
||||
impl->PlayDevice = PAUDIO_PlayDevice;
|
||||
impl->WaitDevice = PAUDIO_WaitDevice;
|
||||
impl->GetDeviceBuf = PAUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = PAUDIO_CloseDevice;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE; /* !!! FIXME: add device enum! */
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap PAUDIO_bootstrap = {
|
||||
"paud", "AIX Paudio", PAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_PAUDIO */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
48
third_party/SDL/src/audio/paudio/SDL_paudio.h
vendored
Normal file
48
third_party/SDL/src/audio/paudio/SDL_paudio.h
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_paudio_h_
|
||||
#define SDL_paudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The file descriptor for the audio device */
|
||||
int audio_fd;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
|
||||
/* Support for audio timing using a timer, in addition to SDL_IOReady() */
|
||||
float frame_ticks;
|
||||
float next_frame;
|
||||
};
|
||||
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
|
||||
|
||||
#endif /* SDL_paudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
1387
third_party/SDL/src/audio/pipewire/SDL_pipewire.c
vendored
Normal file
1387
third_party/SDL/src/audio/pipewire/SDL_pipewire.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
47
third_party/SDL/src/audio/pipewire/SDL_pipewire.h
vendored
Normal file
47
third_party/SDL/src/audio/pipewire/SDL_pipewire.h
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_pipewire_h_
|
||||
#define SDL_pipewire_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
#endif /* SDL_pipewire_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
177
third_party/SDL/src/audio/ps2/SDL_ps2audio.c
vendored
Normal file
177
third_party/SDL/src/audio/ps2/SDL_ps2audio.c
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
/* Output audio to nowhere... */
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_ps2audio.h"
|
||||
|
||||
#include <kernel.h>
|
||||
#include <malloc.h>
|
||||
#include <audsrv.h>
|
||||
#include <ps2_audio_driver.h>
|
||||
|
||||
/* The tag name used by PS2 audio */
|
||||
#define PS2AUDIO_DRIVER_NAME "ps2"
|
||||
|
||||
static int PS2AUDIO_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
int i, mixlen;
|
||||
struct audsrv_fmt_t format;
|
||||
|
||||
this->hidden = (struct SDL_PrivateAudioData *)
|
||||
SDL_malloc(sizeof(*this->hidden));
|
||||
if (this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(this->hidden);
|
||||
|
||||
/* These are the native supported audio PS2 configs */
|
||||
switch (this->spec.freq) {
|
||||
case 11025:
|
||||
case 12000:
|
||||
case 22050:
|
||||
case 24000:
|
||||
case 32000:
|
||||
case 44100:
|
||||
case 48000:
|
||||
this->spec.freq = this->spec.freq;
|
||||
break;
|
||||
default:
|
||||
this->spec.freq = 48000;
|
||||
break;
|
||||
}
|
||||
|
||||
this->spec.samples = 512;
|
||||
this->spec.channels = this->spec.channels == 1 ? 1 : 2;
|
||||
this->spec.format = this->spec.format == AUDIO_S8 ? AUDIO_S8 : AUDIO_S16;
|
||||
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
format.bits = this->spec.format == AUDIO_S8 ? 8 : 16;
|
||||
format.freq = this->spec.freq;
|
||||
format.channels = this->spec.channels;
|
||||
|
||||
this->hidden->channel = audsrv_set_format(&format);
|
||||
audsrv_set_volume(MAX_VOLUME);
|
||||
|
||||
if (this->hidden->channel < 0) {
|
||||
free(this->hidden->rawbuf);
|
||||
this->hidden->rawbuf = NULL;
|
||||
return SDL_SetError("Couldn't reserve hardware channel");
|
||||
}
|
||||
|
||||
/* Update the fragment size as size in bytes. */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
/* 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 *)memalign(64, mixlen);
|
||||
if (this->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];
|
||||
}
|
||||
|
||||
this->hidden->next_buffer = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void PS2AUDIO_PlayDevice(_THIS)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void PS2AUDIO_WaitDevice(_THIS)
|
||||
{
|
||||
audsrv_wait_audio(this->spec.size);
|
||||
}
|
||||
|
||||
static Uint8 *PS2AUDIO_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return this->hidden->mixbufs[this->hidden->next_buffer];
|
||||
}
|
||||
|
||||
static void PS2AUDIO_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->channel >= 0) {
|
||||
audsrv_stop_audio();
|
||||
this->hidden->channel = -1;
|
||||
}
|
||||
|
||||
if (this->hidden->rawbuf != NULL) {
|
||||
free(this->hidden->rawbuf);
|
||||
this->hidden->rawbuf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void PS2AUDIO_ThreadInit(_THIS)
|
||||
{
|
||||
/* Increase the priority of this audio thread by 1 to put it
|
||||
ahead of other SDL threads. */
|
||||
int32_t thid;
|
||||
ee_thread_status_t status;
|
||||
thid = GetThreadId();
|
||||
if (ReferThreadStatus(GetThreadId(), &status) == 0) {
|
||||
ChangeThreadPriority(thid, status.current_priority - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void PS2AUDIO_Deinitialize(void)
|
||||
{
|
||||
deinit_audio_driver();
|
||||
}
|
||||
|
||||
static SDL_bool PS2AUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (init_audio_driver() < 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = PS2AUDIO_OpenDevice;
|
||||
impl->PlayDevice = PS2AUDIO_PlayDevice;
|
||||
impl->WaitDevice = PS2AUDIO_WaitDevice;
|
||||
impl->GetDeviceBuf = PS2AUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = PS2AUDIO_CloseDevice;
|
||||
impl->ThreadInit = PS2AUDIO_ThreadInit;
|
||||
impl->Deinitialize = PS2AUDIO_Deinitialize;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap PS2AUDIO_bootstrap = {
|
||||
"ps2", "PS2 audio driver", PS2AUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
46
third_party/SDL/src/audio/ps2/SDL_ps2audio.h
vendored
Normal file
46
third_party/SDL/src/audio/ps2/SDL_ps2audio.h
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_ps2audio_h_
|
||||
#define SDL_ps2audio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
#define NUM_BUFFERS 2
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The hardware output channel. */
|
||||
int channel;
|
||||
/* The raw allocated mixing buffer. */
|
||||
Uint8 *rawbuf;
|
||||
/* Individual mixing buffers. */
|
||||
Uint8 *mixbufs[NUM_BUFFERS];
|
||||
/* Index of the next available mixing buffer. */
|
||||
int next_buffer;
|
||||
};
|
||||
|
||||
#endif /* SDL_ps2audio_h_ */
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
203
third_party/SDL/src/audio/psp/SDL_pspaudio.c
vendored
Normal file
203
third_party/SDL/src/audio/psp/SDL_pspaudio.c
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_PSP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h> /* memalign() */
|
||||
|
||||
#include "SDL_audio.h"
|
||||
#include "SDL_error.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_audiodev_c.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_pspaudio.h"
|
||||
|
||||
#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(_THIS, const char *devname)
|
||||
{
|
||||
int format, mixlen, i;
|
||||
|
||||
this->hidden = (struct SDL_PrivateAudioData *)
|
||||
SDL_malloc(sizeof(*this->hidden));
|
||||
if (this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(this->hidden);
|
||||
|
||||
/* device only natively supports S16LSB */
|
||||
this->spec.format = AUDIO_S16LSB;
|
||||
|
||||
/* PSP has some limitations with the Audio. It fully supports 44.1KHz (Mono & Stereo),
|
||||
however with frequencies differents 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);
|
||||
} else {
|
||||
/* 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11050, 8000 */
|
||||
switch (this->spec.freq) {
|
||||
case 8000:
|
||||
case 11025:
|
||||
case 12000:
|
||||
case 16000:
|
||||
case 22050:
|
||||
case 24000:
|
||||
case 32000:
|
||||
case 44100:
|
||||
case 48000:
|
||||
this->spec.freq = this->spec.freq;
|
||||
break;
|
||||
default:
|
||||
this->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);
|
||||
}
|
||||
|
||||
if (this->hidden->channel < 0) {
|
||||
free(this->hidden->rawbuf);
|
||||
this->hidden->rawbuf = NULL;
|
||||
return SDL_SetError("Couldn't reserve hardware channel");
|
||||
}
|
||||
|
||||
/* Update the fragment size as size in bytes. */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
/* 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 *)memalign(64, mixlen);
|
||||
if (this->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];
|
||||
}
|
||||
|
||||
this->hidden->next_buffer = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void PSPAUDIO_PlayDevice(_THIS)
|
||||
{
|
||||
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);
|
||||
} else {
|
||||
sceAudioOutputPannedBlocking(this->hidden->channel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, mixbuf);
|
||||
}
|
||||
|
||||
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(_THIS)
|
||||
{
|
||||
/* Because we block when sending audio, there's no need for this function to do anything. */
|
||||
}
|
||||
|
||||
static Uint8 *PSPAUDIO_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return this->hidden->mixbufs[this->hidden->next_buffer];
|
||||
}
|
||||
|
||||
static void PSPAUDIO_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->channel >= 0) {
|
||||
if (!isBasicAudioConfig(&this->spec)) {
|
||||
sceAudioSRCChRelease();
|
||||
} else {
|
||||
sceAudioChRelease(this->hidden->channel);
|
||||
}
|
||||
this->hidden->channel = -1;
|
||||
}
|
||||
|
||||
if (this->hidden->rawbuf != NULL) {
|
||||
free(this->hidden->rawbuf);
|
||||
this->hidden->rawbuf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void PSPAUDIO_ThreadInit(_THIS)
|
||||
{
|
||||
/* Increase the priority of this audio thread by 1 to put it
|
||||
ahead of other SDL threads. */
|
||||
SceUID thid;
|
||||
SceKernelThreadInfo status;
|
||||
thid = sceKernelGetThreadId();
|
||||
status.size = sizeof(SceKernelThreadInfo);
|
||||
if (sceKernelReferThreadStatus(thid, &status) == 0) {
|
||||
sceKernelChangeThreadPriority(thid, status.currentPriority - 1);
|
||||
}
|
||||
}
|
||||
|
||||
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. */
|
||||
}
|
||||
|
||||
AudioBootStrap PSPAUDIO_bootstrap = {
|
||||
"psp", "PSP audio driver", PSPAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_PSP */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
46
third_party/SDL/src/audio/psp/SDL_pspaudio.h
vendored
Normal file
46
third_party/SDL/src/audio/psp/SDL_pspaudio.h
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_pspaudio_h_
|
||||
#define SDL_pspaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
#define NUM_BUFFERS 2
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The hardware output channel. */
|
||||
int channel;
|
||||
/* The raw allocated mixing buffer. */
|
||||
Uint8 *rawbuf;
|
||||
/* Individual mixing buffers. */
|
||||
Uint8 *mixbufs[NUM_BUFFERS];
|
||||
/* Index of the next available mixing buffer. */
|
||||
int next_buffer;
|
||||
};
|
||||
|
||||
#endif /* SDL_pspaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
918
third_party/SDL/src/audio/pulseaudio/SDL_pulseaudio.c
vendored
Normal file
918
third_party/SDL/src/audio/pulseaudio/SDL_pulseaudio.c
vendored
Normal file
@@ -0,0 +1,918 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
The PulseAudio target for SDL 1.3 is based on the 1.3 arts target, with
|
||||
the appropriate parts replaced with the 1.2 PulseAudio target code. This
|
||||
was the cleanest way to move it to 1.3. The 1.2 target was written by
|
||||
Stéphan Kochen: stephan .a.t. kochen.nl
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
#include "SDL_hints.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_PULSEAUDIO
|
||||
|
||||
/* Allow access to a raw mixing buffer */
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
#include <signal.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_pulseaudio.h"
|
||||
#include "SDL_loadso.h"
|
||||
#include "../../thread/SDL_systhread.h"
|
||||
|
||||
/* should we include monitors in the device list? Set at SDL_Init time */
|
||||
static SDL_bool include_monitors = SDL_FALSE;
|
||||
|
||||
#if (PA_API_VERSION < 12)
|
||||
/** Return non-zero if the passed state is one of the connected states */
|
||||
static SDL_INLINE int PA_CONTEXT_IS_GOOD(pa_context_state_t x)
|
||||
{
|
||||
return x == PA_CONTEXT_CONNECTING || x == PA_CONTEXT_AUTHORIZING || x == PA_CONTEXT_SETTING_NAME || x == PA_CONTEXT_READY;
|
||||
}
|
||||
/** Return non-zero if the passed state is one of the connected states */
|
||||
static SDL_INLINE int PA_STREAM_IS_GOOD(pa_stream_state_t x)
|
||||
{
|
||||
return x == PA_STREAM_CREATING || x == PA_STREAM_READY;
|
||||
}
|
||||
#endif /* pulseaudio <= 0.9.10 */
|
||||
|
||||
static const char *(*PULSEAUDIO_pa_get_library_version)(void);
|
||||
static pa_channel_map *(*PULSEAUDIO_pa_channel_map_init_auto)(
|
||||
pa_channel_map *, unsigned, pa_channel_map_def_t);
|
||||
static const char *(*PULSEAUDIO_pa_strerror)(int);
|
||||
static pa_mainloop *(*PULSEAUDIO_pa_mainloop_new)(void);
|
||||
static pa_mainloop_api *(*PULSEAUDIO_pa_mainloop_get_api)(pa_mainloop *);
|
||||
static int (*PULSEAUDIO_pa_mainloop_iterate)(pa_mainloop *, int, int *);
|
||||
static int (*PULSEAUDIO_pa_mainloop_run)(pa_mainloop *, int *);
|
||||
static void (*PULSEAUDIO_pa_mainloop_quit)(pa_mainloop *, int);
|
||||
static void (*PULSEAUDIO_pa_mainloop_free)(pa_mainloop *);
|
||||
|
||||
static pa_operation_state_t (*PULSEAUDIO_pa_operation_get_state)(
|
||||
const pa_operation *);
|
||||
static void (*PULSEAUDIO_pa_operation_cancel)(pa_operation *);
|
||||
static void (*PULSEAUDIO_pa_operation_unref)(pa_operation *);
|
||||
|
||||
static pa_context *(*PULSEAUDIO_pa_context_new)(pa_mainloop_api *,
|
||||
const char *);
|
||||
static int (*PULSEAUDIO_pa_context_connect)(pa_context *, const char *,
|
||||
pa_context_flags_t, const pa_spawn_api *);
|
||||
static pa_operation *(*PULSEAUDIO_pa_context_get_sink_info_list)(pa_context *, pa_sink_info_cb_t, void *);
|
||||
static pa_operation *(*PULSEAUDIO_pa_context_get_source_info_list)(pa_context *, pa_source_info_cb_t, void *);
|
||||
static pa_operation *(*PULSEAUDIO_pa_context_get_sink_info_by_index)(pa_context *, uint32_t, pa_sink_info_cb_t, void *);
|
||||
static pa_operation *(*PULSEAUDIO_pa_context_get_source_info_by_index)(pa_context *, uint32_t, pa_source_info_cb_t, void *);
|
||||
static pa_context_state_t (*PULSEAUDIO_pa_context_get_state)(const pa_context *);
|
||||
static pa_operation *(*PULSEAUDIO_pa_context_subscribe)(pa_context *, pa_subscription_mask_t, pa_context_success_cb_t, void *);
|
||||
static void (*PULSEAUDIO_pa_context_set_subscribe_callback)(pa_context *, pa_context_subscribe_cb_t, void *);
|
||||
static void (*PULSEAUDIO_pa_context_disconnect)(pa_context *);
|
||||
static void (*PULSEAUDIO_pa_context_unref)(pa_context *);
|
||||
|
||||
static pa_stream *(*PULSEAUDIO_pa_stream_new)(pa_context *, const char *,
|
||||
const pa_sample_spec *, const pa_channel_map *);
|
||||
static int (*PULSEAUDIO_pa_stream_connect_playback)(pa_stream *, const char *,
|
||||
const pa_buffer_attr *, pa_stream_flags_t, const pa_cvolume *, pa_stream *);
|
||||
static int (*PULSEAUDIO_pa_stream_connect_record)(pa_stream *, const char *,
|
||||
const pa_buffer_attr *, pa_stream_flags_t);
|
||||
static pa_stream_state_t (*PULSEAUDIO_pa_stream_get_state)(const pa_stream *);
|
||||
static size_t (*PULSEAUDIO_pa_stream_writable_size)(const pa_stream *);
|
||||
static size_t (*PULSEAUDIO_pa_stream_readable_size)(const pa_stream *);
|
||||
static int (*PULSEAUDIO_pa_stream_write)(pa_stream *, const void *, size_t,
|
||||
pa_free_cb_t, int64_t, pa_seek_mode_t);
|
||||
static pa_operation *(*PULSEAUDIO_pa_stream_drain)(pa_stream *,
|
||||
pa_stream_success_cb_t, void *);
|
||||
static int (*PULSEAUDIO_pa_stream_peek)(pa_stream *, const void **, size_t *);
|
||||
static int (*PULSEAUDIO_pa_stream_drop)(pa_stream *);
|
||||
static pa_operation *(*PULSEAUDIO_pa_stream_flush)(pa_stream *,
|
||||
pa_stream_success_cb_t, void *);
|
||||
static int (*PULSEAUDIO_pa_stream_disconnect)(pa_stream *);
|
||||
static void (*PULSEAUDIO_pa_stream_unref)(pa_stream *);
|
||||
static void (*PULSEAUDIO_pa_stream_set_write_callback)(pa_stream *, pa_stream_request_cb_t, void *);
|
||||
static pa_operation *(*PULSEAUDIO_pa_context_get_server_info)(pa_context *, pa_server_info_cb_t, void *);
|
||||
|
||||
static int load_pulseaudio_syms(void);
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC
|
||||
|
||||
static const char *pulseaudio_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC;
|
||||
static void *pulseaudio_handle = NULL;
|
||||
|
||||
static int load_pulseaudio_sym(const char *fn, void **addr)
|
||||
{
|
||||
*addr = SDL_LoadFunction(pulseaudio_handle, fn);
|
||||
if (*addr == NULL) {
|
||||
/* Don't call SDL_SetError(): SDL_LoadFunction already did. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
|
||||
#define SDL_PULSEAUDIO_SYM(x) \
|
||||
if (!load_pulseaudio_sym(#x, (void **)(char *)&PULSEAUDIO_##x)) \
|
||||
return -1
|
||||
|
||||
static void UnloadPulseAudioLibrary(void)
|
||||
{
|
||||
if (pulseaudio_handle != NULL) {
|
||||
SDL_UnloadObject(pulseaudio_handle);
|
||||
pulseaudio_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadPulseAudioLibrary(void)
|
||||
{
|
||||
int retval = 0;
|
||||
if (pulseaudio_handle == NULL) {
|
||||
pulseaudio_handle = SDL_LoadObject(pulseaudio_library);
|
||||
if (pulseaudio_handle == NULL) {
|
||||
retval = -1;
|
||||
/* Don't call SDL_SetError(): SDL_LoadObject already did. */
|
||||
} else {
|
||||
retval = load_pulseaudio_syms();
|
||||
if (retval < 0) {
|
||||
UnloadPulseAudioLibrary();
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define SDL_PULSEAUDIO_SYM(x) PULSEAUDIO_##x = x
|
||||
|
||||
static void UnloadPulseAudioLibrary(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int LoadPulseAudioLibrary(void)
|
||||
{
|
||||
load_pulseaudio_syms();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC */
|
||||
|
||||
static int load_pulseaudio_syms(void)
|
||||
{
|
||||
SDL_PULSEAUDIO_SYM(pa_get_library_version);
|
||||
SDL_PULSEAUDIO_SYM(pa_mainloop_new);
|
||||
SDL_PULSEAUDIO_SYM(pa_mainloop_get_api);
|
||||
SDL_PULSEAUDIO_SYM(pa_mainloop_iterate);
|
||||
SDL_PULSEAUDIO_SYM(pa_mainloop_run);
|
||||
SDL_PULSEAUDIO_SYM(pa_mainloop_quit);
|
||||
SDL_PULSEAUDIO_SYM(pa_mainloop_free);
|
||||
SDL_PULSEAUDIO_SYM(pa_operation_get_state);
|
||||
SDL_PULSEAUDIO_SYM(pa_operation_cancel);
|
||||
SDL_PULSEAUDIO_SYM(pa_operation_unref);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_new);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_connect);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_list);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_get_source_info_list);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_by_index);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_get_source_info_by_index);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_get_state);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_subscribe);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_set_subscribe_callback);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_disconnect);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_unref);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_new);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_connect_playback);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_connect_record);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_get_state);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_writable_size);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_readable_size);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_write);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_drain);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_disconnect);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_peek);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_drop);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_flush);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_unref);
|
||||
SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto);
|
||||
SDL_PULSEAUDIO_SYM(pa_strerror);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_set_write_callback);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_get_server_info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SDL_INLINE int squashVersion(const int major, const int minor, const int patch)
|
||||
{
|
||||
return ((major & 0xFF) << 16) | ((minor & 0xFF) << 8) | (patch & 0xFF);
|
||||
}
|
||||
|
||||
/* Workaround for older pulse: pa_context_new() must have non-NULL appname */
|
||||
static const char *getAppName(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;
|
||||
} else {
|
||||
const char *verstr = PULSEAUDIO_pa_get_library_version();
|
||||
retval = "SDL Application"; /* the "oh well" default. */
|
||||
if (verstr != NULL) {
|
||||
int maj, min, patch;
|
||||
if (SDL_sscanf(verstr, "%d.%d.%d", &maj, &min, &patch) == 3) {
|
||||
if (squashVersion(maj, min, patch) >= squashVersion(0, 9, 15)) {
|
||||
retval = NULL; /* 0.9.15+ handles NULL correctly. */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void WaitForPulseOperation(pa_mainloop *mainloop, pa_operation *o)
|
||||
{
|
||||
/* This checks for NO errors currently. Either fix that, check results elsewhere, or do things you don't care about. */
|
||||
if (mainloop && o) {
|
||||
SDL_bool okay = SDL_TRUE;
|
||||
while (okay && (PULSEAUDIO_pa_operation_get_state(o) == PA_OPERATION_RUNNING)) {
|
||||
okay = (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1, NULL) >= 0);
|
||||
}
|
||||
PULSEAUDIO_pa_operation_unref(o);
|
||||
}
|
||||
}
|
||||
|
||||
static void DisconnectFromPulseServer(pa_mainloop *mainloop, pa_context *context)
|
||||
{
|
||||
if (context) {
|
||||
PULSEAUDIO_pa_context_disconnect(context);
|
||||
PULSEAUDIO_pa_context_unref(context);
|
||||
}
|
||||
if (mainloop != NULL) {
|
||||
PULSEAUDIO_pa_mainloop_free(mainloop);
|
||||
}
|
||||
}
|
||||
|
||||
static int ConnectToPulseServer_Internal(pa_mainloop **_mainloop, pa_context **_context)
|
||||
{
|
||||
pa_mainloop *mainloop = NULL;
|
||||
pa_context *context = NULL;
|
||||
pa_mainloop_api *mainloop_api = NULL;
|
||||
int state = 0;
|
||||
|
||||
*_mainloop = NULL;
|
||||
*_context = NULL;
|
||||
|
||||
/* Set up a new main loop */
|
||||
if (!(mainloop = PULSEAUDIO_pa_mainloop_new())) {
|
||||
return SDL_SetError("pa_mainloop_new() failed");
|
||||
}
|
||||
|
||||
mainloop_api = PULSEAUDIO_pa_mainloop_get_api(mainloop);
|
||||
SDL_assert(mainloop_api); /* this never fails, right? */
|
||||
|
||||
context = PULSEAUDIO_pa_context_new(mainloop_api, getAppName());
|
||||
if (context == NULL) {
|
||||
PULSEAUDIO_pa_mainloop_free(mainloop);
|
||||
return SDL_SetError("pa_context_new() failed");
|
||||
}
|
||||
|
||||
/* Connect to the PulseAudio server */
|
||||
if (PULSEAUDIO_pa_context_connect(context, NULL, 0, NULL) < 0) {
|
||||
PULSEAUDIO_pa_context_unref(context);
|
||||
PULSEAUDIO_pa_mainloop_free(mainloop);
|
||||
return SDL_SetError("Could not setup connection to PulseAudio");
|
||||
}
|
||||
|
||||
do {
|
||||
if (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1, NULL) < 0) {
|
||||
PULSEAUDIO_pa_context_unref(context);
|
||||
PULSEAUDIO_pa_mainloop_free(mainloop);
|
||||
return SDL_SetError("pa_mainloop_iterate() failed");
|
||||
}
|
||||
state = PULSEAUDIO_pa_context_get_state(context);
|
||||
if (!PA_CONTEXT_IS_GOOD(state)) {
|
||||
PULSEAUDIO_pa_context_unref(context);
|
||||
PULSEAUDIO_pa_mainloop_free(mainloop);
|
||||
return SDL_SetError("Could not connect to PulseAudio");
|
||||
}
|
||||
} while (state != PA_CONTEXT_READY);
|
||||
|
||||
*_context = context;
|
||||
*_mainloop = mainloop;
|
||||
|
||||
return 0; /* connected and ready! */
|
||||
}
|
||||
|
||||
static int ConnectToPulseServer(pa_mainloop **_mainloop, pa_context **_context)
|
||||
{
|
||||
const int retval = ConnectToPulseServer_Internal(_mainloop, _context);
|
||||
if (retval < 0) {
|
||||
DisconnectFromPulseServer(*_mainloop, *_context);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void PULSEAUDIO_WaitDevice(_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;
|
||||
/*printf("PULSEAUDIO WRITE CALLBACK! nbytes=%u\n", (unsigned int) nbytes);*/
|
||||
h->bytes_requested += nbytes;
|
||||
}
|
||||
|
||||
static void PULSEAUDIO_PlayDevice(_THIS)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = this->hidden;
|
||||
int available = h->mixlen;
|
||||
int written = 0;
|
||||
int cpy;
|
||||
|
||||
/*printf("PULSEAUDIO PLAYDEVICE START! mixlen=%d\n", available);*/
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
/*printf("PULSEAUDIO FEED! nbytes=%u\n", (unsigned int) cpy);*/
|
||||
h->bytes_requested -= cpy;
|
||||
written += cpy;
|
||||
available -= cpy;
|
||||
}
|
||||
|
||||
/* let WriteCallback fire if necessary. */
|
||||
/*printf("PULSEAUDIO ITERATE!\n");*/
|
||||
if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
|
||||
PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
|
||||
PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*printf("PULSEAUDIO PLAYDEVICE END! written=%d\n", written);*/
|
||||
}
|
||||
|
||||
static Uint8 *PULSEAUDIO_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return this->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static int PULSEAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = this->hidden;
|
||||
const void *data = NULL;
|
||||
size_t nbytes = 0;
|
||||
|
||||
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. */
|
||||
}
|
||||
return cpy; /* new data, return it. */
|
||||
}
|
||||
|
||||
if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
|
||||
PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
|
||||
PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
return -1; /* uhoh, pulse failed! */
|
||||
}
|
||||
|
||||
if (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0) {
|
||||
continue; /* no data available yet. */
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
return -1; /* not enabled? */
|
||||
}
|
||||
|
||||
static void PULSEAUDIO_FlushCapture(_THIS)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = this->hidden;
|
||||
const void *data = NULL;
|
||||
size_t nbytes = 0;
|
||||
|
||||
if (h->capturebuf != NULL) {
|
||||
PULSEAUDIO_pa_stream_drop(h->stream);
|
||||
h->capturebuf = NULL;
|
||||
h->capturelen = 0;
|
||||
}
|
||||
|
||||
while (SDL_AtomicGet(&this->enabled)) {
|
||||
if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
|
||||
PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
|
||||
PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
return; /* uhoh, pulse failed! */
|
||||
}
|
||||
|
||||
if (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0) {
|
||||
break; /* no data available, so we're done. */
|
||||
}
|
||||
|
||||
/* a new fragment is available! Just dump it. */
|
||||
PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes);
|
||||
PULSEAUDIO_pa_stream_drop(h->stream); /* drop this fragment. */
|
||||
}
|
||||
}
|
||||
|
||||
static void PULSEAUDIO_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->stream) {
|
||||
if (this->hidden->capturebuf != NULL) {
|
||||
PULSEAUDIO_pa_stream_drop(this->hidden->stream);
|
||||
}
|
||||
PULSEAUDIO_pa_stream_disconnect(this->hidden->stream);
|
||||
PULSEAUDIO_pa_stream_unref(this->hidden->stream);
|
||||
}
|
||||
|
||||
DisconnectFromPulseServer(this->hidden->mainloop, this->hidden->context);
|
||||
SDL_free(this->hidden->mixbuf);
|
||||
SDL_free(this->hidden->device_name);
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
static void SinkDeviceNameCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
|
||||
{
|
||||
if (i) {
|
||||
char **devname = (char **)data;
|
||||
*devname = SDL_strdup(i->name);
|
||||
}
|
||||
}
|
||||
|
||||
static void SourceDeviceNameCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
|
||||
{
|
||||
if (i) {
|
||||
char **devname = (char **)data;
|
||||
*devname = SDL_strdup(i->name);
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_bool FindDeviceName(struct SDL_PrivateAudioData *h, const SDL_bool iscapture, void *handle)
|
||||
{
|
||||
const uint32_t idx = ((uint32_t)((intptr_t)handle)) - 1;
|
||||
|
||||
if (handle == NULL) { /* NULL == default device. */
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
if (iscapture) {
|
||||
WaitForPulseOperation(h->mainloop,
|
||||
PULSEAUDIO_pa_context_get_source_info_by_index(h->context, idx,
|
||||
SourceDeviceNameCallback, &h->device_name));
|
||||
} else {
|
||||
WaitForPulseOperation(h->mainloop,
|
||||
PULSEAUDIO_pa_context_get_sink_info_by_index(h->context, idx,
|
||||
SinkDeviceNameCallback, &h->device_name));
|
||||
}
|
||||
|
||||
return h->device_name != NULL;
|
||||
}
|
||||
|
||||
static int PULSEAUDIO_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = NULL;
|
||||
SDL_AudioFormat test_format;
|
||||
pa_sample_spec paspec;
|
||||
pa_buffer_attr paattr;
|
||||
pa_channel_map pacmap;
|
||||
pa_stream_flags_t flags = 0;
|
||||
const char *name = NULL;
|
||||
SDL_bool iscapture = this->iscapture;
|
||||
int state = 0, format = PA_SAMPLE_INVALID;
|
||||
int rc = 0;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
h = this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*this->hidden));
|
||||
if (this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(this->hidden);
|
||||
|
||||
/* Try for a closest match on audio format */
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
|
||||
#endif
|
||||
switch (test_format) {
|
||||
case AUDIO_U8:
|
||||
format = PA_SAMPLE_U8;
|
||||
break;
|
||||
case AUDIO_S16LSB:
|
||||
format = PA_SAMPLE_S16LE;
|
||||
break;
|
||||
case AUDIO_S16MSB:
|
||||
format = PA_SAMPLE_S16BE;
|
||||
break;
|
||||
case AUDIO_S32LSB:
|
||||
format = PA_SAMPLE_S32LE;
|
||||
break;
|
||||
case AUDIO_S32MSB:
|
||||
format = PA_SAMPLE_S32BE;
|
||||
break;
|
||||
case AUDIO_F32LSB:
|
||||
format = PA_SAMPLE_FLOAT32LE;
|
||||
break;
|
||||
case AUDIO_F32MSB:
|
||||
format = PA_SAMPLE_FLOAT32BE;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "pulseaudio");
|
||||
}
|
||||
this->spec.format = test_format;
|
||||
paspec.format = format;
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
if (!iscapture) {
|
||||
h->mixlen = this->spec.size;
|
||||
h->mixbuf = (Uint8 *)SDL_malloc(h->mixlen);
|
||||
if (h->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(h->mixbuf, this->spec.silence, this->spec.size);
|
||||
}
|
||||
|
||||
paspec.channels = this->spec.channels;
|
||||
paspec.rate = this->spec.freq;
|
||||
|
||||
/* Reduced prebuffering compared to the defaults. */
|
||||
paattr.fragsize = this->spec.size;
|
||||
paattr.tlength = h->mixlen;
|
||||
paattr.prebuf = -1;
|
||||
paattr.maxlength = -1;
|
||||
paattr.minreq = -1;
|
||||
flags |= PA_STREAM_ADJUST_LATENCY;
|
||||
|
||||
if (ConnectToPulseServer(&h->mainloop, &h->context) < 0) {
|
||||
return SDL_SetError("Could not connect to PulseAudio server");
|
||||
}
|
||||
|
||||
if (!FindDeviceName(h, iscapture, this->handle)) {
|
||||
return SDL_SetError("Requested PulseAudio sink/source missing?");
|
||||
}
|
||||
|
||||
/* The SDL ALSA output hints us that we use Windows' channel mapping */
|
||||
/* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
|
||||
PULSEAUDIO_pa_channel_map_init_auto(&pacmap, this->spec.channels,
|
||||
PA_CHANNEL_MAP_WAVEEX);
|
||||
|
||||
name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME);
|
||||
|
||||
h->stream = PULSEAUDIO_pa_stream_new(
|
||||
h->context,
|
||||
(name && *name) ? name : "Audio Stream", /* stream description */
|
||||
&paspec, /* sample format spec */
|
||||
&pacmap /* channel map */
|
||||
);
|
||||
|
||||
if (h->stream == NULL) {
|
||||
return SDL_SetError("Could not set up PulseAudio stream");
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
if (iscapture) {
|
||||
rc = PULSEAUDIO_pa_stream_connect_record(h->stream, h->device_name, &paattr, flags);
|
||||
} else {
|
||||
PULSEAUDIO_pa_stream_set_write_callback(h->stream, WriteCallback, h);
|
||||
rc = PULSEAUDIO_pa_stream_connect_playback(h->stream, h->device_name, &paattr, flags, NULL, NULL);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
return SDL_SetError("Could not connect PulseAudio stream");
|
||||
}
|
||||
|
||||
do {
|
||||
if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
|
||||
return SDL_SetError("pa_mainloop_iterate() failed");
|
||||
}
|
||||
state = PULSEAUDIO_pa_stream_get_state(h->stream);
|
||||
if (!PA_STREAM_IS_GOOD(state)) {
|
||||
return SDL_SetError("Could not connect PulseAudio stream");
|
||||
}
|
||||
} while (state != PA_STREAM_READY);
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pa_mainloop *hotplug_mainloop = NULL;
|
||||
static pa_context *hotplug_context = NULL;
|
||||
static SDL_Thread *hotplug_thread = NULL;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* device handles are device index + 1, cast to void*, so we never pass a NULL. */
|
||||
|
||||
static SDL_AudioFormat PulseFormatToSDLFormat(pa_sample_format_t format)
|
||||
{
|
||||
switch (format) {
|
||||
case PA_SAMPLE_U8:
|
||||
return AUDIO_U8;
|
||||
case PA_SAMPLE_S16LE:
|
||||
return AUDIO_S16LSB;
|
||||
case PA_SAMPLE_S16BE:
|
||||
return AUDIO_S16MSB;
|
||||
case PA_SAMPLE_S32LE:
|
||||
return AUDIO_S32LSB;
|
||||
case PA_SAMPLE_S32BE:
|
||||
return AUDIO_S32MSB;
|
||||
case PA_SAMPLE_FLOAT32LE:
|
||||
return AUDIO_F32LSB;
|
||||
case PA_SAMPLE_FLOAT32BE:
|
||||
return AUDIO_F32MSB;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is called when PulseAudio adds an output ("sink") device. */
|
||||
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;
|
||||
|
||||
if (add) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This is called when PulseAudio adds a capture ("source") device. */
|
||||
static void SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
|
||||
{
|
||||
SDL_AudioSpec spec;
|
||||
SDL_bool add = (SDL_bool)((intptr_t)data);
|
||||
if (i) {
|
||||
/* Maybe skip "monitor" sources. These are just output from other sinks. */
|
||||
if (include_monitors || (i->monitor_of_sink == PA_INVALID_INDEX)) {
|
||||
spec.freq = i->sample_spec.rate;
|
||||
spec.channels = i->sample_spec.channels;
|
||||
spec.format = PulseFormatToSDLFormat(i->sample_spec.format);
|
||||
spec.silence = 0;
|
||||
spec.samples = 0;
|
||||
spec.size = 0;
|
||||
spec.callback = NULL;
|
||||
spec.userdata = NULL;
|
||||
|
||||
if (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ServerInfoCallback(pa_context *c, const pa_server_info *i, void *data)
|
||||
{
|
||||
if (default_sink_path != NULL) {
|
||||
SDL_free(default_sink_path);
|
||||
}
|
||||
if (default_source_path != NULL) {
|
||||
SDL_free(default_source_path);
|
||||
}
|
||||
default_sink_path = SDL_strdup(i->default_sink_name);
|
||||
default_source_path = SDL_strdup(i->default_source_name);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
const SDL_bool removed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE);
|
||||
const SDL_bool changed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE);
|
||||
|
||||
if (added || removed || changed) { /* we only care about add/remove events. */
|
||||
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... */
|
||||
if ((added || changed) && sink) {
|
||||
if (changed) {
|
||||
PULSEAUDIO_pa_context_get_server_info(hotplug_context, ServerInfoCallback, NULL);
|
||||
}
|
||||
PULSEAUDIO_pa_context_get_sink_info_by_index(hotplug_context, idx, SinkInfoCallback, (void *)((intptr_t)added));
|
||||
} else if ((added || changed) && source) {
|
||||
if (changed) {
|
||||
PULSEAUDIO_pa_context_get_server_info(hotplug_context, ServerInfoCallback, NULL);
|
||||
}
|
||||
PULSEAUDIO_pa_context_get_source_info_by_index(hotplug_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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* this runs as a thread while the Pulse target is initialized to catch hotplug events. */
|
||||
static int SDLCALL HotplugThread(void *data)
|
||||
{
|
||||
pa_operation *o;
|
||||
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
|
||||
PULSEAUDIO_pa_context_set_subscribe_callback(hotplug_context, HotplugCallback, NULL);
|
||||
o = PULSEAUDIO_pa_context_subscribe(hotplug_context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, NULL, NULL);
|
||||
PULSEAUDIO_pa_operation_unref(o); /* don't wait for it, just do our thing. */
|
||||
PULSEAUDIO_pa_mainloop_run(hotplug_mainloop, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void PULSEAUDIO_DetectDevices()
|
||||
{
|
||||
WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_server_info(hotplug_context, ServerInfoCallback, NULL));
|
||||
WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_sink_info_list(hotplug_context, SinkInfoCallback, (void *)((intptr_t)SDL_TRUE)));
|
||||
WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_source_info_list(hotplug_context, SourceInfoCallback, (void *)((intptr_t)SDL_TRUE)));
|
||||
|
||||
/* ok, we have a sane list, let's set up hotplug notifications now... */
|
||||
hotplug_thread = SDL_CreateThreadInternal(HotplugThread, "PulseHotplug", 256 * 1024, NULL);
|
||||
}
|
||||
|
||||
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 (hotplug_thread) {
|
||||
PULSEAUDIO_pa_mainloop_quit(hotplug_mainloop, 0);
|
||||
SDL_WaitThread(hotplug_thread, NULL);
|
||||
hotplug_thread = NULL;
|
||||
}
|
||||
|
||||
DisconnectFromPulseServer(hotplug_mainloop, hotplug_context);
|
||||
hotplug_mainloop = NULL;
|
||||
hotplug_context = NULL;
|
||||
|
||||
if (default_sink_path != NULL) {
|
||||
SDL_free(default_sink_path);
|
||||
default_sink_path = NULL;
|
||||
}
|
||||
if (default_source_path != NULL) {
|
||||
SDL_free(default_source_path);
|
||||
default_source_path = NULL;
|
||||
}
|
||||
if (default_sink_name != NULL) {
|
||||
SDL_free(default_sink_name);
|
||||
default_sink_name = NULL;
|
||||
}
|
||||
if (default_source_name != NULL) {
|
||||
SDL_free(default_source_name);
|
||||
default_source_name = NULL;
|
||||
}
|
||||
|
||||
UnloadPulseAudioLibrary();
|
||||
}
|
||||
|
||||
static SDL_bool PULSEAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (LoadPulseAudioLibrary() < 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
if (ConnectToPulseServer(&hotplug_mainloop, &hotplug_context) < 0) {
|
||||
UnloadPulseAudioLibrary();
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
include_monitors = SDL_GetHintBoolean(SDL_HINT_AUDIO_INCLUDE_MONITORS, SDL_FALSE);
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = PULSEAUDIO_DetectDevices;
|
||||
impl->OpenDevice = PULSEAUDIO_OpenDevice;
|
||||
impl->PlayDevice = PULSEAUDIO_PlayDevice;
|
||||
impl->WaitDevice = PULSEAUDIO_WaitDevice;
|
||||
impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = PULSEAUDIO_CloseDevice;
|
||||
impl->Deinitialize = PULSEAUDIO_Deinitialize;
|
||||
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. */
|
||||
}
|
||||
|
||||
AudioBootStrap PULSEAUDIO_bootstrap = {
|
||||
"pulseaudio", "PulseAudio", PULSEAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_PULSEAUDIO */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
54
third_party/SDL/src/audio/pulseaudio/SDL_pulseaudio.h
vendored
Normal file
54
third_party/SDL/src/audio/pulseaudio/SDL_pulseaudio.h
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_pulseaudio_h_
|
||||
#define SDL_pulseaudio_h_
|
||||
|
||||
#include <pulse/simple.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
char *device_name;
|
||||
|
||||
/* pulseaudio structures */
|
||||
pa_mainloop *mainloop;
|
||||
pa_context *context;
|
||||
pa_stream *stream;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
|
||||
int bytes_requested; /* bytes of data the hardware wants _now_. */
|
||||
|
||||
const Uint8 *capturebuf;
|
||||
int capturelen;
|
||||
};
|
||||
|
||||
#endif /* SDL_pulseaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
618
third_party/SDL/src/audio/qsa/SDL_qsa_audio.c
vendored
Normal file
618
third_party/SDL/src/audio/qsa/SDL_qsa_audio.c
vendored
Normal file
@@ -0,0 +1,618 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!! 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: ...does SDL2 even support QNX? */
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_QSA
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sched.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/neutrino.h>
|
||||
#include <sys/asoundlib.h>
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "../../core/unix/SDL_poll.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_qsa_audio.h"
|
||||
|
||||
/* default channel communication parameters */
|
||||
#define DEFAULT_CPARAMS_RATE 44100
|
||||
#define DEFAULT_CPARAMS_VOICES 1
|
||||
|
||||
#define DEFAULT_CPARAMS_FRAG_SIZE 4096
|
||||
#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;
|
||||
|
||||
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(_THIS)
|
||||
{
|
||||
/* Increase default 10 priority to 25 to avoid jerky sound */
|
||||
struct sched_param param;
|
||||
if (SchedGet(0, 0, ¶m) != -1) {
|
||||
param.sched_priority = param.sched_curpriority + 15;
|
||||
SchedSet(0, 0, SCHED_NOCHANGE, ¶m);
|
||||
}
|
||||
}
|
||||
|
||||
/* PCM channel parameters initialize function */
|
||||
static void QSA_InitAudioParams(snd_pcm_channel_params_t * cpars)
|
||||
{
|
||||
SDL_zerop(cpars);
|
||||
cpars->channel = SND_PCM_CHANNEL_PLAYBACK;
|
||||
cpars->mode = SND_PCM_MODE_BLOCK;
|
||||
cpars->start_mode = SND_PCM_START_DATA;
|
||||
cpars->stop_mode = SND_PCM_STOP_STOP;
|
||||
cpars->format.format = SND_PCM_SFMT_S16_LE;
|
||||
cpars->format.interleave = 1;
|
||||
cpars->format.rate = DEFAULT_CPARAMS_RATE;
|
||||
cpars->format.voices = DEFAULT_CPARAMS_VOICES;
|
||||
cpars->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE;
|
||||
cpars->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN;
|
||||
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(_THIS)
|
||||
{
|
||||
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->iscapture ? SDL_IOR_READ : SDL_IOR_WRITE,
|
||||
2 * 1000);
|
||||
switch (result) {
|
||||
case -1:
|
||||
SDL_SetError("QSA: SDL_IOReady() failed: %s", strerror(errno));
|
||||
break;
|
||||
case 0:
|
||||
SDL_SetError("QSA: timeout on buffer waiting occurred");
|
||||
this->hidden->timeout_on_wait = 1;
|
||||
break;
|
||||
default:
|
||||
this->hidden->timeout_on_wait = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void QSA_PlayDevice(_THIS)
|
||||
{
|
||||
snd_pcm_channel_status_t cstatus;
|
||||
int written;
|
||||
int status;
|
||||
int towrite;
|
||||
void *pcmbuffer;
|
||||
|
||||
if (!SDL_AtomicGet(&this->enabled) || !this->hidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
towrite = this->spec.size;
|
||||
pcmbuffer = this->hidden->pcm_buf;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
/* if we wrote some data */
|
||||
towrite -= written;
|
||||
pcmbuffer += written * this->spec.channels;
|
||||
continue;
|
||||
} else {
|
||||
if ((errno == EINVAL) || (errno == EIO)) {
|
||||
SDL_zero(cstatus);
|
||||
if (!this->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->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;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* we wrote all remaining data */
|
||||
towrite -= written;
|
||||
pcmbuffer += written * this->spec.channels;
|
||||
}
|
||||
} while ((towrite > 0) && SDL_AtomicGet(&this->enabled));
|
||||
|
||||
/* If we couldn't write, assume fatal error for now */
|
||||
if (towrite != 0) {
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 *QSA_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return this->hidden->pcm_buf;
|
||||
}
|
||||
|
||||
static void QSA_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->audio_handle != NULL) {
|
||||
#if _NTO_VERSION < 710
|
||||
if (!this->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);
|
||||
}
|
||||
#endif
|
||||
snd_pcm_close(this->hidden->audio_handle);
|
||||
}
|
||||
|
||||
SDL_free(this->hidden->pcm_buf);
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
static int QSA_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
const QSA_Device *device = (const QSA_Device *) this->handle;
|
||||
SDL_bool iscapture = this->iscapture;
|
||||
int status = 0;
|
||||
int format = 0;
|
||||
SDL_AudioFormat test_format;
|
||||
snd_pcm_channel_setup_t csetup;
|
||||
snd_pcm_channel_params_t cparams;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
this->hidden =
|
||||
(struct SDL_PrivateAudioData *) SDL_calloc(1,
|
||||
(sizeof
|
||||
(struct
|
||||
SDL_PrivateAudioData)));
|
||||
if (this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
/* Initialize channel transfer parameters to default */
|
||||
QSA_InitAudioParams(&cparams);
|
||||
|
||||
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 */
|
||||
if (status < 0) {
|
||||
this->hidden->audio_handle = NULL;
|
||||
return QSA_SetError("snd_pcm_open", status);
|
||||
}
|
||||
|
||||
/* Try for a closest match on audio format */
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
/* if match found set format to equivalent QSA format */
|
||||
switch (test_format) {
|
||||
case AUDIO_U8:
|
||||
format = SND_PCM_SFMT_U8;
|
||||
break;
|
||||
case AUDIO_S8:
|
||||
format = SND_PCM_SFMT_S8;
|
||||
break;
|
||||
case AUDIO_S16LSB:
|
||||
format = SND_PCM_SFMT_S16_LE;
|
||||
break;
|
||||
case AUDIO_S16MSB:
|
||||
format = SND_PCM_SFMT_S16_BE;
|
||||
break;
|
||||
case AUDIO_U16LSB:
|
||||
format = SND_PCM_SFMT_U16_LE;
|
||||
break;
|
||||
case AUDIO_U16MSB:
|
||||
format = SND_PCM_SFMT_U16_BE;
|
||||
break;
|
||||
case AUDIO_S32LSB:
|
||||
format = SND_PCM_SFMT_S32_LE;
|
||||
break;
|
||||
case AUDIO_S32MSB:
|
||||
format = SND_PCM_SFMT_S32_BE;
|
||||
break;
|
||||
case AUDIO_F32LSB:
|
||||
format = SND_PCM_SFMT_FLOAT_LE;
|
||||
break;
|
||||
case AUDIO_F32MSB:
|
||||
format = SND_PCM_SFMT_FLOAT_BE;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* assumes test_format not 0 on success */
|
||||
/* can't use format as SND_PCM_SFMT_U8 = 0 in qsa */
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "qsa");
|
||||
}
|
||||
this->spec.format = test_format;
|
||||
|
||||
/* Set the audio format */
|
||||
cparams.format.format = format;
|
||||
|
||||
/* Set mono/stereo/4ch/6ch/8ch audio */
|
||||
cparams.format.voices = this->spec.channels;
|
||||
|
||||
/* 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);
|
||||
if (status < 0) {
|
||||
return QSA_SetError("snd_pcm_plugin_params", status);
|
||||
}
|
||||
|
||||
/* Make sure channel is setup right one last time */
|
||||
SDL_zero(csetup);
|
||||
if (!this->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) {
|
||||
return SDL_SetError("QSA: Unable to setup channel");
|
||||
}
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
this->hidden->pcm_len = this->spec.size;
|
||||
|
||||
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) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(this->hidden->pcm_buf, this->spec.silence,
|
||||
this->hidden->pcm_len);
|
||||
|
||||
/* get the file descriptor */
|
||||
if (!this->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->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);
|
||||
}
|
||||
|
||||
if (status < 0) {
|
||||
return QSA_SetError("snd_pcm_plugin_prepare", status);
|
||||
}
|
||||
|
||||
/* We're really ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void QSA_DetectDevices(void)
|
||||
{
|
||||
uint32_t it;
|
||||
uint32_t cards;
|
||||
uint32_t devices;
|
||||
int32_t status;
|
||||
|
||||
/* Detect amount of available devices */
|
||||
/* this value can be changed in the runtime */
|
||||
cards = snd_cards();
|
||||
|
||||
/* 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 */
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Store associated card number id */
|
||||
qsa_playback_device[qsa_playback_devices].cardno = it;
|
||||
|
||||
/* Check if this device id could play anything */
|
||||
status =
|
||||
snd_pcm_open(&handle, it, devices,
|
||||
SND_PCM_OPEN_PLAYBACK);
|
||||
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++;
|
||||
}
|
||||
} else {
|
||||
/* Check if we got end of devices list */
|
||||
if (status == -ENOENT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if we reached maximum devices count */
|
||||
if (qsa_playback_devices >= QSA_MAX_DEVICES) {
|
||||
break;
|
||||
}
|
||||
devices++;
|
||||
} while (1);
|
||||
|
||||
/* Check if we reached maximum devices count */
|
||||
if (qsa_playback_devices >= QSA_MAX_DEVICES) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ /* capture devices */
|
||||
/* Capture devices enumeration requested */
|
||||
for (it = 0; it < cards; it++) {
|
||||
devices = 0;
|
||||
do {
|
||||
status =
|
||||
snd_card_get_longname(it,
|
||||
qsa_capture_device
|
||||
[qsa_capture_devices].name,
|
||||
QSA_MAX_NAME_LENGTH);
|
||||
if (status == EOK) {
|
||||
snd_pcm_t *handle;
|
||||
|
||||
/* Add device number to device name */
|
||||
sprintf(qsa_capture_device[qsa_capture_devices].name +
|
||||
SDL_strlen(qsa_capture_device
|
||||
[qsa_capture_devices].name), " d%d",
|
||||
devices);
|
||||
|
||||
/* Store associated card number id */
|
||||
qsa_capture_device[qsa_capture_devices].cardno = it;
|
||||
|
||||
/* Check if this device id could play anything */
|
||||
status =
|
||||
snd_pcm_open(&handle, it, devices,
|
||||
SND_PCM_OPEN_CAPTURE);
|
||||
if (status == EOK) {
|
||||
qsa_capture_device[qsa_capture_devices].deviceno =
|
||||
devices;
|
||||
status = snd_pcm_close(handle);
|
||||
if (status == EOK) {
|
||||
/* Note that spec is NULL, because we are required to open the device before
|
||||
* acquiring the mix format, making this information inaccessible at
|
||||
* enumeration time
|
||||
*/
|
||||
SDL_AddAudioDevice(SDL_TRUE, qsa_capture_device[qsa_capture_devices].name, NULL, &qsa_capture_device[qsa_capture_devices]);
|
||||
qsa_capture_devices++;
|
||||
}
|
||||
} else {
|
||||
/* Check if we got end of devices list */
|
||||
if (status == -ENOENT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we reached maximum devices count */
|
||||
if (qsa_capture_devices >= QSA_MAX_DEVICES) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
devices++;
|
||||
} while (1);
|
||||
|
||||
/* Check if we reached maximum devices count */
|
||||
if (qsa_capture_devices >= QSA_MAX_DEVICES) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
impl->WaitDevice = QSA_WaitDevice;
|
||||
impl->PlayDevice = QSA_PlayDevice;
|
||||
impl->GetDeviceBuf = QSA_GetDeviceBuf;
|
||||
impl->CloseDevice = QSA_CloseDevice;
|
||||
impl->Deinitialize = QSA_Deinitialize;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap QSAAUDIO_bootstrap = {
|
||||
"qsa", "QNX QSA Audio", QSA_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_QSA */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
54
third_party/SDL/src/audio/qsa/SDL_qsa_audio.h
vendored
Normal file
54
third_party/SDL/src/audio/qsa/SDL_qsa_audio.h
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef __SDL_QSA_AUDIO_H__
|
||||
#define __SDL_QSA_AUDIO_H__
|
||||
|
||||
#include <sys/asoundlib.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice* this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* 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;
|
||||
};
|
||||
|
||||
#endif /* __SDL_QSA_AUDIO_H__ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
371
third_party/SDL/src/audio/sndio/SDL_sndioaudio.c
vendored
Normal file
371
third_party/SDL/src/audio/sndio/SDL_sndioaudio.c
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_SNDIO
|
||||
|
||||
/* OpenBSD sndio target */
|
||||
|
||||
#if HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_sndioaudio.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
|
||||
#include "SDL_loadso.h"
|
||||
#endif
|
||||
|
||||
#ifndef INFTIM
|
||||
#define INFTIM -1
|
||||
#endif
|
||||
|
||||
#ifndef SIO_DEVANY
|
||||
#define SIO_DEVANY "default"
|
||||
#endif
|
||||
|
||||
static struct sio_hdl *(*SNDIO_sio_open)(const char *, unsigned int, int);
|
||||
static void (*SNDIO_sio_close)(struct sio_hdl *);
|
||||
static int (*SNDIO_sio_setpar)(struct sio_hdl *, struct sio_par *);
|
||||
static int (*SNDIO_sio_getpar)(struct sio_hdl *, struct sio_par *);
|
||||
static int (*SNDIO_sio_start)(struct sio_hdl *);
|
||||
static int (*SNDIO_sio_stop)(struct sio_hdl *);
|
||||
static size_t (*SNDIO_sio_read)(struct sio_hdl *, void *, size_t);
|
||||
static size_t (*SNDIO_sio_write)(struct sio_hdl *, const void *, size_t);
|
||||
static int (*SNDIO_sio_nfds)(struct sio_hdl *);
|
||||
static int (*SNDIO_sio_pollfd)(struct sio_hdl *, struct pollfd *, int);
|
||||
static int (*SNDIO_sio_revents)(struct sio_hdl *, struct pollfd *);
|
||||
static int (*SNDIO_sio_eof)(struct sio_hdl *);
|
||||
static void (*SNDIO_sio_initpar)(struct sio_par *);
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
|
||||
static const char *sndio_library = SDL_AUDIO_DRIVER_SNDIO_DYNAMIC;
|
||||
static void *sndio_handle = NULL;
|
||||
|
||||
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 1;
|
||||
}
|
||||
|
||||
/* 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
|
||||
#else
|
||||
#define SDL_SNDIO_SYM(x) SNDIO_##x = x
|
||||
#endif
|
||||
|
||||
static int load_sndio_syms(void)
|
||||
{
|
||||
SDL_SNDIO_SYM(sio_open);
|
||||
SDL_SNDIO_SYM(sio_close);
|
||||
SDL_SNDIO_SYM(sio_setpar);
|
||||
SDL_SNDIO_SYM(sio_getpar);
|
||||
SDL_SNDIO_SYM(sio_start);
|
||||
SDL_SNDIO_SYM(sio_stop);
|
||||
SDL_SNDIO_SYM(sio_read);
|
||||
SDL_SNDIO_SYM(sio_write);
|
||||
SDL_SNDIO_SYM(sio_nfds);
|
||||
SDL_SNDIO_SYM(sio_pollfd);
|
||||
SDL_SNDIO_SYM(sio_revents);
|
||||
SDL_SNDIO_SYM(sio_eof);
|
||||
SDL_SNDIO_SYM(sio_initpar);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef SDL_SNDIO_SYM
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
|
||||
|
||||
static void UnloadSNDIOLibrary(void)
|
||||
{
|
||||
if (sndio_handle != NULL) {
|
||||
SDL_UnloadObject(sndio_handle);
|
||||
sndio_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadSNDIOLibrary(void)
|
||||
{
|
||||
int retval = 0;
|
||||
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. */
|
||||
} else {
|
||||
retval = load_sndio_syms();
|
||||
if (retval < 0) {
|
||||
UnloadSNDIOLibrary();
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void UnloadSNDIOLibrary(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int LoadSNDIOLibrary(void)
|
||||
{
|
||||
load_sndio_syms();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_SNDIO_DYNAMIC */
|
||||
|
||||
static void SNDIO_WaitDevice(_THIS)
|
||||
{
|
||||
/* no-op; SNDIO_sio_write() blocks if necessary. */
|
||||
}
|
||||
|
||||
static void SNDIO_PlayDevice(_THIS)
|
||||
{
|
||||
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);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int SNDIO_CaptureFromDevice(_THIS, 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;
|
||||
}
|
||||
}
|
||||
return (int)r;
|
||||
}
|
||||
|
||||
static void SNDIO_FlushCapture(_THIS)
|
||||
{
|
||||
char buf[512];
|
||||
|
||||
while (SNDIO_sio_read(this->hidden->dev, buf, sizeof(buf)) != 0) {
|
||||
/* do nothing */;
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 *SNDIO_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return this->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static void SNDIO_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->pfd != NULL) {
|
||||
SDL_free(this->hidden->pfd);
|
||||
}
|
||||
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(_THIS, const char *devname)
|
||||
{
|
||||
SDL_AudioFormat test_format;
|
||||
struct sio_par par;
|
||||
SDL_bool iscapture = this->iscapture;
|
||||
|
||||
this->hidden = (struct SDL_PrivateAudioData *)
|
||||
SDL_malloc(sizeof(*this->hidden));
|
||||
if (this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(this->hidden);
|
||||
|
||||
this->hidden->mixlen = this->spec.size;
|
||||
|
||||
/* 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) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
SNDIO_sio_initpar(&par);
|
||||
|
||||
par.rate = this->spec.freq;
|
||||
par.pchan = this->spec.channels;
|
||||
par.round = this->spec.samples;
|
||||
par.appbufsz = par.round * 2;
|
||||
|
||||
/* Try for a closest match on audio format */
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
if (SNDIO_sio_getpar(this->hidden->dev, &par) == 0) {
|
||||
return SDL_SetError("sio_getpar() failed");
|
||||
}
|
||||
if (par.bps != SIO_BPS(par.bits)) {
|
||||
continue;
|
||||
}
|
||||
if ((par.bits == 8 * par.bps) || (par.msb)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "sndio");
|
||||
}
|
||||
|
||||
if ((par.bps == 4) && (par.sig) && (par.le)) {
|
||||
this->spec.format = AUDIO_S32LSB;
|
||||
} else if ((par.bps == 4) && (par.sig) && (!par.le)) {
|
||||
this->spec.format = AUDIO_S32MSB;
|
||||
} else if ((par.bps == 2) && (par.sig) && (par.le)) {
|
||||
this->spec.format = AUDIO_S16LSB;
|
||||
} else if ((par.bps == 2) && (par.sig) && (!par.le)) {
|
||||
this->spec.format = AUDIO_S16MSB;
|
||||
} else if ((par.bps == 2) && (!par.sig) && (par.le)) {
|
||||
this->spec.format = AUDIO_U16LSB;
|
||||
} else if ((par.bps == 2) && (!par.sig) && (!par.le)) {
|
||||
this->spec.format = AUDIO_U16MSB;
|
||||
} else if ((par.bps == 1) && (par.sig)) {
|
||||
this->spec.format = AUDIO_S8;
|
||||
} else if ((par.bps == 1) && (!par.sig)) {
|
||||
this->spec.format = 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;
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
this->hidden->mixlen = this->spec.size;
|
||||
this->hidden->mixbuf = (Uint8 *)SDL_malloc(this->hidden->mixlen);
|
||||
if (this->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
|
||||
|
||||
if (!SNDIO_sio_start(this->hidden->dev)) {
|
||||
return SDL_SetError("sio_start() failed");
|
||||
}
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void SNDIO_Deinitialize(void)
|
||||
{
|
||||
UnloadSNDIOLibrary();
|
||||
}
|
||||
|
||||
static void SNDIO_DetectDevices(void)
|
||||
{
|
||||
SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *)0x1);
|
||||
SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *)0x2);
|
||||
}
|
||||
|
||||
static SDL_bool SNDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (LoadSNDIOLibrary() < 0) {
|
||||
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->CaptureFromDevice = SNDIO_CaptureFromDevice;
|
||||
impl->FlushCapture = SNDIO_FlushCapture;
|
||||
impl->Deinitialize = SNDIO_Deinitialize;
|
||||
impl->DetectDevices = SNDIO_DetectDevices;
|
||||
|
||||
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap SNDIO_bootstrap = {
|
||||
"sndio", "OpenBSD sndio", SNDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_SNDIO */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
49
third_party/SDL/src/audio/sndio/SDL_sndioaudio.h
vendored
Normal file
49
third_party/SDL/src/audio/sndio/SDL_sndioaudio.h
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_sndioaudio_h_
|
||||
#define SDL_sndioaudio_h_
|
||||
|
||||
#include <poll.h>
|
||||
#include <sndio.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
#endif /* SDL_sndioaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
410
third_party/SDL/src/audio/sun/SDL_sunaudio.c
vendored
Normal file
410
third_party/SDL/src/audio/sun/SDL_sunaudio.c
vendored
Normal file
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_SUNAUDIO
|
||||
|
||||
/* Allow access to a raw mixing buffer */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#ifdef __NETBSD__
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/audioio.h>
|
||||
#endif
|
||||
#ifdef __SVR4
|
||||
#include <sys/audioio.h>
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "../../core/unix/SDL_poll.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_audiodev_c.h"
|
||||
#include "SDL_sunaudio.h"
|
||||
|
||||
/* Open the audio device for playback, and don't block if busy */
|
||||
|
||||
#if defined(AUDIO_GETINFO) && !defined(AUDIO_GETBUFINFO)
|
||||
#define AUDIO_GETBUFINFO AUDIO_GETINFO
|
||||
#endif
|
||||
|
||||
/* Audio driver functions */
|
||||
static Uint8 snd2au(int sample);
|
||||
|
||||
/* Audio driver bootstrap functions */
|
||||
static void SUNAUDIO_DetectDevices(void)
|
||||
{
|
||||
SDL_EnumUnixAudioDevices(1, (int (*)(int)) NULL);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
void CheckUnderflow(_THIS)
|
||||
{
|
||||
#ifdef AUDIO_GETBUFINFO
|
||||
audio_info_t info;
|
||||
int left;
|
||||
|
||||
ioctl(this->hidden->audio_fd, AUDIO_GETBUFINFO, &info);
|
||||
left = (this->hidden->written - info.play.samples);
|
||||
if (this->hidden->written && (left == 0)) {
|
||||
fprintf(stderr, "audio underflow!\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void SUNAUDIO_WaitDevice(_THIS)
|
||||
{
|
||||
#ifdef AUDIO_GETBUFINFO
|
||||
#define SLEEP_FUDGE 10 /* 10 ms scheduling fudge factor */
|
||||
audio_info_t info;
|
||||
Sint32 left;
|
||||
|
||||
ioctl(this->hidden->audio_fd, AUDIO_GETBUFINFO, &info);
|
||||
left = (this->hidden->written - info.play.samples);
|
||||
if (left > this->hidden->fragsize) {
|
||||
Sint32 sleepy;
|
||||
|
||||
sleepy = ((left - this->hidden->fragsize) / this->hidden->frequency);
|
||||
sleepy -= SLEEP_FUDGE;
|
||||
if (sleepy > 0) {
|
||||
SDL_Delay(sleepy);
|
||||
}
|
||||
}
|
||||
#else
|
||||
SDL_IOReady(this->hidden->audio_fd, SDL_IOR_WRITE, -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void SUNAUDIO_PlayDevice(_THIS)
|
||||
{
|
||||
/* Write the audio data */
|
||||
if (this->hidden->ulaw_only) {
|
||||
/* Assuming that this->spec.freq >= 8000 Hz */
|
||||
int accum, incr, pos;
|
||||
Uint8 *aubuf;
|
||||
|
||||
accum = 0;
|
||||
incr = this->spec.freq / 8;
|
||||
aubuf = this->hidden->ulaw_buf;
|
||||
switch (this->hidden->audio_fmt & 0xFF) {
|
||||
case 8:
|
||||
{
|
||||
Uint8 *sndbuf;
|
||||
|
||||
sndbuf = this->hidden->mixbuf;
|
||||
for (pos = 0; pos < this->hidden->fragsize; ++pos) {
|
||||
*aubuf = snd2au((0x80 - *sndbuf) * 64);
|
||||
accum += incr;
|
||||
while (accum > 0) {
|
||||
accum -= 1000;
|
||||
sndbuf += 1;
|
||||
}
|
||||
aubuf += 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
{
|
||||
Sint16 *sndbuf;
|
||||
|
||||
sndbuf = (Sint16 *) this->hidden->mixbuf;
|
||||
for (pos = 0; pos < this->hidden->fragsize; ++pos) {
|
||||
*aubuf = snd2au(*sndbuf / 4);
|
||||
accum += incr;
|
||||
while (accum > 0) {
|
||||
accum -= 1000;
|
||||
sndbuf += 1;
|
||||
}
|
||||
aubuf += 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
CheckUnderflow(this);
|
||||
#endif
|
||||
if (write(this->hidden->audio_fd, this->hidden->ulaw_buf,
|
||||
this->hidden->fragsize) < 0) {
|
||||
/* Assume fatal error, for now */
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
}
|
||||
this->hidden->written += this->hidden->fragsize;
|
||||
} else {
|
||||
#ifdef DEBUG_AUDIO
|
||||
CheckUnderflow(this);
|
||||
#endif
|
||||
if (write(this->hidden->audio_fd, this->hidden->mixbuf,
|
||||
this->spec.size) < 0) {
|
||||
/* Assume fatal error, for now */
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
}
|
||||
this->hidden->written += this->hidden->fragsize;
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 *SUNAUDIO_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return (this->hidden->mixbuf);
|
||||
}
|
||||
|
||||
static void SUNAUDIO_CloseDevice(_THIS)
|
||||
{
|
||||
SDL_free(this->hidden->ulaw_buf);
|
||||
if (this->hidden->audio_fd >= 0) {
|
||||
close(this->hidden->audio_fd);
|
||||
}
|
||||
SDL_free(this->hidden->mixbuf);
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
static int SUNAUDIO_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
#ifdef AUDIO_SETINFO
|
||||
int enc;
|
||||
#endif
|
||||
SDL_bool iscapture = this->iscapture;
|
||||
int desired_freq = 0;
|
||||
const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
|
||||
SDL_AudioFormat format = 0;
|
||||
audio_info_t info;
|
||||
|
||||
/* 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) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(this->hidden);
|
||||
|
||||
/* Open the audio device */
|
||||
this->hidden->audio_fd = open(devname, flags, 0);
|
||||
if (this->hidden->audio_fd < 0) {
|
||||
return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
|
||||
}
|
||||
|
||||
desired_freq = this->spec.freq;
|
||||
|
||||
/* Determine the audio parameters from the AudioSpec */
|
||||
switch (SDL_AUDIO_BITSIZE(this->spec.format)) {
|
||||
|
||||
case 8:
|
||||
{ /* Unsigned 8 bit audio data */
|
||||
this->spec.format = AUDIO_U8;
|
||||
#ifdef AUDIO_SETINFO
|
||||
enc = AUDIO_ENCODING_LINEAR8;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case 16:
|
||||
{ /* Signed 16 bit audio data */
|
||||
this->spec.format = AUDIO_S16SYS;
|
||||
#ifdef AUDIO_SETINFO
|
||||
enc = AUDIO_ENCODING_LINEAR;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
/* !!! FIXME: fallback to conversion on unsupported types! */
|
||||
return SDL_SetError("Unsupported audio format");
|
||||
}
|
||||
}
|
||||
this->hidden->audio_fmt = this->spec.format;
|
||||
|
||||
this->hidden->ulaw_only = 0; /* modern Suns do support linear audio */
|
||||
#ifdef AUDIO_SETINFO
|
||||
for (;;) {
|
||||
audio_info_t info;
|
||||
AUDIO_INITINFO(&info); /* init all fields to "no change" */
|
||||
|
||||
/* Try to set the requested settings */
|
||||
info.play.sample_rate = this->spec.freq;
|
||||
info.play.channels = this->spec.channels;
|
||||
info.play.precision = (enc == AUDIO_ENCODING_ULAW)
|
||||
? 8 : this->spec.format & 0xff;
|
||||
info.play.encoding = enc;
|
||||
if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == 0) {
|
||||
|
||||
/* Check to be sure we got what we wanted */
|
||||
if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
|
||||
return SDL_SetError("Error getting audio parameters: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
if (info.play.encoding == enc
|
||||
&& info.play.precision == (this->spec.format & 0xff)
|
||||
&& info.play.channels == this->spec.channels) {
|
||||
/* Yow! All seems to be well! */
|
||||
this->spec.freq = info.play.sample_rate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (enc) {
|
||||
case AUDIO_ENCODING_LINEAR8:
|
||||
/* unsigned 8bit apparently not supported here */
|
||||
enc = AUDIO_ENCODING_LINEAR;
|
||||
this->spec.format = AUDIO_S16SYS;
|
||||
break; /* try again */
|
||||
|
||||
case AUDIO_ENCODING_LINEAR:
|
||||
/* linear 16bit didn't work either, resort to µ-law */
|
||||
enc = AUDIO_ENCODING_ULAW;
|
||||
this->spec.channels = 1;
|
||||
this->spec.freq = 8000;
|
||||
this->spec.format = AUDIO_U8;
|
||||
this->hidden->ulaw_only = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* oh well... */
|
||||
return SDL_SetError("Error setting audio parameters: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
#endif /* AUDIO_SETINFO */
|
||||
this->hidden->written = 0;
|
||||
|
||||
/* We can actually convert on-the-fly to U-Law */
|
||||
if (this->hidden->ulaw_only) {
|
||||
this->spec.freq = desired_freq;
|
||||
this->hidden->fragsize = (this->spec.samples * 1000) /
|
||||
(this->spec.freq / 8);
|
||||
this->hidden->frequency = 8;
|
||||
this->hidden->ulaw_buf = (Uint8 *) SDL_malloc(this->hidden->fragsize);
|
||||
if (this->hidden->ulaw_buf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
this->spec.channels = 1;
|
||||
} else {
|
||||
this->hidden->fragsize = this->spec.samples;
|
||||
this->hidden->frequency = this->spec.freq / 1000;
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Audio device %s U-Law only\n",
|
||||
this->hidden->ulaw_only ? "is" : "is not");
|
||||
fprintf(stderr, "format=0x%x chan=%d freq=%d\n",
|
||||
this->spec.format, this->spec.channels, this->spec.freq);
|
||||
#endif
|
||||
|
||||
/* Update the fragment size as size in bytes */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->spec.size);
|
||||
if (this->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* This function (snd2au()) copyrighted: */
|
||||
/************************************************************************/
|
||||
/* Copyright 1989 by Rich Gopstein and Harris Corporation */
|
||||
/* */
|
||||
/* Permission to use, copy, modify, and distribute this software */
|
||||
/* and its documentation for any purpose and without fee is */
|
||||
/* hereby granted, provided that the above copyright notice */
|
||||
/* appears in all copies and that both that copyright notice and */
|
||||
/* this permission notice appear in supporting documentation, and */
|
||||
/* that the name of Rich Gopstein and Harris Corporation not be */
|
||||
/* used in advertising or publicity pertaining to distribution */
|
||||
/* of the software without specific, written prior permission. */
|
||||
/* Rich Gopstein and Harris Corporation make no representations */
|
||||
/* about the suitability of this software for any purpose. It */
|
||||
/* provided "as is" without express or implied warranty. */
|
||||
/************************************************************************/
|
||||
|
||||
static Uint8 snd2au(int sample)
|
||||
{
|
||||
|
||||
int mask;
|
||||
|
||||
if (sample < 0) {
|
||||
sample = -sample;
|
||||
mask = 0x7f;
|
||||
} else {
|
||||
mask = 0xff;
|
||||
}
|
||||
|
||||
if (sample < 32) {
|
||||
sample = 0xF0 | (15 - sample / 2);
|
||||
} else if (sample < 96) {
|
||||
sample = 0xE0 | (15 - (sample - 32) / 4);
|
||||
} else if (sample < 224) {
|
||||
sample = 0xD0 | (15 - (sample - 96) / 8);
|
||||
} else if (sample < 480) {
|
||||
sample = 0xC0 | (15 - (sample - 224) / 16);
|
||||
} else if (sample < 992) {
|
||||
sample = 0xB0 | (15 - (sample - 480) / 32);
|
||||
} else if (sample < 2016) {
|
||||
sample = 0xA0 | (15 - (sample - 992) / 64);
|
||||
} else if (sample < 4064) {
|
||||
sample = 0x90 | (15 - (sample - 2016) / 128);
|
||||
} else if (sample < 8160) {
|
||||
sample = 0x80 | (15 - (sample - 4064) / 256);
|
||||
} else {
|
||||
sample = 0x80;
|
||||
}
|
||||
return (mask & sample);
|
||||
}
|
||||
|
||||
static SDL_bool SUNAUDIO_Init(SDL_AudioDriverImpl * impl)
|
||||
{
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = SUNAUDIO_DetectDevices;
|
||||
impl->OpenDevice = SUNAUDIO_OpenDevice;
|
||||
impl->PlayDevice = SUNAUDIO_PlayDevice;
|
||||
impl->WaitDevice = SUNAUDIO_WaitDevice;
|
||||
impl->GetDeviceBuf = SUNAUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = SUNAUDIO_CloseDevice;
|
||||
|
||||
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap SUNAUDIO_bootstrap = {
|
||||
"audio", "UNIX /dev/audio interface", SUNAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_SUNAUDIO */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
47
third_party/SDL/src/audio/sun/SDL_sunaudio.h
vendored
Normal file
47
third_party/SDL/src/audio/sun/SDL_sunaudio.h
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_sunaudio_h_
|
||||
#define SDL_sunaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The file descriptor for the audio device */
|
||||
int audio_fd;
|
||||
|
||||
SDL_AudioFormat audio_fmt; /* The app audio format */
|
||||
Uint8 *mixbuf; /* The app mixing buffer */
|
||||
int ulaw_only; /* Flag -- does hardware only output U-law? */
|
||||
Uint8 *ulaw_buf; /* The U-law mixing buffer */
|
||||
Sint32 written; /* The number of samples written */
|
||||
int fragsize; /* The audio fragment size in samples */
|
||||
int frequency; /* The audio frequency in KHz */
|
||||
};
|
||||
|
||||
#endif /* SDL_sunaudio_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
221
third_party/SDL/src/audio/vita/SDL_vitaaudio.c
vendored
Normal file
221
third_party/SDL/src/audio/vita/SDL_vitaaudio.c
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_VITA
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h> /* memalign() */
|
||||
|
||||
#include "SDL_audio.h"
|
||||
#include "SDL_error.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_audiodev_c.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_vitaaudio.h"
|
||||
|
||||
#include <psp2/kernel/threadmgr.h>
|
||||
#include <psp2/audioout.h>
|
||||
#include <psp2/audioin.h>
|
||||
|
||||
#define SCE_AUDIO_SAMPLE_ALIGN(s) (((s) + 63) & ~63)
|
||||
#define SCE_AUDIO_MAX_VOLUME 0x8000
|
||||
|
||||
static int VITAAUD_OpenCaptureDevice(_THIS)
|
||||
{
|
||||
this->spec.freq = 16000;
|
||||
this->spec.samples = 512;
|
||||
this->spec.channels = 1;
|
||||
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
this->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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int VITAAUD_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
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;
|
||||
|
||||
this->hidden = (struct SDL_PrivateAudioData *)
|
||||
SDL_malloc(sizeof(*this->hidden));
|
||||
if (this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(this->hidden, 0, sizeof(*this->hidden));
|
||||
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
if (test_format == AUDIO_S16LSB) {
|
||||
this->spec.format = test_format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
return SDL_SetError("Unsupported audio format");
|
||||
}
|
||||
|
||||
if (this->iscapture) {
|
||||
return VITAAUD_OpenCaptureDevice(this);
|
||||
}
|
||||
|
||||
/* The sample count must be a multiple of 64. */
|
||||
this->spec.samples = SCE_AUDIO_SAMPLE_ALIGN(this->spec.samples);
|
||||
|
||||
/* Update the fragment size as size in bytes. */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
/* 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 *)memalign(64, mixlen);
|
||||
if (this->hidden->rawbuf == NULL) {
|
||||
return SDL_SetError("Couldn't allocate mixing buffer");
|
||||
}
|
||||
|
||||
/* Setup the hardware channel. */
|
||||
if (this->spec.channels == 1) {
|
||||
format = SCE_AUDIO_OUT_MODE_MONO;
|
||||
} else {
|
||||
format = SCE_AUDIO_OUT_MODE_STEREO;
|
||||
}
|
||||
|
||||
if (this->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) {
|
||||
free(this->hidden->rawbuf);
|
||||
this->hidden->rawbuf = NULL;
|
||||
return SDL_SetError("Couldn't open audio out port: %x", this->hidden->port);
|
||||
}
|
||||
|
||||
sceAudioOutSetVolume(this->hidden->port, SCE_AUDIO_VOLUME_FLAG_L_CH | SCE_AUDIO_VOLUME_FLAG_R_CH, vols);
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
this->hidden->next_buffer = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void VITAAUD_PlayDevice(_THIS)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void VITAAUD_WaitDevice(_THIS)
|
||||
{
|
||||
/* Because we block when sending audio, there's no need for this function to do anything. */
|
||||
}
|
||||
|
||||
static Uint8 *VITAAUD_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return this->hidden->mixbufs[this->hidden->next_buffer];
|
||||
}
|
||||
|
||||
static void VITAAUD_CloseDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->port >= 0) {
|
||||
if (this->iscapture) {
|
||||
sceAudioInReleasePort(this->hidden->port);
|
||||
} else {
|
||||
sceAudioOutReleasePort(this->hidden->port);
|
||||
}
|
||||
this->hidden->port = -1;
|
||||
}
|
||||
|
||||
if (!this->iscapture && this->hidden->rawbuf != NULL) {
|
||||
free(this->hidden->rawbuf); /* this uses memalign(), not SDL_malloc(). */
|
||||
this->hidden->rawbuf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int VITAAUD_CaptureFromDevice(_THIS, void *buffer, int buflen)
|
||||
{
|
||||
int ret;
|
||||
SDL_assert(buflen == this->spec.size);
|
||||
ret = sceAudioInInput(this->hidden->port, buffer);
|
||||
if (ret < 0) {
|
||||
return SDL_SetError("Failed to capture from device: %x", ret);
|
||||
}
|
||||
return this->spec.size;
|
||||
}
|
||||
|
||||
static void VITAAUD_ThreadInit(_THIS)
|
||||
{
|
||||
/* Increase the priority of this audio thread by 1 to put it
|
||||
ahead of other SDL threads. */
|
||||
SceUID thid;
|
||||
SceKernelThreadInfo info;
|
||||
thid = sceKernelGetThreadId();
|
||||
info.size = sizeof(SceKernelThreadInfo);
|
||||
if (sceKernelGetThreadInfo(thid, &info) == 0) {
|
||||
sceKernelChangeThreadPriority(thid, info.currentPriority - 1);
|
||||
}
|
||||
}
|
||||
|
||||
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->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. */
|
||||
}
|
||||
|
||||
AudioBootStrap VITAAUD_bootstrap = {
|
||||
"vita", "VITA audio driver", VITAAUD_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_VITA */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
46
third_party/SDL/src/audio/vita/SDL_vitaaudio.h
vendored
Normal file
46
third_party/SDL/src/audio/vita/SDL_vitaaudio.h
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef _SDL_vitaaudio_h
|
||||
#define _SDL_vitaaudio_h
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
#define NUM_BUFFERS 2
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The hardware input/output port. */
|
||||
int port;
|
||||
/* The raw allocated mixing buffer. */
|
||||
Uint8 *rawbuf;
|
||||
/* Individual mixing buffers. */
|
||||
Uint8 *mixbufs[NUM_BUFFERS];
|
||||
/* Index of the next available mixing buffer. */
|
||||
int next_buffer;
|
||||
};
|
||||
|
||||
#endif /* _SDL_vitaaudio_h */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
622
third_party/SDL/src/audio/wasapi/SDL_wasapi.c
vendored
Normal file
622
third_party/SDL/src/audio/wasapi/SDL_wasapi.c
vendored
Normal file
@@ -0,0 +1,622 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_WASAPI
|
||||
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
#include "../../core/windows/SDL_immdevice.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#define COBJMACROS
|
||||
#include <audioclient.h>
|
||||
|
||||
#include "SDL_wasapi.h"
|
||||
|
||||
/* These constants aren't available in older SDKs */
|
||||
#ifndef AUDCLNT_STREAMFLAGS_RATEADJUST
|
||||
#define AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
|
||||
#endif
|
||||
#ifndef AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
|
||||
#define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
|
||||
#endif
|
||||
#ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
|
||||
#define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
|
||||
#endif
|
||||
|
||||
/* Some GUIDs we need to know without linking to libraries that aren't available before Vista. */
|
||||
static const IID SDL_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483, { 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } };
|
||||
static const IID SDL_IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0, { 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17 } };
|
||||
|
||||
|
||||
static void WASAPI_DetectDevices(void)
|
||||
{
|
||||
WASAPI_EnumerateEndpoints();
|
||||
}
|
||||
|
||||
static SDL_INLINE SDL_bool WasapiFailed(_THIS, const HRESULT err)
|
||||
{
|
||||
if (err == S_OK) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
if (err == AUDCLNT_E_DEVICE_INVALIDATED) {
|
||||
this->hidden->device_lost = SDL_TRUE;
|
||||
} else if (SDL_AtomicGet(&this->enabled)) {
|
||||
IAudioClient_Stop(this->hidden->client);
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
SDL_assert(!SDL_AtomicGet(&this->enabled));
|
||||
}
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
static int UpdateAudioStream(_THIS, const SDL_AudioSpec *oldspec)
|
||||
{
|
||||
/* Since WASAPI requires us to handle all audio conversion, and our
|
||||
device format might have changed, we might have to add/remove/change
|
||||
the audio stream that the higher level uses to convert data, so
|
||||
SDL keeps firing the callback as if nothing happened here. */
|
||||
|
||||
if ((this->callbackspec.channels == this->spec.channels) &&
|
||||
(this->callbackspec.format == this->spec.format) &&
|
||||
(this->callbackspec.freq == this->spec.freq) &&
|
||||
(this->callbackspec.samples == this->spec.samples)) {
|
||||
/* no need to buffer/convert in an AudioStream! */
|
||||
SDL_FreeAudioStream(this->stream);
|
||||
this->stream = NULL;
|
||||
} else if ((oldspec->channels == this->spec.channels) &&
|
||||
(oldspec->format == this->spec.format) &&
|
||||
(oldspec->freq == this->spec.freq)) {
|
||||
/* The existing audio stream is okay to keep using. */
|
||||
} else {
|
||||
/* replace the audiostream for new format */
|
||||
SDL_FreeAudioStream(this->stream);
|
||||
if (this->iscapture) {
|
||||
this->stream = SDL_NewAudioStream(this->spec.format,
|
||||
this->spec.channels, this->spec.freq,
|
||||
this->callbackspec.format,
|
||||
this->callbackspec.channels,
|
||||
this->callbackspec.freq);
|
||||
} else {
|
||||
this->stream = SDL_NewAudioStream(this->callbackspec.format,
|
||||
this->callbackspec.channels,
|
||||
this->callbackspec.freq, this->spec.format,
|
||||
this->spec.channels, this->spec.freq);
|
||||
}
|
||||
|
||||
if (!this->stream) {
|
||||
return -1; /* SDL_NewAudioStream should have called SDL_SetError. */
|
||||
}
|
||||
}
|
||||
|
||||
/* make sure our scratch buffer can cover the new device spec. */
|
||||
if (this->spec.size > this->work_buffer_len) {
|
||||
Uint8 *ptr = (Uint8 *)SDL_realloc(this->work_buffer, this->spec.size);
|
||||
if (ptr == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
this->work_buffer = ptr;
|
||||
this->work_buffer_len = this->spec.size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ReleaseWasapiDevice(_THIS);
|
||||
|
||||
static SDL_bool RecoverWasapiDevice(_THIS)
|
||||
{
|
||||
ReleaseWasapiDevice(this); /* dump the lost device's handles. */
|
||||
|
||||
if (this->hidden->default_device_generation) {
|
||||
this->hidden->default_device_generation = SDL_AtomicGet(this->iscapture ? &SDL_IMMDevice_DefaultCaptureGeneration : &SDL_IMMDevice_DefaultPlaybackGeneration);
|
||||
}
|
||||
|
||||
/* this can fail for lots of reasons, but the most likely is we had a
|
||||
non-default device that was disconnected, so we can't recover. Default
|
||||
devices try to reinitialize whatever the new default is, so it's more
|
||||
likely to carry on here, but this handles a non-default device that
|
||||
simply had its format changed in the Windows Control Panel. */
|
||||
if (WASAPI_ActivateDevice(this, SDL_TRUE) == -1) {
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
this->hidden->device_lost = SDL_FALSE;
|
||||
|
||||
return SDL_TRUE; /* okay, carry on with new device details! */
|
||||
}
|
||||
|
||||
static SDL_bool RecoverWasapiIfLost(_THIS)
|
||||
{
|
||||
const int generation = this->hidden->default_device_generation;
|
||||
SDL_bool lost = this->hidden->device_lost;
|
||||
|
||||
if (!SDL_AtomicGet(&this->enabled)) {
|
||||
return SDL_FALSE; /* already failed. */
|
||||
}
|
||||
|
||||
if (!this->hidden->client) {
|
||||
return SDL_TRUE; /* still waiting for activation. */
|
||||
}
|
||||
|
||||
if (!lost && (generation > 0)) { /* is a default device? */
|
||||
const int newgen = SDL_AtomicGet(this->iscapture ? &SDL_IMMDevice_DefaultCaptureGeneration : &SDL_IMMDevice_DefaultPlaybackGeneration);
|
||||
if (generation != newgen) { /* the desired default device was changed, jump over to it. */
|
||||
lost = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return lost ? RecoverWasapiDevice(this) : SDL_TRUE;
|
||||
}
|
||||
|
||||
static Uint8 *WASAPI_GetDeviceBuf(_THIS)
|
||||
{
|
||||
/* get an endpoint buffer from WASAPI. */
|
||||
BYTE *buffer = NULL;
|
||||
|
||||
while (RecoverWasapiIfLost(this) && this->hidden->render) {
|
||||
if (!WasapiFailed(this, IAudioRenderClient_GetBuffer(this->hidden->render, this->spec.samples, &buffer))) {
|
||||
return (Uint8 *)buffer;
|
||||
}
|
||||
SDL_assert(buffer == NULL);
|
||||
}
|
||||
|
||||
return (Uint8 *)buffer;
|
||||
}
|
||||
|
||||
static void WASAPI_PlayDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->render != NULL) { /* definitely activated? */
|
||||
/* WasapiFailed() will mark the device for reacquisition or removal elsewhere. */
|
||||
WasapiFailed(this, IAudioRenderClient_ReleaseBuffer(this->hidden->render, this->spec.samples, 0));
|
||||
}
|
||||
}
|
||||
|
||||
static void WASAPI_WaitDevice(_THIS)
|
||||
{
|
||||
while (RecoverWasapiIfLost(this) && this->hidden->client && this->hidden->event) {
|
||||
DWORD waitResult = WaitForSingleObjectEx(this->hidden->event, 200, FALSE);
|
||||
if (waitResult == WAIT_OBJECT_0) {
|
||||
const UINT32 maxpadding = this->spec.samples;
|
||||
UINT32 padding = 0;
|
||||
if (!WasapiFailed(this, IAudioClient_GetCurrentPadding(this->hidden->client, &padding))) {
|
||||
/*SDL_Log("WASAPI EVENT! padding=%u maxpadding=%u", (unsigned int)padding, (unsigned int)maxpadding);*/
|
||||
if (this->iscapture) {
|
||||
if (padding > 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (padding <= maxpadding) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (waitResult != WAIT_TIMEOUT) {
|
||||
/*SDL_Log("WASAPI FAILED EVENT!");*/
|
||||
IAudioClient_Stop(this->hidden->client);
|
||||
SDL_OpenedAudioDeviceDisconnected(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int WASAPI_CaptureFromDevice(_THIS, void *buffer, int buflen)
|
||||
{
|
||||
SDL_AudioStream *stream = this->hidden->capturestream;
|
||||
const int avail = SDL_AudioStreamAvailable(stream);
|
||||
if (avail > 0) {
|
||||
const int cpy = SDL_min(buflen, avail);
|
||||
SDL_AudioStreamGet(stream, buffer, cpy);
|
||||
return cpy;
|
||||
}
|
||||
|
||||
while (RecoverWasapiIfLost(this)) {
|
||||
HRESULT ret;
|
||||
BYTE *ptr = NULL;
|
||||
UINT32 frames = 0;
|
||||
DWORD flags = 0;
|
||||
|
||||
/* uhoh, client isn't activated yet, just return silence. */
|
||||
if (!this->hidden->capture) {
|
||||
/* Delay so we run at about the speed that audio would be arriving. */
|
||||
SDL_Delay(((this->spec.samples * 1000) / this->spec.freq));
|
||||
SDL_memset(buffer, this->spec.silence, buflen);
|
||||
return buflen;
|
||||
}
|
||||
|
||||
ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
|
||||
if (ret != AUDCLNT_S_BUFFER_EMPTY) {
|
||||
WasapiFailed(this, ret); /* mark device lost/failed if necessary. */
|
||||
}
|
||||
|
||||
if ((ret == AUDCLNT_S_BUFFER_EMPTY) || !frames) {
|
||||
WASAPI_WaitDevice(this);
|
||||
} else if (ret == S_OK) {
|
||||
const int total = ((int)frames) * this->hidden->framesize;
|
||||
const int cpy = SDL_min(buflen, total);
|
||||
const int leftover = total - cpy;
|
||||
const SDL_bool silent = (flags & AUDCLNT_BUFFERFLAGS_SILENT) ? SDL_TRUE : SDL_FALSE;
|
||||
|
||||
if (silent) {
|
||||
SDL_memset(buffer, this->spec.silence, cpy);
|
||||
} else {
|
||||
SDL_memcpy(buffer, ptr, cpy);
|
||||
}
|
||||
|
||||
if (leftover > 0) {
|
||||
ptr += cpy;
|
||||
if (silent) {
|
||||
SDL_memset(ptr, this->spec.silence, leftover); /* I guess this is safe? */
|
||||
}
|
||||
|
||||
if (SDL_AudioStreamPut(stream, ptr, leftover) == -1) {
|
||||
return -1; /* uhoh, out of memory, etc. Kill device. :( */
|
||||
}
|
||||
}
|
||||
|
||||
ret = IAudioCaptureClient_ReleaseBuffer(this->hidden->capture, frames);
|
||||
WasapiFailed(this, ret); /* mark device lost/failed if necessary. */
|
||||
|
||||
return cpy;
|
||||
}
|
||||
}
|
||||
|
||||
return -1; /* unrecoverable error. */
|
||||
}
|
||||
|
||||
static void WASAPI_FlushCapture(_THIS)
|
||||
{
|
||||
BYTE *ptr = NULL;
|
||||
UINT32 frames = 0;
|
||||
DWORD flags = 0;
|
||||
|
||||
if (!this->hidden->capture) {
|
||||
return; /* not activated yet? */
|
||||
}
|
||||
|
||||
/* just read until we stop getting packets, throwing them away. */
|
||||
while (SDL_TRUE) {
|
||||
const HRESULT ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
|
||||
if (ret == AUDCLNT_S_BUFFER_EMPTY) {
|
||||
break; /* no more buffered data; we're done. */
|
||||
} else if (WasapiFailed(this, ret)) {
|
||||
break; /* failed for some other reason, abort. */
|
||||
} else if (WasapiFailed(this, IAudioCaptureClient_ReleaseBuffer(this->hidden->capture, frames))) {
|
||||
break; /* something broke. */
|
||||
}
|
||||
}
|
||||
SDL_AudioStreamClear(this->hidden->capturestream);
|
||||
}
|
||||
|
||||
static void ReleaseWasapiDevice(_THIS)
|
||||
{
|
||||
if (this->hidden->client) {
|
||||
IAudioClient_Stop(this->hidden->client);
|
||||
IAudioClient_Release(this->hidden->client);
|
||||
this->hidden->client = NULL;
|
||||
}
|
||||
|
||||
if (this->hidden->render) {
|
||||
IAudioRenderClient_Release(this->hidden->render);
|
||||
this->hidden->render = NULL;
|
||||
}
|
||||
|
||||
if (this->hidden->capture) {
|
||||
IAudioCaptureClient_Release(this->hidden->capture);
|
||||
this->hidden->capture = NULL;
|
||||
}
|
||||
|
||||
if (this->hidden->waveformat) {
|
||||
CoTaskMemFree(this->hidden->waveformat);
|
||||
this->hidden->waveformat = NULL;
|
||||
}
|
||||
|
||||
if (this->hidden->capturestream) {
|
||||
SDL_FreeAudioStream(this->hidden->capturestream);
|
||||
this->hidden->capturestream = NULL;
|
||||
}
|
||||
|
||||
if (this->hidden->activation_handler) {
|
||||
WASAPI_PlatformDeleteActivationHandler(this->hidden->activation_handler);
|
||||
this->hidden->activation_handler = NULL;
|
||||
}
|
||||
|
||||
if (this->hidden->event) {
|
||||
CloseHandle(this->hidden->event);
|
||||
this->hidden->event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void WASAPI_CloseDevice(_THIS)
|
||||
{
|
||||
WASAPI_UnrefDevice(this);
|
||||
}
|
||||
|
||||
void WASAPI_RefDevice(_THIS)
|
||||
{
|
||||
SDL_AtomicIncRef(&this->hidden->refcount);
|
||||
}
|
||||
|
||||
void WASAPI_UnrefDevice(_THIS)
|
||||
{
|
||||
if (!SDL_AtomicDecRef(&this->hidden->refcount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* actual closing happens here. */
|
||||
|
||||
/* don't touch this->hidden->task in here; it has to be reverted from
|
||||
our callback thread. We do that in WASAPI_ThreadDeinit().
|
||||
(likewise for this->hidden->coinitialized). */
|
||||
ReleaseWasapiDevice(this);
|
||||
|
||||
if (SDL_ThreadID() == this->hidden->open_threadid) {
|
||||
WIN_CoUninitialize(); /* if you closed from a different thread than you opened, sorry, it's a leak. We can't help you. */
|
||||
}
|
||||
|
||||
SDL_free(this->hidden->devid);
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
/* This is called once a device is activated, possibly asynchronously. */
|
||||
int WASAPI_PrepDevice(_THIS, const SDL_bool updatestream)
|
||||
{
|
||||
/* !!! FIXME: we could request an exclusive mode stream, which is lower latency;
|
||||
!!! it will write into the kernel's audio buffer directly instead of
|
||||
!!! shared memory that a user-mode mixer then writes to the kernel with
|
||||
!!! everything else. Doing this means any other sound using this device will
|
||||
!!! stop playing, including the user's MP3 player and system notification
|
||||
!!! sounds. You'd probably need to release the device when the app isn't in
|
||||
!!! the foreground, to be a good citizen of the system. It's doable, but it's
|
||||
!!! more work and causes some annoyances, and I don't know what the latency
|
||||
!!! wins actually look like. Maybe add a hint to force exclusive mode at
|
||||
!!! some point. To be sure, defaulting to shared mode is the right thing to
|
||||
!!! do in any case. */
|
||||
const SDL_AudioSpec oldspec = this->spec;
|
||||
const AUDCLNT_SHAREMODE sharemode = AUDCLNT_SHAREMODE_SHARED;
|
||||
UINT32 bufsize = 0; /* this is in sample frames, not samples, not bytes. */
|
||||
REFERENCE_TIME default_period = 0;
|
||||
IAudioClient *client = this->hidden->client;
|
||||
IAudioRenderClient *render = NULL;
|
||||
IAudioCaptureClient *capture = NULL;
|
||||
WAVEFORMATEX *waveformat = NULL;
|
||||
SDL_AudioFormat test_format;
|
||||
SDL_AudioFormat wasapi_format = 0;
|
||||
HRESULT ret = S_OK;
|
||||
DWORD streamflags = 0;
|
||||
|
||||
SDL_assert(client != NULL);
|
||||
|
||||
#if defined(__WINRT__) || defined(__GDK__) /* CreateEventEx() arrived in Vista, so we need an #ifdef for XP. */
|
||||
this->hidden->event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
|
||||
#else
|
||||
this->hidden->event = CreateEventW(NULL, 0, 0, NULL);
|
||||
#endif
|
||||
|
||||
if (this->hidden->event == NULL) {
|
||||
return WIN_SetError("WASAPI can't create an event handle");
|
||||
}
|
||||
|
||||
ret = IAudioClient_GetMixFormat(client, &waveformat);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't determine mix format", ret);
|
||||
}
|
||||
|
||||
SDL_assert(waveformat != NULL);
|
||||
this->hidden->waveformat = waveformat;
|
||||
|
||||
this->spec.channels = (Uint8)waveformat->nChannels;
|
||||
|
||||
/* Make sure we have a valid format that we can convert to whatever WASAPI wants. */
|
||||
wasapi_format = WaveFormatToSDLFormat(waveformat);
|
||||
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
if (test_format == wasapi_format) {
|
||||
this->spec.format = test_format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "wasapi");
|
||||
}
|
||||
|
||||
ret = IAudioClient_GetDevicePeriod(client, &default_period, NULL);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't determine minimum device period", ret);
|
||||
}
|
||||
|
||||
/* we've gotten reports that WASAPI's resampler introduces distortions, but in the short term
|
||||
it fixes some other WASAPI-specific quirks we haven't quite tracked down.
|
||||
Refer to bug #6326 for the immediate concern. */
|
||||
#if 0
|
||||
this->spec.freq = waveformat->nSamplesPerSec; /* force sampling rate so our resampler kicks in, if necessary. */
|
||||
#else
|
||||
/* favor WASAPI's resampler over our own */
|
||||
if (this->spec.freq != waveformat->nSamplesPerSec) {
|
||||
streamflags |= (AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY);
|
||||
waveformat->nSamplesPerSec = this->spec.freq;
|
||||
waveformat->nAvgBytesPerSec = waveformat->nSamplesPerSec * waveformat->nChannels * (waveformat->wBitsPerSample / 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
||||
ret = IAudioClient_Initialize(client, sharemode, streamflags, 0, 0, waveformat, NULL);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't initialize audio client", ret);
|
||||
}
|
||||
|
||||
ret = IAudioClient_SetEventHandle(client, this->hidden->event);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't set event handle", ret);
|
||||
}
|
||||
|
||||
ret = IAudioClient_GetBufferSize(client, &bufsize);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't determine buffer size", ret);
|
||||
}
|
||||
|
||||
/* Match the callback size to the period size to cut down on the number of
|
||||
interrupts waited for in each call to WaitDevice */
|
||||
{
|
||||
const float period_millis = default_period / 10000.0f;
|
||||
const float period_frames = period_millis * this->spec.freq / 1000.0f;
|
||||
this->spec.samples = (Uint16)SDL_ceilf(period_frames);
|
||||
}
|
||||
|
||||
/* Update the fragment size as size in bytes */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
this->hidden->framesize = (SDL_AUDIO_BITSIZE(this->spec.format) / 8) * this->spec.channels;
|
||||
|
||||
if (this->iscapture) {
|
||||
this->hidden->capturestream = SDL_NewAudioStream(this->spec.format, this->spec.channels, this->spec.freq, this->spec.format, this->spec.channels, this->spec.freq);
|
||||
if (!this->hidden->capturestream) {
|
||||
return -1; /* already set SDL_Error */
|
||||
}
|
||||
|
||||
ret = IAudioClient_GetService(client, &SDL_IID_IAudioCaptureClient, (void **)&capture);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't get capture client service", ret);
|
||||
}
|
||||
|
||||
SDL_assert(capture != NULL);
|
||||
this->hidden->capture = capture;
|
||||
ret = IAudioClient_Start(client);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't start capture", ret);
|
||||
}
|
||||
|
||||
WASAPI_FlushCapture(this); /* MSDN says you should flush capture endpoint right after startup. */
|
||||
} else {
|
||||
ret = IAudioClient_GetService(client, &SDL_IID_IAudioRenderClient, (void **)&render);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't get render client service", ret);
|
||||
}
|
||||
|
||||
SDL_assert(render != NULL);
|
||||
this->hidden->render = render;
|
||||
ret = IAudioClient_Start(client);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't start playback", ret);
|
||||
}
|
||||
}
|
||||
|
||||
if (updatestream) {
|
||||
return UpdateAudioStream(this, &oldspec);
|
||||
}
|
||||
|
||||
return 0; /* good to go. */
|
||||
}
|
||||
|
||||
static int WASAPI_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
LPCWSTR devid = (LPCWSTR)this->handle;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc(sizeof(*this->hidden));
|
||||
if (this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(this->hidden);
|
||||
|
||||
WASAPI_RefDevice(this); /* so CloseDevice() will unref to zero. */
|
||||
|
||||
if (FAILED(WIN_CoInitialize())) { /* WASAPI uses COM, we need to make sure it's initialized. You have to close the device from the same thread!! */
|
||||
return SDL_SetError("WIN_CoInitialize failed during WASAPI device open");
|
||||
}
|
||||
this->hidden->open_threadid = SDL_ThreadID(); /* set this _after_ coinitialize so we don't uninit if device fails at the wrong moment. */
|
||||
|
||||
if (!devid) { /* is default device? */
|
||||
this->hidden->default_device_generation = SDL_AtomicGet(this->iscapture ? &SDL_IMMDevice_DefaultCaptureGeneration : &SDL_IMMDevice_DefaultPlaybackGeneration);
|
||||
} else {
|
||||
this->hidden->devid = SDL_wcsdup(devid);
|
||||
if (!this->hidden->devid) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
}
|
||||
|
||||
if (WASAPI_ActivateDevice(this, SDL_FALSE) == -1) {
|
||||
return -1; /* already set error. */
|
||||
}
|
||||
|
||||
/* Ready, but waiting for async device activation.
|
||||
Until activation is successful, we will report silence from capture
|
||||
devices and ignore data on playback devices.
|
||||
Also, since we don't know the _actual_ device format until after
|
||||
activation, we let the app have whatever it asks for. We set up
|
||||
an SDL_AudioStream to convert, if necessary, once the activation
|
||||
completes. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void WASAPI_ThreadInit(_THIS)
|
||||
{
|
||||
WASAPI_PlatformThreadInit(this);
|
||||
}
|
||||
|
||||
static void WASAPI_ThreadDeinit(_THIS)
|
||||
{
|
||||
WASAPI_PlatformThreadDeinit(this);
|
||||
}
|
||||
|
||||
static void WASAPI_Deinitialize(void)
|
||||
{
|
||||
WASAPI_PlatformDeinit();
|
||||
}
|
||||
|
||||
static SDL_bool WASAPI_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (WASAPI_PlatformInit() == -1) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = WASAPI_DetectDevices;
|
||||
impl->ThreadInit = WASAPI_ThreadInit;
|
||||
impl->ThreadDeinit = WASAPI_ThreadDeinit;
|
||||
impl->OpenDevice = WASAPI_OpenDevice;
|
||||
impl->PlayDevice = WASAPI_PlayDevice;
|
||||
impl->WaitDevice = WASAPI_WaitDevice;
|
||||
impl->GetDeviceBuf = WASAPI_GetDeviceBuf;
|
||||
impl->CaptureFromDevice = WASAPI_CaptureFromDevice;
|
||||
impl->FlushCapture = WASAPI_FlushCapture;
|
||||
impl->CloseDevice = WASAPI_CloseDevice;
|
||||
impl->Deinitialize = WASAPI_Deinitialize;
|
||||
impl->GetDefaultAudioInfo = WASAPI_GetDefaultAudioInfo;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->SupportsNonPow2Samples = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap WASAPI_bootstrap = {
|
||||
"wasapi", "WASAPI", WASAPI_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_WASAPI */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
80
third_party/SDL/src/audio/wasapi/SDL_wasapi.h
vendored
Normal file
80
third_party/SDL/src/audio/wasapi/SDL_wasapi.h
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_wasapi_h_
|
||||
#define SDL_wasapi_h_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#ifdef __cplusplus
|
||||
#define _THIS SDL_AudioDevice *_this
|
||||
#else
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
#endif
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
SDL_atomic_t refcount;
|
||||
WCHAR *devid;
|
||||
WAVEFORMATEX *waveformat;
|
||||
IAudioClient *client;
|
||||
IAudioRenderClient *render;
|
||||
IAudioCaptureClient *capture;
|
||||
SDL_AudioStream *capturestream;
|
||||
HANDLE event;
|
||||
HANDLE task;
|
||||
SDL_threadID open_threadid;
|
||||
SDL_bool coinitialized;
|
||||
int framesize;
|
||||
int default_device_generation;
|
||||
SDL_bool device_lost;
|
||||
void *activation_handler;
|
||||
SDL_atomic_t just_activated;
|
||||
};
|
||||
|
||||
/* win32 and winrt implementations call into these. */
|
||||
int WASAPI_PrepDevice(_THIS, const SDL_bool updatestream);
|
||||
void WASAPI_RefDevice(_THIS);
|
||||
void WASAPI_UnrefDevice(_THIS);
|
||||
|
||||
/* These are functions that are implemented differently for Windows vs WinRT. */
|
||||
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(_THIS, const SDL_bool isrecovery);
|
||||
void WASAPI_PlatformThreadInit(_THIS);
|
||||
void WASAPI_PlatformThreadDeinit(_THIS);
|
||||
void WASAPI_PlatformDeleteActivationHandler(void *handler);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SDL_wasapi_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
153
third_party/SDL/src/audio/wasapi/SDL_wasapi_win32.c
vendored
Normal file
153
third_party/SDL/src/audio/wasapi/SDL_wasapi_win32.c
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
/* This is code that Windows uses to talk to WASAPI-related system APIs.
|
||||
This is for non-WinRT desktop apps. The C++/CX implementation of these
|
||||
functions, exclusive to WinRT, are in SDL_wasapi_winrt.cpp.
|
||||
The code in SDL_wasapi.c is used by both standard Windows and WinRT builds
|
||||
to deal with audio and calls into these functions. */
|
||||
|
||||
#if SDL_AUDIO_DRIVER_WASAPI && !defined(__WINRT__)
|
||||
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
#include "../../core/windows/SDL_immdevice.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#include <audioclient.h>
|
||||
|
||||
#include "SDL_wasapi.h"
|
||||
|
||||
/* handle to Avrt.dll--Vista and later!--for flagging the callback thread as "Pro Audio" (low latency). */
|
||||
static HMODULE libavrt = NULL;
|
||||
typedef HANDLE(WINAPI *pfnAvSetMmThreadCharacteristicsW)(LPCWSTR, LPDWORD);
|
||||
typedef BOOL(WINAPI *pfnAvRevertMmThreadCharacteristics)(HANDLE);
|
||||
static pfnAvSetMmThreadCharacteristicsW pAvSetMmThreadCharacteristicsW = NULL;
|
||||
static pfnAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL;
|
||||
|
||||
/* Some GUIDs we need to know without linking to libraries that aren't available before Vista. */
|
||||
static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } };
|
||||
|
||||
int WASAPI_PlatformInit(void)
|
||||
{
|
||||
if (SDL_IMMDevice_Init() < 0) {
|
||||
return -1; /* This is set by SDL_IMMDevice_Init */
|
||||
}
|
||||
|
||||
libavrt = LoadLibrary(TEXT("avrt.dll")); /* this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now! */
|
||||
if (libavrt) {
|
||||
pAvSetMmThreadCharacteristicsW = (pfnAvSetMmThreadCharacteristicsW)GetProcAddress(libavrt, "AvSetMmThreadCharacteristicsW");
|
||||
pAvRevertMmThreadCharacteristics = (pfnAvRevertMmThreadCharacteristics)GetProcAddress(libavrt, "AvRevertMmThreadCharacteristics");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WASAPI_PlatformDeinit(void)
|
||||
{
|
||||
if (libavrt) {
|
||||
FreeLibrary(libavrt);
|
||||
libavrt = NULL;
|
||||
}
|
||||
|
||||
pAvSetMmThreadCharacteristicsW = NULL;
|
||||
pAvRevertMmThreadCharacteristics = NULL;
|
||||
|
||||
SDL_IMMDevice_Quit();
|
||||
}
|
||||
|
||||
void WASAPI_PlatformThreadInit(_THIS)
|
||||
{
|
||||
/* this thread uses COM. */
|
||||
if (SUCCEEDED(WIN_CoInitialize())) { /* can't report errors, hope it worked! */
|
||||
this->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);
|
||||
}
|
||||
}
|
||||
|
||||
void WASAPI_PlatformThreadDeinit(_THIS)
|
||||
{
|
||||
/* Set this thread back to normal priority. */
|
||||
if (this->hidden->task && pAvRevertMmThreadCharacteristics) {
|
||||
pAvRevertMmThreadCharacteristics(this->hidden->task);
|
||||
this->hidden->task = NULL;
|
||||
}
|
||||
|
||||
if (this->hidden->coinitialized) {
|
||||
WIN_CoUninitialize();
|
||||
this->hidden->coinitialized = SDL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
int WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery)
|
||||
{
|
||||
IMMDevice *device = NULL;
|
||||
HRESULT ret;
|
||||
|
||||
if (SDL_IMMDevice_Get(this->hidden->devid, &device, this->iscapture) < 0) {
|
||||
this->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);
|
||||
|
||||
if (FAILED(ret)) {
|
||||
SDL_assert(this->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. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0; /* good to go. */
|
||||
}
|
||||
|
||||
void WASAPI_EnumerateEndpoints(void)
|
||||
{
|
||||
SDL_IMMDevice_EnumerateEndpoints(SDL_FALSE);
|
||||
}
|
||||
|
||||
int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
|
||||
{
|
||||
return SDL_IMMDevice_GetDefaultAudioInfo(name, spec, iscapture);
|
||||
}
|
||||
|
||||
void WASAPI_PlatformDeleteActivationHandler(void *handler)
|
||||
{
|
||||
/* not asynchronous. */
|
||||
SDL_assert(!"This function should have only been called on WinRT.");
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_WASAPI && !defined(__WINRT__) */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
431
third_party/SDL/src/audio/wasapi/SDL_wasapi_winrt.cpp
vendored
Normal file
431
third_party/SDL/src/audio/wasapi/SDL_wasapi_winrt.cpp
vendored
Normal file
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
// This is C++/CX code that the WinRT port uses to talk to WASAPI-related
|
||||
// system APIs. The C implementation of these functions, for non-WinRT apps,
|
||||
// is in SDL_wasapi_win32.c. The code in SDL_wasapi.c is used by both standard
|
||||
// Windows and WinRT builds to deal with audio and calls into these functions.
|
||||
|
||||
#if SDL_AUDIO_DRIVER_WASAPI && defined(__WINRT__)
|
||||
|
||||
#include <Windows.h>
|
||||
#include <windows.ui.core.h>
|
||||
#include <windows.devices.enumeration.h>
|
||||
#include <windows.media.devices.h>
|
||||
#include <wrl/implements.h>
|
||||
#include <collection.h>
|
||||
|
||||
extern "C" {
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
}
|
||||
|
||||
#define COBJMACROS
|
||||
#include <mmdeviceapi.h>
|
||||
#include <audioclient.h>
|
||||
|
||||
#include "SDL_wasapi.h"
|
||||
|
||||
using namespace Windows::Devices::Enumeration;
|
||||
using namespace Windows::Media::Devices;
|
||||
using namespace Windows::Foundation;
|
||||
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_atomic_t SDL_IMMDevice_DefaultPlaybackGeneration;
|
||||
SDL_atomic_t SDL_IMMDevice_DefaultCaptureGeneration;
|
||||
}
|
||||
|
||||
/* This is a list of device id strings we have inflight, so we have consistent pointers to the same device. */
|
||||
typedef struct DevIdList
|
||||
{
|
||||
WCHAR *str;
|
||||
struct DevIdList *next;
|
||||
} DevIdList;
|
||||
|
||||
static DevIdList *deviceid_list = NULL;
|
||||
|
||||
class SDL_WasapiDeviceEventHandler
|
||||
{
|
||||
public:
|
||||
SDL_WasapiDeviceEventHandler(const SDL_bool _iscapture);
|
||||
~SDL_WasapiDeviceEventHandler();
|
||||
void OnDeviceAdded(DeviceWatcher ^ sender, DeviceInformation ^ args);
|
||||
void OnDeviceRemoved(DeviceWatcher ^ sender, DeviceInformationUpdate ^ args);
|
||||
void OnDeviceUpdated(DeviceWatcher ^ sender, DeviceInformationUpdate ^ args);
|
||||
void OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args);
|
||||
void OnDefaultRenderDeviceChanged(Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args);
|
||||
void OnDefaultCaptureDeviceChanged(Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args);
|
||||
SDL_semaphore *completed;
|
||||
|
||||
private:
|
||||
const SDL_bool iscapture;
|
||||
DeviceWatcher ^ watcher;
|
||||
Windows::Foundation::EventRegistrationToken added_handler;
|
||||
Windows::Foundation::EventRegistrationToken removed_handler;
|
||||
Windows::Foundation::EventRegistrationToken updated_handler;
|
||||
Windows::Foundation::EventRegistrationToken completed_handler;
|
||||
Windows::Foundation::EventRegistrationToken default_changed_handler;
|
||||
};
|
||||
|
||||
SDL_WasapiDeviceEventHandler::SDL_WasapiDeviceEventHandler(const SDL_bool _iscapture)
|
||||
: iscapture(_iscapture), completed(SDL_CreateSemaphore(0))
|
||||
{
|
||||
if (!completed)
|
||||
return; // uhoh.
|
||||
|
||||
Platform::String ^ selector = _iscapture ? MediaDevice::GetAudioCaptureSelector() : MediaDevice::GetAudioRenderSelector();
|
||||
Platform::Collections::Vector<Platform::String ^> properties;
|
||||
properties.Append(SDL_PKEY_AudioEngine_DeviceFormat);
|
||||
watcher = DeviceInformation::CreateWatcher(selector, properties.GetView());
|
||||
if (!watcher)
|
||||
return; // uhoh.
|
||||
|
||||
// !!! FIXME: this doesn't need a lambda here, I think, if I make SDL_WasapiDeviceEventHandler a proper C++/CX class. --ryan.
|
||||
added_handler = watcher->Added += ref new TypedEventHandler<DeviceWatcher ^, DeviceInformation ^>([this](DeviceWatcher ^ sender, DeviceInformation ^ args) { OnDeviceAdded(sender, args); });
|
||||
removed_handler = watcher->Removed += ref new TypedEventHandler<DeviceWatcher ^, DeviceInformationUpdate ^>([this](DeviceWatcher ^ sender, DeviceInformationUpdate ^ args) { OnDeviceRemoved(sender, args); });
|
||||
updated_handler = watcher->Updated += ref new TypedEventHandler<DeviceWatcher ^, DeviceInformationUpdate ^>([this](DeviceWatcher ^ sender, DeviceInformationUpdate ^ args) { OnDeviceUpdated(sender, args); });
|
||||
completed_handler = watcher->EnumerationCompleted += ref new TypedEventHandler<DeviceWatcher ^, Platform::Object ^>([this](DeviceWatcher ^ sender, Platform::Object ^ args) { OnEnumerationCompleted(sender, args); });
|
||||
if (iscapture) {
|
||||
default_changed_handler = MediaDevice::DefaultAudioCaptureDeviceChanged += ref new TypedEventHandler<Platform::Object ^, DefaultAudioCaptureDeviceChangedEventArgs ^>([this](Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args) { OnDefaultCaptureDeviceChanged(sender, args); });
|
||||
} else {
|
||||
default_changed_handler = MediaDevice::DefaultAudioRenderDeviceChanged += ref new TypedEventHandler<Platform::Object ^, DefaultAudioRenderDeviceChangedEventArgs ^>([this](Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args) { OnDefaultRenderDeviceChanged(sender, args); });
|
||||
}
|
||||
watcher->Start();
|
||||
}
|
||||
|
||||
SDL_WasapiDeviceEventHandler::~SDL_WasapiDeviceEventHandler()
|
||||
{
|
||||
if (watcher) {
|
||||
watcher->Added -= added_handler;
|
||||
watcher->Removed -= removed_handler;
|
||||
watcher->Updated -= updated_handler;
|
||||
watcher->EnumerationCompleted -= completed_handler;
|
||||
watcher->Stop();
|
||||
watcher = nullptr;
|
||||
}
|
||||
if (completed) {
|
||||
SDL_DestroySemaphore(completed);
|
||||
completed = nullptr;
|
||||
}
|
||||
|
||||
if (iscapture) {
|
||||
MediaDevice::DefaultAudioCaptureDeviceChanged -= default_changed_handler;
|
||||
} else {
|
||||
MediaDevice::DefaultAudioRenderDeviceChanged -= default_changed_handler;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnDeviceAdded(DeviceWatcher ^ sender, DeviceInformation ^ info)
|
||||
{
|
||||
SDL_assert(sender == this->watcher);
|
||||
char *utf8dev = WIN_StringToUTF8(info->Name->Data());
|
||||
if (utf8dev) {
|
||||
WAVEFORMATEXTENSIBLE fmt;
|
||||
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 {
|
||||
SDL_zero(fmt);
|
||||
}
|
||||
|
||||
WASAPI_AddDevice(this->iscapture, utf8dev, &fmt, info->Id->Data());
|
||||
SDL_free(utf8dev);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnDeviceRemoved(DeviceWatcher ^ sender, DeviceInformationUpdate ^ info)
|
||||
{
|
||||
SDL_assert(sender == this->watcher);
|
||||
WASAPI_RemoveDevice(this->iscapture, info->Id->Data());
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnDeviceUpdated(DeviceWatcher ^ sender, DeviceInformationUpdate ^ args)
|
||||
{
|
||||
SDL_assert(sender == this->watcher);
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args)
|
||||
{
|
||||
SDL_assert(sender == this->watcher);
|
||||
SDL_SemPost(this->completed);
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnDefaultRenderDeviceChanged(Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args)
|
||||
{
|
||||
SDL_assert(!this->iscapture);
|
||||
SDL_AtomicAdd(&SDL_IMMDevice_DefaultPlaybackGeneration, 1);
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnDefaultCaptureDeviceChanged(Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args)
|
||||
{
|
||||
SDL_assert(this->iscapture);
|
||||
SDL_AtomicAdd(&SDL_IMMDevice_DefaultCaptureGeneration, 1);
|
||||
}
|
||||
|
||||
static SDL_WasapiDeviceEventHandler *playback_device_event_handler;
|
||||
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)
|
||||
{
|
||||
// DeviceWatchers will fire an Added event for each existing device at
|
||||
// startup, so we don't need to enumerate them separately before
|
||||
// listening for updates.
|
||||
playback_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_FALSE);
|
||||
capture_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_TRUE);
|
||||
SDL_SemWait(playback_device_event_handler->completed);
|
||||
SDL_SemWait(capture_device_event_handler->completed);
|
||||
}
|
||||
|
||||
struct SDL_WasapiActivationHandler : public RuntimeClass<RuntimeClassFlags<ClassicCom>, FtmBase, IActivateAudioInterfaceCompletionHandler>
|
||||
{
|
||||
SDL_WasapiActivationHandler() : device(nullptr) {}
|
||||
STDMETHOD(ActivateCompleted)
|
||||
(IActivateAudioInterfaceAsyncOperation *operation);
|
||||
SDL_AudioDevice *device;
|
||||
};
|
||||
|
||||
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);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void WASAPI_PlatformDeleteActivationHandler(void *handler)
|
||||
{
|
||||
((SDL_WasapiActivationHandler *)handler)->Release();
|
||||
}
|
||||
|
||||
int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
int WASAPI_ActivateDevice(_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);
|
||||
|
||||
ComPtr<SDL_WasapiActivationHandler> handler = Make<SDL_WasapiActivationHandler>();
|
||||
if (handler == nullptr) {
|
||||
return SDL_SetError("Failed to allocate WASAPI activation handler");
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
WASAPI_RefDevice(_this); /* completion handler will unref it. */
|
||||
IActivateAudioInterfaceAsyncOperation *async = nullptr;
|
||||
const HRESULT ret = ActivateAudioInterfaceAsync(devid, __uuidof(IAudioClient), nullptr, handler.Get(), &async);
|
||||
|
||||
if (FAILED(ret) || async == nullptr) {
|
||||
if (async != nullptr) {
|
||||
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);
|
||||
}
|
||||
|
||||
HRESULT activateRes = S_OK;
|
||||
IUnknown *iunknown = nullptr;
|
||||
const HRESULT getActivateRes = async->GetActivateResult(&activateRes, &iunknown);
|
||||
async->Release();
|
||||
if (FAILED(getActivateRes)) {
|
||||
return WIN_SetErrorFromHRESULT("Failed to get WASAPI activate result", getActivateRes);
|
||||
} else if (FAILED(activateRes)) {
|
||||
return WIN_SetErrorFromHRESULT("Failed to activate WASAPI device", activateRes);
|
||||
}
|
||||
|
||||
iunknown->QueryInterface(IID_PPV_ARGS(&_this->hidden->client));
|
||||
if (!_this->hidden->client) {
|
||||
return SDL_SetError("Failed to query WASAPI client interface");
|
||||
}
|
||||
|
||||
if (WASAPI_PrepDevice(_this, isrecovery) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WASAPI_PlatformThreadInit(_THIS)
|
||||
{
|
||||
// !!! FIXME: set this thread to "Pro Audio" priority.
|
||||
}
|
||||
|
||||
void WASAPI_PlatformThreadDeinit(_THIS)
|
||||
{
|
||||
// !!! 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 AUDIO_F32SYS;
|
||||
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
|
||||
return AUDIO_S16SYS;
|
||||
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
|
||||
return 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 AUDIO_F32SYS;
|
||||
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
|
||||
return AUDIO_S16SYS;
|
||||
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
|
||||
return 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);
|
||||
}
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_WASAPI && defined(__WINRT__)
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
443
third_party/SDL/src/audio/winmm/SDL_winmm.c
vendored
Normal file
443
third_party/SDL/src/audio/winmm/SDL_winmm.c
vendored
Normal file
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_AUDIO_DRIVER_WINMM
|
||||
|
||||
/* Allow access to a raw mixing buffer */
|
||||
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
#include <mmsystem.h>
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_winmm.h"
|
||||
|
||||
/* MinGW32 mmsystem.h doesn't include these structures */
|
||||
#if defined(__MINGW32__) && defined(_MMSYSTEM_H)
|
||||
|
||||
typedef struct tagWAVEINCAPS2W
|
||||
{
|
||||
WORD wMid;
|
||||
WORD wPid;
|
||||
MMVERSION vDriverVersion;
|
||||
WCHAR szPname[MAXPNAMELEN];
|
||||
DWORD dwFormats;
|
||||
WORD wChannels;
|
||||
WORD wReserved1;
|
||||
GUID ManufacturerGuid;
|
||||
GUID ProductGuid;
|
||||
GUID NameGuid;
|
||||
} WAVEINCAPS2W,*PWAVEINCAPS2W,*NPWAVEINCAPS2W,*LPWAVEINCAPS2W;
|
||||
|
||||
typedef struct tagWAVEOUTCAPS2W
|
||||
{
|
||||
WORD wMid;
|
||||
WORD wPid;
|
||||
MMVERSION vDriverVersion;
|
||||
WCHAR szPname[MAXPNAMELEN];
|
||||
DWORD dwFormats;
|
||||
WORD wChannels;
|
||||
WORD wReserved1;
|
||||
DWORD dwSupport;
|
||||
GUID ManufacturerGuid;
|
||||
GUID ProductGuid;
|
||||
GUID NameGuid;
|
||||
} WAVEOUTCAPS2W,*PWAVEOUTCAPS2W,*NPWAVEOUTCAPS2W,*LPWAVEOUTCAPS2W;
|
||||
|
||||
#endif /* defined(__MINGW32__) && defined(_MMSYSTEM_H) */
|
||||
|
||||
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
||||
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
|
||||
#endif
|
||||
|
||||
#define DETECT_DEV_IMPL(iscap, typ, capstyp) \
|
||||
static void DetectWave##typ##Devs(void) { \
|
||||
const UINT iscapture = iscap ? 1 : 0; \
|
||||
const UINT devcount = wave##typ##GetNumDevs(); \
|
||||
capstyp##2W caps; \
|
||||
SDL_AudioSpec spec; \
|
||||
UINT i; \
|
||||
SDL_zero(spec); \
|
||||
for (i = 0; i < devcount; i++) { \
|
||||
if (wave##typ##GetDevCaps(i,(LP##capstyp##W)&caps,sizeof(caps))==MMSYSERR_NOERROR) { \
|
||||
char *name = WIN_LookupAudioDeviceName(caps.szPname,&caps.NameGuid); \
|
||||
if (name != NULL) { \
|
||||
/* Note that freq/format are not filled in, as this information \
|
||||
* is not provided by the caps struct! At best, we get possible \
|
||||
* sample formats, but not an _active_ format. \
|
||||
*/ \
|
||||
spec.channels = (Uint8)caps.wChannels; \
|
||||
SDL_AddAudioDevice((int) iscapture, name, &spec, (void *) ((size_t) i+1)); \
|
||||
SDL_free(name); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
DETECT_DEV_IMPL(SDL_FALSE, Out, WAVEOUTCAPS)
|
||||
DETECT_DEV_IMPL(SDL_TRUE, In, WAVEINCAPS)
|
||||
|
||||
static void WINMM_DetectDevices(void)
|
||||
{
|
||||
DetectWaveInDevs();
|
||||
DetectWaveOutDevs();
|
||||
}
|
||||
|
||||
static void CALLBACK CaptureSound(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
||||
{
|
||||
SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
|
||||
|
||||
/* Only service "buffer is filled" messages */
|
||||
if (uMsg != WIM_DATA)
|
||||
return;
|
||||
|
||||
/* Signal that we have a new buffer of data */
|
||||
ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
|
||||
}
|
||||
|
||||
|
||||
/* The Win32 callback for filling the WAVE device */
|
||||
static void CALLBACK FillSound(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
||||
{
|
||||
SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
|
||||
|
||||
/* Only service "buffer done playing" messages */
|
||||
if (uMsg != WOM_DONE)
|
||||
return;
|
||||
|
||||
/* Signal that we are done playing a buffer */
|
||||
ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
|
||||
}
|
||||
|
||||
static int SetMMerror(const char *function, MMRESULT code)
|
||||
{
|
||||
int len;
|
||||
char errbuf[MAXERRORLENGTH];
|
||||
wchar_t werrbuf[MAXERRORLENGTH];
|
||||
|
||||
SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function);
|
||||
len = SDL_static_cast(int, SDL_strlen(errbuf));
|
||||
|
||||
waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH - len);
|
||||
WideCharToMultiByte(CP_ACP, 0, werrbuf, -1, errbuf + len,
|
||||
MAXERRORLENGTH - len, NULL, NULL);
|
||||
|
||||
return SDL_SetError("%s", errbuf);
|
||||
}
|
||||
|
||||
static void WINMM_WaitDevice(_THIS)
|
||||
{
|
||||
/* Wait for an audio chunk to finish */
|
||||
WaitForSingleObject(this->hidden->audio_sem, INFINITE);
|
||||
}
|
||||
|
||||
static Uint8 *WINMM_GetDeviceBuf(_THIS)
|
||||
{
|
||||
return (Uint8 *) (this->hidden->
|
||||
wavebuf[this->hidden->next_buffer].lpData);
|
||||
}
|
||||
|
||||
static void WINMM_PlayDevice(_THIS)
|
||||
{
|
||||
/* Queue it up */
|
||||
waveOutWrite(this->hidden->hout,
|
||||
&this->hidden->wavebuf[this->hidden->next_buffer],
|
||||
sizeof(this->hidden->wavebuf[0]));
|
||||
this->hidden->next_buffer = (this->hidden->next_buffer + 1) % NUM_BUFFERS;
|
||||
}
|
||||
|
||||
static int WINMM_CaptureFromDevice(_THIS, void *buffer, int buflen)
|
||||
{
|
||||
const int nextbuf = this->hidden->next_buffer;
|
||||
MMRESULT result;
|
||||
|
||||
SDL_assert(buflen == this->spec.size);
|
||||
|
||||
/* Wait for an audio chunk to finish */
|
||||
WaitForSingleObject(this->hidden->audio_sem, INFINITE);
|
||||
|
||||
/* Copy it to caller's buffer... */
|
||||
SDL_memcpy(buffer, this->hidden->wavebuf[nextbuf].lpData, this->spec.size);
|
||||
|
||||
/* requeue the buffer that just finished. */
|
||||
result = waveInAddBuffer(this->hidden->hin,
|
||||
&this->hidden->wavebuf[nextbuf],
|
||||
sizeof(this->hidden->wavebuf[nextbuf]));
|
||||
if (result != MMSYSERR_NOERROR) {
|
||||
return -1; /* uhoh! Disable the device. */
|
||||
}
|
||||
|
||||
/* queue the next buffer in sequence, next time. */
|
||||
this->hidden->next_buffer = (nextbuf + 1) % NUM_BUFFERS;
|
||||
return this->spec.size;
|
||||
}
|
||||
|
||||
static void WINMM_FlushCapture(_THIS)
|
||||
{
|
||||
/* Wait for an audio chunk to finish */
|
||||
if (WaitForSingleObject(this->hidden->audio_sem, 0) == WAIT_OBJECT_0) {
|
||||
const int nextbuf = this->hidden->next_buffer;
|
||||
/* requeue the buffer that just finished without reading from it. */
|
||||
waveInAddBuffer(this->hidden->hin,
|
||||
&this->hidden->wavebuf[nextbuf],
|
||||
sizeof(this->hidden->wavebuf[nextbuf]));
|
||||
this->hidden->next_buffer = (nextbuf + 1) % NUM_BUFFERS;
|
||||
}
|
||||
}
|
||||
|
||||
static void WINMM_CloseDevice(_THIS)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (this->hidden->hout) {
|
||||
waveOutReset(this->hidden->hout);
|
||||
|
||||
/* Clean up mixing buffers */
|
||||
for (i = 0; i < NUM_BUFFERS; ++i) {
|
||||
if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
|
||||
waveOutUnprepareHeader(this->hidden->hout,
|
||||
&this->hidden->wavebuf[i],
|
||||
sizeof(this->hidden->wavebuf[i]));
|
||||
}
|
||||
}
|
||||
|
||||
waveOutClose(this->hidden->hout);
|
||||
}
|
||||
|
||||
if (this->hidden->hin) {
|
||||
waveInReset(this->hidden->hin);
|
||||
|
||||
/* Clean up mixing buffers */
|
||||
for (i = 0; i < NUM_BUFFERS; ++i) {
|
||||
if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
|
||||
waveInUnprepareHeader(this->hidden->hin,
|
||||
&this->hidden->wavebuf[i],
|
||||
sizeof(this->hidden->wavebuf[i]));
|
||||
}
|
||||
}
|
||||
waveInClose(this->hidden->hin);
|
||||
}
|
||||
|
||||
if (this->hidden->audio_sem) {
|
||||
CloseHandle(this->hidden->audio_sem);
|
||||
}
|
||||
|
||||
SDL_free(this->hidden->mixbuf);
|
||||
SDL_free(this->hidden);
|
||||
}
|
||||
|
||||
static SDL_bool PrepWaveFormat(_THIS, UINT devId, WAVEFORMATEX *pfmt, const int iscapture)
|
||||
{
|
||||
SDL_zerop(pfmt);
|
||||
|
||||
if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
|
||||
pfmt->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
} else {
|
||||
pfmt->wFormatTag = WAVE_FORMAT_PCM;
|
||||
}
|
||||
pfmt->wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
|
||||
|
||||
pfmt->nChannels = this->spec.channels;
|
||||
pfmt->nSamplesPerSec = this->spec.freq;
|
||||
pfmt->nBlockAlign = pfmt->nChannels * (pfmt->wBitsPerSample / 8);
|
||||
pfmt->nAvgBytesPerSec = pfmt->nSamplesPerSec * pfmt->nBlockAlign;
|
||||
|
||||
if (iscapture) {
|
||||
return (waveInOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
|
||||
} else {
|
||||
return (waveOutOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int WINMM_OpenDevice(_THIS, const char *devname)
|
||||
{
|
||||
SDL_AudioFormat test_format;
|
||||
SDL_bool iscapture = this->iscapture;
|
||||
void *handle = this->handle;
|
||||
MMRESULT result;
|
||||
WAVEFORMATEX waveformat;
|
||||
UINT devId = WAVE_MAPPER; /* WAVE_MAPPER == choose system's default */
|
||||
UINT i;
|
||||
|
||||
if (handle != NULL) { /* specific device requested? */
|
||||
/* -1 because we increment the original value to avoid NULL. */
|
||||
const size_t val = ((size_t) handle) - 1;
|
||||
devId = (UINT) val;
|
||||
}
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*this->hidden));
|
||||
if (this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(this->hidden);
|
||||
|
||||
/* Initialize the wavebuf structures for closing */
|
||||
for (i = 0; i < NUM_BUFFERS; ++i)
|
||||
this->hidden->wavebuf[i].dwUser = 0xFFFF;
|
||||
|
||||
if (this->spec.channels > 2)
|
||||
this->spec.channels = 2; /* !!! FIXME: is this right? */
|
||||
|
||||
for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
|
||||
switch (test_format) {
|
||||
case AUDIO_U8:
|
||||
case AUDIO_S16:
|
||||
case AUDIO_S32:
|
||||
case AUDIO_F32:
|
||||
this->spec.format = test_format;
|
||||
if (PrepWaveFormat(this, devId, &waveformat, iscapture)) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "winmm");
|
||||
}
|
||||
|
||||
/* Update the fragment size as size in bytes */
|
||||
SDL_CalculateAudioSpec(&this->spec);
|
||||
|
||||
/* Open the audio device */
|
||||
if (iscapture) {
|
||||
result = waveInOpen(&this->hidden->hin, devId, &waveformat,
|
||||
(DWORD_PTR) CaptureSound, (DWORD_PTR) this,
|
||||
CALLBACK_FUNCTION);
|
||||
if (result != MMSYSERR_NOERROR) {
|
||||
return SetMMerror("waveInOpen()", result);
|
||||
}
|
||||
} else {
|
||||
result = waveOutOpen(&this->hidden->hout, devId, &waveformat,
|
||||
(DWORD_PTR) FillSound, (DWORD_PTR) this,
|
||||
CALLBACK_FUNCTION);
|
||||
if (result != MMSYSERR_NOERROR) {
|
||||
return SetMMerror("waveOutOpen()", result);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SOUND_DEBUG
|
||||
/* Check the sound device we retrieved */
|
||||
{
|
||||
if (iscapture) {
|
||||
WAVEINCAPS caps;
|
||||
result = waveInGetDevCaps((UINT) this->hidden->hout,
|
||||
&caps, sizeof(caps));
|
||||
if (result != MMSYSERR_NOERROR) {
|
||||
return SetMMerror("waveInGetDevCaps()", result);
|
||||
}
|
||||
printf("Audio device: %s\n", caps.szPname);
|
||||
} else {
|
||||
WAVEOUTCAPS caps;
|
||||
result = waveOutGetDevCaps((UINT) this->hidden->hout,
|
||||
&caps, sizeof(caps));
|
||||
if (result != MMSYSERR_NOERROR) {
|
||||
return SetMMerror("waveOutGetDevCaps()", result);
|
||||
}
|
||||
printf("Audio device: %s\n", caps.szPname);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Create the audio buffer semaphore */
|
||||
this->hidden->audio_sem = CreateSemaphore(NULL, iscapture ? 0 : NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
|
||||
if (this->hidden->audio_sem == NULL) {
|
||||
return SDL_SetError("Couldn't create semaphore");
|
||||
}
|
||||
|
||||
/* Create the sound buffers */
|
||||
this->hidden->mixbuf =
|
||||
(Uint8 *) SDL_malloc(NUM_BUFFERS * this->spec.size);
|
||||
if (this->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
SDL_zeroa(this->hidden->wavebuf);
|
||||
for (i = 0; i < NUM_BUFFERS; ++i) {
|
||||
this->hidden->wavebuf[i].dwBufferLength = this->spec.size;
|
||||
this->hidden->wavebuf[i].dwFlags = WHDR_DONE;
|
||||
this->hidden->wavebuf[i].lpData =
|
||||
(LPSTR) & this->hidden->mixbuf[i * this->spec.size];
|
||||
|
||||
if (iscapture) {
|
||||
result = waveInPrepareHeader(this->hidden->hin,
|
||||
&this->hidden->wavebuf[i],
|
||||
sizeof(this->hidden->wavebuf[i]));
|
||||
if (result != MMSYSERR_NOERROR) {
|
||||
return SetMMerror("waveInPrepareHeader()", result);
|
||||
}
|
||||
|
||||
result = waveInAddBuffer(this->hidden->hin,
|
||||
&this->hidden->wavebuf[i],
|
||||
sizeof(this->hidden->wavebuf[i]));
|
||||
if (result != MMSYSERR_NOERROR) {
|
||||
return SetMMerror("waveInAddBuffer()", result);
|
||||
}
|
||||
} else {
|
||||
result = waveOutPrepareHeader(this->hidden->hout,
|
||||
&this->hidden->wavebuf[i],
|
||||
sizeof(this->hidden->wavebuf[i]));
|
||||
if (result != MMSYSERR_NOERROR) {
|
||||
return SetMMerror("waveOutPrepareHeader()", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iscapture) {
|
||||
result = waveInStart(this->hidden->hin);
|
||||
if (result != MMSYSERR_NOERROR) {
|
||||
return SetMMerror("waveInStart()", result);
|
||||
}
|
||||
}
|
||||
|
||||
return 0; /* Ready to go! */
|
||||
}
|
||||
|
||||
static SDL_bool WINMM_Init(SDL_AudioDriverImpl * impl)
|
||||
{
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = WINMM_DetectDevices;
|
||||
impl->OpenDevice = WINMM_OpenDevice;
|
||||
impl->PlayDevice = WINMM_PlayDevice;
|
||||
impl->WaitDevice = WINMM_WaitDevice;
|
||||
impl->GetDeviceBuf = WINMM_GetDeviceBuf;
|
||||
impl->CaptureFromDevice = WINMM_CaptureFromDevice;
|
||||
impl->FlushCapture = WINMM_FlushCapture;
|
||||
impl->CloseDevice = WINMM_CloseDevice;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap WINMM_bootstrap = {
|
||||
"winmm", "Windows Waveform Audio", WINMM_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_WINMM */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
45
third_party/SDL/src/audio/winmm/SDL_winmm.h
vendored
Normal file
45
third_party/SDL/src/audio/winmm/SDL_winmm.h
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_winmm_h_
|
||||
#define SDL_winmm_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* Hidden "this" pointer for the audio functions */
|
||||
#define _THIS SDL_AudioDevice *this
|
||||
|
||||
#define NUM_BUFFERS 2 /* -- Don't lower this! */
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
HWAVEOUT hout;
|
||||
HWAVEIN hin;
|
||||
HANDLE audio_sem;
|
||||
Uint8 *mixbuf; /* The raw allocated mixing buffer */
|
||||
WAVEHDR wavebuf[NUM_BUFFERS]; /* Wave audio fragments */
|
||||
int next_buffer;
|
||||
};
|
||||
|
||||
#endif /* SDL_winmm_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
2709
third_party/SDL/src/core/android/SDL_android.c
vendored
Normal file
2709
third_party/SDL/src/core/android/SDL_android.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
156
third_party/SDL/src/core/android/SDL_android.h
vendored
Normal file
156
third_party/SDL/src/core/android/SDL_android.h
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
#include "SDL_system.h"
|
||||
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
/* *INDENT-OFF* */
|
||||
extern "C" {
|
||||
/* *INDENT-ON* */
|
||||
#endif
|
||||
|
||||
#include <EGL/eglplatform.h>
|
||||
#include <android/native_window_jni.h>
|
||||
|
||||
#include "SDL_audio.h"
|
||||
#include "SDL_rect.h"
|
||||
#include "SDL_video.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);
|
||||
extern void Android_JNI_SetOrientation(int w, int h, int resizable, const char *hint);
|
||||
extern void Android_JNI_MinizeWindow(void);
|
||||
extern SDL_bool Android_JNI_ShouldMinimizeOnFocusLoss(void);
|
||||
|
||||
extern SDL_bool Android_JNI_GetAccelerometerValues(float values[3]);
|
||||
extern void Android_JNI_ShowTextInput(SDL_Rect *inputRect);
|
||||
extern void Android_JNI_HideTextInput(void);
|
||||
extern SDL_bool Android_JNI_IsScreenKeyboardShown(void);
|
||||
extern ANativeWindow *Android_JNI_GetNativeWindow(void);
|
||||
|
||||
extern SDL_DisplayOrientation Android_JNI_GetDisplayOrientation(void);
|
||||
extern int Android_JNI_GetDisplayDPI(float *ddpi, float *xdpi, float *ydpi);
|
||||
|
||||
/* Audio support */
|
||||
extern void Android_DetectDevices(void);
|
||||
extern int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spec);
|
||||
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);
|
||||
extern SDL_bool Android_IsChromebook(void);
|
||||
|
||||
#include "SDL_rwops.h"
|
||||
|
||||
int Android_JNI_FileOpen(SDL_RWops* ctx, const char* fileName, const char* mode);
|
||||
Sint64 Android_JNI_FileSize(SDL_RWops* ctx);
|
||||
Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence);
|
||||
size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer, size_t size, size_t maxnum);
|
||||
size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer, size_t size, size_t num);
|
||||
int Android_JNI_FileClose(SDL_RWops* ctx);
|
||||
|
||||
/* Environment support */
|
||||
void Android_JNI_GetManifestEnvironmentVariables(void);
|
||||
|
||||
/* Clipboard support */
|
||||
int Android_JNI_SetClipboardText(const char *text);
|
||||
char *Android_JNI_GetClipboardText(void);
|
||||
SDL_bool Android_JNI_HasClipboardText(void);
|
||||
|
||||
/* Power support */
|
||||
int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seconds, int *percent);
|
||||
|
||||
/* Joystick support */
|
||||
void Android_JNI_PollInputDevices(void);
|
||||
|
||||
/* Haptic support */
|
||||
void Android_JNI_PollHapticDevices(void);
|
||||
void Android_JNI_HapticRun(int device_id, float intensity, int length);
|
||||
void Android_JNI_HapticStop(int device_id);
|
||||
|
||||
/* Video */
|
||||
void Android_JNI_SuspendScreenSaver(SDL_bool suspend);
|
||||
|
||||
/* Touch support */
|
||||
void Android_JNI_InitTouch(void);
|
||||
|
||||
/* Threads */
|
||||
#include <jni.h>
|
||||
JNIEnv *Android_JNI_GetEnv(void);
|
||||
int Android_JNI_SetupThread(void);
|
||||
|
||||
/* Locale */
|
||||
int Android_JNI_GetLocale(char *buf, size_t buflen);
|
||||
|
||||
/* Generic messages */
|
||||
int Android_JNI_SendMessage(int command, int param);
|
||||
|
||||
/* Init */
|
||||
JNIEXPORT void JNICALL SDL_Android_Init(JNIEnv *mEnv, jclass cls);
|
||||
|
||||
/* MessageBox */
|
||||
#include "SDL_messagebox.h"
|
||||
int Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid);
|
||||
|
||||
/* Cursor support */
|
||||
int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y);
|
||||
void Android_JNI_DestroyCustomCursor(int cursorID);
|
||||
SDL_bool Android_JNI_SetCustomCursor(int cursorID);
|
||||
SDL_bool Android_JNI_SetSystemCursor(int cursorID);
|
||||
|
||||
/* Relative mouse support */
|
||||
SDL_bool Android_JNI_SupportsRelativeMouse(void);
|
||||
SDL_bool Android_JNI_SetRelativeMouseEnabled(SDL_bool enabled);
|
||||
|
||||
/* Request permission */
|
||||
SDL_bool Android_JNI_RequestPermission(const char *permission);
|
||||
|
||||
/* Show toast notification */
|
||||
int Android_JNI_ShowToast(const char *message, int duration, int gravity, int xOffset, int yOffset);
|
||||
|
||||
int Android_JNI_OpenURL(const char *url);
|
||||
|
||||
int SDL_GetAndroidSDKVersion(void);
|
||||
|
||||
SDL_bool SDL_IsAndroidTablet(void);
|
||||
SDL_bool SDL_IsAndroidTV(void);
|
||||
SDL_bool SDL_IsChromebook(void);
|
||||
SDL_bool SDL_IsDeXMode(void);
|
||||
|
||||
void Android_ActivityMutex_Lock(void);
|
||||
void Android_ActivityMutex_Unlock(void);
|
||||
void Android_ActivityMutex_Lock_Running(void);
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
/* *INDENT-OFF* */
|
||||
}
|
||||
/* *INDENT-ON* */
|
||||
#endif
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
167
third_party/SDL/src/core/freebsd/SDL_evdev_kbd_default_keyaccmap.h
vendored
Normal file
167
third_party/SDL/src/core/freebsd/SDL_evdev_kbd_default_keyaccmap.h
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
#include <sys/kbio.h>
|
||||
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
/*
|
||||
* Automatically generated from /usr/share/vt/keymaps/us.acc.kbd.
|
||||
* DO NOT EDIT!
|
||||
*/
|
||||
static keymap_t keymap_default_us_acc = { 0x6d, {
|
||||
/* alt
|
||||
* scan cntrl alt alt cntrl
|
||||
* code base shift cntrl shift alt shift cntrl shift spcl flgs
|
||||
* ---------------------------------------------------------------------------
|
||||
*/
|
||||
/*00*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
|
||||
/*01*/{{ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, DBG, }, 0x03,0x00 },
|
||||
/*02*/{{ '1', '!', NOP, NOP, '1', '!', NOP, NOP, }, 0x33,0x00 },
|
||||
/*03*/{{ '2', '@', 0x00, 0x00, '2', '@', 0x00, 0x00, }, 0x00,0x00 },
|
||||
/*04*/{{ '3', '#', NOP, NOP, '3', '#', NOP, NOP, }, 0x33,0x00 },
|
||||
/*05*/{{ '4', '$', NOP, NOP, '4', '$', NOP, NOP, }, 0x33,0x00 },
|
||||
/*06*/{{ '5', '%', NOP, NOP, '5', '%', NOP, NOP, }, 0x33,0x00 },
|
||||
/*07*/{{ '6', '^', 0x1E, 0x1E, '6', DCIR, 0x1E, 0x1E, }, 0x04,0x00 },
|
||||
/*08*/{{ '7', '&', NOP, NOP, '7', '&', NOP, NOP, }, 0x33,0x00 },
|
||||
/*09*/{{ '8', '*', NOP, NOP, '8', DRIN, NOP, NOP, }, 0x37,0x00 },
|
||||
/*0a*/{{ '9', '(', NOP, NOP, '9', '(', NOP, NOP, }, 0x33,0x00 },
|
||||
/*0b*/{{ '0', ')', NOP, NOP, '0', ')', NOP, NOP, }, 0x33,0x00 },
|
||||
/*0c*/{{ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, }, 0x00,0x00 },
|
||||
/*0d*/{{ '=', '+', NOP, NOP, '=', '+', NOP, NOP, }, 0x33,0x00 },
|
||||
/*0e*/{{ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, }, 0x00,0x00 },
|
||||
/*0f*/{{ 0x09, BTAB, NEXT, NEXT, 0x09, BTAB, NOP, NOP, }, 0x77,0x00 },
|
||||
/*10*/{{ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, }, 0x00,0x01 },
|
||||
/*11*/{{ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, }, 0x00,0x01 },
|
||||
/*12*/{{ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, }, 0x00,0x01 },
|
||||
/*13*/{{ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, }, 0x00,0x01 },
|
||||
/*14*/{{ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, }, 0x00,0x01 },
|
||||
/*15*/{{ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, }, 0x00,0x01 },
|
||||
/*16*/{{ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, }, 0x00,0x01 },
|
||||
/*17*/{{ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, }, 0x00,0x01 },
|
||||
/*18*/{{ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, }, 0x00,0x01 },
|
||||
/*19*/{{ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, }, 0x00,0x01 },
|
||||
/*1a*/{{ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, }, 0x00,0x00 },
|
||||
/*1b*/{{ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, }, 0x00,0x00 },
|
||||
/*1c*/{{ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, }, 0x00,0x00 },
|
||||
/*1d*/{{ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, }, 0xFF,0x00 },
|
||||
/*1e*/{{ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, }, 0x00,0x01 },
|
||||
/*1f*/{{ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, }, 0x00,0x01 },
|
||||
/*20*/{{ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, }, 0x00,0x01 },
|
||||
/*21*/{{ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, }, 0x00,0x01 },
|
||||
/*22*/{{ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, }, 0x00,0x01 },
|
||||
/*23*/{{ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, }, 0x00,0x01 },
|
||||
/*24*/{{ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, }, 0x00,0x01 },
|
||||
/*25*/{{ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, }, 0x00,0x01 },
|
||||
/*26*/{{ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, }, 0x00,0x01 },
|
||||
/*27*/{{ ';', ':', NOP, NOP, ';', ':', NOP, NOP, }, 0x33,0x00 },
|
||||
/*28*/{{ '\'', '"', NOP, NOP, DACU, DUML, NOP, NOP, }, 0x3F,0x00 },
|
||||
/*29*/{{ '`', '~', NOP, NOP, DGRA, DTIL, NOP, NOP, }, 0x3F,0x00 },
|
||||
/*2a*/{{ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, }, 0xFF,0x00 },
|
||||
/*2b*/{{ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, }, 0x00,0x00 },
|
||||
/*2c*/{{ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, }, 0x00,0x01 },
|
||||
/*2d*/{{ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, }, 0x00,0x01 },
|
||||
/*2e*/{{ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, }, 0x00,0x01 },
|
||||
/*2f*/{{ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, }, 0x00,0x01 },
|
||||
/*30*/{{ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, }, 0x00,0x01 },
|
||||
/*31*/{{ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, }, 0x00,0x01 },
|
||||
/*32*/{{ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, }, 0x00,0x01 },
|
||||
/*33*/{{ ',', '<', NOP, NOP, DCED, '<', NOP, NOP, }, 0x3B,0x00 },
|
||||
/*34*/{{ '.', '>', NOP, NOP, '.', '>', NOP, NOP, }, 0x33,0x00 },
|
||||
/*35*/{{ '/', '?', NOP, NOP, '/', '?', NOP, NOP, }, 0x33,0x00 },
|
||||
/*36*/{{ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, }, 0xFF,0x00 },
|
||||
/*37*/{{ '*', '*', '*', '*', '*', '*', '*', '*', }, 0x00,0x00 },
|
||||
/*38*/{{ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, }, 0xFF,0x00 },
|
||||
/*39*/{{ ' ', ' ', 0x00, 0x00, ' ', ' ', SUSP, SUSP, }, 0x03,0x00 },
|
||||
/*3a*/{{ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, }, 0xFF,0x00 },
|
||||
/*3b*/{{ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11),}, 0xFF,0x00 },
|
||||
/*3c*/{{ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12),}, 0xFF,0x00 },
|
||||
/*3d*/{{ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13),}, 0xFF,0x00 },
|
||||
/*3e*/{{ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14),}, 0xFF,0x00 },
|
||||
/*3f*/{{ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15),}, 0xFF,0x00 },
|
||||
/*40*/{{ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16),}, 0xFF,0x00 },
|
||||
/*41*/{{ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7),}, 0xFF,0x00 },
|
||||
/*42*/{{ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8),}, 0xFF,0x00 },
|
||||
/*43*/{{ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9),}, 0xFF,0x00 },
|
||||
/*44*/{{ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10),}, 0xFF,0x00 },
|
||||
/*45*/{{ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, }, 0xFF,0x00 },
|
||||
/*46*/{{ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, }, 0xFF,0x00 },
|
||||
/*47*/{{ F(49), '7', '7', '7', '7', '7', '7', '7', }, 0x80,0x02 },
|
||||
/*48*/{{ F(50), '8', '8', '8', '8', '8', '8', '8', }, 0x80,0x02 },
|
||||
/*49*/{{ F(51), '9', '9', '9', '9', '9', '9', '9', }, 0x80,0x02 },
|
||||
/*4a*/{{ F(52), '-', '-', '-', '-', '-', '-', '-', }, 0x80,0x02 },
|
||||
/*4b*/{{ F(53), '4', '4', '4', '4', '4', '4', '4', }, 0x80,0x02 },
|
||||
/*4c*/{{ F(54), '5', '5', '5', '5', '5', '5', '5', }, 0x80,0x02 },
|
||||
/*4d*/{{ F(55), '6', '6', '6', '6', '6', '6', '6', }, 0x80,0x02 },
|
||||
/*4e*/{{ F(56), '+', '+', '+', '+', '+', '+', '+', }, 0x80,0x02 },
|
||||
/*4f*/{{ F(57), '1', '1', '1', '1', '1', '1', '1', }, 0x80,0x02 },
|
||||
/*50*/{{ F(58), '2', '2', '2', '2', '2', '2', '2', }, 0x80,0x02 },
|
||||
/*51*/{{ F(59), '3', '3', '3', '3', '3', '3', '3', }, 0x80,0x02 },
|
||||
/*52*/{{ F(60), '0', '0', '0', '0', '0', '0', '0', }, 0x80,0x02 },
|
||||
/*53*/{{ 0x7F, '.', '.', '.', '.', '.', RBT, RBT, }, 0x03,0x02 },
|
||||
/*54*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
|
||||
/*55*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
|
||||
/*56*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
|
||||
/*57*/{{ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11),}, 0xFF,0x00 },
|
||||
/*58*/{{ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12),}, 0xFF,0x00 },
|
||||
/*59*/{{ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, }, 0x00,0x00 },
|
||||
/*5a*/{{ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, }, 0xFF,0x00 },
|
||||
/*5b*/{{ '/', '/', '/', '/', '/', '/', '/', '/', }, 0x00,0x02 },
|
||||
/*5c*/{{ NEXT, NEXT, NOP, NOP, DBG, DBG, DBG, DBG, }, 0xFF,0x00 },
|
||||
/*5d*/{{ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, }, 0xFF,0x00 },
|
||||
/*5e*/{{ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49),}, 0xFF,0x00 },
|
||||
/*5f*/{{ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50),}, 0xFF,0x00 },
|
||||
/*60*/{{ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51),}, 0xFF,0x00 },
|
||||
/*61*/{{ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53),}, 0xFF,0x00 },
|
||||
/*62*/{{ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55),}, 0xFF,0x00 },
|
||||
/*63*/{{ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57),}, 0xFF,0x00 },
|
||||
/*64*/{{ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58),}, 0xFF,0x00 },
|
||||
/*65*/{{ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59),}, 0xFF,0x00 },
|
||||
/*66*/{{ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60),}, 0xFF,0x00 },
|
||||
/*67*/{{ F(61), F(61), F(61), F(61), F(61), F(61), RBT, F(61),}, 0xFF,0x00 },
|
||||
/*68*/{{ SPSC, SPSC, SUSP, SUSP, NOP, NOP, SUSP, SUSP, }, 0xFF,0x00 },
|
||||
/*69*/{{ F(62), F(62), F(62), F(62), F(62), F(62), F(62), F(62),}, 0xFF,0x00 },
|
||||
/*6a*/{{ F(63), F(63), F(63), F(63), F(63), F(63), F(63), F(63),}, 0xFF,0x00 },
|
||||
/*6b*/{{ F(64), F(64), F(64), F(64), F(64), F(64), F(64), F(64),}, 0xFF,0x00 },
|
||||
/*6c*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
|
||||
} };
|
||||
|
||||
static accentmap_t accentmap_default_us_acc = { 11, {
|
||||
/* dgra=0 */
|
||||
{ '`', { { 'a',0xe0 }, { 'A',0xc0 }, { 'e',0xe8 }, { 'E',0xc8 },
|
||||
{ 'i',0xec }, { 'I',0xcc }, { 'o',0xf2 }, { 'O',0xd2 },
|
||||
{ 'u',0xf9 }, { 'U',0xd9 }, }, },
|
||||
/* dacu=1 */
|
||||
{ 0xb4, { { 'a',0xe1 }, { 'A',0xc1 }, { 'e',0xe9 }, { 'E',0xc9 },
|
||||
{ 'i',0xed }, { 'I',0xcd }, { 'o',0xf3 }, { 'O',0xd3 },
|
||||
{ 'u',0xfa }, { 'U',0xda }, { 'y',0xfd }, { 'Y',0xdd }, }, },
|
||||
/* dcir=2 */
|
||||
{ '^', { { 'a',0xe2 }, { 'A',0xc2 }, { 'e',0xea }, { 'E',0xca },
|
||||
{ 'i',0xee }, { 'I',0xce }, { 'o',0xf4 }, { 'O',0xd4 },
|
||||
{ 'u',0xfb }, { 'U',0xdb }, }, },
|
||||
/* dtil=3 */
|
||||
{ '~', { { 'a',0xe3 }, { 'A',0xc3 }, { 'n',0xf1 }, { 'N',0xd1 },
|
||||
{ 'o',0xf5 }, { 'O',0xd5 }, }, },
|
||||
/* dmac=4 */
|
||||
{ 0x00 },
|
||||
/* dbre=5 */
|
||||
{ 0x00 },
|
||||
/* ddot=6 */
|
||||
{ 0x00 },
|
||||
/* duml=7 */
|
||||
{ 0xa8, { { 'a',0xe4 }, { 'A',0xc4 }, { 'e',0xeb }, { 'E',0xcb },
|
||||
{ 'i',0xef }, { 'I',0xcf }, { 'o',0xf6 }, { 'O',0xd6 },
|
||||
{ 'u',0xfc }, { 'U',0xdc }, { 'y',0xff }, }, },
|
||||
/* dsla=8 */
|
||||
{ 0x00 },
|
||||
/* drin=9 */
|
||||
{ 0xb0, { { 'a',0xe5 }, { 'A',0xc5 }, }, },
|
||||
/* dced=10 */
|
||||
{ 0xb8, { { 'c',0xe7 }, { 'C',0xc7 }, }, },
|
||||
/* dapo=11 */
|
||||
{ 0x00 },
|
||||
/* ddac=12 */
|
||||
{ 0x00 },
|
||||
/* dogo=13 */
|
||||
{ 0x00 },
|
||||
/* dcar=14 */
|
||||
{ 0x00 },
|
||||
} };
|
||||
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
601
third_party/SDL/src/core/freebsd/SDL_evdev_kbd_freebsd.c
vendored
Normal file
601
third_party/SDL/src/core/freebsd/SDL_evdev_kbd_freebsd.c
vendored
Normal file
@@ -0,0 +1,601 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#include "../linux/SDL_evdev_kbd.h"
|
||||
#include "SDL_hints.h"
|
||||
|
||||
#ifdef SDL_INPUT_FBSDKBIO
|
||||
|
||||
/* This logic is adapted from drivers/tty/vt/keyboard.c in the Linux kernel source, slightly modified to work with FreeBSD */
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/kbio.h>
|
||||
#include <sys/consio.h>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../../events/SDL_events_c.h"
|
||||
#include "SDL_evdev_kbd_default_keyaccmap.h"
|
||||
|
||||
typedef void(fn_handler_fn)(SDL_EVDEV_keyboard_state *kbd);
|
||||
|
||||
/*
|
||||
* Keyboard State
|
||||
*/
|
||||
|
||||
struct SDL_EVDEV_keyboard_state
|
||||
{
|
||||
int console_fd;
|
||||
int keyboard_fd;
|
||||
unsigned long old_kbd_mode;
|
||||
unsigned short **key_maps;
|
||||
keymap_t *key_map;
|
||||
keyboard_info_t *kbInfo;
|
||||
unsigned char shift_down[4]; /* shift state counters.. */
|
||||
SDL_bool dead_key_next;
|
||||
int npadch; /* -1 or number assembled on pad */
|
||||
accentmap_t *accents;
|
||||
unsigned int diacr;
|
||||
SDL_bool rep; /* flag telling character repeat */
|
||||
unsigned char lockstate;
|
||||
unsigned char ledflagstate;
|
||||
char shift_state;
|
||||
char text[128];
|
||||
unsigned int text_len;
|
||||
};
|
||||
|
||||
static int SDL_EVDEV_kbd_load_keymaps(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
return ioctl(kbd->keyboard_fd, GIO_KEYMAP, kbd->key_map) >= 0;
|
||||
}
|
||||
|
||||
static SDL_EVDEV_keyboard_state *kbd_cleanup_state = NULL;
|
||||
static int kbd_cleanup_sigactions_installed = 0;
|
||||
static int kbd_cleanup_atexit_installed = 0;
|
||||
|
||||
static struct sigaction old_sigaction[NSIG];
|
||||
|
||||
static int fatal_signals[] = {
|
||||
/* Handlers for SIGTERM and SIGINT are installed in SDL_QuitInit. */
|
||||
SIGHUP, SIGQUIT, SIGILL, SIGABRT,
|
||||
SIGFPE, SIGSEGV, SIGPIPE, SIGBUS,
|
||||
SIGSYS
|
||||
};
|
||||
|
||||
static void kbd_cleanup(void)
|
||||
{
|
||||
struct mouse_info mData;
|
||||
SDL_EVDEV_keyboard_state *kbd = kbd_cleanup_state;
|
||||
if (kbd == NULL) {
|
||||
return;
|
||||
}
|
||||
kbd_cleanup_state = NULL;
|
||||
SDL_zero(mData);
|
||||
mData.operation = MOUSE_SHOW;
|
||||
ioctl(kbd->keyboard_fd, KDSKBMODE, kbd->old_kbd_mode);
|
||||
if (kbd->keyboard_fd != kbd->console_fd) {
|
||||
close(kbd->keyboard_fd);
|
||||
}
|
||||
ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
|
||||
ioctl(kbd->console_fd, CONS_MOUSECTL, &mData);
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_reraise_signal(int sig)
|
||||
{
|
||||
raise(sig);
|
||||
}
|
||||
|
||||
siginfo_t *SDL_EVDEV_kdb_cleanup_siginfo = NULL;
|
||||
void *SDL_EVDEV_kdb_cleanup_ucontext = NULL;
|
||||
|
||||
static void kbd_cleanup_signal_action(int signum, siginfo_t *info, void *ucontext)
|
||||
{
|
||||
struct sigaction *old_action_p = &(old_sigaction[signum]);
|
||||
sigset_t sigset;
|
||||
|
||||
/* Restore original signal handler before going any further. */
|
||||
sigaction(signum, old_action_p, NULL);
|
||||
|
||||
/* Unmask current signal. */
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, signum);
|
||||
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
|
||||
|
||||
/* Save original signal info and context for archeologists. */
|
||||
SDL_EVDEV_kdb_cleanup_siginfo = info;
|
||||
SDL_EVDEV_kdb_cleanup_ucontext = ucontext;
|
||||
|
||||
/* Restore keyboard. */
|
||||
kbd_cleanup();
|
||||
|
||||
/* Reraise signal. */
|
||||
SDL_EVDEV_kbd_reraise_signal(signum);
|
||||
}
|
||||
|
||||
static void kbd_unregister_emerg_cleanup()
|
||||
{
|
||||
int tabidx, signum;
|
||||
|
||||
kbd_cleanup_state = NULL;
|
||||
|
||||
if (!kbd_cleanup_sigactions_installed) {
|
||||
return;
|
||||
}
|
||||
kbd_cleanup_sigactions_installed = 0;
|
||||
|
||||
for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
|
||||
struct sigaction *old_action_p;
|
||||
struct sigaction cur_action;
|
||||
signum = fatal_signals[tabidx];
|
||||
old_action_p = &(old_sigaction[signum]);
|
||||
|
||||
/* Examine current signal action */
|
||||
if (sigaction(signum, NULL, &cur_action)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if action installed and not modifed */
|
||||
if (!(cur_action.sa_flags & SA_SIGINFO) || cur_action.sa_sigaction != &kbd_cleanup_signal_action) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Restore original action */
|
||||
sigaction(signum, old_action_p, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void kbd_cleanup_atexit(void)
|
||||
{
|
||||
/* Restore keyboard. */
|
||||
kbd_cleanup();
|
||||
|
||||
/* Try to restore signal handlers in case shared library is being unloaded */
|
||||
kbd_unregister_emerg_cleanup();
|
||||
}
|
||||
|
||||
static void kbd_register_emerg_cleanup(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
int tabidx, signum;
|
||||
|
||||
if (kbd_cleanup_state != NULL) {
|
||||
return;
|
||||
}
|
||||
kbd_cleanup_state = kbd;
|
||||
|
||||
if (!kbd_cleanup_atexit_installed) {
|
||||
/* Since glibc 2.2.3, atexit() (and on_exit(3)) can be used within a shared library to establish
|
||||
* functions that are called when the shared library is unloaded.
|
||||
* -- man atexit(3)
|
||||
*/
|
||||
atexit(kbd_cleanup_atexit);
|
||||
kbd_cleanup_atexit_installed = 1;
|
||||
}
|
||||
|
||||
if (kbd_cleanup_sigactions_installed) {
|
||||
return;
|
||||
}
|
||||
kbd_cleanup_sigactions_installed = 1;
|
||||
|
||||
for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
|
||||
struct sigaction *old_action_p;
|
||||
struct sigaction new_action;
|
||||
signum = fatal_signals[tabidx];
|
||||
old_action_p = &(old_sigaction[signum]);
|
||||
if (sigaction(signum, NULL, old_action_p)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip SIGHUP and SIGPIPE if handler is already installed
|
||||
* - assume the handler will do the cleanup
|
||||
*/
|
||||
if ((signum == SIGHUP || signum == SIGPIPE) && (old_action_p->sa_handler != SIG_DFL || (void (*)(int))old_action_p->sa_sigaction != SIG_DFL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
new_action = *old_action_p;
|
||||
new_action.sa_flags |= SA_SIGINFO;
|
||||
new_action.sa_sigaction = &kbd_cleanup_signal_action;
|
||||
sigaction(signum, &new_action, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
|
||||
{
|
||||
SDL_EVDEV_keyboard_state *kbd;
|
||||
struct mouse_info mData;
|
||||
char flag_state;
|
||||
char *devicePath;
|
||||
|
||||
SDL_zero(mData);
|
||||
mData.operation = MOUSE_HIDE;
|
||||
kbd = (SDL_EVDEV_keyboard_state *)SDL_calloc(1, sizeof(SDL_EVDEV_keyboard_state));
|
||||
if (kbd == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
kbd->npadch = -1;
|
||||
|
||||
/* This might fail if we're not connected to a tty (e.g. on the Steam Link) */
|
||||
kbd->keyboard_fd = kbd->console_fd = open("/dev/tty", O_RDONLY | O_CLOEXEC);
|
||||
|
||||
kbd->shift_state = 0;
|
||||
|
||||
kbd->accents = SDL_calloc(sizeof(accentmap_t), 1);
|
||||
kbd->key_map = SDL_calloc(sizeof(keymap_t), 1);
|
||||
kbd->kbInfo = SDL_calloc(sizeof(keyboard_info_t), 1);
|
||||
|
||||
ioctl(kbd->console_fd, KDGKBINFO, kbd->kbInfo);
|
||||
ioctl(kbd->console_fd, CONS_MOUSECTL, &mData);
|
||||
|
||||
if (ioctl(kbd->console_fd, KDGKBSTATE, &flag_state) == 0) {
|
||||
kbd->ledflagstate = flag_state;
|
||||
}
|
||||
|
||||
if (ioctl(kbd->console_fd, GIO_DEADKEYMAP, kbd->accents) < 0) {
|
||||
SDL_free(kbd->accents);
|
||||
kbd->accents = &accentmap_default_us_acc;
|
||||
}
|
||||
|
||||
if (ioctl(kbd->console_fd, KDGKBMODE, &kbd->old_kbd_mode) == 0) {
|
||||
/* Set the keyboard in XLATE mode and load the keymaps */
|
||||
ioctl(kbd->console_fd, KDSKBMODE, (unsigned long)(K_XLATE));
|
||||
if (!SDL_EVDEV_kbd_load_keymaps(kbd)) {
|
||||
SDL_free(kbd->key_map);
|
||||
kbd->key_map = &keymap_default_us_acc;
|
||||
}
|
||||
/* Allow inhibiting keyboard mute with env. variable for debugging etc. */
|
||||
if (SDL_getenv("SDL_INPUT_FREEBSD_KEEP_KBD") == NULL) {
|
||||
/* Take keyboard from console and open the actual keyboard device.
|
||||
* Ensures that the keystrokes do not leak through to the console.
|
||||
*/
|
||||
ioctl(kbd->console_fd, CONS_RELKBD, 1ul);
|
||||
SDL_asprintf(&devicePath, "/dev/kbd%d", kbd->kbInfo->kb_index);
|
||||
kbd->keyboard_fd = open(devicePath, O_WRONLY | O_CLOEXEC);
|
||||
if (kbd->keyboard_fd == -1) {
|
||||
// Give keyboard back.
|
||||
ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
|
||||
kbd->keyboard_fd = kbd->console_fd;
|
||||
}
|
||||
|
||||
/* Make sure to restore keyboard if application fails to call
|
||||
* SDL_Quit before exit or fatal signal is raised.
|
||||
*/
|
||||
if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, SDL_FALSE)) {
|
||||
kbd_register_emerg_cleanup(kbd);
|
||||
}
|
||||
SDL_free(devicePath);
|
||||
} else
|
||||
kbd->keyboard_fd = kbd->console_fd;
|
||||
}
|
||||
|
||||
return kbd;
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
struct mouse_info mData;
|
||||
|
||||
if (kbd == NULL) {
|
||||
return;
|
||||
}
|
||||
SDL_zero(mData);
|
||||
mData.operation = MOUSE_SHOW;
|
||||
ioctl(kbd->console_fd, CONS_MOUSECTL, &mData);
|
||||
|
||||
kbd_unregister_emerg_cleanup();
|
||||
|
||||
if (kbd->keyboard_fd >= 0) {
|
||||
/* Restore the original keyboard mode */
|
||||
ioctl(kbd->keyboard_fd, KDSKBMODE, kbd->old_kbd_mode);
|
||||
|
||||
close(kbd->keyboard_fd);
|
||||
if (kbd->console_fd != kbd->keyboard_fd && kbd->console_fd >= 0) {
|
||||
// Give back keyboard.
|
||||
ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
|
||||
}
|
||||
kbd->console_fd = kbd->keyboard_fd = -1;
|
||||
}
|
||||
|
||||
SDL_free(kbd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper Functions.
|
||||
*/
|
||||
static void put_queue(SDL_EVDEV_keyboard_state *kbd, uint c)
|
||||
{
|
||||
/* c is already part of a UTF-8 sequence and safe to add as a character */
|
||||
if (kbd->text_len < (sizeof(kbd->text) - 1)) {
|
||||
kbd->text[kbd->text_len++] = (char)c;
|
||||
}
|
||||
}
|
||||
|
||||
static void put_utf8(SDL_EVDEV_keyboard_state *kbd, uint c)
|
||||
{
|
||||
if (c < 0x80)
|
||||
/* 0******* */
|
||||
put_queue(kbd, c);
|
||||
else if (c < 0x800) {
|
||||
/* 110***** 10****** */
|
||||
put_queue(kbd, 0xc0 | (c >> 6));
|
||||
put_queue(kbd, 0x80 | (c & 0x3f));
|
||||
} else if (c < 0x10000) {
|
||||
if (c >= 0xD800 && c < 0xE000) {
|
||||
return;
|
||||
}
|
||||
if (c == 0xFFFF) {
|
||||
return;
|
||||
}
|
||||
/* 1110**** 10****** 10****** */
|
||||
put_queue(kbd, 0xe0 | (c >> 12));
|
||||
put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
|
||||
put_queue(kbd, 0x80 | (c & 0x3f));
|
||||
} else if (c < 0x110000) {
|
||||
/* 11110*** 10****** 10****** 10****** */
|
||||
put_queue(kbd, 0xf0 | (c >> 18));
|
||||
put_queue(kbd, 0x80 | ((c >> 12) & 0x3f));
|
||||
put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
|
||||
put_queue(kbd, 0x80 | (c & 0x3f));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We have a combining character DIACR here, followed by the character CH.
|
||||
* If the combination occurs in the table, return the corresponding value.
|
||||
* Otherwise, if CH is a space or equals DIACR, return DIACR.
|
||||
* Otherwise, conclude that DIACR was not combining after all,
|
||||
* queue it and return CH.
|
||||
*/
|
||||
static unsigned int handle_diacr(SDL_EVDEV_keyboard_state *kbd, unsigned int ch)
|
||||
{
|
||||
unsigned int d = kbd->diacr;
|
||||
unsigned int i, j;
|
||||
|
||||
kbd->diacr = 0;
|
||||
|
||||
for (i = 0; i < kbd->accents->n_accs; i++) {
|
||||
if (kbd->accents->acc[i].accchar == d) {
|
||||
for (j = 0; j < NUM_ACCENTCHARS; ++j) {
|
||||
if (kbd->accents->acc[i].map[j][0] == 0) { /* end of table */
|
||||
break;
|
||||
}
|
||||
if (kbd->accents->acc[i].map[j][0] == ch) {
|
||||
return kbd->accents->acc[i].map[j][1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == ' ' || ch == d) {
|
||||
put_utf8(kbd, d);
|
||||
return 0;
|
||||
}
|
||||
put_utf8(kbd, d);
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
static int vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
|
||||
{
|
||||
return (kbd->ledflagstate & flag) != 0;
|
||||
}
|
||||
|
||||
static void chg_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
|
||||
{
|
||||
kbd->ledflagstate ^= flag;
|
||||
ioctl(kbd->keyboard_fd, KDSKBSTATE, (unsigned long)(kbd->ledflagstate));
|
||||
}
|
||||
|
||||
/*
|
||||
* Special function handlers
|
||||
*/
|
||||
|
||||
static void k_self(SDL_EVDEV_keyboard_state *kbd, unsigned int value, char up_flag)
|
||||
{
|
||||
if (up_flag) {
|
||||
return; /* no action, if this is a key release */
|
||||
}
|
||||
|
||||
if (kbd->diacr) {
|
||||
value = handle_diacr(kbd, value);
|
||||
}
|
||||
|
||||
if (kbd->dead_key_next) {
|
||||
kbd->dead_key_next = SDL_FALSE;
|
||||
kbd->diacr = value;
|
||||
return;
|
||||
}
|
||||
put_utf8(kbd, value);
|
||||
}
|
||||
|
||||
static void k_deadunicode(SDL_EVDEV_keyboard_state *kbd, unsigned int value, char up_flag)
|
||||
{
|
||||
if (up_flag)
|
||||
return;
|
||||
|
||||
kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value);
|
||||
}
|
||||
|
||||
static void k_shift(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
int old_state = kbd->shift_state;
|
||||
|
||||
if (kbd->rep)
|
||||
return;
|
||||
|
||||
if (up_flag) {
|
||||
/*
|
||||
* handle the case that two shift or control
|
||||
* keys are depressed simultaneously
|
||||
*/
|
||||
if (kbd->shift_down[value]) {
|
||||
kbd->shift_down[value]--;
|
||||
}
|
||||
} else
|
||||
kbd->shift_down[value]++;
|
||||
|
||||
if (kbd->shift_down[value])
|
||||
kbd->shift_state |= (1 << value);
|
||||
else
|
||||
kbd->shift_state &= ~(1 << value);
|
||||
|
||||
/* kludge */
|
||||
if (up_flag && kbd->shift_state != old_state && kbd->npadch != -1) {
|
||||
put_utf8(kbd, kbd->npadch);
|
||||
kbd->npadch = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *kbd, unsigned int keycode, int down)
|
||||
{
|
||||
keymap_t key_map;
|
||||
struct keyent_t keysym;
|
||||
unsigned int final_key_state;
|
||||
unsigned int map_from_key_sym;
|
||||
|
||||
if (kbd == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
key_map = *kbd->key_map;
|
||||
|
||||
kbd->rep = (down == 2);
|
||||
|
||||
if (keycode < NUM_KEYS) {
|
||||
if (keycode >= 89 && keycode <= 95) {
|
||||
/* These constitute unprintable language-related keys, so ignore them. */
|
||||
return;
|
||||
}
|
||||
if (keycode > 95) {
|
||||
keycode -= 7;
|
||||
}
|
||||
if (vc_kbd_led(kbd, ALKED) || (kbd->shift_state & 0x8)) {
|
||||
keycode += ALTGR_OFFSET;
|
||||
}
|
||||
keysym = key_map.key[keycode];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
final_key_state = kbd->shift_state & 0x7;
|
||||
if ((keysym.flgs & FLAG_LOCK_C) && vc_kbd_led(kbd, LED_CAP)) {
|
||||
final_key_state ^= 0x1;
|
||||
}
|
||||
if ((keysym.flgs & FLAG_LOCK_N) && vc_kbd_led(kbd, LED_NUM)) {
|
||||
final_key_state ^= 0x1;
|
||||
}
|
||||
|
||||
map_from_key_sym = keysym.map[final_key_state];
|
||||
if ((keysym.spcl & (0x80 >> final_key_state)) || (map_from_key_sym & SPCLKEY)) {
|
||||
/* Special function.*/
|
||||
if (map_from_key_sym == 0)
|
||||
return; /* Nothing to do. */
|
||||
if (map_from_key_sym & SPCLKEY) {
|
||||
map_from_key_sym &= ~SPCLKEY;
|
||||
}
|
||||
if (map_from_key_sym >= F_ACC && map_from_key_sym <= L_ACC) {
|
||||
/* Accent function.*/
|
||||
unsigned int accent_index = map_from_key_sym - F_ACC;
|
||||
if (kbd->accents->acc[accent_index].accchar != 0) {
|
||||
k_deadunicode(kbd, kbd->accents->acc[accent_index].accchar, !down);
|
||||
}
|
||||
} else {
|
||||
switch (map_from_key_sym) {
|
||||
case ASH: /* alt/meta shift */
|
||||
k_shift(kbd, 3, down == 0);
|
||||
break;
|
||||
case LSHA: /* left shift + alt lock */
|
||||
case RSHA: /* right shift + alt lock */
|
||||
if (down == 0) {
|
||||
chg_vc_kbd_led(kbd, ALKED);
|
||||
}
|
||||
case LSH: /* left shift */
|
||||
case RSH: /* right shift */
|
||||
k_shift(kbd, 0, down == 0);
|
||||
break;
|
||||
case LCTRA: /* left ctrl + alt lock */
|
||||
case RCTRA: /* right ctrl + alt lock */
|
||||
if (down == 0) {
|
||||
chg_vc_kbd_led(kbd, ALKED);
|
||||
}
|
||||
case LCTR: /* left ctrl */
|
||||
case RCTR: /* right ctrl */
|
||||
k_shift(kbd, 1, down == 0);
|
||||
break;
|
||||
case LALTA: /* left alt + alt lock */
|
||||
case RALTA: /* right alt + alt lock */
|
||||
if (down == 0) {
|
||||
chg_vc_kbd_led(kbd, ALKED);
|
||||
}
|
||||
case LALT: /* left alt */
|
||||
case RALT: /* right alt */
|
||||
k_shift(kbd, 2, down == 0);
|
||||
break;
|
||||
case ALK: /* alt lock */
|
||||
if (down == 1) {
|
||||
chg_vc_kbd_led(kbd, ALKED);
|
||||
}
|
||||
break;
|
||||
case CLK: /* caps lock*/
|
||||
if (down == 1) {
|
||||
chg_vc_kbd_led(kbd, CLKED);
|
||||
}
|
||||
break;
|
||||
case NLK: /* num lock */
|
||||
if (down == 1) {
|
||||
chg_vc_kbd_led(kbd, NLKED);
|
||||
}
|
||||
break;
|
||||
case SLK: /* scroll lock */
|
||||
if (down == 1) {
|
||||
chg_vc_kbd_led(kbd, SLKED);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (map_from_key_sym == '\n' || map_from_key_sym == '\r') {
|
||||
if (kbd->diacr) {
|
||||
kbd->diacr = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (map_from_key_sym >= ' ' && map_from_key_sym != 127) {
|
||||
k_self(kbd, map_from_key_sym, !down);
|
||||
}
|
||||
}
|
||||
|
||||
if (kbd->text_len > 0) {
|
||||
kbd->text[kbd->text_len] = '\0';
|
||||
SDL_SendKeyboardText(kbd->text);
|
||||
kbd->text_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDL_INPUT_FBSDKBIO */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user