initial commit-moved from vulkan_guide

This commit is contained in:
2025-10-10 22:53:54 +09:00
commit 8853429937
2484 changed files with 973414 additions and 0 deletions

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
View 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: */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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
View 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: */

View 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: */

File diff suppressed because it is too large Load Diff

345
third_party/SDL/src/audio/SDL_mixer.c vendored Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

153
third_party/SDL/src/audio/SDL_wave.h vendored Normal file
View 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: */

View 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: */

View 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: */

View 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 */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

File diff suppressed because it is too large Load Diff

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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(&current_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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

File diff suppressed because it is too large Load Diff

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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, &param) != -1) {
param.sched_priority = param.sched_curpriority + 15;
SchedSet(0, 0, SCHED_NOCHANGE, &param);
}
}
/* 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */

View 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: */