early-access version 1611
This commit is contained in:
1709
externals/SDL/src/audio/SDL_audio.c
vendored
Executable file
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
80
externals/SDL/src/audio/SDL_audio_c.h
vendored
Executable 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
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
124
externals/SDL/src/audio/SDL_audiodev.c
vendored
Executable 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
44
externals/SDL/src/audio/SDL_audiodev_c.h
vendored
Executable 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
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
369
externals/SDL/src/audio/SDL_mixer.c
vendored
Executable 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
213
externals/SDL/src/audio/SDL_sysaudio.h
vendored
Executable 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
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
149
externals/SDL/src/audio/SDL_wave.h
vendored
Executable 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
998
externals/SDL/src/audio/alsa/SDL_alsa_audio.c
vendored
Executable 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
48
externals/SDL/src/audio/alsa/SDL_alsa_audio.h
vendored
Executable 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: */
|
||||
211
externals/SDL/src/audio/android/SDL_androidaudio.c
vendored
Executable file
211
externals/SDL/src/audio/android/SDL_androidaudio.c
vendored
Executable 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: */
|
||||
|
||||
42
externals/SDL/src/audio/android/SDL_androidaudio.h
vendored
Executable file
42
externals/SDL/src/audio/android/SDL_androidaudio.h
vendored
Executable 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
365
externals/SDL/src/audio/arts/SDL_artsaudio.c
vendored
Executable 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
53
externals/SDL/src/audio/arts/SDL_artsaudio.h
vendored
Executable 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: */
|
||||
68
externals/SDL/src/audio/coreaudio/SDL_coreaudio.h
vendored
Executable file
68
externals/SDL/src/audio/coreaudio/SDL_coreaudio.h
vendored
Executable 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: */
|
||||
984
externals/SDL/src/audio/coreaudio/SDL_coreaudio.m
vendored
Executable file
984
externals/SDL/src/audio/coreaudio/SDL_coreaudio.m
vendored
Executable 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: */
|
||||
604
externals/SDL/src/audio/directsound/SDL_directsound.c
vendored
Executable file
604
externals/SDL/src/audio/directsound/SDL_directsound.c
vendored
Executable 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: */
|
||||
47
externals/SDL/src/audio/directsound/SDL_directsound.h
vendored
Executable file
47
externals/SDL/src/audio/directsound/SDL_directsound.h
vendored
Executable 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
207
externals/SDL/src/audio/disk/SDL_diskaudio.c
vendored
Executable 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
41
externals/SDL/src/audio/disk/SDL_diskaudio.h
vendored
Executable 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
320
externals/SDL/src/audio/dsp/SDL_dspaudio.c
vendored
Executable 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
43
externals/SDL/src/audio/dsp/SDL_dspaudio.h
vendored
Executable 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: */
|
||||
65
externals/SDL/src/audio/dummy/SDL_dummyaudio.c
vendored
Executable file
65
externals/SDL/src/audio/dummy/SDL_dummyaudio.c
vendored
Executable 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: */
|
||||
41
externals/SDL/src/audio/dummy/SDL_dummyaudio.h
vendored
Executable file
41
externals/SDL/src/audio/dummy/SDL_dummyaudio.h
vendored
Executable 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: */
|
||||
389
externals/SDL/src/audio/emscripten/SDL_emscriptenaudio.c
vendored
Executable file
389
externals/SDL/src/audio/emscripten/SDL_emscriptenaudio.c
vendored
Executable 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: */
|
||||
38
externals/SDL/src/audio/emscripten/SDL_emscriptenaudio.h
vendored
Executable file
38
externals/SDL/src/audio/emscripten/SDL_emscriptenaudio.h
vendored
Executable 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
335
externals/SDL/src/audio/esd/SDL_esdaudio.c
vendored
Executable 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
51
externals/SDL/src/audio/esd/SDL_esdaudio.h
vendored
Executable 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: */
|
||||
328
externals/SDL/src/audio/fusionsound/SDL_fsaudio.c
vendored
Executable file
328
externals/SDL/src/audio/fusionsound/SDL_fsaudio.c
vendored
Executable 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: */
|
||||
50
externals/SDL/src/audio/fusionsound/SDL_fsaudio.h
vendored
Executable file
50
externals/SDL/src/audio/fusionsound/SDL_fsaudio.h
vendored
Executable 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: */
|
||||
248
externals/SDL/src/audio/haiku/SDL_haikuaudio.cc
vendored
Executable file
248
externals/SDL/src/audio/haiku/SDL_haikuaudio.cc
vendored
Executable 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: */
|
||||
38
externals/SDL/src/audio/haiku/SDL_haikuaudio.h
vendored
Executable file
38
externals/SDL/src/audio/haiku/SDL_haikuaudio.h
vendored
Executable 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
446
externals/SDL/src/audio/jack/SDL_jackaudio.c
vendored
Executable 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
41
externals/SDL/src/audio/jack/SDL_jackaudio.h
vendored
Executable 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
165
externals/SDL/src/audio/nacl/SDL_naclaudio.c
vendored
Executable 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
43
externals/SDL/src/audio/nacl/SDL_naclaudio.h
vendored
Executable 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
463
externals/SDL/src/audio/nas/SDL_nasaudio.c
vendored
Executable 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
56
externals/SDL/src/audio/nas/SDL_nasaudio.h
vendored
Executable 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: */
|
||||
333
externals/SDL/src/audio/netbsd/SDL_netbsdaudio.c
vendored
Executable file
333
externals/SDL/src/audio/netbsd/SDL_netbsdaudio.c
vendored
Executable 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: */
|
||||
48
externals/SDL/src/audio/netbsd/SDL_netbsdaudio.h
vendored
Executable file
48
externals/SDL/src/audio/netbsd/SDL_netbsdaudio.h
vendored
Executable 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: */
|
||||
766
externals/SDL/src/audio/openslES/SDL_openslES.c
vendored
Executable file
766
externals/SDL/src/audio/openslES/SDL_openslES.c
vendored
Executable 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: */
|
||||
46
externals/SDL/src/audio/openslES/SDL_openslES.h
vendored
Executable file
46
externals/SDL/src/audio/openslES/SDL_openslES.h
vendored
Executable 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
516
externals/SDL/src/audio/paudio/SDL_paudio.c
vendored
Executable 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
48
externals/SDL/src/audio/paudio/SDL_paudio.h
vendored
Executable 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
181
externals/SDL/src/audio/psp/SDL_pspaudio.c
vendored
Executable 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
45
externals/SDL/src/audio/psp/SDL_pspaudio.h
vendored
Executable 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: */
|
||||
782
externals/SDL/src/audio/pulseaudio/SDL_pulseaudio.c
vendored
Executable file
782
externals/SDL/src/audio/pulseaudio/SDL_pulseaudio.c
vendored
Executable 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: */
|
||||
52
externals/SDL/src/audio/pulseaudio/SDL_pulseaudio.h
vendored
Executable file
52
externals/SDL/src/audio/pulseaudio/SDL_pulseaudio.h
vendored
Executable 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
666
externals/SDL/src/audio/qsa/SDL_qsa_audio.c
vendored
Executable 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, ¶m) != -1) {
|
||||
param.sched_priority = param.sched_curpriority + 15;
|
||||
SchedSet(0, 0, SCHED_NOCHANGE, ¶m);
|
||||
}
|
||||
}
|
||||
|
||||
/* PCM channel parameters initialize function */
|
||||
static void
|
||||
QSA_InitAudioParams(snd_pcm_channel_params_t * cpars)
|
||||
{
|
||||
SDL_zerop(cpars);
|
||||
cpars->channel = SND_PCM_CHANNEL_PLAYBACK;
|
||||
cpars->mode = SND_PCM_MODE_BLOCK;
|
||||
cpars->start_mode = SND_PCM_START_DATA;
|
||||
cpars->stop_mode = SND_PCM_STOP_STOP;
|
||||
cpars->format.format = SND_PCM_SFMT_S16_LE;
|
||||
cpars->format.interleave = 1;
|
||||
cpars->format.rate = DEFAULT_CPARAMS_RATE;
|
||||
cpars->format.voices = DEFAULT_CPARAMS_VOICES;
|
||||
cpars->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE;
|
||||
cpars->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN;
|
||||
cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX;
|
||||
}
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void
|
||||
QSA_WaitDevice(_THIS)
|
||||
{
|
||||
int result;
|
||||
|
||||
/* Setup timeout for playing one fragment equal to 2 seconds */
|
||||
/* If timeout 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
57
externals/SDL/src/audio/qsa/SDL_qsa_audio.h
vendored
Executable 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
382
externals/SDL/src/audio/sndio/SDL_sndioaudio.c
vendored
Executable 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: */
|
||||
49
externals/SDL/src/audio/sndio/SDL_sndioaudio.h
vendored
Executable file
49
externals/SDL/src/audio/sndio/SDL_sndioaudio.h
vendored
Executable 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
419
externals/SDL/src/audio/sun/SDL_sunaudio.c
vendored
Executable 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
47
externals/SDL/src/audio/sun/SDL_sunaudio.h
vendored
Executable 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
769
externals/SDL/src/audio/wasapi/SDL_wasapi.c
vendored
Executable 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
85
externals/SDL/src/audio/wasapi/SDL_wasapi.h
vendored
Executable 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: */
|
||||
457
externals/SDL/src/audio/wasapi/SDL_wasapi_win32.c
vendored
Executable file
457
externals/SDL/src/audio/wasapi/SDL_wasapi_win32.c
vendored
Executable 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 = { ¬ification_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 *) ¬ification_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 *) ¬ification_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: */
|
||||
|
||||
304
externals/SDL/src/audio/wasapi/SDL_wasapi_winrt.cpp
vendored
Executable file
304
externals/SDL/src/audio/wasapi/SDL_wasapi_winrt.cpp
vendored
Executable 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
456
externals/SDL/src/audio/winmm/SDL_winmm.c
vendored
Executable 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
45
externals/SDL/src/audio/winmm/SDL_winmm.h
vendored
Executable 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: */
|
||||
Reference in New Issue
Block a user