early-access version 1611

This commit is contained in:
pineappleEA
2021-04-18 05:35:25 +02:00
parent 16f54e367d
commit 18db69f039
1409 changed files with 545335 additions and 10 deletions

1709
externals/SDL/src/audio/SDL_audio.c vendored Executable file

File diff suppressed because it is too large Load Diff

80
externals/SDL/src/audio/SDL_audio_c.h vendored Executable file
View File

@@ -0,0 +1,80 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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) fprintf(stderr, "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);
#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;
/* You need to call SDL_PrepareResampleFilter() before using the internal resampler.
SDL_AudioQuit() calls SDL_FreeResamplerFilter(), you should never call it yourself. */
extern int SDL_PrepareResampleFilter(void);
extern void SDL_FreeResampleFilter(void);
#endif /* SDL_audio_c_h_ */
/* vi: set ts=4 sw=4 expandtab: */

1683
externals/SDL/src/audio/SDL_audiocvt.c vendored Executable file

File diff suppressed because it is too large Load Diff

124
externals/SDL/src/audio/SDL_audiodev.c vendored Executable file
View File

@@ -0,0 +1,124 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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, 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);
SDL_AddAudioDevice(iscapture, fname, (void *) 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 */
if (((audiodev = SDL_getenv("SDL_PATH_DSP")) == NULL) &&
((audiodev = SDL_getenv("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) {
SDL_snprintf(audiopath, SDL_arraysize(audiopath),
"%s%d", audiodev, instance);
instance++;
test_device(iscapture, audiopath, flags, test);
}
}
}
void
SDL_EnumUnixAudioDevices(const int classic, int (*test)(int))
{
SDL_EnumUnixAudioDevices_Internal(SDL_TRUE, classic, test);
SDL_EnumUnixAudioDevices_Internal(SDL_FALSE, classic, test);
}
#endif /* Audio driver selection */
/* vi: set ts=4 sw=4 expandtab: */

44
externals/SDL/src/audio/SDL_audiodev_c.h vendored Executable file
View File

@@ -0,0 +1,44 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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: */

1430
externals/SDL/src/audio/SDL_audiotypecvt.c vendored Executable file

File diff suppressed because it is too large Load Diff

369
externals/SDL/src/audio/SDL_mixer.c vendored Executable file
View File

@@ -0,0 +1,369 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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)
* Changed to use 0xFE instead of 0xFF for better sound quality.
*/
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, 0xFE, 0xFE,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE
};
/* 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)
void
SDL_MixAudioFormat(Uint8 * dst, const Uint8 * src, SDL_AudioFormat format,
Uint32 len, int volume)
{
if (volume == 0) {
return;
}
switch (format) {
case AUDIO_U8:
{
#if defined(__GNUC__) && defined(__M68000__) && !defined(__mcoldfire__) && defined(SDL_ASSEMBLY_ROUTINES)
SDL_MixAudio_m68k_U8((char *) dst, (char *) src,
(unsigned long) len, (long) volume,
(char *) mix8);
#else
Uint8 src_sample;
while (len--) {
src_sample = *src;
ADJUST_VOLUME_U8(src_sample, volume);
*dst = mix8[*dst + src_sample];
++dst;
++src;
}
#endif
}
break;
case AUDIO_S8:
{
Sint8 *dst8, *src8;
Sint8 src_sample;
int dst_sample;
const int max_audioval = ((1 << (8 - 1)) - 1);
const int min_audioval = -(1 << (8 - 1));
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) {
*dst8 = max_audioval;
} else if (dst_sample < min_audioval) {
*dst8 = min_audioval;
} else {
*dst8 = dst_sample;
}
++dst8;
++src8;
}
}
break;
case AUDIO_S16LSB:
{
Sint16 src1, src2;
int dst_sample;
const int max_audioval = ((1 << (16 - 1)) - 1);
const int min_audioval = -(1 << (16 - 1));
len /= 2;
while (len--) {
src1 = ((src[1]) << 8 | src[0]);
ADJUST_VOLUME(src1, volume);
src2 = ((dst[1]) << 8 | dst[0]);
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;
}
dst[0] = dst_sample & 0xFF;
dst_sample >>= 8;
dst[1] = dst_sample & 0xFF;
dst += 2;
}
}
break;
case AUDIO_S16MSB:
{
#if defined(__GNUC__) && defined(__M68000__) && !defined(__mcoldfire__) && defined(SDL_ASSEMBLY_ROUTINES)
SDL_MixAudio_m68k_S16MSB((short *) dst, (short *) src,
(unsigned long) len, (long) volume);
#else
Sint16 src1, src2;
int dst_sample;
const int max_audioval = ((1 << (16 - 1)) - 1);
const int min_audioval = -(1 << (16 - 1));
len /= 2;
while (len--) {
src1 = ((src[0]) << 8 | src[1]);
ADJUST_VOLUME(src1, volume);
src2 = ((dst[0]) << 8 | dst[1]);
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;
}
dst[1] = dst_sample & 0xFF;
dst_sample >>= 8;
dst[0] = dst_sample & 0xFF;
dst += 2;
}
#endif
}
break;
case AUDIO_U16LSB:
{
Uint16 src1, src2;
int dst_sample;
const int max_audioval = 0xFFFF;
len /= 2;
while (len--) {
src1 = ((src[1]) << 8 | src[0]);
ADJUST_VOLUME(src1, volume);
src2 = ((dst[1]) << 8 | dst[0]);
src += 2;
dst_sample = src1 + src2;
if (dst_sample > max_audioval) {
dst_sample = max_audioval;
}
dst[0] = dst_sample & 0xFF;
dst_sample >>= 8;
dst[1] = dst_sample & 0xFF;
dst += 2;
}
}
break;
case AUDIO_U16MSB:
{
Uint16 src1, src2;
int dst_sample;
const int max_audioval = 0xFFFF;
len /= 2;
while (len--) {
src1 = ((src[0]) << 8 | src[1]);
ADJUST_VOLUME(src1, volume);
src2 = ((dst[0]) << 8 | dst[1]);
src += 2;
dst_sample = src1 + src2;
if (dst_sample > max_audioval) {
dst_sample = max_audioval;
}
dst[1] = dst_sample & 0xFF;
dst_sample >>= 8;
dst[0] = dst_sample & 0xFF;
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 = ((((Sint64) 1) << (32 - 1)) - 1);
const Sint64 min_audioval = -(((Sint64) 1) << (32 - 1));
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 = ((((Sint64) 1) << (32 - 1)) - 1);
const Sint64 min_audioval = -(((Sint64) 1) << (32 - 1));
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: */

213
externals/SDL/src/audio/SDL_sysaudio.h vendored Executable file
View File

@@ -0,0 +1,213 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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 int iscapture, const char *name, 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 int 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, void *handle, const char *devname, int iscapture);
void (*ThreadInit) (_THIS); /* Called by audio thread at start */
void (*ThreadDeinit) (_THIS); /* Called by audio thread at end */
void (*BeginLoopIteration)(_THIS); /* Called by audio thread at top of loop */
void (*WaitDevice) (_THIS);
void (*PlayDevice) (_THIS);
Uint8 *(*GetDeviceBuf) (_THIS);
int (*CaptureFromDevice) (_THIS, void *buffer, int buflen);
void (*FlushCapture) (_THIS);
void (*PrepareToClose) (_THIS); /**< Called between run and draining wait for playback devices */
void (*CloseDevice) (_THIS);
void (*LockDevice) (_THIS);
void (*UnlockDevice) (_THIS);
void (*FreeDeviceHandle) (void *handle); /**< SDL is done with handle from SDL_AddAudioDevice() */
void (*Deinitialize) (void);
/* !!! FIXME: add pause(), so we can optimize instead of mixing silence. */
/* Some flags to push duplicate code into the core and reduce #ifdefs. */
/* !!! FIXME: these should be SDL_bool */
int ProvidesOwnCallbackThread;
int SkipMixerLock;
int HasCaptureSupport;
int OnlyHasDefaultOutputDevice;
int OnlyHasDefaultCaptureDevice;
int AllowsArbitraryDeviceNames;
} SDL_AudioDriverImpl;
typedef struct SDL_AudioDeviceItem
{
void *handle;
char *name;
char *original_name;
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;
int (*init) (SDL_AudioDriverImpl * impl);
int 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 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 openslES_bootstrap;
extern AudioBootStrap ANDROIDAUDIO_bootstrap;
extern AudioBootStrap PSPAUDIO_bootstrap;
extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap;
#endif /* SDL_sysaudio_h_ */
/* vi: set ts=4 sw=4 expandtab: */

2156
externals/SDL/src/audio/SDL_wave.c vendored Executable file

File diff suppressed because it is too large Load Diff

149
externals/SDL/src/audio/SDL_wave.h vendored Executable file
View File

@@ -0,0 +1,149 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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 malloc 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: */

998
externals/SDL/src/audio/alsa/SDL_alsa_audio.c vendored Executable file
View File

@@ -0,0 +1,998 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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
/* Allow access to a raw mixing buffer */
#include <sys/types.h>
#include <signal.h> /* For kill() */
#include <string.h>
#include "SDL_assert.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? */
/*
* http://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, buf, numframes) \
T *ptr = (T *) buf; \
Uint32 i; \
for (i = 0; i < numframes; 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; \
}
static void
swizzle_alsa_channels_6_64bit(void *buffer, Uint32 bufferlen)
{
SWIZ6(Uint64, buffer, bufferlen);
}
static void
swizzle_alsa_channels_6_32bit(void *buffer, Uint32 bufferlen)
{
SWIZ6(Uint32, buffer, bufferlen);
}
static void
swizzle_alsa_channels_6_16bit(void *buffer, Uint32 bufferlen)
{
SWIZ6(Uint16, buffer, bufferlen);
}
static void
swizzle_alsa_channels_6_8bit(void *buffer, Uint32 bufferlen)
{
SWIZ6(Uint8, buffer, bufferlen);
}
#undef SWIZ6
/*
* 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)
{
if (this->spec.channels == 6) {
switch (SDL_AUDIO_BITSIZE(this->spec.format)) {
case 8: swizzle_alsa_channels_6_8bit(buffer, bufferlen); break;
case 16: swizzle_alsa_channels_6_16bit(buffer, bufferlen); break;
case 32: swizzle_alsa_channels_6_32bit(buffer, bufferlen); break;
case 64: swizzle_alsa_channels_6_64bit(buffer, bufferlen); break;
default: SDL_assert(!"unhandled bitsize"); break;
}
}
/* !!! FIXME: update this for 7.1 if needed, later. */
}
#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 */
fprintf(stderr, "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 */
fprintf(stderr, "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);
fprintf(stderr,
"ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
persize, periods, bufsize);
}
return(0);
}
static int
ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
{
int status = 0;
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(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 */
status = -1;
for (test_format = SDL_FirstAudioFormat(this->spec.format);
test_format && (status < 0);) {
status = 0; /* if we can't support a format, it'll become -1. */
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:
status = -1;
break;
}
if (status >= 0) {
status = ALSA_snd_pcm_hw_params_set_format(pcm_handle,
hwparams, format);
}
if (status < 0) {
test_format = SDL_NextAudioFormat();
}
}
if (status < 0) {
return SDL_SetError("ALSA: Couldn't find any hardware audio formats");
}
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) {
ALSA_snd_pcm_chmap_print(chmap, sizeof(chmap_str), chmap_str);
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) {
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) {
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. */
if ((ptr = strchr(desc, '\n')) != NULL) {
*ptr = '\0';
}
/*printf("ALSA: adding %s device '%s' (%s)\n", iscapture ? "capture" : "output", name, desc);*/
handle = SDL_strdup(name);
if (!handle) {
if (hint) {
free(desc);
}
SDL_free(dev);
return;
}
SDL_AddAudioDevice(iscapture, desc, handle);
if (hint)
free(desc);
dev->name = handle;
dev->iscapture = iscapture;
dev->next = *pSeen;
*pSeen = dev;
}
static SDL_atomic_t ALSA_hotplug_shutdown;
static SDL_Thread *ALSA_hotplug_thread;
static int SDLCALL
ALSA_HotplugThread(void *arg)
{
SDL_sem *first_run_semaphore = (SDL_sem *) arg;
ALSA_Device *devices = NULL;
ALSA_Device *next;
ALSA_Device *dev;
Uint32 ticks;
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) {
void **hints = NULL;
ALSA_Device *unseen;
ALSA_Device *seen;
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 = 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) {
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) && (defaultdev != i)) {
continue; /* ...skip anything that isn't the default device. */
}
name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
if (!name) {
continue;
}
/* only want physical hardware interfaces */
if (!match || (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);
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);
}
}
/* On first run, tell ALSA_DetectDevices() that we have a complete device list so it can return. */
if (first_run_semaphore) {
SDL_SemPost(first_run_semaphore);
first_run_semaphore = NULL; /* let other thread clean it up. */
}
/* Block awhile before checking again, unless we're told to stop. */
ticks = SDL_GetTicks() + 5000;
while (!SDL_AtomicGet(&ALSA_hotplug_shutdown) && !SDL_TICKS_PASSED(SDL_GetTicks(), ticks)) {
SDL_Delay(100);
}
}
/* Shutting down! Clean up any data we've gathered. */
for (dev = 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);
}
return 0;
}
static void
ALSA_DetectDevices(void)
{
/* Start the device detection thread here, wait for an initial iteration to complete. */
SDL_sem *semaphore = SDL_CreateSemaphore(0);
if (!semaphore) {
return; /* oh well. */
}
SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", semaphore);
if (ALSA_hotplug_thread) {
SDL_SemWait(semaphore); /* wait for the first iteration to finish. */
}
SDL_DestroySemaphore(semaphore);
}
static void
ALSA_Deinitialize(void)
{
if (ALSA_hotplug_thread != NULL) {
SDL_AtomicSet(&ALSA_hotplug_shutdown, 1);
SDL_WaitThread(ALSA_hotplug_thread, NULL);
ALSA_hotplug_thread = NULL;
}
UnloadALSALibrary();
}
static int
ALSA_Init(SDL_AudioDriverImpl * impl)
{
if (LoadALSALibrary() < 0) {
return 0;
}
/* 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;
return 1; /* this audio target is available. */
}
AudioBootStrap ALSA_bootstrap = {
"alsa", "ALSA PCM audio", ALSA_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_ALSA */
/* vi: set ts=4 sw=4 expandtab: */

48
externals/SDL/src/audio/alsa/SDL_alsa_audio.h vendored Executable file
View File

@@ -0,0 +1,48 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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,211 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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_assert.h"
#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, void *handle, const char *devname, int iscapture)
{
SDL_AudioFormat test_format;
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();
}
test_format = SDL_FirstAudioFormat(this->spec.format);
while (test_format != 0) { /* no "UNKNOWN" constant */
if ((test_format == AUDIO_U8) ||
(test_format == AUDIO_S16) ||
(test_format == AUDIO_F32)) {
this->spec.format = test_format;
break;
}
test_format = SDL_NextAudioFormat();
}
if (test_format == 0) {
/* Didn't find a compatible format :( */
return SDL_SetError("No compatible audio format!");
}
if (Android_JNI_OpenAudioDevice(iscapture, &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 int
ANDROIDAUDIO_Init(SDL_AudioDriverImpl * impl)
{
/* Set the function pointers */
impl->OpenDevice = ANDROIDAUDIO_OpenDevice;
impl->PlayDevice = ANDROIDAUDIO_PlayDevice;
impl->GetDeviceBuf = ANDROIDAUDIO_GetDeviceBuf;
impl->CloseDevice = ANDROIDAUDIO_CloseDevice;
impl->CaptureFromDevice = ANDROIDAUDIO_CaptureFromDevice;
impl->FlushCapture = ANDROIDAUDIO_FlushCapture;
/* and the capabilities */
impl->HasCaptureSupport = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = 1;
impl->OnlyHasDefaultCaptureDevice = 1;
return 1; /* this audio target is available. */
}
AudioBootStrap ANDROIDAUDIO_bootstrap = {
"android", "SDL Android audio driver", ANDROIDAUDIO_Init, 0
};
/* 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-2020 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: */

365
externals/SDL/src/audio/arts/SDL_artsaudio.c vendored Executable file
View File

@@ -0,0 +1,365 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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, void *handle, const char *devname, int iscapture)
{
int rc = 0;
int bits = 0, frag_spec = 0;
SDL_AudioFormat test_format = 0, 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);
!format && test_format;) {
#ifdef DEBUG_AUDIO
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
#endif
switch (test_format) {
case AUDIO_U8:
bits = 8;
format = 1;
break;
case AUDIO_S16LSB:
bits = 16;
format = 1;
break;
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;
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 int
ARTS_Init(SDL_AudioDriverImpl * impl)
{
if (LoadARTSLibrary() < 0) {
return 0;
} else {
if (SDL_NAME(arts_init) () != 0) {
UnloadARTSLibrary();
SDL_SetError("ARTS: arts_init failed (no audio server?)");
return 0;
}
/* 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 = 1;
return 1; /* this audio target is available. */
}
AudioBootStrap ARTS_bootstrap = {
"arts", "Analog RealTime Synthesizer", ARTS_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_ARTS */
/* vi: set ts=4 sw=4 expandtab: */

53
externals/SDL/src/audio/arts/SDL_artsaudio.h vendored Executable file
View File

@@ -0,0 +1,53 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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,68 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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>
#include <CoreServices/CoreServices.h>
#else
#import <AVFoundation/AVFoundation.h>
#import <UIKit/UIApplication.h>
#endif
#include <AudioToolbox/AudioToolbox.h>
#include <AudioUnit/AudioUnit.h>
/* Hidden "this" pointer for the audio functions */
#define _THIS SDL_AudioDevice *this
struct SDL_PrivateAudioData
{
SDL_Thread *thread;
AudioQueueRef audioQueue;
AudioQueueBufferRef *audioBuffer;
void *buffer;
UInt32 bufferOffset;
UInt32 bufferSize;
AudioStreamBasicDescription strdesc;
SDL_sem *ready_semaphore;
char *thread_error;
SDL_atomic_t shutdown;
#if MACOSX_COREAUDIO
AudioDeviceID deviceID;
#else
SDL_bool interrupted;
CFTypeRef interruption_listener;
#endif
};
#endif /* SDL_coreaudio_h_ */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,984 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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_COREAUDIO
/* !!! FIXME: clean out some of the macro salsa in here. */
#include "SDL_audio.h"
#include "SDL_hints.h"
#include "../SDL_audio_c.h"
#include "../SDL_sysaudio.h"
#include "SDL_coreaudio.h"
#include "SDL_assert.h"
#include "../../thread/SDL_systhread.h"
#define DEBUG_COREAUDIO 0
#define CHECK_RESULT(msg) \
if (result != noErr) { \
SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
return 0; \
}
#if MACOSX_COREAUDIO
static const AudioObjectPropertyAddress devlist_address = {
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
typedef void (*addDevFn)(const char *name, const int iscapture, AudioDeviceID devId, void *data);
typedef struct AudioDeviceList
{
AudioDeviceID devid;
SDL_bool alive;
struct AudioDeviceList *next;
} AudioDeviceList;
static AudioDeviceList *output_devs = NULL;
static AudioDeviceList *capture_devs = NULL;
static SDL_bool
add_to_internal_dev_list(const int iscapture, AudioDeviceID devId)
{
AudioDeviceList *item = (AudioDeviceList *) SDL_malloc(sizeof (AudioDeviceList));
if (item == NULL) {
return SDL_FALSE;
}
item->devid = devId;
item->alive = SDL_TRUE;
item->next = iscapture ? capture_devs : output_devs;
if (iscapture) {
capture_devs = item;
} else {
output_devs = item;
}
return SDL_TRUE;
}
static void
addToDevList(const char *name, const int iscapture, AudioDeviceID devId, void *data)
{
if (add_to_internal_dev_list(iscapture, devId)) {
SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
}
}
static void
build_device_list(int iscapture, addDevFn addfn, void *addfndata)
{
OSStatus result = noErr;
UInt32 size = 0;
AudioDeviceID *devs = NULL;
UInt32 i = 0;
UInt32 max = 0;
result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
&devlist_address, 0, NULL, &size);
if (result != kAudioHardwareNoError)
return;
devs = (AudioDeviceID *) alloca(size);
if (devs == NULL)
return;
result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
&devlist_address, 0, NULL, &size, devs);
if (result != kAudioHardwareNoError)
return;
max = size / sizeof (AudioDeviceID);
for (i = 0; i < max; i++) {
CFStringRef cfstr = NULL;
char *ptr = NULL;
AudioDeviceID dev = devs[i];
AudioBufferList *buflist = NULL;
int usable = 0;
CFIndex len = 0;
const AudioObjectPropertyAddress addr = {
kAudioDevicePropertyStreamConfiguration,
iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
};
const AudioObjectPropertyAddress nameaddr = {
kAudioObjectPropertyName,
iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
};
result = AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size);
if (result != noErr)
continue;
buflist = (AudioBufferList *) SDL_malloc(size);
if (buflist == NULL)
continue;
result = AudioObjectGetPropertyData(dev, &addr, 0, NULL,
&size, buflist);
if (result == noErr) {
UInt32 j;
for (j = 0; j < buflist->mNumberBuffers; j++) {
if (buflist->mBuffers[j].mNumberChannels > 0) {
usable = 1;
break;
}
}
}
SDL_free(buflist);
if (!usable)
continue;
size = sizeof (CFStringRef);
result = AudioObjectGetPropertyData(dev, &nameaddr, 0, NULL, &size, &cfstr);
if (result != kAudioHardwareNoError)
continue;
len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
kCFStringEncodingUTF8);
ptr = (char *) SDL_malloc(len + 1);
usable = ((ptr != NULL) &&
(CFStringGetCString
(cfstr, ptr, len + 1, kCFStringEncodingUTF8)));
CFRelease(cfstr);
if (usable) {
len = strlen(ptr);
/* Some devices have whitespace at the end...trim it. */
while ((len > 0) && (ptr[len - 1] == ' ')) {
len--;
}
usable = (len > 0);
}
if (usable) {
ptr[len] = '\0';
#if DEBUG_COREAUDIO
printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
((iscapture) ? "capture" : "output"),
(int) i, ptr, (int) dev);
#endif
addfn(ptr, iscapture, dev, addfndata);
}
SDL_free(ptr); /* addfn() would have copied the string. */
}
}
static void
free_audio_device_list(AudioDeviceList **list)
{
AudioDeviceList *item = *list;
while (item) {
AudioDeviceList *next = item->next;
SDL_free(item);
item = next;
}
*list = NULL;
}
static void
COREAUDIO_DetectDevices(void)
{
build_device_list(SDL_TRUE, addToDevList, NULL);
build_device_list(SDL_FALSE, addToDevList, NULL);
}
static void
build_device_change_list(const char *name, const int iscapture, AudioDeviceID devId, void *data)
{
AudioDeviceList **list = (AudioDeviceList **) data;
AudioDeviceList *item;
for (item = *list; item != NULL; item = item->next) {
if (item->devid == devId) {
item->alive = SDL_TRUE;
return;
}
}
add_to_internal_dev_list(iscapture, devId); /* new device, add it. */
SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
}
static void
reprocess_device_list(const int iscapture, AudioDeviceList **list)
{
AudioDeviceList *item;
AudioDeviceList *prev = NULL;
for (item = *list; item != NULL; item = item->next) {
item->alive = SDL_FALSE;
}
build_device_list(iscapture, build_device_change_list, list);
/* free items in the list that aren't still alive. */
item = *list;
while (item != NULL) {
AudioDeviceList *next = item->next;
if (item->alive) {
prev = item;
} else {
SDL_RemoveAudioDevice(iscapture, (void *) ((size_t) item->devid));
if (prev) {
prev->next = item->next;
} else {
*list = item->next;
}
SDL_free(item);
}
item = next;
}
}
/* this is called when the system's list of available audio devices changes. */
static OSStatus
device_list_changed(AudioObjectID systemObj, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
{
reprocess_device_list(SDL_TRUE, &capture_devs);
reprocess_device_list(SDL_FALSE, &output_devs);
return 0;
}
#endif
static int open_playback_devices = 0;
static int open_capture_devices = 0;
#if !MACOSX_COREAUDIO
static void interruption_begin(_THIS)
{
if (this != NULL && this->hidden->audioQueue != NULL) {
this->hidden->interrupted = SDL_TRUE;
AudioQueuePause(this->hidden->audioQueue);
}
}
static void interruption_end(_THIS)
{
if (this != NULL && this->hidden != NULL && this->hidden->audioQueue != NULL
&& this->hidden->interrupted
&& AudioQueueStart(this->hidden->audioQueue, NULL) == AVAudioSessionErrorCodeNone) {
this->hidden->interrupted = SDL_FALSE;
}
}
@interface SDLInterruptionListener : NSObject
@property (nonatomic, assign) SDL_AudioDevice *device;
@end
@implementation SDLInterruptionListener
- (void)audioSessionInterruption:(NSNotification *)note
{
@synchronized (self) {
NSNumber *type = note.userInfo[AVAudioSessionInterruptionTypeKey];
if (type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) {
interruption_begin(self.device);
} else {
interruption_end(self.device);
}
}
}
- (void)applicationBecameActive:(NSNotification *)note
{
@synchronized (self) {
interruption_end(self.device);
}
}
@end
static BOOL update_audio_session(_THIS, SDL_bool open)
{
@autoreleasepool {
AVAudioSession *session = [AVAudioSession sharedInstance];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
/* Set category to ambient by default so that other music continues playing. */
NSString *category = AVAudioSessionCategoryAmbient;
NSString *mode = AVAudioSessionModeDefault;
NSUInteger options = 0;
NSError *err = nil;
if (open_playback_devices && open_capture_devices) {
category = AVAudioSessionCategoryPlayAndRecord;
#if !TARGET_OS_TV
options = AVAudioSessionCategoryOptionDefaultToSpeaker;
#endif
} else if (open_capture_devices) {
category = AVAudioSessionCategoryRecord;
} else {
const char *hint = SDL_GetHint(SDL_HINT_AUDIO_CATEGORY);
if (hint) {
if (SDL_strcasecmp(hint, "AVAudioSessionCategoryAmbient") == 0) {
category = AVAudioSessionCategoryAmbient;
} else if (SDL_strcasecmp(hint, "AVAudioSessionCategorySoloAmbient") == 0) {
category = AVAudioSessionCategorySoloAmbient;
} else if (SDL_strcasecmp(hint, "AVAudioSessionCategoryPlayback") == 0 ||
SDL_strcasecmp(hint, "playback") == 0) {
category = AVAudioSessionCategoryPlayback;
}
}
}
if ([session respondsToSelector:@selector(setCategory:mode:options:error:)]) {
if (![session setCategory:category mode:mode options:options error:&err]) {
NSString *desc = err.description;
SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
return NO;
}
} else {
if (![session setCategory:category error:&err]) {
NSString *desc = err.description;
SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
return NO;
}
}
if (open && (open_playback_devices + open_capture_devices) == 1) {
if (![session setActive:YES error:&err]) {
NSString *desc = err.description;
SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String);
return NO;
}
} else if (!open_playback_devices && !open_capture_devices) {
[session setActive:NO error:nil];
}
if (open) {
SDLInterruptionListener *listener = [SDLInterruptionListener new];
listener.device = this;
[center addObserver:listener
selector:@selector(audioSessionInterruption:)
name:AVAudioSessionInterruptionNotification
object:session];
/* An interruption end notification is not guaranteed to be sent if
we were previously interrupted... resuming if needed when the app
becomes active seems to be the way to go. */
// Note: object: below needs to be nil, as otherwise it filters by the object, and session doesn't send foreground / active notifications. johna
[center addObserver:listener
selector:@selector(applicationBecameActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
[center addObserver:listener
selector:@selector(applicationBecameActive:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
this->hidden->interruption_listener = CFBridgingRetain(listener);
} else {
if (this->hidden->interruption_listener != NULL) {
SDLInterruptionListener *listener = nil;
listener = (SDLInterruptionListener *) CFBridgingRelease(this->hidden->interruption_listener);
[center removeObserver:listener];
@synchronized (listener) {
listener.device = NULL;
}
}
}
}
return YES;
}
#endif
/* The AudioQueue callback */
static void
outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
{
SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
if (SDL_AtomicGet(&this->hidden->shutdown)) {
return; /* don't do anything. */
}
if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
/* Supply silence if audio is not enabled or paused */
SDL_memset(inBuffer->mAudioData, this->spec.silence, inBuffer->mAudioDataBytesCapacity);
} else if (this->stream ) {
UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
Uint8 *ptr = (Uint8 *) inBuffer->mAudioData;
while (remaining > 0) {
if ( SDL_AudioStreamAvailable(this->stream) == 0 ) {
/* Generate the data */
SDL_LockMutex(this->mixer_lock);
(*this->callbackspec.callback)(this->callbackspec.userdata,
this->hidden->buffer, this->hidden->bufferSize);
SDL_UnlockMutex(this->mixer_lock);
this->hidden->bufferOffset = 0;
SDL_AudioStreamPut(this->stream, this->hidden->buffer, this->hidden->bufferSize);
}
if ( SDL_AudioStreamAvailable(this->stream) > 0 ) {
int got;
UInt32 len = SDL_AudioStreamAvailable(this->stream);
if ( len > remaining )
len = remaining;
got = SDL_AudioStreamGet(this->stream, ptr, len);
SDL_assert((got < 0) || (got == len));
if (got != len) {
SDL_memset(ptr, this->spec.silence, len);
}
ptr = ptr + len;
remaining -= len;
}
}
} else {
UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
Uint8 *ptr = (Uint8 *) inBuffer->mAudioData;
while (remaining > 0) {
UInt32 len;
if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
/* Generate the data */
SDL_LockMutex(this->mixer_lock);
(*this->callbackspec.callback)(this->callbackspec.userdata,
this->hidden->buffer, this->hidden->bufferSize);
SDL_UnlockMutex(this->mixer_lock);
this->hidden->bufferOffset = 0;
}
len = this->hidden->bufferSize - this->hidden->bufferOffset;
if (len > remaining) {
len = remaining;
}
SDL_memcpy(ptr, (char *)this->hidden->buffer +
this->hidden->bufferOffset, len);
ptr = ptr + len;
remaining -= len;
this->hidden->bufferOffset += len;
}
}
AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
}
static void
inputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions,
const AudioStreamPacketDescription *inPacketDescs )
{
SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
if (SDL_AtomicGet(&this->shutdown)) {
return; /* don't do anything. */
}
/* ignore unless we're active. */
if (!SDL_AtomicGet(&this->paused) && SDL_AtomicGet(&this->enabled) && !SDL_AtomicGet(&this->paused)) {
const Uint8 *ptr = (const Uint8 *) inBuffer->mAudioData;
UInt32 remaining = inBuffer->mAudioDataByteSize;
while (remaining > 0) {
UInt32 len = this->hidden->bufferSize - this->hidden->bufferOffset;
if (len > remaining) {
len = remaining;
}
SDL_memcpy((char *)this->hidden->buffer + this->hidden->bufferOffset, ptr, len);
ptr += len;
remaining -= len;
this->hidden->bufferOffset += len;
if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
SDL_LockMutex(this->mixer_lock);
(*this->callbackspec.callback)(this->callbackspec.userdata, this->hidden->buffer, this->hidden->bufferSize);
SDL_UnlockMutex(this->mixer_lock);
this->hidden->bufferOffset = 0;
}
}
}
AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
}
#if MACOSX_COREAUDIO
static const AudioObjectPropertyAddress alive_address =
{
kAudioDevicePropertyDeviceIsAlive,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
static OSStatus
device_unplugged(AudioObjectID devid, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
{
SDL_AudioDevice *this = (SDL_AudioDevice *) data;
SDL_bool dead = SDL_FALSE;
UInt32 isAlive = 1;
UInt32 size = sizeof (isAlive);
OSStatus error;
if (!SDL_AtomicGet(&this->enabled)) {
return 0; /* already known to be dead. */
}
error = AudioObjectGetPropertyData(this->hidden->deviceID, &alive_address,
0, NULL, &size, &isAlive);
if (error == kAudioHardwareBadDeviceError) {
dead = SDL_TRUE; /* device was unplugged. */
} else if ((error == kAudioHardwareNoError) && (!isAlive)) {
dead = SDL_TRUE; /* device died in some other way. */
}
if (dead) {
SDL_OpenedAudioDeviceDisconnected(this);
}
return 0;
}
#endif
static void
COREAUDIO_CloseDevice(_THIS)
{
const SDL_bool iscapture = this->iscapture;
/* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
/* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
#if MACOSX_COREAUDIO
/* Fire a callback if the device stops being "alive" (disconnected, etc). */
AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
#endif
if (iscapture) {
open_capture_devices--;
} else {
open_playback_devices--;
}
#if !MACOSX_COREAUDIO
update_audio_session(this, SDL_FALSE);
#endif
/* if callback fires again, feed silence; don't call into the app. */
SDL_AtomicSet(&this->paused, 1);
if (this->hidden->audioQueue) {
AudioQueueDispose(this->hidden->audioQueue, 1);
}
if (this->hidden->thread) {
SDL_AtomicSet(&this->hidden->shutdown, 1);
SDL_WaitThread(this->hidden->thread, NULL);
}
if (this->hidden->ready_semaphore) {
SDL_DestroySemaphore(this->hidden->ready_semaphore);
}
/* AudioQueueDispose() frees the actual buffer objects. */
SDL_free(this->hidden->audioBuffer);
SDL_free(this->hidden->thread_error);
SDL_free(this->hidden->buffer);
SDL_free(this->hidden);
}
#if MACOSX_COREAUDIO
static int
prepare_device(_THIS, void *handle, int iscapture)
{
AudioDeviceID devid = (AudioDeviceID) ((size_t) handle);
OSStatus result = noErr;
UInt32 size = 0;
UInt32 alive = 0;
pid_t pid = 0;
AudioObjectPropertyAddress addr = {
0,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
if (handle == NULL) {
size = sizeof (AudioDeviceID);
addr.mSelector =
((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
kAudioHardwarePropertyDefaultOutputDevice);
result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
0, NULL, &size, &devid);
CHECK_RESULT("AudioHardwareGetProperty (default device)");
}
addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
kAudioDevicePropertyScopeOutput;
size = sizeof (alive);
result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &alive);
CHECK_RESULT
("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
if (!alive) {
SDL_SetError("CoreAudio: requested device exists, but isn't alive.");
return 0;
}
addr.mSelector = kAudioDevicePropertyHogMode;
size = sizeof (pid);
result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &pid);
/* some devices don't support this property, so errors are fine here. */
if ((result == noErr) && (pid != -1)) {
SDL_SetError("CoreAudio: requested device is being hogged.");
return 0;
}
this->hidden->deviceID = devid;
return 1;
}
#endif
static int
prepare_audioqueue(_THIS)
{
const AudioStreamBasicDescription *strdesc = &this->hidden->strdesc;
const int iscapture = this->iscapture;
OSStatus result;
int i;
SDL_assert(CFRunLoopGetCurrent() != NULL);
if (iscapture) {
result = AudioQueueNewInput(strdesc, inputCallback, this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
CHECK_RESULT("AudioQueueNewInput");
} else {
result = AudioQueueNewOutput(strdesc, outputCallback, this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
CHECK_RESULT("AudioQueueNewOutput");
}
#if MACOSX_COREAUDIO
{
const AudioObjectPropertyAddress prop = {
kAudioDevicePropertyDeviceUID,
iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
};
CFStringRef devuid;
UInt32 devuidsize = sizeof (devuid);
result = AudioObjectGetPropertyData(this->hidden->deviceID, &prop, 0, NULL, &devuidsize, &devuid);
CHECK_RESULT("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID)");
result = AudioQueueSetProperty(this->hidden->audioQueue, kAudioQueueProperty_CurrentDevice, &devuid, devuidsize);
CHECK_RESULT("AudioQueueSetProperty (kAudioQueueProperty_CurrentDevice)");
/* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
/* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
/* Fire a callback if the device stops being "alive" (disconnected, etc). */
AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
}
#endif
/* Calculate the final parameters for this audio specification */
SDL_CalculateAudioSpec(&this->spec);
/* Set the channel layout for the audio queue */
AudioChannelLayout layout;
SDL_zero(layout);
switch (this->spec.channels) {
case 1:
layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
break;
case 2:
layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
break;
case 3:
layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4;
break;
case 4:
layout.mChannelLayoutTag = kAudioChannelLayoutTag_Quadraphonic;
break;
case 5:
layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_0_A;
break;
case 6:
layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_1_A;
break;
case 7:
/* FIXME: Need to move channel[4] (BC) to channel[6] */
layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A;
break;
case 8:
layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A;
break;
}
if (layout.mChannelLayoutTag != 0) {
result = AudioQueueSetProperty(this->hidden->audioQueue, kAudioQueueProperty_ChannelLayout, &layout, sizeof(layout));
CHECK_RESULT("AudioQueueSetProperty(kAudioQueueProperty_ChannelLayout)");
}
/* Allocate a sample buffer */
this->hidden->bufferSize = this->spec.size;
this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize;
this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
if (this->hidden->buffer == NULL) {
SDL_OutOfMemory();
return 0;
}
/* Make sure we can feed the device a minimum amount of time */
double MINIMUM_AUDIO_BUFFER_TIME_MS = 15.0;
#if defined(__IPHONEOS__)
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) {
/* Older iOS hardware, use 40 ms as a minimum time */
MINIMUM_AUDIO_BUFFER_TIME_MS = 40.0;
}
#endif
const double msecs = (this->spec.samples / ((double) this->spec.freq)) * 1000.0;
int numAudioBuffers = 2;
if (msecs < MINIMUM_AUDIO_BUFFER_TIME_MS) { /* use more buffers if we have a VERY small sample set. */
numAudioBuffers = ((int)SDL_ceil(MINIMUM_AUDIO_BUFFER_TIME_MS / msecs) * 2);
}
this->hidden->audioBuffer = SDL_calloc(1, sizeof (AudioQueueBufferRef) * numAudioBuffers);
if (this->hidden->audioBuffer == NULL) {
SDL_OutOfMemory();
return 0;
}
#if DEBUG_COREAUDIO
printf("COREAUDIO: numAudioBuffers == %d\n", numAudioBuffers);
#endif
for (i = 0; i < numAudioBuffers; i++) {
result = AudioQueueAllocateBuffer(this->hidden->audioQueue, this->spec.size, &this->hidden->audioBuffer[i]);
CHECK_RESULT("AudioQueueAllocateBuffer");
SDL_memset(this->hidden->audioBuffer[i]->mAudioData, this->spec.silence, this->hidden->audioBuffer[i]->mAudioDataBytesCapacity);
this->hidden->audioBuffer[i]->mAudioDataByteSize = this->hidden->audioBuffer[i]->mAudioDataBytesCapacity;
result = AudioQueueEnqueueBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i], 0, NULL);
CHECK_RESULT("AudioQueueEnqueueBuffer");
}
result = AudioQueueStart(this->hidden->audioQueue, NULL);
CHECK_RESULT("AudioQueueStart");
/* We're running! */
return 1;
}
static int
audioqueue_thread(void *arg)
{
SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
const int rc = prepare_audioqueue(this);
if (!rc) {
this->hidden->thread_error = SDL_strdup(SDL_GetError());
SDL_SemPost(this->hidden->ready_semaphore);
return 0;
}
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
/* init was successful, alert parent thread and start running... */
SDL_SemPost(this->hidden->ready_semaphore);
while (!SDL_AtomicGet(&this->hidden->shutdown)) {
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
}
if (!this->iscapture) { /* Drain off any pending playback. */
const CFTimeInterval secs = (((this->spec.size / (SDL_AUDIO_BITSIZE(this->spec.format) / 8)) / this->spec.channels) / ((CFTimeInterval) this->spec.freq)) * 2.0;
CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 0);
}
return 0;
}
static int
COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
{
AudioStreamBasicDescription *strdesc;
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
int valid_datatype = 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);
strdesc = &this->hidden->strdesc;
if (iscapture) {
open_capture_devices++;
} else {
open_playback_devices++;
}
#if !MACOSX_COREAUDIO
if (!update_audio_session(this, SDL_TRUE)) {
return -1;
}
/* Stop CoreAudio from doing expensive audio rate conversion */
@autoreleasepool {
AVAudioSession* session = [AVAudioSession sharedInstance];
[session setPreferredSampleRate:this->spec.freq error:nil];
this->spec.freq = (int)session.sampleRate;
#if TARGET_OS_TV
if (iscapture) {
[session setPreferredInputNumberOfChannels:this->spec.channels error:nil];
this->spec.channels = session.preferredInputNumberOfChannels;
} else {
[session setPreferredOutputNumberOfChannels:this->spec.channels error:nil];
this->spec.channels = session.preferredOutputNumberOfChannels;
}
#else
/* Calling setPreferredOutputNumberOfChannels seems to break audio output on iOS */
#endif /* TARGET_OS_TV */
}
#endif
/* Setup a AudioStreamBasicDescription with the requested format */
SDL_zerop(strdesc);
strdesc->mFormatID = kAudioFormatLinearPCM;
strdesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
strdesc->mChannelsPerFrame = this->spec.channels;
strdesc->mSampleRate = this->spec.freq;
strdesc->mFramesPerPacket = 1;
while ((!valid_datatype) && (test_format)) {
this->spec.format = test_format;
/* CoreAudio handles most of SDL's formats natively, but not U16, apparently. */
switch (test_format) {
case AUDIO_U8:
case AUDIO_S8:
case AUDIO_S16LSB:
case AUDIO_S16MSB:
case AUDIO_S32LSB:
case AUDIO_S32MSB:
case AUDIO_F32LSB:
case AUDIO_F32MSB:
valid_datatype = 1;
strdesc->mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
if (SDL_AUDIO_ISBIGENDIAN(this->spec.format))
strdesc->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
if (SDL_AUDIO_ISFLOAT(this->spec.format))
strdesc->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
else if (SDL_AUDIO_ISSIGNED(this->spec.format))
strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
break;
default:
test_format = SDL_NextAudioFormat();
break;
}
}
if (!valid_datatype) { /* shouldn't happen, but just in case... */
return SDL_SetError("Unsupported audio format");
}
strdesc->mBytesPerFrame = strdesc->mChannelsPerFrame * strdesc->mBitsPerChannel / 8;
strdesc->mBytesPerPacket = strdesc->mBytesPerFrame * strdesc->mFramesPerPacket;
#if MACOSX_COREAUDIO
if (!prepare_device(this, handle, iscapture)) {
return -1;
}
#endif
/* This has to init in a new thread so it can get its own CFRunLoop. :/ */
SDL_AtomicSet(&this->hidden->shutdown, 0);
this->hidden->ready_semaphore = SDL_CreateSemaphore(0);
if (!this->hidden->ready_semaphore) {
return -1; /* oh well. */
}
this->hidden->thread = SDL_CreateThreadInternal(audioqueue_thread, "AudioQueue thread", 512 * 1024, this);
if (!this->hidden->thread) {
return -1;
}
SDL_SemWait(this->hidden->ready_semaphore);
SDL_DestroySemaphore(this->hidden->ready_semaphore);
this->hidden->ready_semaphore = NULL;
if ((this->hidden->thread != NULL) && (this->hidden->thread_error != NULL)) {
SDL_SetError("%s", this->hidden->thread_error);
return -1;
}
return (this->hidden->thread != NULL) ? 0 : -1;
}
static void
COREAUDIO_Deinitialize(void)
{
#if MACOSX_COREAUDIO
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
free_audio_device_list(&capture_devs);
free_audio_device_list(&output_devs);
#endif
}
static int
COREAUDIO_Init(SDL_AudioDriverImpl * impl)
{
/* Set the function pointers */
impl->OpenDevice = COREAUDIO_OpenDevice;
impl->CloseDevice = COREAUDIO_CloseDevice;
impl->Deinitialize = COREAUDIO_Deinitialize;
#if MACOSX_COREAUDIO
impl->DetectDevices = COREAUDIO_DetectDevices;
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
#else
impl->OnlyHasDefaultOutputDevice = 1;
impl->OnlyHasDefaultCaptureDevice = 1;
#endif
impl->ProvidesOwnCallbackThread = 1;
impl->HasCaptureSupport = 1;
return 1; /* this audio target is available. */
}
AudioBootStrap COREAUDIO_bootstrap = {
"coreaudio", "CoreAudio", COREAUDIO_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_COREAUDIO */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,604 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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_assert.h"
#include "SDL_timer.h"
#include "SDL_loadso.h"
#include "SDL_audio.h"
#include "../SDL_audio_c.h"
#include "SDL_directsound.h"
#ifndef WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif
/* 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 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)
{
static const char *error;
static char errbuf[1024];
errbuf[0] = 0;
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:
SDL_snprintf(errbuf, SDL_arraysize(errbuf),
"%s: Unknown DirectSound error: 0x%x", function, code);
break;
}
if (!errbuf[0]) {
SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: %s", function,
error);
}
return SDL_SetError("%s", errbuf);
}
static void
DSOUND_FreeDeviceHandle(void *handle)
{
SDL_free(handle);
}
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));
SDL_AddAudioDevice(iscapture, str, cpyguid);
SDL_free(str); /* addfn() makes a copy of this string. */
}
}
return TRUE; /* keep enumerating. */
}
static void
DSOUND_DetectDevices(void)
{
pDirectSoundCaptureEnumerateW(FindAllDevs, (void *) ((size_t) 1));
pDirectSoundEnumerateW(FindAllDevs, (void *) ((size_t) 0));
}
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, void *handle, const char *devname, int iscapture)
{
const DWORD numchunks = 8;
HRESULT result;
SDL_bool valid_format = SDL_FALSE;
SDL_bool tried_format = SDL_FALSE;
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
LPGUID guid = (LPGUID) 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);
}
}
while ((!valid_format) && (test_format)) {
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;
WAVEFORMATEX wfmt;
SDL_zero(wfmt);
if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
wfmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
} else {
wfmt.wFormatTag = WAVE_FORMAT_PCM;
}
wfmt.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
wfmt.nChannels = this->spec.channels;
wfmt.nSamplesPerSec = this->spec.freq;
wfmt.nBlockAlign = wfmt.nChannels * (wfmt.wBitsPerSample / 8);
wfmt.nAvgBytesPerSec = wfmt.nSamplesPerSec * wfmt.nBlockAlign;
rc = iscapture ? CreateCaptureBuffer(this, bufsize, &wfmt) : CreateSecondary(this, bufsize, &wfmt);
if (rc == 0) {
this->hidden->num_buffers = numchunks;
valid_format = SDL_TRUE;
}
}
break;
}
test_format = SDL_NextAudioFormat();
}
if (!valid_format) {
if (tried_format) {
return -1; /* CreateSecondary() should have called SDL_SetError(). */
}
return SDL_SetError("DirectSound: Unsupported audio format");
}
/* Playback buffers will auto-start playing in DSOUND_WaitDevice() */
return 0; /* good to go. */
}
static void
DSOUND_Deinitialize(void)
{
DSOUND_Unload();
}
static int
DSOUND_Init(SDL_AudioDriverImpl * impl)
{
if (!DSOUND_Load()) {
return 0;
}
/* 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->HasCaptureSupport = SDL_TRUE;
return 1; /* this audio target is available. */
}
AudioBootStrap DSOUND_bootstrap = {
"directsound", "DirectSound", DSOUND_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_DSOUND */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,47 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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: */

207
externals/SDL/src/audio/disk/SDL_diskaudio.c vendored Executable file
View File

@@ -0,0 +1,207 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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"
#include "SDL_log.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 int 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, void *handle, const char *devname, int iscapture)
{
/* handle != NULL means "user specified the placeholder name on the fake detected device list" */
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, (void *) 0x1);
SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, (void *) 0x2);
}
static int
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 = 1;
impl->HasCaptureSupport = SDL_TRUE;
return 1; /* this audio target is available. */
}
AudioBootStrap DISKAUDIO_bootstrap = {
"disk", "direct-to-disk audio", DISKAUDIO_Init, 1
};
#endif /* SDL_AUDIO_DRIVER_DISK */
/* vi: set ts=4 sw=4 expandtab: */

41
externals/SDL/src/audio/disk/SDL_diskaudio.h vendored Executable file
View File

@@ -0,0 +1,41 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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: */

320
externals/SDL/src/audio/dsp/SDL_dspaudio.c vendored Executable file
View File

@@ -0,0 +1,320 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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>
#if SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H
/* This is installed on some systems */
#include <soundcard.h>
#else
/* This is recommended by OSS */
#include <sys/soundcard.h>
#endif
#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, void *handle, const char *devname, int 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, 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 int
DSP_Init(SDL_AudioDriverImpl * impl)
{
/* 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 = 1;
impl->HasCaptureSupport = SDL_TRUE;
return 1; /* this audio target is available. */
}
AudioBootStrap DSP_bootstrap = {
"dsp", "OSS /dev/dsp standard audio", DSP_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_OSS */
/* vi: set ts=4 sw=4 expandtab: */

43
externals/SDL/src/audio/dsp/SDL_dspaudio.h vendored Executable file
View File

@@ -0,0 +1,43 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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,65 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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, void *handle, const char *devname, int iscapture)
{
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 int
DUMMYAUDIO_Init(SDL_AudioDriverImpl * impl)
{
/* Set the function pointers */
impl->OpenDevice = DUMMYAUDIO_OpenDevice;
impl->CaptureFromDevice = DUMMYAUDIO_CaptureFromDevice;
impl->OnlyHasDefaultOutputDevice = 1;
impl->OnlyHasDefaultCaptureDevice = 1;
impl->HasCaptureSupport = SDL_TRUE;
return 1; /* this audio target is available. */
}
AudioBootStrap DUMMYAUDIO_bootstrap = {
"dummy", "SDL dummy audio driver", DUMMYAUDIO_Init, 1
};
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,41 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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,389 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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_log.h"
#include "../SDL_audio_c.h"
#include "SDL_emscriptenaudio.h"
#include "SDL_assert.h"
#include <emscripten/emscripten.h>
static void
FeedAudioDevice(_THIS, const void *buf, const int buflen)
{
const int framelen = (SDL_AUDIO_BITSIZE(this->spec.format) / 8) * this->spec.channels;
EM_ASM_ARGS({
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);
}
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);
}
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;
}
EM_ASM_ARGS({
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);
/* 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)
{
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);
#if 0 /* !!! FIXME: currently not used. Can we move some stuff off the SDL2 namespace? --ryan. */
SDL_free(this->hidden);
#endif
}
static int
EMSCRIPTENAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
{
SDL_bool valid_format = SDL_FALSE;
SDL_AudioFormat test_format;
int result;
/* based on parts of library_sdl.js */
/* create context */
result = 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();
}
}
return SDL2.audioContext === undefined ? -1 : 0;
}, iscapture);
if (result < 0) {
return SDL_SetError("Web Audio API is not available!");
}
test_format = SDL_FirstAudioFormat(this->spec.format);
while ((!valid_format) && (test_format)) {
switch (test_format) {
case AUDIO_F32: /* web audio only supports floats */
this->spec.format = test_format;
valid_format = SDL_TRUE;
break;
}
test_format = SDL_NextAudioFormat();
}
if (!valid_format) {
/* Didn't find a compatible format :( */
return SDL_SetError("No compatible audio 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_V({
var SDL2 = Module['SDL2'];
return SDL2.audioContext.sampleRate;
});
SDL_CalculateAudioSpec(&this->spec);
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. */
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 */
EM_ASM_ARGS({
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);
}
return 0;
}
static int
EMSCRIPTENAUDIO_Init(SDL_AudioDriverImpl * impl)
{
int available;
int capture_available;
/* Set the function pointers */
impl->OpenDevice = EMSCRIPTENAUDIO_OpenDevice;
impl->CloseDevice = EMSCRIPTENAUDIO_CloseDevice;
impl->OnlyHasDefaultOutputDevice = 1;
/* no threads here */
impl->SkipMixerLock = 1;
impl->ProvidesOwnCallbackThread = 1;
/* check availability */
available = EM_ASM_INT_V({
if (typeof(AudioContext) !== 'undefined') {
return 1;
} else if (typeof(webkitAudioContext) !== 'undefined') {
return 1;
}
return 0;
});
if (!available) {
SDL_SetError("No audio context available");
}
capture_available = available && EM_ASM_INT_V({
if ((typeof(navigator.mediaDevices) !== 'undefined') && (typeof(navigator.mediaDevices.getUserMedia) !== 'undefined')) {
return 1;
} else if (typeof(navigator.webkitGetUserMedia) !== 'undefined') {
return 1;
}
return 0;
});
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, 0
};
#endif /* SDL_AUDIO_DRIVER_EMSCRIPTEN */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,38 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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: */

335
externals/SDL/src/audio/esd/SDL_esdaudio.c vendored Executable file
View File

@@ -0,0 +1,335 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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, void *handle, const char *devname, int iscapture)
{
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 int
ESD_Init(SDL_AudioDriverImpl * impl)
{
if (LoadESDLibrary() < 0) {
return 0;
} 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 0;
}
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 = 1;
return 1; /* this audio target is available. */
}
AudioBootStrap ESD_bootstrap = {
"esd", "Enlightened Sound Daemon", ESD_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_ESD */
/* vi: set ts=4 sw=4 expandtab: */

51
externals/SDL/src/audio/esd/SDL_esdaudio.h vendored Executable file
View File

@@ -0,0 +1,51 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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,328 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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, void *handle, const char *devname, int iscapture)
{
int bytes;
SDL_AudioFormat test_format = 0, format = 0;
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);
!format && test_format;) {
#ifdef DEBUG_AUDIO
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
#endif
switch (test_format) {
case AUDIO_U8:
fs_format = FSSF_U8;
bytes = 1;
format = 1;
break;
case AUDIO_S16SYS:
fs_format = FSSF_S16;
bytes = 2;
format = 1;
break;
case AUDIO_S32SYS:
fs_format = FSSF_S32;
bytes = 4;
format = 1;
break;
case AUDIO_F32SYS:
fs_format = FSSF_FLOAT;
bytes = 4;
format = 1;
break;
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;
/* 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 int
SDL_FS_Init(SDL_AudioDriverImpl * impl)
{
if (LoadFusionSoundLibrary() < 0) {
return 0;
} else {
DirectResult ret;
ret = SDL_NAME(FusionSoundInit) (NULL, NULL);
if (ret) {
UnloadFusionSoundLibrary();
SDL_SetError
("FusionSound: SDL_FS_init failed (FusionSoundInit: %d)",
ret);
return 0;
}
}
/* 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 = 1;
return 1; /* this audio target is available. */
}
AudioBootStrap FUSIONSOUND_bootstrap = {
"fusionsound", "FusionSound", SDL_FS_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_FUSIONSOUND */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,50 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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,248 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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"
#include "SDL_assert.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;
/* 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);
return;
}
SDL_assert(audio->spec.size == len);
if (audio->stream == NULL) { /* no conversion necessary. */
SDL_LockMutex(audio->mixer_lock);
callback(audio->callbackspec.userdata, (Uint8 *) stream, len);
SDL_UnlockMutex(audio->mixer_lock);
} 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);
}
}
}
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, void *handle, const char *devname, int iscapture)
{
int valid_datatype = 0;
media_raw_audio_format format;
SDL_AudioFormat test_format = SDL_FirstAudioFormat(_this->spec.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? */
while ((!valid_datatype) && (test_format)) {
valid_datatype = 1;
_this->spec.format = test_format;
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:
valid_datatype = 0;
test_format = SDL_NextAudioFormat();
break;
}
}
if (!valid_datatype) { /* shouldn't happen, but just in case... */
return SDL_SetError("Unsupported audio 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 int
HAIKUAUDIO_Init(SDL_AudioDriverImpl * impl)
{
/* Initialize the Be Application, if it's not already started */
if (SDL_InitBeApp() < 0) {
return 0;
}
/* Set the function pointers */
impl->OpenDevice = HAIKUAUDIO_OpenDevice;
impl->CloseDevice = HAIKUAUDIO_CloseDevice;
impl->Deinitialize = HAIKUAUDIO_Deinitialize;
impl->ProvidesOwnCallbackThread = 1;
impl->OnlyHasDefaultOutputDevice = 1;
return 1; /* this audio target is available. */
}
extern "C"
{
extern AudioBootStrap HAIKUAUDIO_bootstrap;
}
AudioBootStrap HAIKUAUDIO_bootstrap = {
"haiku", "Haiku BSoundPlayer", HAIKUAUDIO_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_HAIKU */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,38 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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: */

446
externals/SDL/src/audio/jack/SDL_jackaudio.c vendored Executable file
View File

@@ -0,0 +1,446 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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_assert.h"
#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 = ((float *) 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; /* success */
}
/* 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 = ((float *) 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; /* success */
}
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);
}
static int
JACK_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
{
/* 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). */
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 || !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) {
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) {
return -1; /* error was set by SDL_CreateSemaphore */
}
this->hidden->iobuffer = (float *) SDL_calloc(1, this->spec.size);
if (!this->hidden->iobuffer) {
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) {
return SDL_OutOfMemory();
}
for (i = 0; i < channels; i++) {
char portname[32];
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) {
return SDL_SetError("jack_port_register failed");
}
}
if (JACK_jack_set_process_callback(client, callback, this) != 0) {
return SDL_SetError("JACK: Couldn't set process callback");
}
JACK_jack_on_shutdown(client, jackShutdownCallback, this);
if (JACK_jack_activate(client) != 0) {
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) {
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 int
JACK_Init(SDL_AudioDriverImpl * impl)
{
if (LoadJackLibrary() < 0) {
return 0;
} 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 0;
}
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 1; /* this audio target is available. */
}
AudioBootStrap JACK_bootstrap = {
"jack", "JACK Audio Connection Kit", JACK_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_JACK */
/* vi: set ts=4 sw=4 expandtab: */

41
externals/SDL/src/audio/jack/SDL_jackaudio.h vendored Executable file
View File

@@ -0,0 +1,41 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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: */

165
externals/SDL/src/audio/nacl/SDL_naclaudio.c vendored Executable file
View File

@@ -0,0 +1,165 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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(private->mutex); /* !!! FIXME: is this mutex necessary? */
/* 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);
return;
}
SDL_assert(_this->spec.size == len);
if (_this->stream == NULL) { /* no conversion necessary. */
SDL_LockMutex(_this->mixer_lock);
callback(_this->callbackspec.userdata, stream, len);
SDL_UnlockMutex(_this->mixer_lock);
} 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(private->mutex);
}
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);
SDL_DestroyMutex(hidden->mutex);
core->ReleaseResource(hidden->audio);
}
static int
NACLAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) {
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();
}
private->mutex = SDL_CreateMutex();
_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 int
NACLAUDIO_Init(SDL_AudioDriverImpl * impl)
{
if (PSGetInstanceId() == 0) {
return 0;
}
/* Set the function pointers */
impl->OpenDevice = NACLAUDIO_OpenDevice;
impl->CloseDevice = NACLAUDIO_CloseDevice;
impl->OnlyHasDefaultOutputDevice = 1;
impl->ProvidesOwnCallbackThread = 1;
/*
* impl->WaitDevice = NACLAUDIO_WaitDevice;
* impl->GetDeviceBuf = NACLAUDIO_GetDeviceBuf;
* impl->PlayDevice = NACLAUDIO_PlayDevice;
* impl->Deinitialize = NACLAUDIO_Deinitialize;
*/
return 1;
}
AudioBootStrap NACLAUDIO_bootstrap = {
NACLAUDIO_DRIVER_NAME, "SDL NaCl Audio Driver",
NACLAUDIO_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_NACL */
/* vi: set ts=4 sw=4 expandtab: */

43
externals/SDL/src/audio/nacl/SDL_naclaudio.h vendored Executable file
View File

@@ -0,0 +1,43 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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 {
SDL_mutex* mutex;
PP_Resource audio;
} SDL_PrivateAudioData;
#endif /* SDL_naclaudio_h_ */
/* vi: set ts=4 sw=4 expandtab: */

463
externals/SDL/src/audio/nas/SDL_nasaudio.c vendored Executable file
View File

@@ -0,0 +1,463 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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 = (char *) alloca(len);
SDL_strlcpy(err, origerr, len);
retval = -1;
SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s",
nas_library, err);
} 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 unsigned char
sdlformat_to_auformat(unsigned int fmt)
{
switch (fmt) {
case AUDIO_U8:
return AuFormatLinearUnsigned8;
case AUDIO_S8:
return AuFormatLinearSigned8;
case AUDIO_U16LSB:
return AuFormatLinearUnsigned16LSB;
case AUDIO_U16MSB:
return AuFormatLinearUnsigned16MSB;
case AUDIO_S16LSB:
return AuFormatLinearSigned16LSB;
case AUDIO_S16MSB:
return AuFormatLinearSigned16MSB;
}
return AuNone;
}
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, void *handle, const char *devname, int iscapture)
{
AuElement elms[3];
int buffer_size;
SDL_AudioFormat test_format, format;
/* 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 */
format = 0;
for (test_format = SDL_FirstAudioFormat(this->spec.format);
!format && test_format;) {
format = sdlformat_to_auformat(test_format);
if (format == AuNone) {
test_format = SDL_NextAudioFormat();
}
}
if (format == 0) {
return SDL_SetError("NAS: Couldn't find any hardware audio formats");
}
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 int
NAS_Init(SDL_AudioDriverImpl * impl)
{
if (LoadNASLibrary() < 0) {
return 0;
} else {
AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
if (aud == NULL) {
SDL_SetError("NAS: AuOpenServer() failed (no audio server?)");
return 0;
}
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 = 1;
impl->OnlyHasDefaultCaptureDevice = 1;
impl->HasCaptureSupport = SDL_TRUE;
return 1; /* this audio target is available. */
}
AudioBootStrap NAS_bootstrap = {
"nas", "Network Audio System", NAS_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_NAS */
/* vi: set ts=4 sw=4 expandtab: */

56
externals/SDL/src/audio/nas/SDL_nasaudio.h vendored Executable file
View File

@@ -0,0 +1,56 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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,333 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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).
* vedge@vedge.com.ar.
*/
#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* */
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* */
#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, void *handle, const char *devname, int iscapture)
{
SDL_AudioFormat format = 0;
audio_info_t info;
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);
if (this->hidden->audio_fd < 0) {
return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
}
AUDIO_INITINFO(&info);
prinfo->encoding = AUDIO_ENCODING_NONE;
for (format = SDL_FirstAudioFormat(this->spec.format); format;) {
switch (format) {
case AUDIO_U8:
prinfo->encoding = AUDIO_ENCODING_ULINEAR;
prinfo->precision = 8;
break;
case AUDIO_S8:
prinfo->encoding = AUDIO_ENCODING_SLINEAR;
prinfo->precision = 8;
break;
case AUDIO_S16LSB:
prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE;
prinfo->precision = 16;
break;
case AUDIO_S16MSB:
prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE;
prinfo->precision = 16;
break;
case AUDIO_U16LSB:
prinfo->encoding = AUDIO_ENCODING_ULINEAR_LE;
prinfo->precision = 16;
break;
case AUDIO_U16MSB:
prinfo->encoding = AUDIO_ENCODING_ULINEAR_BE;
prinfo->precision = 16;
break;
}
if (prinfo->encoding != AUDIO_ENCODING_NONE) {
break;
}
format = SDL_NextAudioFormat();
}
if (prinfo->encoding == AUDIO_ENCODING_NONE) {
return SDL_SetError("No supported encoding for 0x%x", this->spec.format);
}
this->spec.format = format;
/* Calculate spec parameters based on our chosen format */
SDL_CalculateAudioSpec(&this->spec);
info.mode = iscapture ? AUMODE_RECORD : AUMODE_PLAY;
info.blocksize = this->spec.size;
info.hiwat = 5;
info.lowat = 3;
prinfo->sample_rate = this->spec.freq;
prinfo->channels = this->spec.channels;
(void) ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info);
(void) ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info);
this->spec.freq = prinfo->sample_rate;
this->spec.channels = prinfo->channels;
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 int
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 = 1;
return 1; /* this audio target is available. */
}
AudioBootStrap NETBSDAUDIO_bootstrap = {
"netbsd", "NetBSD audio", NETBSDAUDIO_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_NETBSD */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,48 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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,766 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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_assert.h"
#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;
static SLEngineItf engineEngine;
/* output mix interfaces */
static SLObjectItf outputMixObject;
/* buffer queue player interfaces */
static SLObjectItf bqPlayerObject;
static SLPlayItf bqPlayerPlay;
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
#if 0
static SLVolumeItf bqPlayerVolume;
#endif
/* recorder interfaces */
static SLObjectItf recorderObject;
static SLRecordItf recorderRecord;
static SLAndroidSimpleBufferQueueItf recorderBufferQueue;
#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)
{
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 */
const SLInterfaceID ids[1] = { SL_IID_VOLUME };
const SLboolean req[1] = { SL_BOOLEAN_FALSE };
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;
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 */
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
SLDataSource audioSrc = {&loc_dev, NULL};
/* configure audio sink */
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 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;
SLDataSink audioSnk = { &loc_bufq, &format_pcm };
/* create audio recorder */
/* (requires the RECORD_AUDIO permission) */
const SLInterfaceID ids[1] = {
SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
};
const SLboolean req[1] = {
SL_BOOLEAN_TRUE,
};
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:
openslES_DestroyPCMRecorder(this);
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;
SLDataFormat_PCM format_pcm;
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 1
/* Just go with signed 16-bit audio as it's the most compatible */
this->spec.format = AUDIO_S16SYS;
#else
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
while (test_format != 0) {
if (SDL_AUDIO_ISSIGNED(test_format) && SDL_AUDIO_ISINT(test_format)) {
break;
}
test_format = SDL_NextAudioFormat();
}
if (test_format == 0) {
/* 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;
#endif
/* 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 */
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 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;
}
SLDataSource audioSrc = { &loc_bufq, &format_pcm };
/* configure audio sink */
SLDataLocator_OutputMix loc_outmix = { SL_DATALOCATOR_OUTPUTMIX, outputMixObject };
SLDataSink audioSnk = { &loc_outmix, NULL };
/* create audio player */
const SLInterfaceID ids[2] = {
SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
SL_IID_VOLUME
};
const SLboolean req[2] = {
SL_BOOLEAN_TRUE,
SL_BOOLEAN_FALSE,
};
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:
openslES_DestroyPCMPlayer(this);
return SDL_SetError("Open device failed!");
}
static int
openslES_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
{
this->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, (sizeof *this->hidden));
if (this->hidden == NULL) {
return SDL_OutOfMemory();
}
if (iscapture) {
LOGI("openslES_OpenDevice() %s for capture", devname);
return openslES_CreatePCMRecorder(this);
} else {
LOGI("openslES_OpenDevice() %s for playing", devname);
return openslES_CreatePCMPlayer(this);
}
}
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 int
openslES_Init(SDL_AudioDriverImpl * impl)
{
LOGI("openslES_Init() called");
if (!openslES_CreateEngine()) {
return 0;
}
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 = 1;
impl->OnlyHasDefaultOutputDevice = 1;
impl->OnlyHasDefaultCaptureDevice = 1;
LOGI("openslES_Init() - success");
/* this audio target is available. */
return 1;
}
AudioBootStrap openslES_bootstrap = {
"openslES", "opensl ES audio driver", openslES_Init, 0
};
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-2020 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: */

516
externals/SDL/src/audio/paudio/SDL_paudio.c vendored Executable file
View File

@@ -0,0 +1,516 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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_TRUE, 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, void *handle, const char *devname, int iscapture)
{
const char *workaround = SDL_getenv("SDL_DSP_NOSELECT");
char audiodev[1024];
const char *err = NULL;
int format;
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 */
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:
bytes_per_sample = 1;
paud_init.bits_per_sample = 8;
paud_init.flags = TWOS_COMPLEMENT | FIXED;
format = 1;
break;
case AUDIO_S8:
bytes_per_sample = 1;
paud_init.bits_per_sample = 8;
paud_init.flags = SIGNED | TWOS_COMPLEMENT | FIXED;
format = 1;
break;
case AUDIO_S16LSB:
bytes_per_sample = 2;
paud_init.bits_per_sample = 16;
paud_init.flags = SIGNED | TWOS_COMPLEMENT | FIXED;
format = 1;
break;
case AUDIO_S16MSB:
bytes_per_sample = 2;
paud_init.bits_per_sample = 16;
paud_init.flags = BIG_ENDIAN | SIGNED | TWOS_COMPLEMENT | FIXED;
format = 1;
break;
case AUDIO_U16LSB:
bytes_per_sample = 2;
paud_init.bits_per_sample = 16;
paud_init.flags = TWOS_COMPLEMENT | FIXED;
format = 1;
break;
case AUDIO_U16MSB:
bytes_per_sample = 2;
paud_init.bits_per_sample = 16;
paud_init.flags = BIG_ENDIAN | TWOS_COMPLEMENT | FIXED;
format = 1;
break;
default:
break;
}
if (!format) {
test_format = SDL_NextAudioFormat();
}
}
if (format == 0) {
#ifdef DEBUG_AUDIO
fprintf(stderr, "Couldn't find any hardware audio formats\n");
#endif
return SDL_SetError("Couldn't find any hardware audio formats");
}
this->spec.format = test_format;
/*
* 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 = "Couldn't set audio format: DSP can't do play requests";
break;
case 2:
err = "Couldn't set audio format: DSP can't do record requests";
break;
case 4:
err = "Couldn't set audio format: request was invalid";
break;
case 5:
err = "Couldn't set audio format: conflict with open's flags";
break;
case 6:
err = "Couldn't set audio format: out of DSP MIPS or memory";
break;
default:
err = "Couldn't set audio format: not documented in sys/audio.h";
break;
}
}
if (err != NULL) {
return SDL_SetError("Paudio: %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 int
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 0;
}
close(fd);
/* Set the function pointers */
impl->OpenDevice = PAUDIO_OpenDevice;
impl->PlayDevice = PAUDIO_PlayDevice;
impl->PlayDevice = PAUDIO_WaitDevice;
impl->GetDeviceBuf = PAUDIO_GetDeviceBuf;
impl->CloseDevice = PAUDIO_CloseDevice;
impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: add device enum! */
return 1; /* this audio target is available. */
}
AudioBootStrap PAUDIO_bootstrap = {
"paud", "AIX Paudio", PAUDIO_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_PAUDIO */
/* vi: set ts=4 sw=4 expandtab: */

48
externals/SDL/src/audio/paudio/SDL_paudio.h vendored Executable file
View File

@@ -0,0 +1,48 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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: */

181
externals/SDL/src/audio/psp/SDL_pspaudio.c vendored Executable file
View File

@@ -0,0 +1,181 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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>
#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 int
PSPAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
{
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);
switch (this->spec.format & 0xff) {
case 8:
case 16:
this->spec.format = AUDIO_S16LSB;
break;
default:
return SDL_SetError("Unsupported audio format");
}
/* The sample count must be a multiple of 64. */
this->spec.samples = PSP_AUDIO_SAMPLE_ALIGN(this->spec.samples);
this->spec.freq = 44100;
/* 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 = PSP_AUDIO_FORMAT_MONO;
} else {
this->spec.channels = 2;
format = PSP_AUDIO_FORMAT_STEREO;
}
this->hidden->channel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, this->spec.samples, format);
if (this->hidden->channel < 0) {
free(this->hidden->rawbuf);
this->hidden->rawbuf = NULL;
return SDL_SetError("Couldn't reserve hardware channel");
}
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 (this->spec.channels == 1) {
sceAudioOutputBlocking(this->hidden->channel, 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) {
sceAudioChRelease(this->hidden->channel);
}
free(this->hidden->rawbuf); /* this uses memalign(), not SDL_malloc(). */
SDL_free(this->hidden);
}
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 int
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 = 1;
/*
impl->HasCaptureSupport = 1;
impl->OnlyHasDefaultCaptureDevice = 1;
*/
/*
impl->DetectDevices = DSOUND_DetectDevices;
impl->Deinitialize = DSOUND_Deinitialize;
*/
return 1; /* this audio target is available. */
}
AudioBootStrap PSPAUDIO_bootstrap = {
"psp", "PSP audio driver", PSPAUDIO_Init, 0
};
/* SDL_AUDI */
#endif /* SDL_AUDIO_DRIVER_PSP */
/* vi: set ts=4 sw=4 expandtab: */

45
externals/SDL/src/audio/psp/SDL_pspaudio.h vendored Executable file
View File

@@ -0,0 +1,45 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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,782 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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_assert.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"
#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) (
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) (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, 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) (pa_stream *);
static size_t (*PULSEAUDIO_pa_stream_writable_size) (pa_stream *);
static size_t (*PULSEAUDIO_pa_stream_readable_size) (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 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);
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 *verstr = PULSEAUDIO_pa_get_library_version();
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)) {
return NULL; /* 0.9.15+ handles NULL correctly. */
}
}
}
return "SDL Application"; /* oh well. */
}
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 = mainloop;
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) {
return SDL_SetError("pa_context_new() failed");
}
*_context = context;
/* Connect to the PulseAudio server */
if (PULSEAUDIO_pa_context_connect(context, NULL, 0, NULL) < 0) {
return SDL_SetError("Could not setup connection to PulseAudio");
}
do {
if (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1, NULL) < 0) {
return SDL_SetError("pa_mainloop_iterate() failed");
}
state = PULSEAUDIO_pa_context_get_state(context);
if (!PA_CONTEXT_IS_GOOD(state)) {
return SDL_SetError("Could not connect to PulseAudio");
}
} while (state != PA_CONTEXT_READY);
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)
{
struct SDL_PrivateAudioData *h = this->hidden;
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;
}
if (PULSEAUDIO_pa_stream_writable_size(h->stream) >= h->mixlen) {
return;
}
}
}
static void
PULSEAUDIO_PlayDevice(_THIS)
{
/* Write the audio data */
struct SDL_PrivateAudioData *h = this->hidden;
if (SDL_AtomicGet(&this->enabled)) {
if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, h->mixlen, NULL, 0LL, PA_SEEK_RELATIVE) < 0) {
SDL_OpenedAudioDeviceDisconnected(this);
}
}
}
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) { /* NULL==buffer had a hole. Ignore that. */
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 int iscapture, void *handle)
{
const uint32_t idx = ((uint32_t) ((size_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, void *handle, const char *devname, int iscapture)
{
struct SDL_PrivateAudioData *h = NULL;
Uint16 test_format = 0;
pa_sample_spec paspec;
pa_buffer_attr paattr;
pa_channel_map pacmap;
pa_stream_flags_t flags = 0;
int state = 0;
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);
paspec.format = PA_SAMPLE_INVALID;
/* Try for a closest match on audio format */
for (test_format = SDL_FirstAudioFormat(this->spec.format);
(paspec.format == PA_SAMPLE_INVALID) && test_format;) {
#ifdef DEBUG_AUDIO
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
#endif
switch (test_format) {
case AUDIO_U8:
paspec.format = PA_SAMPLE_U8;
break;
case AUDIO_S16LSB:
paspec.format = PA_SAMPLE_S16LE;
break;
case AUDIO_S16MSB:
paspec.format = PA_SAMPLE_S16BE;
break;
case AUDIO_S32LSB:
paspec.format = PA_SAMPLE_S32LE;
break;
case AUDIO_S32MSB:
paspec.format = PA_SAMPLE_S32BE;
break;
case AUDIO_F32LSB:
paspec.format = PA_SAMPLE_FLOAT32LE;
break;
case AUDIO_F32MSB:
paspec.format = PA_SAMPLE_FLOAT32BE;
break;
default:
paspec.format = PA_SAMPLE_INVALID;
break;
}
if (paspec.format == PA_SAMPLE_INVALID) {
test_format = SDL_NextAudioFormat();
}
}
if (paspec.format == PA_SAMPLE_INVALID) {
return SDL_SetError("Couldn't find any hardware audio formats");
}
this->spec.format = test_format;
/* Calculate the final parameters for this audio specification */
#ifdef PA_STREAM_ADJUST_LATENCY
this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */
#endif
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. */
#ifdef PA_STREAM_ADJUST_LATENCY
/* 2x original requested bufsize */
paattr.tlength = h->mixlen * 4;
paattr.prebuf = -1;
paattr.maxlength = -1;
/* -1 can lead to pa_stream_writable_size() >= mixlen never being true */
paattr.minreq = h->mixlen;
flags = PA_STREAM_ADJUST_LATENCY;
#else
paattr.tlength = h->mixlen*2;
paattr.prebuf = h->mixlen*2;
paattr.maxlength = h->mixlen*2;
paattr.minreq = h->mixlen;
#endif
if (ConnectToPulseServer(&h->mainloop, &h->context) < 0) {
return SDL_SetError("Could not connect to PulseAudio server");
}
if (!FindDeviceName(h, iscapture, 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);
h->stream = PULSEAUDIO_pa_stream_new(
h->context,
"Simple DirectMedia Layer", /* 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 {
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;
/* device handles are device index + 1, cast to void*, so we never pass a NULL. */
/* 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)
{
if (i) {
SDL_AddAudioDevice(SDL_FALSE, i->description, (void *) ((size_t) i->index+1));
}
}
/* 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)
{
if (i) {
/* Skip "monitor" sources. These are just output from other sinks. */
if (i->monitor_of_sink == PA_INVALID_INDEX) {
SDL_AddAudioDevice(SDL_TRUE, i->description, (void *) ((size_t) i->index+1));
}
}
}
/* 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);
if (added || removed) { /* 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 && sink) {
PULSEAUDIO_pa_context_get_sink_info_by_index(hotplug_context, idx, SinkInfoCallback, NULL);
} else if (added && source) {
PULSEAUDIO_pa_context_get_source_info_by_index(hotplug_context, idx, SourceInfoCallback, NULL);
} else if (removed && (sink || source)) {
/* removes we can handle just with the device index. */
SDL_RemoveAudioDevice(source != 0, (void *) ((size_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_sink_info_list(hotplug_context, SinkInfoCallback, NULL));
WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_source_info_list(hotplug_context, SourceInfoCallback, NULL));
/* ok, we have a sane list, let's set up hotplug notifications now... */
hotplug_thread = SDL_CreateThreadInternal(HotplugThread, "PulseHotplug", 256 * 1024, NULL);
}
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;
UnloadPulseAudioLibrary();
}
static int
PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
{
if (LoadPulseAudioLibrary() < 0) {
return 0;
}
if (ConnectToPulseServer(&hotplug_mainloop, &hotplug_context) < 0) {
UnloadPulseAudioLibrary();
return 0;
}
/* 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->HasCaptureSupport = SDL_TRUE;
return 1; /* this audio target is available. */
}
AudioBootStrap PULSEAUDIO_bootstrap = {
"pulseaudio", "PulseAudio", PULSEAUDIO_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_PULSEAUDIO */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,52 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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;
const Uint8 *capturebuf;
int capturelen;
};
#endif /* SDL_pulseaudio_h_ */
/* vi: set ts=4 sw=4 expandtab: */

666
externals/SDL/src/audio/qsa/SDL_qsa_audio.c vendored Executable file
View File

@@ -0,0 +1,666 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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 SDL_INLINE 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 occured than something wrong with hardware or driver */
/* For example, Vortex 8820 audio driver stucks on second DAC because */
/* it doesn't exist ! */
result = SDL_IOReady(this->hidden->audio_fd, !this->hidden->iscapture, 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 occured");
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->hidden->iscapture) {
cstatus.channel = SND_PCM_CHANNEL_PLAYBACK;
} else {
cstatus.channel = SND_PCM_CHANNEL_CAPTURE;
}
status =
snd_pcm_plugin_status(this->hidden->audio_handle,
&cstatus);
if (status < 0) {
QSA_SetError("snd_pcm_plugin_status", status);
return;
}
if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) ||
(cstatus.status == SND_PCM_STATUS_READY)) {
if (!this->hidden->iscapture) {
status =
snd_pcm_plugin_prepare(this->hidden->
audio_handle,
SND_PCM_CHANNEL_PLAYBACK);
} else {
status =
snd_pcm_plugin_prepare(this->hidden->
audio_handle,
SND_PCM_CHANNEL_CAPTURE);
}
if (status < 0) {
QSA_SetError("snd_pcm_plugin_prepare", status);
return;
}
}
continue;
} else {
return;
}
}
} 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 (!this->hidden->iscapture) {
/* Finish playing available samples */
snd_pcm_plugin_flush(this->hidden->audio_handle,
SND_PCM_CHANNEL_PLAYBACK);
} else {
/* Cancel unread samples during capture */
snd_pcm_plugin_flush(this->hidden->audio_handle,
SND_PCM_CHANNEL_CAPTURE);
}
snd_pcm_close(this->hidden->audio_handle);
}
SDL_free(this->hidden->pcm_buf);
SDL_free(this->hidden);
}
static int
QSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
{
const QSA_Device *device = (const QSA_Device *) handle;
int status = 0;
int format = 0;
SDL_AudioFormat test_format = 0;
int found = 0;
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);
/* Initialize channel direction: capture or playback */
this->hidden->iscapture = iscapture ? SDL_TRUE : SDL_FALSE;
if (device != NULL) {
/* Open requested audio device */
this->hidden->deviceno = device->deviceno;
this->hidden->cardno = device->cardno;
status = snd_pcm_open(&this->hidden->audio_handle,
device->cardno, device->deviceno,
iscapture ? SND_PCM_OPEN_CAPTURE : SND_PCM_OPEN_PLAYBACK);
} else {
/* Open system default audio device */
status = snd_pcm_open_preferred(&this->hidden->audio_handle,
&this->hidden->cardno,
&this->hidden->deviceno,
iscapture ? SND_PCM_OPEN_CAPTURE : SND_PCM_OPEN_PLAYBACK);
}
/* Check if requested device is opened */
if (status < 0) {
this->hidden->audio_handle = NULL;
return QSA_SetError("snd_pcm_open", status);
}
/* Try for a closest match on audio format */
format = 0;
/* can't use format as SND_PCM_SFMT_U8 = 0 in qsa */
found = 0;
for (test_format = SDL_FirstAudioFormat(this->spec.format); !found;) {
/* if match found set format to equivalent QSA format */
switch (test_format) {
case AUDIO_U8:
{
format = SND_PCM_SFMT_U8;
found = 1;
}
break;
case AUDIO_S8:
{
format = SND_PCM_SFMT_S8;
found = 1;
}
break;
case AUDIO_S16LSB:
{
format = SND_PCM_SFMT_S16_LE;
found = 1;
}
break;
case AUDIO_S16MSB:
{
format = SND_PCM_SFMT_S16_BE;
found = 1;
}
break;
case AUDIO_U16LSB:
{
format = SND_PCM_SFMT_U16_LE;
found = 1;
}
break;
case AUDIO_U16MSB:
{
format = SND_PCM_SFMT_U16_BE;
found = 1;
}
break;
case AUDIO_S32LSB:
{
format = SND_PCM_SFMT_S32_LE;
found = 1;
}
break;
case AUDIO_S32MSB:
{
format = SND_PCM_SFMT_S32_BE;
found = 1;
}
break;
case AUDIO_F32LSB:
{
format = SND_PCM_SFMT_FLOAT_LE;
found = 1;
}
break;
case AUDIO_F32MSB:
{
format = SND_PCM_SFMT_FLOAT_BE;
found = 1;
}
break;
default:
{
break;
}
}
if (!found) {
test_format = SDL_NextAudioFormat();
}
}
/* assumes test_format not 0 on success */
if (test_format == 0) {
return SDL_SetError("QSA: Couldn't find any hardware audio formats");
}
this->spec.format = test_format;
/* 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->hidden->iscapture) {
csetup.channel = SND_PCM_CHANNEL_PLAYBACK;
} else {
csetup.channel = SND_PCM_CHANNEL_CAPTURE;
}
/* Setup an audio channel */
if (snd_pcm_plugin_setup(this->hidden->audio_handle, &csetup) < 0) {
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->hidden->iscapture) {
this->hidden->audio_fd =
snd_pcm_file_descriptor(this->hidden->audio_handle,
SND_PCM_CHANNEL_PLAYBACK);
} else {
this->hidden->audio_fd =
snd_pcm_file_descriptor(this->hidden->audio_handle,
SND_PCM_CHANNEL_CAPTURE);
}
if (this->hidden->audio_fd < 0) {
return QSA_SetError("snd_pcm_file_descriptor", status);
}
/* Prepare an audio channel */
if (!this->hidden->iscapture) {
/* Prepare audio playback */
status =
snd_pcm_plugin_prepare(this->hidden->audio_handle,
SND_PCM_CHANNEL_PLAYBACK);
} else {
/* Prepare audio capture */
status =
snd_pcm_plugin_prepare(this->hidden->audio_handle,
SND_PCM_CHANNEL_CAPTURE);
}
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) {
SDL_AddAudioDevice(SDL_FALSE, qsa_playback_device[qsa_playback_devices].name, &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) {
SDL_AddAudioDevice(SDL_TRUE, qsa_capture_device[qsa_capture_devices].name, &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 int
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->LockDevice = NULL;
impl->UnlockDevice = NULL;
impl->ProvidesOwnCallbackThread = 0;
impl->SkipMixerLock = 0;
impl->HasCaptureSupport = 1;
impl->OnlyHasDefaultOutputDevice = 0;
impl->OnlyHasDefaultCaptureDevice = 0;
return 1; /* this audio target is available. */
}
AudioBootStrap QSAAUDIO_bootstrap = {
"qsa", "QNX QSA Audio", QSA_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_QSA */
/* vi: set ts=4 sw=4 expandtab: */

57
externals/SDL/src/audio/qsa/SDL_qsa_audio.h vendored Executable file
View File

@@ -0,0 +1,57 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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
{
/* SDL capture state */
SDL_bool iscapture;
/* The audio device handle */
int cardno;
int deviceno;
snd_pcm_t *audio_handle;
/* The audio file descriptor */
int audio_fd;
/* Select timeout status */
uint32_t timeout_on_wait;
/* Raw mixing buffer */
Uint8 *pcm_buf;
Uint32 pcm_len;
};
#endif /* __SDL_QSA_AUDIO_H__ */
/* vi: set ts=4 sw=4 expandtab: */

382
externals/SDL/src/audio/sndio/SDL_sndioaudio.c vendored Executable file
View File

@@ -0,0 +1,382 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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)) {
if ((nfds = SNDIO_sio_pollfd(this->hidden->dev, this->hidden->pfd, POLLIN)) <= 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, void *handle, const char *devname, int iscapture)
{
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
struct sio_par par;
int status;
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 */
if ((this->hidden->dev =
SNDIO_sio_open(devname != NULL ? devname : SIO_DEVANY,
iscapture ? SIO_REC : SIO_PLAY, iscapture)) == 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))) == 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 */
status = -1;
while (test_format && (status < 0)) {
if (!SDL_AUDIO_ISFLOAT(test_format)) {
par.le = SDL_AUDIO_ISLITTLEENDIAN(test_format) ? 1 : 0;
par.sig = SDL_AUDIO_ISSIGNED(test_format) ? 1 : 0;
par.bits = SDL_AUDIO_BITSIZE(test_format);
if (SNDIO_sio_setpar(this->hidden->dev, &par) == 0) {
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)) {
status = 0;
break;
}
}
test_format = SDL_NextAudioFormat();
}
if (status < 0) {
return SDL_SetError("sndio: Couldn't find any hardware audio formats");
}
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 int
SNDIO_Init(SDL_AudioDriverImpl * impl)
{
if (LoadSNDIOLibrary() < 0) {
return 0;
}
/* 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->AllowsArbitraryDeviceNames = 1;
impl->HasCaptureSupport = SDL_TRUE;
return 1; /* this audio target is available. */
}
AudioBootStrap SNDIO_bootstrap = {
"sndio", "OpenBSD sndio", SNDIO_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_SNDIO */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,49 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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: */

419
externals/SDL/src/audio/sun/SDL_sunaudio.c vendored Executable file
View File

@@ -0,0 +1,419 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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_TRUE, -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, void *handle, const char *devname, int iscapture)
{
#ifdef AUDIO_SETINFO
int enc;
#endif
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 <20>-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 int
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 = 1;
return 1; /* this audio target is available. */
}
AudioBootStrap SUNAUDIO_bootstrap = {
"audio", "UNIX /dev/audio interface", SUNAUDIO_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_SUNAUDIO */
/* vi: set ts=4 sw=4 expandtab: */

47
externals/SDL/src/audio/sun/SDL_sunaudio.h vendored Executable file
View File

@@ -0,0 +1,47 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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: */

769
externals/SDL/src/audio/wasapi/SDL_wasapi.c vendored Executable file
View File

@@ -0,0 +1,769 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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 "SDL_audio.h"
#include "SDL_timer.h"
#include "../SDL_audio_c.h"
#include "../SDL_sysaudio.h"
#include "SDL_assert.h"
#include "SDL_log.h"
#define COBJMACROS
#include <mmdeviceapi.h>
#include <audioclient.h>
#include "SDL_wasapi.h"
/* This constant isn't available on MinGW-w64 */
#ifndef AUDCLNT_STREAMFLAGS_RATEADJUST
#define AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
#endif
/* these increment as default devices change. Opened default devices pick up changes in their threads. */
SDL_atomic_t WASAPI_DefaultPlaybackGeneration;
SDL_atomic_t WASAPI_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;
/* 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 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 SDL_bool
WStrEqual(const WCHAR *a, const WCHAR *b)
{
while (*a) {
if (*a != *b) {
return SDL_FALSE;
}
a++;
b++;
}
return *b == 0;
}
static size_t
WStrLen(const WCHAR *wstr)
{
size_t retval = 0;
if (wstr) {
while (*(wstr++)) {
retval++;
}
}
return retval;
}
static WCHAR *
WStrDupe(const WCHAR *wstr)
{
const size_t len = (WStrLen(wstr) + 1) * sizeof (WCHAR);
WCHAR *retval = (WCHAR *) SDL_malloc(len);
if (retval) {
SDL_memcpy(retval, wstr, len);
}
return retval;
}
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 (WStrEqual(i->str, devid)) {
if (prev) {
prev->next = next;
} else {
deviceid_list = next;
}
SDL_RemoveAudioDevice(iscapture, i->str);
SDL_free(i->str);
SDL_free(i);
}
prev = i;
}
}
void
WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, LPCWSTR devid)
{
DevIdList *devidlist;
/* 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 (WStrEqual(devidlist->str, devid)) {
return; /* we already have this. */
}
}
devidlist = (DevIdList *) SDL_malloc(sizeof (*devidlist));
if (!devidlist) {
return; /* oh well. */
}
devid = WStrDupe(devid);
if (!devid) {
SDL_free(devidlist);
return; /* oh well. */
}
devidlist->str = (WCHAR *) devid;
devidlist->next = deviceid_list;
deviceid_list = devidlist;
SDL_AddAudioDevice(iscapture, devname, (void *) devid);
}
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;
}
}
/* 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 ? &WASAPI_DefaultCaptureGeneration : &WASAPI_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 ? &WASAPI_DefaultCaptureGeneration : &WASAPI_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 (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_SetEventHandle(this->hidden->client, NULL);
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);
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 duration = 0;
IAudioClient *client = this->hidden->client;
IAudioRenderClient *render = NULL;
IAudioCaptureClient *capture = NULL;
WAVEFORMATEX *waveformat = NULL;
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
SDL_AudioFormat wasapi_format = 0;
SDL_bool valid_format = SDL_FALSE;
HRESULT ret = S_OK;
DWORD streamflags = 0;
SDL_assert(client != NULL);
#ifdef __WINRT__ /* 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. */
if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
wasapi_format = AUDIO_F32SYS;
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
wasapi_format = AUDIO_S16SYS;
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
wasapi_format = 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)) {
wasapi_format = AUDIO_F32SYS;
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
wasapi_format = AUDIO_S16SYS;
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
wasapi_format = AUDIO_S32SYS;
}
}
while ((!valid_format) && (test_format)) {
if (test_format == wasapi_format) {
this->spec.format = test_format;
valid_format = SDL_TRUE;
break;
}
test_format = SDL_NextAudioFormat();
}
if (!valid_format) {
return SDL_SetError("WASAPI: Unsupported audio format");
}
ret = IAudioClient_GetDevicePeriod(client, NULL, &duration);
if (FAILED(ret)) {
return WIN_SetErrorFromHRESULT("WASAPI can't determine minimum device period", ret);
}
/* favor WASAPI's resampler over our own, in Win7+. */
if (this->spec.freq != waveformat->nSamplesPerSec) {
/* RATEADJUST only works with output devices in share mode, and is available in Win7 and later.*/
if (WIN_IsWindows7OrGreater() && !this->iscapture && (sharemode == AUDCLNT_SHAREMODE_SHARED)) {
streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST;
waveformat->nSamplesPerSec = this->spec.freq;
waveformat->nAvgBytesPerSec = waveformat->nSamplesPerSec * waveformat->nChannels * (waveformat->wBitsPerSample / 8);
}
else {
this->spec.freq = waveformat->nSamplesPerSec; /* force sampling rate so our resampler kicks in. */
}
}
streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
ret = IAudioClient_Initialize(client, sharemode, streamflags, duration, sharemode == AUDCLNT_SHAREMODE_SHARED ? 0 : duration, 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);
}
this->spec.samples = (Uint16) bufsize;
if (!this->iscapture) {
this->spec.samples /= 2; /* fill half of the DMA buffer on each run. */
}
/* 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) {
if (UpdateAudioStream(this, &oldspec) == -1) {
return -1;
}
}
return 0; /* good to go. */
}
static int
WASAPI_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
{
LPCWSTR devid = (LPCWSTR) 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 (!devid) { /* is default device? */
this->hidden->default_device_generation = SDL_AtomicGet(iscapture ? &WASAPI_DefaultCaptureGeneration : &WASAPI_DefaultPlaybackGeneration);
} else {
this->hidden->devid = WStrDupe(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);
}
void
WASAPI_BeginLoopIteration(_THIS)
{
/* no-op. */
}
static void
WASAPI_Deinitialize(void)
{
DevIdList *devidlist;
DevIdList *next;
WASAPI_PlatformDeinit();
for (devidlist = deviceid_list; devidlist; devidlist = next) {
next = devidlist->next;
SDL_free(devidlist->str);
SDL_free(devidlist);
}
deviceid_list = NULL;
}
static int
WASAPI_Init(SDL_AudioDriverImpl * impl)
{
SDL_AtomicSet(&WASAPI_DefaultPlaybackGeneration, 1);
SDL_AtomicSet(&WASAPI_DefaultCaptureGeneration, 1);
if (WASAPI_PlatformInit() == -1) {
return 0;
}
/* Set the function pointers */
impl->DetectDevices = WASAPI_DetectDevices;
impl->ThreadInit = WASAPI_ThreadInit;
impl->ThreadDeinit = WASAPI_ThreadDeinit;
impl->BeginLoopIteration = WASAPI_BeginLoopIteration;
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->HasCaptureSupport = 1;
return 1; /* this audio target is available. */
}
AudioBootStrap WASAPI_bootstrap = {
"wasapi", "WASAPI", WASAPI_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_WASAPI */
/* vi: set ts=4 sw=4 expandtab: */

85
externals/SDL/src/audio/wasapi/SDL_wasapi.h vendored Executable file
View File

@@ -0,0 +1,85 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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_bool coinitialized;
int framesize;
int default_device_generation;
SDL_bool device_lost;
void *activation_handler;
SDL_atomic_t just_activated;
};
/* these increment as default devices change. Opened default devices pick up changes in their threads. */
extern SDL_atomic_t WASAPI_DefaultPlaybackGeneration;
extern SDL_atomic_t WASAPI_DefaultCaptureGeneration;
/* win32 and winrt implementations call into these. */
int WASAPI_PrepDevice(_THIS, const SDL_bool updatestream);
void WASAPI_RefDevice(_THIS);
void WASAPI_UnrefDevice(_THIS);
void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, LPCWSTR devid);
void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid);
/* 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_ActivateDevice(_THIS, const SDL_bool isrecovery);
void WASAPI_PlatformThreadInit(_THIS);
void WASAPI_PlatformThreadDeinit(_THIS);
void WASAPI_PlatformDeleteActivationHandler(void *handler);
void WASAPI_BeginLoopIteration(_THIS);
#ifdef __cplusplus
}
#endif
#endif /* SDL_wasapi_h_ */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,457 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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 "SDL_audio.h"
#include "SDL_timer.h"
#include "../SDL_audio_c.h"
#include "../SDL_sysaudio.h"
#include "SDL_assert.h"
#include "SDL_log.h"
#define COBJMACROS
#include <mmdeviceapi.h>
#include <audioclient.h>
#include "SDL_wasapi.h"
static const ERole SDL_WASAPI_role = eConsole; /* !!! FIXME: should this be eMultimedia? Should be a hint? */
/* This is global to the WASAPI target, to handle hotplug and default device lookup. */
static IMMDeviceEnumerator *enumerator = NULL;
/* PropVariantInit() is an inline function/macro in PropIdl.h that calls the C runtime's memset() directly. Use ours instead, to avoid dependency. */
#ifdef PropVariantInit
#undef PropVariantInit
#endif
#define PropVariantInit(p) SDL_zerop(p)
/* 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)(LPWSTR, 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 CLSID SDL_CLSID_MMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c,{ 0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e } };
static const IID SDL_IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35,{ 0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6 } };
static const IID SDL_IID_IMMNotificationClient = { 0x7991eec9, 0x7e89, 0x4d85,{ 0x83, 0x90, 0x6c, 0x70, 0x3c, 0xec, 0x60, 0xc0 } };
static const IID SDL_IID_IMMEndpoint = { 0x1be09788, 0x6894, 0x4089,{ 0x85, 0x86, 0x9a, 0x2a, 0x6c, 0x26, 0x5a, 0xc5 } };
static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32,{ 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } };
static const PROPERTYKEY SDL_PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd,{ 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, } }, 14 };
static char *
GetWasapiDeviceName(IMMDevice *device)
{
/* PKEY_Device_FriendlyName gives you "Speakers (SoundBlaster Pro)" which drives me nuts. I'd rather it be
"SoundBlaster Pro (Speakers)" but I guess that's developers vs users. Windows uses the FriendlyName in
its own UIs, like Volume Control, etc. */
char *utf8dev = NULL;
IPropertyStore *props = NULL;
if (SUCCEEDED(IMMDevice_OpenPropertyStore(device, STGM_READ, &props))) {
PROPVARIANT var;
PropVariantInit(&var);
if (SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_Device_FriendlyName, &var))) {
utf8dev = WIN_StringToUTF8(var.pwszVal);
}
PropVariantClear(&var);
IPropertyStore_Release(props);
}
return utf8dev;
}
/* We need a COM subclass of IMMNotificationClient for hotplug support, which is
easy in C++, but we have to tapdance more to make work in C.
Thanks to this page for coaching on how to make this work:
https://www.codeproject.com/Articles/13601/COM-in-plain-C */
typedef struct SDLMMNotificationClient
{
const IMMNotificationClientVtbl *lpVtbl;
SDL_atomic_t refcount;
} SDLMMNotificationClient;
static HRESULT STDMETHODCALLTYPE
SDLMMNotificationClient_QueryInterface(IMMNotificationClient *this, REFIID iid, void **ppv)
{
if ((WIN_IsEqualIID(iid, &IID_IUnknown)) || (WIN_IsEqualIID(iid, &SDL_IID_IMMNotificationClient)))
{
*ppv = this;
this->lpVtbl->AddRef(this);
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
static ULONG STDMETHODCALLTYPE
SDLMMNotificationClient_AddRef(IMMNotificationClient *ithis)
{
SDLMMNotificationClient *this = (SDLMMNotificationClient *) ithis;
return (ULONG) (SDL_AtomicIncRef(&this->refcount) + 1);
}
static ULONG STDMETHODCALLTYPE
SDLMMNotificationClient_Release(IMMNotificationClient *ithis)
{
/* this is a static object; we don't ever free it. */
SDLMMNotificationClient *this = (SDLMMNotificationClient *) ithis;
const ULONG retval = SDL_AtomicDecRef(&this->refcount);
if (retval == 0) {
SDL_AtomicSet(&this->refcount, 0); /* uhh... */
return 0;
}
return retval - 1;
}
/* These are the entry points called when WASAPI device endpoints change. */
static HRESULT STDMETHODCALLTYPE
SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *ithis, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
{
if (role != SDL_WASAPI_role) {
return S_OK; /* ignore it. */
}
/* Increment the "generation," so opened devices will pick this up in their threads. */
switch (flow) {
case eRender:
SDL_AtomicAdd(&WASAPI_DefaultPlaybackGeneration, 1);
break;
case eCapture:
SDL_AtomicAdd(&WASAPI_DefaultCaptureGeneration, 1);
break;
case eAll:
SDL_AtomicAdd(&WASAPI_DefaultPlaybackGeneration, 1);
SDL_AtomicAdd(&WASAPI_DefaultCaptureGeneration, 1);
break;
default:
SDL_assert(!"uhoh, unexpected OnDefaultDeviceChange flow!");
break;
}
return S_OK;
}
static HRESULT STDMETHODCALLTYPE
SDLMMNotificationClient_OnDeviceAdded(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId)
{
/* we ignore this; devices added here then progress to ACTIVE, if appropriate, in
OnDeviceStateChange, making that a better place to deal with device adds. More
importantly: the first time you plug in a USB audio device, this callback will
fire, but when you unplug it, it isn't removed (it's state changes to NOTPRESENT).
Plugging it back in won't fire this callback again. */
return S_OK;
}
static HRESULT STDMETHODCALLTYPE
SDLMMNotificationClient_OnDeviceRemoved(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId)
{
/* See notes in OnDeviceAdded handler about why we ignore this. */
return S_OK;
}
static HRESULT STDMETHODCALLTYPE
SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId, DWORD dwNewState)
{
IMMDevice *device = NULL;
if (SUCCEEDED(IMMDeviceEnumerator_GetDevice(enumerator, pwstrDeviceId, &device))) {
IMMEndpoint *endpoint = NULL;
if (SUCCEEDED(IMMDevice_QueryInterface(device, &SDL_IID_IMMEndpoint, (void **) &endpoint))) {
EDataFlow flow;
if (SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) {
const SDL_bool iscapture = (flow == eCapture);
if (dwNewState == DEVICE_STATE_ACTIVE) {
char *utf8dev = GetWasapiDeviceName(device);
if (utf8dev) {
WASAPI_AddDevice(iscapture, utf8dev, pwstrDeviceId);
SDL_free(utf8dev);
}
} else {
WASAPI_RemoveDevice(iscapture, pwstrDeviceId);
}
}
IMMEndpoint_Release(endpoint);
}
IMMDevice_Release(device);
}
return S_OK;
}
static HRESULT STDMETHODCALLTYPE
SDLMMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *this, LPCWSTR pwstrDeviceId, const PROPERTYKEY key)
{
return S_OK; /* we don't care about these. */
}
static const IMMNotificationClientVtbl notification_client_vtbl = {
SDLMMNotificationClient_QueryInterface,
SDLMMNotificationClient_AddRef,
SDLMMNotificationClient_Release,
SDLMMNotificationClient_OnDeviceStateChanged,
SDLMMNotificationClient_OnDeviceAdded,
SDLMMNotificationClient_OnDeviceRemoved,
SDLMMNotificationClient_OnDefaultDeviceChanged,
SDLMMNotificationClient_OnPropertyValueChanged
};
static SDLMMNotificationClient notification_client = { &notification_client_vtbl, { 1 } };
int
WASAPI_PlatformInit(void)
{
HRESULT ret;
/* just skip the discussion with COM here. */
if (!WIN_IsWindowsVistaOrGreater()) {
return SDL_SetError("WASAPI support requires Windows Vista or later");
}
if (FAILED(WIN_CoInitialize())) {
return SDL_SetError("WASAPI: CoInitialize() failed");
}
ret = CoCreateInstance(&SDL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &SDL_IID_IMMDeviceEnumerator, (LPVOID) &enumerator);
if (FAILED(ret)) {
WIN_CoUninitialize();
return WIN_SetErrorFromHRESULT("WASAPI CoCreateInstance(MMDeviceEnumerator)", ret);
}
libavrt = LoadLibraryW(L"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 (enumerator) {
IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *) &notification_client);
IMMDeviceEnumerator_Release(enumerator);
enumerator = NULL;
}
if (libavrt) {
FreeLibrary(libavrt);
libavrt = NULL;
}
pAvSetMmThreadCharacteristicsW = NULL;
pAvRevertMmThreadCharacteristics = NULL;
WIN_CoUninitialize();
}
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(TEXT("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)
{
LPCWSTR devid = this->hidden->devid;
IMMDevice *device = NULL;
HRESULT ret;
if (devid == NULL) {
const EDataFlow dataflow = this->iscapture ? eCapture : eRender;
ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_WASAPI_role, &device);
} else {
ret = IMMDeviceEnumerator_GetDevice(enumerator, devid, &device);
}
if (FAILED(ret)) {
SDL_assert(device == NULL);
this->hidden->client = NULL;
return WIN_SetErrorFromHRESULT("WASAPI can't find requested audio endpoint", ret);
}
/* 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. */
}
typedef struct
{
LPWSTR devid;
char *devname;
} EndpointItem;
static int sort_endpoints(const void *_a, const void *_b)
{
LPWSTR a = ((const EndpointItem *) _a)->devid;
LPWSTR b = ((const EndpointItem *) _b)->devid;
if (!a && b) {
return -1;
} else if (a && !b) {
return 1;
}
while (SDL_TRUE) {
if (*a < *b) {
return -1;
} else if (*a > *b) {
return 1;
} else if (*a == 0) {
break;
}
a++;
b++;
}
return 0;
}
static void
WASAPI_EnumerateEndpointsForFlow(const SDL_bool iscapture)
{
IMMDeviceCollection *collection = NULL;
EndpointItem *items;
UINT i, total;
/* Note that WASAPI separates "adapter devices" from "audio endpoint devices"
...one adapter device ("SoundBlaster Pro") might have multiple endpoint devices ("Speakers", "Line-Out"). */
if (FAILED(IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, iscapture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &collection))) {
return;
}
if (FAILED(IMMDeviceCollection_GetCount(collection, &total))) {
IMMDeviceCollection_Release(collection);
return;
}
items = (EndpointItem *) SDL_calloc(total, sizeof (EndpointItem));
if (!items) {
return; /* oh well. */
}
for (i = 0; i < total; i++) {
EndpointItem *item = items + i;
IMMDevice *device = NULL;
if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &device))) {
if (SUCCEEDED(IMMDevice_GetId(device, &item->devid))) {
item->devname = GetWasapiDeviceName(device);
}
IMMDevice_Release(device);
}
}
/* sort the list of devices by their guid so list is consistent between runs */
SDL_qsort(items, total, sizeof (*items), sort_endpoints);
/* Send the sorted list on to the SDL's higher level. */
for (i = 0; i < total; i++) {
EndpointItem *item = items + i;
if ((item->devid) && (item->devname)) {
WASAPI_AddDevice(iscapture, item->devname, item->devid);
}
SDL_free(item->devname);
CoTaskMemFree(item->devid);
}
SDL_free(items);
IMMDeviceCollection_Release(collection);
}
void
WASAPI_EnumerateEndpoints(void)
{
WASAPI_EnumerateEndpointsForFlow(SDL_FALSE); /* playback */
WASAPI_EnumerateEndpointsForFlow(SDL_TRUE); /* capture */
/* if this fails, we just won't get hotplug events. Carry on anyhow. */
IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *) &notification_client);
}
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,304 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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>
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"
#include "SDL_assert.h"
#include "SDL_log.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;
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))
, watcher(DeviceInformation::CreateWatcher(_iscapture ? DeviceClass::AudioCapture : DeviceClass::AudioRender))
{
if (!watcher || !completed)
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) {
WASAPI_AddDevice(this->iscapture, utf8dev, 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(&WASAPI_DefaultPlaybackGeneration, 1);
}
void
SDL_WasapiDeviceEventHandler::OnDefaultCaptureDeviceChanged(Platform::Object^ sender, DefaultAudioCaptureDeviceChangedEventArgs^ args)
{
SDL_assert(!this->iscapture);
SDL_AtomicAdd(&WASAPI_DefaultCaptureGeneration, 1);
}
static SDL_WasapiDeviceEventHandler *playback_device_event_handler;
static SDL_WasapiDeviceEventHandler *capture_device_event_handler;
int WASAPI_PlatformInit(void)
{
return 0;
}
void WASAPI_PlatformDeinit(void)
{
delete playback_device_event_handler;
playback_device_event_handler = nullptr;
delete capture_device_event_handler;
capture_device_event_handler = nullptr;
}
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_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.
}
#endif // SDL_AUDIO_DRIVER_WASAPI && defined(__WINRT__)
/* vi: set ts=4 sw=4 expandtab: */

456
externals/SDL/src/audio/winmm/SDL_winmm.c vendored Executable file
View File

@@ -0,0 +1,456 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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_assert.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; \
UINT i; \
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) { \
SDL_AddAudioDevice((int) iscapture, name, (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(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, void *handle, const char *devname, int iscapture)
{
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
int valid_datatype = 0;
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? */
while ((!valid_datatype) && (test_format)) {
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)) {
valid_datatype = 1;
} else {
test_format = SDL_NextAudioFormat();
}
break;
default:
test_format = SDL_NextAudioFormat();
break;
}
}
if (!valid_datatype) {
return SDL_SetError("Unsupported audio format");
}
/* 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 int
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 1; /* this audio target is available. */
}
AudioBootStrap WINMM_bootstrap = {
"winmm", "Windows Waveform Audio", WINMM_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_WINMM */
/* vi: set ts=4 sw=4 expandtab: */

45
externals/SDL/src/audio/winmm/SDL_winmm.h vendored Executable file
View File

@@ -0,0 +1,45 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 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: */