Added a README file regarding WinRT support

To note, this file is currently formatted with CRLF line endings, rather than
LF, to allow the file to be viewed with Notepad.
This commit is contained in:
David Ludwig
2014-04-09 21:29:19 -04:00
commit 3dcb451f85
1838 changed files with 474982 additions and 0 deletions

1303
src/audio/SDL_audio.c Normal file

File diff suppressed because it is too large Load Diff

55
src/audio/SDL_audio_c.h Normal file
View File

@@ -0,0 +1,55 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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"
/* Functions and variables exported from SDL_audio.c for SDL_sysaudio.c */
/* 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 void SDL_CalculateAudioSpec(SDL_AudioSpec * spec);
/* The actual mixing thread function */
extern int SDLCALL SDL_RunAudio(void *audiop);
/* this is used internally to access some autogenerated code. */
typedef struct
{
SDL_AudioFormat src_fmt;
SDL_AudioFormat dst_fmt;
SDL_AudioFilter filter;
} SDL_AudioTypeFilters;
extern const SDL_AudioTypeFilters sdl_audio_type_filters[];
/* this is used internally to access some autogenerated code. */
typedef struct
{
SDL_AudioFormat fmt;
int channels;
int upsample;
int multiple;
SDL_AudioFilter filter;
} SDL_AudioRateFilters;
extern const SDL_AudioRateFilters sdl_audio_rate_filters[];
/* vi: set ts=4 sw=4 expandtab: */

1121
src/audio/SDL_audiocvt.c Normal file

File diff suppressed because it is too large Load Diff

113
src/audio/SDL_audiodev.c Normal file
View File

@@ -0,0 +1,113 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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_BSD || 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 SDL_INLINE void
test_device(const char *fname, int flags, int (*test) (int fd),
SDL_AddAudioDevice addfn)
{
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) {
if (test(audio_fd)) {
addfn(fname);
}
close(audio_fd);
}
}
}
static int
test_stub(int fd)
{
return 1;
}
void
SDL_EnumUnixAudioDevices(int iscapture, int classic, int (*test)(int fd),
SDL_AddAudioDevice addfn)
{
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(audiodev, flags, test, addfn);
if (SDL_strlen(audiodev) < (sizeof(audiopath) - 3)) {
int instance = 0;
while (instance++ <= 64) {
SDL_snprintf(audiopath, SDL_arraysize(audiopath),
"%s%d", audiodev, instance);
test_device(audiopath, flags, test, addfn);
}
}
}
#endif /* Audio driver selection */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,39 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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.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
void SDL_EnumUnixAudioDevices(int iscapture, int classic,
int (*test) (int fd), SDL_AddAudioDevice addfn);
/* vi: set ts=4 sw=4 expandtab: */

25
src/audio/SDL_audiomem.h Normal file
View File

@@ -0,0 +1,25 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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"
#define SDL_AllocAudioMem SDL_malloc
#define SDL_FreeAudioMem SDL_free
/* vi: set ts=4 sw=4 expandtab: */

16015
src/audio/SDL_audiotypecvt.c Normal file

File diff suppressed because it is too large Load Diff

321
src/audio/SDL_mixer.c Normal file
View File

@@ -0,0 +1,321 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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_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_MixAudio(): unknown audio format");
return;
}
}
/* vi: set ts=4 sw=4 expandtab: */

138
src/audio/SDL_sysaudio.h Normal file
View File

@@ -0,0 +1,138 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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"
/* The SDL audio driver */
typedef struct SDL_AudioDevice SDL_AudioDevice;
#define _THIS SDL_AudioDevice *_this
/* Used by audio targets during DetectDevices() */
typedef void (*SDL_AddAudioDevice)(const char *name);
typedef struct SDL_AudioDriverImpl
{
void (*DetectDevices) (int iscapture, SDL_AddAudioDevice addfn);
int (*OpenDevice) (_THIS, const char *devname, int iscapture);
void (*ThreadInit) (_THIS); /* Called by audio thread at start */
void (*WaitDevice) (_THIS);
void (*PlayDevice) (_THIS);
Uint8 *(*GetDeviceBuf) (_THIS);
void (*WaitDone) (_THIS);
void (*CloseDevice) (_THIS);
void (*LockDevice) (_THIS);
void (*UnlockDevice) (_THIS);
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. */
int ProvidesOwnCallbackThread;
int SkipMixerLock; /* !!! FIXME: do we need this anymore? */
int HasCaptureSupport;
int OnlyHasDefaultOutputDevice;
int OnlyHasDefaultInputDevice;
} SDL_AudioDriverImpl;
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;
char **outputDevices;
int outputDeviceCount;
char **inputDevices;
int inputDeviceCount;
} SDL_AudioDriver;
/* Streamer */
typedef struct
{
Uint8 *buffer;
int max_len; /* the maximum length in bytes */
int read_pos, write_pos; /* the position of the write and read heads in bytes */
} SDL_AudioStreamer;
/* Define the SDL audio driver structure */
struct SDL_AudioDevice
{
/* * * */
/* Data common to all devices */
/* The current audio specification (shared with audio thread) */
SDL_AudioSpec spec;
/* An audio conversion block for audio format emulation */
SDL_AudioCVT convert;
/* The streamer, if sample rate conversion necessitates it */
int use_streamer;
SDL_AudioStreamer streamer;
/* Current state flags */
int iscapture;
int enabled;
int paused;
int opened;
/* Fake audio buffer for when the audio hardware is busy */
Uint8 *fake_stream;
/* A semaphore for locking the mixing buffers */
SDL_mutex *mixer_lock;
/* A thread to feed the audio device */
SDL_Thread *thread;
SDL_threadID threadid;
/* * * */
/* Data private to this driver */
struct SDL_PrivateAudioData *hidden;
};
#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;
#endif /* _SDL_sysaudio_h */
/* vi: set ts=4 sw=4 expandtab: */

623
src/audio/SDL_wave.c Normal file
View File

@@ -0,0 +1,623 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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"
/* Microsoft WAVE file loading routines */
#include "SDL_audio.h"
#include "SDL_wave.h"
static int ReadChunk(SDL_RWops * src, Chunk * chunk);
struct MS_ADPCM_decodestate
{
Uint8 hPredictor;
Uint16 iDelta;
Sint16 iSamp1;
Sint16 iSamp2;
};
static struct MS_ADPCM_decoder
{
WaveFMT wavefmt;
Uint16 wSamplesPerBlock;
Uint16 wNumCoef;
Sint16 aCoeff[7][2];
/* * * */
struct MS_ADPCM_decodestate state[2];
} MS_ADPCM_state;
static int
InitMS_ADPCM(WaveFMT * format)
{
Uint8 *rogue_feel;
int i;
/* Set the rogue pointer to the MS_ADPCM specific data */
MS_ADPCM_state.wavefmt.encoding = SDL_SwapLE16(format->encoding);
MS_ADPCM_state.wavefmt.channels = SDL_SwapLE16(format->channels);
MS_ADPCM_state.wavefmt.frequency = SDL_SwapLE32(format->frequency);
MS_ADPCM_state.wavefmt.byterate = SDL_SwapLE32(format->byterate);
MS_ADPCM_state.wavefmt.blockalign = SDL_SwapLE16(format->blockalign);
MS_ADPCM_state.wavefmt.bitspersample =
SDL_SwapLE16(format->bitspersample);
rogue_feel = (Uint8 *) format + sizeof(*format);
if (sizeof(*format) == 16) {
/* const Uint16 extra_info = ((rogue_feel[1] << 8) | rogue_feel[0]); */
rogue_feel += sizeof(Uint16);
}
MS_ADPCM_state.wSamplesPerBlock = ((rogue_feel[1] << 8) | rogue_feel[0]);
rogue_feel += sizeof(Uint16);
MS_ADPCM_state.wNumCoef = ((rogue_feel[1] << 8) | rogue_feel[0]);
rogue_feel += sizeof(Uint16);
if (MS_ADPCM_state.wNumCoef != 7) {
SDL_SetError("Unknown set of MS_ADPCM coefficients");
return (-1);
}
for (i = 0; i < MS_ADPCM_state.wNumCoef; ++i) {
MS_ADPCM_state.aCoeff[i][0] = ((rogue_feel[1] << 8) | rogue_feel[0]);
rogue_feel += sizeof(Uint16);
MS_ADPCM_state.aCoeff[i][1] = ((rogue_feel[1] << 8) | rogue_feel[0]);
rogue_feel += sizeof(Uint16);
}
return (0);
}
static Sint32
MS_ADPCM_nibble(struct MS_ADPCM_decodestate *state,
Uint8 nybble, Sint16 * coeff)
{
const Sint32 max_audioval = ((1 << (16 - 1)) - 1);
const Sint32 min_audioval = -(1 << (16 - 1));
const Sint32 adaptive[] = {
230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230
};
Sint32 new_sample, delta;
new_sample = ((state->iSamp1 * coeff[0]) +
(state->iSamp2 * coeff[1])) / 256;
if (nybble & 0x08) {
new_sample += state->iDelta * (nybble - 0x10);
} else {
new_sample += state->iDelta * nybble;
}
if (new_sample < min_audioval) {
new_sample = min_audioval;
} else if (new_sample > max_audioval) {
new_sample = max_audioval;
}
delta = ((Sint32) state->iDelta * adaptive[nybble]) / 256;
if (delta < 16) {
delta = 16;
}
state->iDelta = (Uint16) delta;
state->iSamp2 = state->iSamp1;
state->iSamp1 = (Sint16) new_sample;
return (new_sample);
}
static int
MS_ADPCM_decode(Uint8 ** audio_buf, Uint32 * audio_len)
{
struct MS_ADPCM_decodestate *state[2];
Uint8 *freeable, *encoded, *decoded;
Sint32 encoded_len, samplesleft;
Sint8 nybble, stereo;
Sint16 *coeff[2];
Sint32 new_sample;
/* Allocate the proper sized output buffer */
encoded_len = *audio_len;
encoded = *audio_buf;
freeable = *audio_buf;
*audio_len = (encoded_len / MS_ADPCM_state.wavefmt.blockalign) *
MS_ADPCM_state.wSamplesPerBlock *
MS_ADPCM_state.wavefmt.channels * sizeof(Sint16);
*audio_buf = (Uint8 *) SDL_malloc(*audio_len);
if (*audio_buf == NULL) {
return SDL_OutOfMemory();
}
decoded = *audio_buf;
/* Get ready... Go! */
stereo = (MS_ADPCM_state.wavefmt.channels == 2);
state[0] = &MS_ADPCM_state.state[0];
state[1] = &MS_ADPCM_state.state[stereo];
while (encoded_len >= MS_ADPCM_state.wavefmt.blockalign) {
/* Grab the initial information for this block */
state[0]->hPredictor = *encoded++;
if (stereo) {
state[1]->hPredictor = *encoded++;
}
state[0]->iDelta = ((encoded[1] << 8) | encoded[0]);
encoded += sizeof(Sint16);
if (stereo) {
state[1]->iDelta = ((encoded[1] << 8) | encoded[0]);
encoded += sizeof(Sint16);
}
state[0]->iSamp1 = ((encoded[1] << 8) | encoded[0]);
encoded += sizeof(Sint16);
if (stereo) {
state[1]->iSamp1 = ((encoded[1] << 8) | encoded[0]);
encoded += sizeof(Sint16);
}
state[0]->iSamp2 = ((encoded[1] << 8) | encoded[0]);
encoded += sizeof(Sint16);
if (stereo) {
state[1]->iSamp2 = ((encoded[1] << 8) | encoded[0]);
encoded += sizeof(Sint16);
}
coeff[0] = MS_ADPCM_state.aCoeff[state[0]->hPredictor];
coeff[1] = MS_ADPCM_state.aCoeff[state[1]->hPredictor];
/* Store the two initial samples we start with */
decoded[0] = state[0]->iSamp2 & 0xFF;
decoded[1] = state[0]->iSamp2 >> 8;
decoded += 2;
if (stereo) {
decoded[0] = state[1]->iSamp2 & 0xFF;
decoded[1] = state[1]->iSamp2 >> 8;
decoded += 2;
}
decoded[0] = state[0]->iSamp1 & 0xFF;
decoded[1] = state[0]->iSamp1 >> 8;
decoded += 2;
if (stereo) {
decoded[0] = state[1]->iSamp1 & 0xFF;
decoded[1] = state[1]->iSamp1 >> 8;
decoded += 2;
}
/* Decode and store the other samples in this block */
samplesleft = (MS_ADPCM_state.wSamplesPerBlock - 2) *
MS_ADPCM_state.wavefmt.channels;
while (samplesleft > 0) {
nybble = (*encoded) >> 4;
new_sample = MS_ADPCM_nibble(state[0], nybble, coeff[0]);
decoded[0] = new_sample & 0xFF;
new_sample >>= 8;
decoded[1] = new_sample & 0xFF;
decoded += 2;
nybble = (*encoded) & 0x0F;
new_sample = MS_ADPCM_nibble(state[1], nybble, coeff[1]);
decoded[0] = new_sample & 0xFF;
new_sample >>= 8;
decoded[1] = new_sample & 0xFF;
decoded += 2;
++encoded;
samplesleft -= 2;
}
encoded_len -= MS_ADPCM_state.wavefmt.blockalign;
}
SDL_free(freeable);
return (0);
}
struct IMA_ADPCM_decodestate
{
Sint32 sample;
Sint8 index;
};
static struct IMA_ADPCM_decoder
{
WaveFMT wavefmt;
Uint16 wSamplesPerBlock;
/* * * */
struct IMA_ADPCM_decodestate state[2];
} IMA_ADPCM_state;
static int
InitIMA_ADPCM(WaveFMT * format)
{
Uint8 *rogue_feel;
/* Set the rogue pointer to the IMA_ADPCM specific data */
IMA_ADPCM_state.wavefmt.encoding = SDL_SwapLE16(format->encoding);
IMA_ADPCM_state.wavefmt.channels = SDL_SwapLE16(format->channels);
IMA_ADPCM_state.wavefmt.frequency = SDL_SwapLE32(format->frequency);
IMA_ADPCM_state.wavefmt.byterate = SDL_SwapLE32(format->byterate);
IMA_ADPCM_state.wavefmt.blockalign = SDL_SwapLE16(format->blockalign);
IMA_ADPCM_state.wavefmt.bitspersample =
SDL_SwapLE16(format->bitspersample);
rogue_feel = (Uint8 *) format + sizeof(*format);
if (sizeof(*format) == 16) {
/* const Uint16 extra_info = ((rogue_feel[1] << 8) | rogue_feel[0]); */
rogue_feel += sizeof(Uint16);
}
IMA_ADPCM_state.wSamplesPerBlock = ((rogue_feel[1] << 8) | rogue_feel[0]);
return (0);
}
static Sint32
IMA_ADPCM_nibble(struct IMA_ADPCM_decodestate *state, Uint8 nybble)
{
const Sint32 max_audioval = ((1 << (16 - 1)) - 1);
const Sint32 min_audioval = -(1 << (16 - 1));
const int index_table[16] = {
-1, -1, -1, -1,
2, 4, 6, 8,
-1, -1, -1, -1,
2, 4, 6, 8
};
const Sint32 step_table[89] = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31,
34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130,
143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408,
449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282,
1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327,
3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630,
9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350,
22385, 24623, 27086, 29794, 32767
};
Sint32 delta, step;
/* Compute difference and new sample value */
if (state->index > 88) {
state->index = 88;
} else if (state->index < 0) {
state->index = 0;
}
step = step_table[state->index];
delta = step >> 3;
if (nybble & 0x04)
delta += step;
if (nybble & 0x02)
delta += (step >> 1);
if (nybble & 0x01)
delta += (step >> 2);
if (nybble & 0x08)
delta = -delta;
state->sample += delta;
/* Update index value */
state->index += index_table[nybble];
/* Clamp output sample */
if (state->sample > max_audioval) {
state->sample = max_audioval;
} else if (state->sample < min_audioval) {
state->sample = min_audioval;
}
return (state->sample);
}
/* Fill the decode buffer with a channel block of data (8 samples) */
static void
Fill_IMA_ADPCM_block(Uint8 * decoded, Uint8 * encoded,
int channel, int numchannels,
struct IMA_ADPCM_decodestate *state)
{
int i;
Sint8 nybble;
Sint32 new_sample;
decoded += (channel * 2);
for (i = 0; i < 4; ++i) {
nybble = (*encoded) & 0x0F;
new_sample = IMA_ADPCM_nibble(state, nybble);
decoded[0] = new_sample & 0xFF;
new_sample >>= 8;
decoded[1] = new_sample & 0xFF;
decoded += 2 * numchannels;
nybble = (*encoded) >> 4;
new_sample = IMA_ADPCM_nibble(state, nybble);
decoded[0] = new_sample & 0xFF;
new_sample >>= 8;
decoded[1] = new_sample & 0xFF;
decoded += 2 * numchannels;
++encoded;
}
}
static int
IMA_ADPCM_decode(Uint8 ** audio_buf, Uint32 * audio_len)
{
struct IMA_ADPCM_decodestate *state;
Uint8 *freeable, *encoded, *decoded;
Sint32 encoded_len, samplesleft;
unsigned int c, channels;
/* Check to make sure we have enough variables in the state array */
channels = IMA_ADPCM_state.wavefmt.channels;
if (channels > SDL_arraysize(IMA_ADPCM_state.state)) {
SDL_SetError("IMA ADPCM decoder can only handle %d channels",
SDL_arraysize(IMA_ADPCM_state.state));
return (-1);
}
state = IMA_ADPCM_state.state;
/* Allocate the proper sized output buffer */
encoded_len = *audio_len;
encoded = *audio_buf;
freeable = *audio_buf;
*audio_len = (encoded_len / IMA_ADPCM_state.wavefmt.blockalign) *
IMA_ADPCM_state.wSamplesPerBlock *
IMA_ADPCM_state.wavefmt.channels * sizeof(Sint16);
*audio_buf = (Uint8 *) SDL_malloc(*audio_len);
if (*audio_buf == NULL) {
return SDL_OutOfMemory();
}
decoded = *audio_buf;
/* Get ready... Go! */
while (encoded_len >= IMA_ADPCM_state.wavefmt.blockalign) {
/* Grab the initial information for this block */
for (c = 0; c < channels; ++c) {
/* Fill the state information for this block */
state[c].sample = ((encoded[1] << 8) | encoded[0]);
encoded += 2;
if (state[c].sample & 0x8000) {
state[c].sample -= 0x10000;
}
state[c].index = *encoded++;
/* Reserved byte in buffer header, should be 0 */
if (*encoded++ != 0) {
/* Uh oh, corrupt data? Buggy code? */ ;
}
/* Store the initial sample we start with */
decoded[0] = (Uint8) (state[c].sample & 0xFF);
decoded[1] = (Uint8) (state[c].sample >> 8);
decoded += 2;
}
/* Decode and store the other samples in this block */
samplesleft = (IMA_ADPCM_state.wSamplesPerBlock - 1) * channels;
while (samplesleft > 0) {
for (c = 0; c < channels; ++c) {
Fill_IMA_ADPCM_block(decoded, encoded,
c, channels, &state[c]);
encoded += 4;
samplesleft -= 8;
}
decoded += (channels * 8 * 2);
}
encoded_len -= IMA_ADPCM_state.wavefmt.blockalign;
}
SDL_free(freeable);
return (0);
}
SDL_AudioSpec *
SDL_LoadWAV_RW(SDL_RWops * src, int freesrc,
SDL_AudioSpec * spec, Uint8 ** audio_buf, Uint32 * audio_len)
{
int was_error;
Chunk chunk;
int lenread;
int IEEE_float_encoded, MS_ADPCM_encoded, IMA_ADPCM_encoded;
int samplesize;
/* WAV magic header */
Uint32 RIFFchunk;
Uint32 wavelen = 0;
Uint32 WAVEmagic;
Uint32 headerDiff = 0;
/* FMT chunk */
WaveFMT *format = NULL;
SDL_zero(chunk);
/* Make sure we are passed a valid data source */
was_error = 0;
if (src == NULL) {
was_error = 1;
goto done;
}
/* Check the magic header */
RIFFchunk = SDL_ReadLE32(src);
wavelen = SDL_ReadLE32(src);
if (wavelen == WAVE) { /* The RIFFchunk has already been read */
WAVEmagic = wavelen;
wavelen = RIFFchunk;
RIFFchunk = RIFF;
} else {
WAVEmagic = SDL_ReadLE32(src);
}
if ((RIFFchunk != RIFF) || (WAVEmagic != WAVE)) {
SDL_SetError("Unrecognized file type (not WAVE)");
was_error = 1;
goto done;
}
headerDiff += sizeof(Uint32); /* for WAVE */
/* Read the audio data format chunk */
chunk.data = NULL;
do {
SDL_free(chunk.data);
chunk.data = NULL;
lenread = ReadChunk(src, &chunk);
if (lenread < 0) {
was_error = 1;
goto done;
}
/* 2 Uint32's for chunk header+len, plus the lenread */
headerDiff += lenread + 2 * sizeof(Uint32);
} while ((chunk.magic == FACT) || (chunk.magic == LIST));
/* Decode the audio data format */
format = (WaveFMT *) chunk.data;
if (chunk.magic != FMT) {
SDL_SetError("Complex WAVE files not supported");
was_error = 1;
goto done;
}
IEEE_float_encoded = MS_ADPCM_encoded = IMA_ADPCM_encoded = 0;
switch (SDL_SwapLE16(format->encoding)) {
case PCM_CODE:
/* We can understand this */
break;
case IEEE_FLOAT_CODE:
IEEE_float_encoded = 1;
/* We can understand this */
break;
case MS_ADPCM_CODE:
/* Try to understand this */
if (InitMS_ADPCM(format) < 0) {
was_error = 1;
goto done;
}
MS_ADPCM_encoded = 1;
break;
case IMA_ADPCM_CODE:
/* Try to understand this */
if (InitIMA_ADPCM(format) < 0) {
was_error = 1;
goto done;
}
IMA_ADPCM_encoded = 1;
break;
case MP3_CODE:
SDL_SetError("MPEG Layer 3 data not supported",
SDL_SwapLE16(format->encoding));
was_error = 1;
goto done;
default:
SDL_SetError("Unknown WAVE data format: 0x%.4x",
SDL_SwapLE16(format->encoding));
was_error = 1;
goto done;
}
SDL_memset(spec, 0, (sizeof *spec));
spec->freq = SDL_SwapLE32(format->frequency);
if (IEEE_float_encoded) {
if ((SDL_SwapLE16(format->bitspersample)) != 32) {
was_error = 1;
} else {
spec->format = AUDIO_F32;
}
} else {
switch (SDL_SwapLE16(format->bitspersample)) {
case 4:
if (MS_ADPCM_encoded || IMA_ADPCM_encoded) {
spec->format = AUDIO_S16;
} else {
was_error = 1;
}
break;
case 8:
spec->format = AUDIO_U8;
break;
case 16:
spec->format = AUDIO_S16;
break;
case 32:
spec->format = AUDIO_S32;
break;
default:
was_error = 1;
break;
}
}
if (was_error) {
SDL_SetError("Unknown %d-bit PCM data format",
SDL_SwapLE16(format->bitspersample));
goto done;
}
spec->channels = (Uint8) SDL_SwapLE16(format->channels);
spec->samples = 4096; /* Good default buffer size */
/* Read the audio data chunk */
*audio_buf = NULL;
do {
SDL_free(*audio_buf);
*audio_buf = NULL;
lenread = ReadChunk(src, &chunk);
if (lenread < 0) {
was_error = 1;
goto done;
}
*audio_len = lenread;
*audio_buf = chunk.data;
if (chunk.magic != DATA)
headerDiff += lenread + 2 * sizeof(Uint32);
} while (chunk.magic != DATA);
headerDiff += 2 * sizeof(Uint32); /* for the data chunk and len */
if (MS_ADPCM_encoded) {
if (MS_ADPCM_decode(audio_buf, audio_len) < 0) {
was_error = 1;
goto done;
}
}
if (IMA_ADPCM_encoded) {
if (IMA_ADPCM_decode(audio_buf, audio_len) < 0) {
was_error = 1;
goto done;
}
}
/* Don't return a buffer that isn't a multiple of samplesize */
samplesize = ((SDL_AUDIO_BITSIZE(spec->format)) / 8) * spec->channels;
*audio_len &= ~(samplesize - 1);
done:
SDL_free(format);
if (src) {
if (freesrc) {
SDL_RWclose(src);
} else {
/* seek to the end of the file (given by the RIFF chunk) */
SDL_RWseek(src, wavelen - chunk.length - headerDiff, RW_SEEK_CUR);
}
}
if (was_error) {
spec = NULL;
}
return (spec);
}
/* Since the WAV memory is allocated in the shared library, it must also
be freed here. (Necessary under Win32, VC++)
*/
void
SDL_FreeWAV(Uint8 * audio_buf)
{
SDL_free(audio_buf);
}
static int
ReadChunk(SDL_RWops * src, Chunk * chunk)
{
chunk->magic = SDL_ReadLE32(src);
chunk->length = SDL_ReadLE32(src);
chunk->data = (Uint8 *) SDL_malloc(chunk->length);
if (chunk->data == NULL) {
return SDL_OutOfMemory();
}
if (SDL_RWread(src, chunk->data, chunk->length, 1) != 1) {
SDL_free(chunk->data);
chunk->data = NULL;
return SDL_Error(SDL_EFREAD);
}
return (chunk->length);
}
/* vi: set ts=4 sw=4 expandtab: */

65
src/audio/SDL_wave.h Normal file
View File

@@ -0,0 +1,65 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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"
/* WAVE files are little-endian */
/*******************************************/
/* Define values for Microsoft WAVE format */
/*******************************************/
#define RIFF 0x46464952 /* "RIFF" */
#define WAVE 0x45564157 /* "WAVE" */
#define FACT 0x74636166 /* "fact" */
#define LIST 0x5453494c /* "LIST" */
#define FMT 0x20746D66 /* "fmt " */
#define DATA 0x61746164 /* "data" */
#define PCM_CODE 0x0001
#define MS_ADPCM_CODE 0x0002
#define IEEE_FLOAT_CODE 0x0003
#define IMA_ADPCM_CODE 0x0011
#define MP3_CODE 0x0055
#define WAVE_MONO 1
#define WAVE_STEREO 2
/* Normally, these three chunks come consecutively in a WAVE file */
typedef struct WaveFMT
{
/* Not saved in the chunk we read:
Uint32 FMTchunk;
Uint32 fmtlen;
*/
Uint16 encoding;
Uint16 channels; /* 1 = mono, 2 = stereo */
Uint32 frequency; /* One of 11025, 22050, or 44100 Hz */
Uint32 byterate; /* Average bytes per second */
Uint16 blockalign; /* Bytes per sample block */
Uint16 bitspersample; /* One of 8, 12, 16, or 4 for ADPCM */
} WaveFMT;
/* The general chunk found in the WAVE file */
typedef struct Chunk
{
Uint32 magic;
Uint32 length;
Uint8 *data;
} Chunk;
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,685 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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
/* Allow access to a raw mixing buffer */
#include <sys/types.h>
#include <signal.h> /* For kill() */
#include <errno.h>
#include <string.h>
#include "SDL_timer.h"
#include "SDL_audio.h"
#include "../SDL_audiomem.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 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_near)
(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);
#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_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_near);
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);
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(int channels)
{
const char *device;
device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */
if (device == NULL) {
switch (channels) {
case 6:
device = "plug:surround51";
break;
case 4:
device = "plug:surround40";
break;
default:
device = "default";
break;
}
}
return device;
}
/* This function waits until it is possible to write a full sound buffer */
static void
ALSA_WaitDevice(_THIS)
{
/* We're in blocking mode, so there's nothing to do here */
}
/* !!! 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) \
T *ptr = (T *) this->hidden->mixbuf; \
Uint32 i; \
for (i = 0; i < this->spec.samples; 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 SDL_INLINE void
swizzle_alsa_channels_6_64bit(_THIS)
{
SWIZ6(Uint64);
}
static SDL_INLINE void
swizzle_alsa_channels_6_32bit(_THIS)
{
SWIZ6(Uint32);
}
static SDL_INLINE void
swizzle_alsa_channels_6_16bit(_THIS)
{
SWIZ6(Uint16);
}
static SDL_INLINE void
swizzle_alsa_channels_6_8bit(_THIS)
{
SWIZ6(Uint8);
}
#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 SDL_INLINE void
swizzle_alsa_channels(_THIS)
{
if (this->spec.channels == 6) {
const Uint16 fmtsize = (this->spec.format & 0xFF); /* bits/channel. */
if (fmtsize == 16)
swizzle_alsa_channels_6_16bit(this);
else if (fmtsize == 8)
swizzle_alsa_channels_6_8bit(this);
else if (fmtsize == 32)
swizzle_alsa_channels_6_32bit(this);
else if (fmtsize == 64)
swizzle_alsa_channels_6_64bit(this);
}
/* !!! FIXME: update this for 7.1 if needed, later. */
}
static void
ALSA_PlayDevice(_THIS)
{
int status;
const Uint8 *sample_buf = (const Uint8 *) this->hidden->mixbuf;
const int frame_size = (((int) (this->spec.format & 0xFF)) / 8) *
this->spec.channels;
snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t) this->spec.samples);
swizzle_alsa_channels(this);
while ( frames_left > 0 && this->enabled ) {
/* !!! FIXME: This works, but needs more testing before going live */
/* ALSA_snd_pcm_wait(this->hidden->pcm_handle, -1); */
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));
this->enabled = 0;
return;
}
continue;
}
sample_buf += status * frame_size;
frames_left -= status;
}
}
static Uint8 *
ALSA_GetDeviceBuf(_THIS)
{
return (this->hidden->mixbuf);
}
static void
ALSA_CloseDevice(_THIS)
{
if (this->hidden != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
if (this->hidden->pcm_handle) {
ALSA_snd_pcm_drain(this->hidden->pcm_handle);
ALSA_snd_pcm_close(this->hidden->pcm_handle);
this->hidden->pcm_handle = NULL;
}
SDL_free(this->hidden);
this->hidden = NULL;
}
}
static int
ALSA_finalize_hardware(_THIS, snd_pcm_hw_params_t *hwparams, int override)
{
int status;
snd_pcm_uframes_t bufsize;
/* "set" the hardware with the desired parameters */
status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams);
if ( status < 0 ) {
return(-1);
}
/* Get samples for the actual buffer size */
status = ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize);
if ( status < 0 ) {
return(-1);
}
if ( !override && bufsize != this->spec.samples * 2 ) {
return(-1);
}
/* !!! FIXME: Is this safe to do? */
this->spec.samples = bufsize / 2;
/* This is useful for debugging */
if ( SDL_getenv("SDL_AUDIO_ALSA_DEBUG") ) {
snd_pcm_uframes_t persize = 0;
unsigned int periods = 0;
ALSA_snd_pcm_hw_params_get_period_size(hwparams, &persize, NULL);
ALSA_snd_pcm_hw_params_get_periods(hwparams, &periods, NULL);
fprintf(stderr,
"ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
persize, periods, bufsize);
}
return(0);
}
static int
ALSA_set_period_size(_THIS, snd_pcm_hw_params_t *params, int override)
{
const char *env;
int status;
snd_pcm_hw_params_t *hwparams;
snd_pcm_uframes_t frames;
unsigned int periods;
/* Copy the hardware parameters for this setup */
snd_pcm_hw_params_alloca(&hwparams);
ALSA_snd_pcm_hw_params_copy(hwparams, params);
if ( !override ) {
env = SDL_getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE");
if ( env ) {
override = SDL_atoi(env);
if ( override == 0 ) {
return(-1);
}
}
}
frames = this->spec.samples;
status = ALSA_snd_pcm_hw_params_set_period_size_near(
this->hidden->pcm_handle, hwparams, &frames, NULL);
if ( status < 0 ) {
return(-1);
}
periods = 2;
status = ALSA_snd_pcm_hw_params_set_periods_near(
this->hidden->pcm_handle, hwparams, &periods, NULL);
if ( status < 0 ) {
return(-1);
}
return ALSA_finalize_hardware(this, hwparams, override);
}
static int
ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params, int override)
{
const char *env;
int status;
snd_pcm_hw_params_t *hwparams;
snd_pcm_uframes_t frames;
/* Copy the hardware parameters for this setup */
snd_pcm_hw_params_alloca(&hwparams);
ALSA_snd_pcm_hw_params_copy(hwparams, params);
if ( !override ) {
env = SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE");
if ( env ) {
override = SDL_atoi(env);
if ( override == 0 ) {
return(-1);
}
}
}
frames = this->spec.samples * 2;
status = ALSA_snd_pcm_hw_params_set_buffer_size_near(
this->hidden->pcm_handle, hwparams, &frames);
if ( status < 0 ) {
return(-1);
}
return ALSA_finalize_hardware(this, hwparams, override);
}
static int
ALSA_OpenDevice(_THIS, 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;
/* 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_memset(this->hidden, 0, (sizeof *this->hidden));
/* Open the audio device */
/* Name of device should depend on # channels in spec */
status = ALSA_snd_pcm_open(&pcm_handle,
get_audio_device(this->spec.channels),
SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (status < 0) {
ALSA_CloseDevice(this);
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) {
ALSA_CloseDevice(this);
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) {
ALSA_CloseDevice(this);
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) {
ALSA_CloseDevice(this);
return SDL_SetError("ALSA: Couldn't find any hardware audio formats");
}
this->spec.format = test_format;
/* 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) {
ALSA_CloseDevice(this);
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) {
ALSA_CloseDevice(this);
return SDL_SetError("ALSA: Couldn't set audio frequency: %s",
ALSA_snd_strerror(status));
}
this->spec.freq = rate;
/* Set the buffer size, in samples */
if ( ALSA_set_period_size(this, hwparams, 0) < 0 &&
ALSA_set_buffer_size(this, hwparams, 0) < 0 ) {
/* Failed to set desired buffer size, do the best you can... */
if ( ALSA_set_period_size(this, hwparams, 1) < 0 ) {
ALSA_CloseDevice(this);
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) {
ALSA_CloseDevice(this);
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) {
ALSA_CloseDevice(this);
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) {
ALSA_CloseDevice(this);
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) {
ALSA_CloseDevice(this);
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 */
this->hidden->mixlen = this->spec.size;
this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
if (this->hidden->mixbuf == NULL) {
ALSA_CloseDevice(this);
return SDL_OutOfMemory();
}
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
/* Switch to blocking mode for playback */
ALSA_snd_pcm_nonblock(pcm_handle, 0);
/* We're ready to rock and roll. :-) */
return 0;
}
static void
ALSA_Deinitialize(void)
{
UnloadALSALibrary();
}
static int
ALSA_Init(SDL_AudioDriverImpl * impl)
{
if (LoadALSALibrary() < 0) {
return 0;
}
/* Set the function pointers */
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->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: Add device enum! */
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: */

View File

@@ -0,0 +1,45 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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;
};
#endif /* _SDL_ALSA_audio_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,141 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#if SDL_AUDIO_DRIVER_ANDROID
/* Output audio to Android */
#include "SDL_audio.h"
#include "../SDL_audio_c.h"
#include "SDL_androidaudio.h"
#include "../../core/android/SDL_android.h"
#include <android/log.h>
static void * audioDevice;
static int
AndroidAUD_OpenDevice(_THIS, const char *devname, int iscapture)
{
SDL_AudioFormat test_format;
if (iscapture) {
/* TODO: implement capture */
return SDL_SetError("Capture not supported on Android");
}
if (audioDevice != NULL) {
return SDL_SetError("Only one audio device at a time please!");
}
audioDevice = this;
test_format = SDL_FirstAudioFormat(this->spec.format);
while (test_format != 0) { /* no "UNKNOWN" constant */
if ((test_format == AUDIO_U8) || (test_format == AUDIO_S16LSB)) {
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 (this->spec.channels > 1) {
this->spec.channels = 2;
} else {
this->spec.channels = 1;
}
if (this->spec.freq < 8000) {
this->spec.freq = 8000;
}
if (this->spec.freq > 48000) {
this->spec.freq = 48000;
}
/* TODO: pass in/return a (Java) device ID, also whether we're opening for input or output */
this->spec.samples = Android_JNI_OpenAudioDevice(this->spec.freq, this->spec.format == AUDIO_U8 ? 0 : 1, this->spec.channels, this->spec.samples);
SDL_CalculateAudioSpec(&this->spec);
if (this->spec.samples == 0) {
/* Init failed? */
return SDL_SetError("Java-side initialization failed!");
}
return 0;
}
static void
AndroidAUD_PlayDevice(_THIS)
{
Android_JNI_WriteAudioBuffer();
}
static Uint8 *
AndroidAUD_GetDeviceBuf(_THIS)
{
return Android_JNI_GetAudioBuffer();
}
static void
AndroidAUD_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();
if (audioDevice == this) {
audioDevice = NULL;
}
}
static int
AndroidAUD_Init(SDL_AudioDriverImpl * impl)
{
/* Set the function pointers */
impl->OpenDevice = AndroidAUD_OpenDevice;
impl->PlayDevice = AndroidAUD_PlayDevice;
impl->GetDeviceBuf = AndroidAUD_GetDeviceBuf;
impl->CloseDevice = AndroidAUD_CloseDevice;
/* and the capabilities */
impl->HasCaptureSupport = 0; /* TODO */
impl->OnlyHasDefaultOutputDevice = 1;
impl->OnlyHasDefaultInputDevice = 1;
return 1; /* this audio target is available. */
}
AudioBootStrap ANDROIDAUD_bootstrap = {
"android", "SDL Android audio driver", AndroidAUD_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_ANDROID */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,39 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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
{
};
static void AndroidAUD_CloseDevice(_THIS);
#endif /* _SDL_androidaudio_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,384 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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_audiomem.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) {
this->enabled = 0;
}
}
}
/* 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) {
this->enabled = 0;
}
#ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
#endif
}
static void
ARTS_WaitDone(_THIS)
{
/* !!! FIXME: camp here until buffer drains... SDL_Delay(???); */
}
static Uint8 *
ARTS_GetDeviceBuf(_THIS)
{
return (this->hidden->mixbuf);
}
static void
ARTS_CloseDevice(_THIS)
{
if (this->hidden != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
if (this->hidden->stream) {
SDL_NAME(arts_close_stream) (this->hidden->stream);
this->hidden->stream = 0;
}
SDL_NAME(arts_free) ();
SDL_free(this->hidden);
this->hidden = NULL;
}
}
static int
ARTS_Suspend(void)
{
const Uint32 abortms = SDL_GetTicks() + 3000; /* give up after 3 secs */
while ( (!SDL_NAME(arts_suspended)()) && !SDL_TICKS_PASSED(SDL_GetTicks(), abortms) ) {
if ( SDL_NAME(arts_suspend)() ) {
break;
}
}
return SDL_NAME(arts_suspended)();
}
static int
ARTS_OpenDevice(_THIS, const char *devname, int 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_memset(this->hidden, 0, (sizeof *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) {
ARTS_CloseDevice(this);
return SDL_SetError("Couldn't find any hardware audio formats");
}
this->spec.format = test_format;
if ((rc = SDL_NAME(arts_init) ()) != 0) {
ARTS_CloseDevice(this);
return SDL_SetError("Unable to initialize ARTS: %s",
SDL_NAME(arts_error_text) (rc));
}
if (!ARTS_Suspend()) {
ARTS_CloseDevice(this);
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) {
ARTS_CloseDevice(this);
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_AllocAudioMem(this->hidden->mixlen);
if (this->hidden->mixbuf == NULL) {
ARTS_CloseDevice(this);
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->WaitDone = ARTS_WaitDone;
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: */

View File

@@ -0,0 +1,52 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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_artscaudio_h
#define _SDL_artscaudio_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 select() */
float frame_ticks;
float next_frame;
};
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
#endif /* _SDL_artscaudio_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,361 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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_BSD
/*
* Driver for native OpenBSD/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 "../SDL_audiomem.h"
#include "../SDL_audio_c.h"
#include "../SDL_audiodev_c.h"
#include "SDL_bsdaudio.h"
/* Use timer for synchronization */
/* #define USE_TIMER_SYNC */
/* #define DEBUG_AUDIO */
/* #define DEBUG_AUDIO_STREAM */
static void
BSDAUDIO_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
{
SDL_EnumUnixAudioDevices(iscapture, 0, NULL, addfn);
}
static void
BSDAUDIO_Status(_THIS)
{
#ifdef DEBUG_AUDIO
/* *INDENT-OFF* */
audio_info_t info;
if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
fprintf(stderr, "AUDIO_GETINFO failed.\n");
return;
}
fprintf(stderr, "\n"
"[play/record 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"
"",
info.play.buffer_size,
info.play.sample_rate,
info.play.channels,
info.play.precision,
info.play.encoding,
info.play.seek,
info.play.samples,
info.play.eof,
info.play.pause ? "yes" : "no",
info.play.error ? "yes" : "no",
info.play.waiting ? "yes" : "no",
info.play.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" : "?"));
/* *INDENT-ON* */
#endif /* DEBUG_AUDIO */
}
/* This function waits until it is possible to write a full sound buffer */
static void
BSDAUDIO_WaitDevice(_THIS)
{
#ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */
/* 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 {
/* Use select() for audio synchronization */
fd_set fdset;
struct timeval timeout;
FD_ZERO(&fdset);
FD_SET(this->hidden->audio_fd, &fdset);
timeout.tv_sec = 10;
timeout.tv_usec = 0;
#ifdef DEBUG_AUDIO
fprintf(stderr, "Waiting for audio to get ready\n");
#endif
if (select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, &timeout)
<= 0) {
const char *message =
"Audio timeout - buggy audio driver? (disabled)";
/* 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\n", message);
this->enabled = 0;
/* 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
}
#endif /* !USE_BLOCKING_WRITES */
}
static void
BSDAUDIO_PlayDevice(_THIS)
{
int written, p = 0;
/* Write the audio data, checking for EAGAIN on broken audio drivers */
do {
written = write(this->hidden->audio_fd,
&this->hidden->mixbuf[p], this->hidden->mixlen - p);
if (written > 0)
p += written;
if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) {
/* Non recoverable error has occurred. It should be reported!!! */
perror("audio");
break;
}
if (p < written
|| ((written < 0) && ((errno == 0) || (errno == EAGAIN)))) {
SDL_Delay(1); /* Let a little CPU time go by */
}
} while (p < written);
/* 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) {
this->enabled = 0;
}
#ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
#endif
}
static Uint8 *
BSDAUDIO_GetDeviceBuf(_THIS)
{
return (this->hidden->mixbuf);
}
static void
BSDAUDIO_CloseDevice(_THIS)
{
if (this->hidden != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
if (this->hidden->audio_fd >= 0) {
close(this->hidden->audio_fd);
this->hidden->audio_fd = -1;
}
SDL_free(this->hidden);
this->hidden = NULL;
}
}
static int
BSDAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
{
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_memset(this->hidden, 0, (sizeof *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));
}
AUDIO_INITINFO(&info);
/* Calculate the final parameters for this audio specification */
SDL_CalculateAudioSpec(&this->spec);
/* Set to play mode */
info.mode = AUMODE_PLAY;
if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) {
BSDAUDIO_CloseDevice(this);
return SDL_SetError("Couldn't put device into play mode");
}
AUDIO_INITINFO(&info);
for (format = SDL_FirstAudioFormat(this->spec.format);
format; format = SDL_NextAudioFormat()) {
switch (format) {
case AUDIO_U8:
info.play.encoding = AUDIO_ENCODING_ULINEAR;
info.play.precision = 8;
break;
case AUDIO_S8:
info.play.encoding = AUDIO_ENCODING_SLINEAR;
info.play.precision = 8;
break;
case AUDIO_S16LSB:
info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
info.play.precision = 16;
break;
case AUDIO_S16MSB:
info.play.encoding = AUDIO_ENCODING_SLINEAR_BE;
info.play.precision = 16;
break;
case AUDIO_U16LSB:
info.play.encoding = AUDIO_ENCODING_ULINEAR_LE;
info.play.precision = 16;
break;
case AUDIO_U16MSB:
info.play.encoding = AUDIO_ENCODING_ULINEAR_BE;
info.play.precision = 16;
break;
default:
continue;
}
if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == 0) {
break;
}
}
if (!format) {
BSDAUDIO_CloseDevice(this);
return SDL_SetError("No supported encoding for 0x%x", this->spec.format);
}
this->spec.format = format;
AUDIO_INITINFO(&info);
info.play.channels = this->spec.channels;
if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == -1) {
this->spec.channels = 1;
}
AUDIO_INITINFO(&info);
info.play.sample_rate = this->spec.freq;
info.blocksize = this->spec.size;
info.hiwat = 5;
info.lowat = 3;
(void) ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info);
(void) ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info);
this->spec.freq = info.play.sample_rate;
/* Allocate mixing buffer */
this->hidden->mixlen = this->spec.size;
this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
if (this->hidden->mixbuf == NULL) {
BSDAUDIO_CloseDevice(this);
return SDL_OutOfMemory();
}
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
BSDAUDIO_Status(this);
/* We're ready to rock and roll. :-) */
return 0;
}
static int
BSDAUDIO_Init(SDL_AudioDriverImpl * impl)
{
/* Set the function pointers */
impl->DetectDevices = BSDAUDIO_DetectDevices;
impl->OpenDevice = BSDAUDIO_OpenDevice;
impl->PlayDevice = BSDAUDIO_PlayDevice;
impl->WaitDevice = BSDAUDIO_WaitDevice;
impl->GetDeviceBuf = BSDAUDIO_GetDeviceBuf;
impl->CloseDevice = BSDAUDIO_CloseDevice;
return 1; /* this audio target is available. */
}
AudioBootStrap BSD_AUDIO_bootstrap = {
"bsd", "BSD audio", BSDAUDIO_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_BSD */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,51 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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_bsdaudio_h
#define _SDL_bsdaudio_h
#include "../SDL_sysaudio.h"
#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, in addition to select() */
float frame_ticks;
float next_frame;
};
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
#endif /* _SDL_bsdaudio_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,557 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#include "SDL_audio.h"
#include "../SDL_audio_c.h"
#include "../SDL_sysaudio.h"
#include "SDL_coreaudio.h"
#include "SDL_assert.h"
#define DEBUG_COREAUDIO 0
static void COREAUDIO_CloseDevice(_THIS);
#define CHECK_RESULT(msg) \
if (result != noErr) { \
COREAUDIO_CloseDevice(this); \
SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
return 0; \
}
#if MACOSX_COREAUDIO
typedef void (*addDevFn)(const char *name, AudioDeviceID devId, void *data);
static void
addToDevList(const char *name, AudioDeviceID devId, void *data)
{
SDL_AddAudioDevice addfn = (SDL_AddAudioDevice) data;
addfn(name);
}
typedef struct
{
const char *findname;
AudioDeviceID devId;
int found;
} FindDevIdData;
static void
findDevId(const char *name, AudioDeviceID devId, void *_data)
{
FindDevIdData *data = (FindDevIdData *) _data;
if (!data->found) {
if (SDL_strcmp(name, data->findname) == 0) {
data->found = 1;
data->devId = 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;
AudioObjectPropertyAddress addr = {
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
0, NULL, &size);
if (result != kAudioHardwareNoError)
return;
devs = (AudioDeviceID *) alloca(size);
if (devs == NULL)
return;
result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
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;
addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
kAudioDevicePropertyScopeOutput;
addr.mSelector = kAudioDevicePropertyStreamConfiguration;
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;
addr.mSelector = kAudioObjectPropertyName;
size = sizeof (CFStringRef);
result = AudioObjectGetPropertyData(dev, &addr, 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) *devCount, ptr, (int) dev);
#endif
addfn(ptr, dev, addfndata);
}
SDL_free(ptr); /* addfn() would have copied the string. */
}
}
static void
COREAUDIO_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
{
build_device_list(iscapture, addToDevList, addfn);
}
static int
find_device_by_name(_THIS, const char *devname, int iscapture)
{
AudioDeviceID devid = 0;
OSStatus result = noErr;
UInt32 size = 0;
UInt32 alive = 0;
pid_t pid = 0;
AudioObjectPropertyAddress addr = {
0,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
if (devname == NULL) {
size = sizeof (AudioDeviceID);
addr.mSelector =
((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
kAudioHardwarePropertyDefaultOutputDevice);
result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
0, NULL, &size, &devid);
CHECK_RESULT("AudioHardwareGetProperty (default device)");
} else {
FindDevIdData data;
SDL_zero(data);
data.findname = devname;
build_device_list(iscapture, findDevId, &data);
if (!data.found) {
SDL_SetError("CoreAudio: No such audio device.");
return 0;
}
devid = data.devId;
}
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
/* The CoreAudio callback */
static OSStatus
outputCallback(void *inRefCon,
AudioUnitRenderActionFlags * ioActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList * ioData)
{
SDL_AudioDevice *this = (SDL_AudioDevice *) inRefCon;
AudioBuffer *abuf;
UInt32 remaining, len;
void *ptr;
UInt32 i;
/* Only do anything if audio is enabled and not paused */
if (!this->enabled || this->paused) {
for (i = 0; i < ioData->mNumberBuffers; i++) {
abuf = &ioData->mBuffers[i];
SDL_memset(abuf->mData, this->spec.silence, abuf->mDataByteSize);
}
return 0;
}
/* No SDL conversion should be needed here, ever, since we accept
any input format in OpenAudio, and leave the conversion to CoreAudio.
*/
/*
SDL_assert(!this->convert.needed);
SDL_assert(this->spec.channels == ioData->mNumberChannels);
*/
for (i = 0; i < ioData->mNumberBuffers; i++) {
abuf = &ioData->mBuffers[i];
remaining = abuf->mDataByteSize;
ptr = abuf->mData;
while (remaining > 0) {
if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
/* Generate the data */
SDL_LockMutex(this->mixer_lock);
(*this->spec.callback)(this->spec.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 = (char *)ptr + len;
remaining -= len;
this->hidden->bufferOffset += len;
}
}
return 0;
}
static OSStatus
inputCallback(void *inRefCon,
AudioUnitRenderActionFlags * ioActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList * ioData)
{
/* err = AudioUnitRender(afr->fAudioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, afr->fAudioBuffer); */
/* !!! FIXME: write me! */
return noErr;
}
static void
COREAUDIO_CloseDevice(_THIS)
{
if (this->hidden != NULL) {
if (this->hidden->audioUnitOpened) {
AURenderCallbackStruct callback;
const AudioUnitElement output_bus = 0;
const AudioUnitElement input_bus = 1;
const int iscapture = this->iscapture;
const AudioUnitElement bus =
((iscapture) ? input_bus : output_bus);
const AudioUnitScope scope =
((iscapture) ? kAudioUnitScope_Output :
kAudioUnitScope_Input);
/* stop processing the audio unit */
AudioOutputUnitStop(this->hidden->audioUnit);
/* Remove the input callback */
SDL_memset(&callback, 0, sizeof(AURenderCallbackStruct));
AudioUnitSetProperty(this->hidden->audioUnit,
kAudioUnitProperty_SetRenderCallback,
scope, bus, &callback, sizeof(callback));
#if MACOSX_COREAUDIO
CloseComponent(this->hidden->audioUnit);
#else
AudioComponentInstanceDispose(this->hidden->audioUnit);
#endif
this->hidden->audioUnitOpened = 0;
}
SDL_free(this->hidden->buffer);
SDL_free(this->hidden);
this->hidden = NULL;
}
}
static int
prepare_audiounit(_THIS, const char *devname, int iscapture,
const AudioStreamBasicDescription * strdesc)
{
OSStatus result = noErr;
AURenderCallbackStruct callback;
#if MACOSX_COREAUDIO
ComponentDescription desc;
Component comp = NULL;
#else
AudioComponentDescription desc;
AudioComponent comp = NULL;
#endif
const AudioUnitElement output_bus = 0;
const AudioUnitElement input_bus = 1;
const AudioUnitElement bus = ((iscapture) ? input_bus : output_bus);
const AudioUnitScope scope = ((iscapture) ? kAudioUnitScope_Output :
kAudioUnitScope_Input);
#if MACOSX_COREAUDIO
if (!find_device_by_name(this, devname, iscapture)) {
SDL_SetError("Couldn't find requested CoreAudio device");
return 0;
}
#endif
SDL_zero(desc);
desc.componentType = kAudioUnitType_Output;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
#if MACOSX_COREAUDIO
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
comp = FindNextComponent(NULL, &desc);
#else
desc.componentSubType = kAudioUnitSubType_RemoteIO;
comp = AudioComponentFindNext(NULL, &desc);
#endif
if (comp == NULL) {
SDL_SetError("Couldn't find requested CoreAudio component");
return 0;
}
/* Open & initialize the audio unit */
#if MACOSX_COREAUDIO
result = OpenAComponent(comp, &this->hidden->audioUnit);
CHECK_RESULT("OpenAComponent");
#else
/*
AudioComponentInstanceNew only available on iPhone OS 2.0 and Mac OS X 10.6
We can't use OpenAComponent on iPhone because it is not present
*/
result = AudioComponentInstanceNew(comp, &this->hidden->audioUnit);
CHECK_RESULT("AudioComponentInstanceNew");
#endif
this->hidden->audioUnitOpened = 1;
#if MACOSX_COREAUDIO
result = AudioUnitSetProperty(this->hidden->audioUnit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global, 0,
&this->hidden->deviceID,
sizeof(AudioDeviceID));
CHECK_RESULT
("AudioUnitSetProperty (kAudioOutputUnitProperty_CurrentDevice)");
#endif
/* Set the data format of the audio unit. */
result = AudioUnitSetProperty(this->hidden->audioUnit,
kAudioUnitProperty_StreamFormat,
scope, bus, strdesc, sizeof(*strdesc));
CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)");
/* Set the audio callback */
SDL_memset(&callback, 0, sizeof(AURenderCallbackStruct));
callback.inputProc = ((iscapture) ? inputCallback : outputCallback);
callback.inputProcRefCon = this;
result = AudioUnitSetProperty(this->hidden->audioUnit,
kAudioUnitProperty_SetRenderCallback,
scope, bus, &callback, sizeof(callback));
CHECK_RESULT
("AudioUnitSetProperty (kAudioUnitProperty_SetRenderCallback)");
/* Calculate the final parameters for this audio specification */
SDL_CalculateAudioSpec(&this->spec);
/* Allocate a sample buffer */
this->hidden->bufferOffset = this->hidden->bufferSize = this->spec.size;
this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
result = AudioUnitInitialize(this->hidden->audioUnit);
CHECK_RESULT("AudioUnitInitialize");
/* Finally, start processing of the audio unit */
result = AudioOutputUnitStart(this->hidden->audioUnit);
CHECK_RESULT("AudioOutputUnitStart");
/* We're running! */
return 1;
}
static int
COREAUDIO_OpenDevice(_THIS, 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_memset(this->hidden, 0, (sizeof *this->hidden));
/* Setup a AudioStreamBasicDescription with the requested format */
SDL_memset(&strdesc, '\0', sizeof(AudioStreamBasicDescription));
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;
/* Just a list of valid SDL formats, so people don't pass junk here. */
switch (test_format) {
case AUDIO_U8:
case AUDIO_S8:
case AUDIO_U16LSB:
case AUDIO_S16LSB:
case AUDIO_U16MSB:
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;
}
}
if (!valid_datatype) { /* shouldn't happen, but just in case... */
COREAUDIO_CloseDevice(this);
return SDL_SetError("Unsupported audio format");
}
strdesc.mBytesPerFrame =
strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
strdesc.mBytesPerPacket =
strdesc.mBytesPerFrame * strdesc.mFramesPerPacket;
if (!prepare_audiounit(this, devname, iscapture, &strdesc)) {
COREAUDIO_CloseDevice(this);
return -1; /* prepare_audiounit() will call SDL_SetError()... */
}
return 0; /* good to go. */
}
static int
COREAUDIO_Init(SDL_AudioDriverImpl * impl)
{
/* Set the function pointers */
impl->OpenDevice = COREAUDIO_OpenDevice;
impl->CloseDevice = COREAUDIO_CloseDevice;
#if MACOSX_COREAUDIO
impl->DetectDevices = COREAUDIO_DetectDevices;
#else
impl->OnlyHasDefaultOutputDevice = 1;
/* Set category to ambient sound so that other music continues playing.
You can change this at runtime in your own code if you need different
behavior. If this is common, we can add an SDL hint for this.
*/
AudioSessionInitialize(NULL, NULL, NULL, nil);
UInt32 category = kAudioSessionCategory_AmbientSound;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(UInt32), &category);
#endif
impl->ProvidesOwnCallbackThread = 1;
return 1; /* this audio target is available. */
}
AudioBootStrap COREAUDIO_bootstrap = {
"coreaudio", "CoreAudio", COREAUDIO_Init, 0
};
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,57 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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
#include <AudioToolbox/AudioToolbox.h>
#endif
#include <AudioUnit/AudioUnit.h>
/* Hidden "this" pointer for the audio functions */
#define _THIS SDL_AudioDevice *this
struct SDL_PrivateAudioData
{
AudioUnit audioUnit;
int audioUnitOpened;
void *buffer;
UInt32 bufferOffset;
UInt32 bufferSize;
#if MACOSX_COREAUDIO
AudioDeviceID deviceID;
#endif
};
#endif /* _SDL_coreaudio_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,550 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#if SDL_AUDIO_DRIVER_DSOUND
/* Allow access to a raw mixing buffer */
#include "SDL_timer.h"
#include "SDL_loadso.h"
#include "SDL_audio.h"
#include "../SDL_audio_c.h"
#include "SDL_directsound.h"
#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*fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW,LPVOID);
static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL;
static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL;
static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = NULL;
static void
DSOUND_Unload(void)
{
pDirectSoundCreate8 = NULL;
pDirectSoundEnumerateW = 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(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 BOOL CALLBACK
FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID data)
{
SDL_AddAudioDevice addfn = (SDL_AddAudioDevice) data;
if (guid != NULL) { /* skip default device */
char *str = WIN_StringToUTF8(desc);
if (str != NULL) {
addfn(str);
SDL_free(str); /* addfn() makes a copy of this string. */
}
}
return TRUE; /* keep enumerating. */
}
static void
DSOUND_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
{
if (iscapture) {
pDirectSoundCaptureEnumerateW(FindAllDevs, addfn);
} else {
pDirectSoundEnumerateW(FindAllDevs, addfn);
}
}
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->hidden->mixlen) == 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->hidden->mixlen, 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->hidden->mixlen;
#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->hidden->mixlen;
/* Lock the audio buffer */
result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor,
this->hidden->mixlen,
(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->hidden->mixlen,
(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 void
DSOUND_WaitDone(_THIS)
{
Uint8 *stream = DSOUND_GetDeviceBuf(this);
/* Wait for the playing chunk to finish */
if (stream != NULL) {
SDL_memset(stream, this->spec.silence, this->hidden->mixlen);
DSOUND_PlayDevice(this);
}
DSOUND_WaitDevice(this);
/* Stop the looping sound buffer */
IDirectSoundBuffer_Stop(this->hidden->mixbuf);
}
static void
DSOUND_CloseDevice(_THIS)
{
if (this->hidden != NULL) {
if (this->hidden->sound != NULL) {
if (this->hidden->mixbuf != NULL) {
/* Clean up the audio buffer */
IDirectSoundBuffer_Release(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
}
IDirectSound_Release(this->hidden->sound);
this->hidden->sound = NULL;
}
SDL_free(this->hidden);
this->hidden = NULL;
}
}
/* This function tries to create a secondary audio buffer, and returns the
number of audio chunks available in the created buffer.
*/
static int
CreateSecondary(_THIS, HWND focus)
{
LPDIRECTSOUND sndObj = this->hidden->sound;
LPDIRECTSOUNDBUFFER *sndbuf = &this->hidden->mixbuf;
Uint32 chunksize = this->spec.size;
const int numchunks = 8;
HRESULT result = DS_OK;
DSBUFFERDESC format;
LPVOID pvAudioPtr1, pvAudioPtr2;
DWORD dwAudioBytes1, dwAudioBytes2;
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;
/* Update the fragment size as size in bytes */
SDL_CalculateAudioSpec(&this->spec);
/* Try to set primary mixing privileges */
if (focus) {
result = IDirectSound_SetCooperativeLevel(sndObj,
focus, DSSCL_PRIORITY);
} else {
result = IDirectSound_SetCooperativeLevel(sndObj,
GetDesktopWindow(),
DSSCL_NORMAL);
}
if (result != DS_OK) {
return SetDSerror("DirectSound SetCooperativeLevel", result);
}
/* Try to create the secondary buffer */
SDL_zero(format);
format.dwSize = sizeof(format);
format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
if (!focus) {
format.dwFlags |= DSBCAPS_GLOBALFOCUS;
} else {
format.dwFlags |= DSBCAPS_STICKYFOCUS;
}
format.dwBufferBytes = numchunks * chunksize;
if ((format.dwBufferBytes < DSBSIZE_MIN) ||
(format.dwBufferBytes > DSBSIZE_MAX)) {
return SDL_SetError("Sound buffer size must be between %d and %d",
DSBSIZE_MIN / numchunks, DSBSIZE_MAX / numchunks);
}
format.dwReserved = 0;
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 (numchunks);
}
typedef struct FindDevGUIDData
{
const char *devname;
GUID guid;
int found;
} FindDevGUIDData;
static BOOL CALLBACK
FindDevGUID(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID _data)
{
if (guid != NULL) { /* skip the default device. */
FindDevGUIDData *data = (FindDevGUIDData *) _data;
char *str = WIN_StringToUTF8(desc);
const int match = (SDL_strcmp(str, data->devname) == 0);
SDL_free(str);
if (match) {
data->found = 1;
SDL_memcpy(&data->guid, guid, sizeof (data->guid));
return FALSE; /* found it! stop enumerating. */
}
}
return TRUE; /* keep enumerating. */
}
static int
DSOUND_OpenDevice(_THIS, const char *devname, int iscapture)
{
HRESULT result;
SDL_bool valid_format = SDL_FALSE;
SDL_bool tried_format = SDL_FALSE;
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
FindDevGUIDData devguid;
LPGUID guid = NULL;
if (devname != NULL) {
devguid.found = 0;
devguid.devname = devname;
if (iscapture)
pDirectSoundCaptureEnumerateW(FindDevGUID, &devguid);
else
pDirectSoundEnumerateW(FindDevGUID, &devguid);
if (!devguid.found) {
return SDL_SetError("DirectSound: Requested device not found");
}
guid = &devguid.guid;
}
/* 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_memset(this->hidden, 0, (sizeof *this->hidden));
/* Open the audio device */
result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL);
if (result != DS_OK) {
DSOUND_CloseDevice(this);
return SetDSerror("DirectSoundCreate", 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;
this->hidden->num_buffers = CreateSecondary(this, NULL);
if (this->hidden->num_buffers > 0) {
valid_format = SDL_TRUE;
}
break;
}
test_format = SDL_NextAudioFormat();
}
if (!valid_format) {
DSOUND_CloseDevice(this);
if (tried_format) {
return -1; /* CreateSecondary() should have called SDL_SetError(). */
}
return SDL_SetError("DirectSound: Unsupported audio format");
}
/* The buffer will auto-start playing in DSOUND_WaitDevice() */
this->hidden->mixlen = this->spec.size;
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->WaitDone = DSOUND_WaitDone;
impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
impl->CloseDevice = DSOUND_CloseDevice;
impl->Deinitialize = DSOUND_Deinitialize;
return 1; /* this audio target is available. */
}
AudioBootStrap DSOUND_bootstrap = {
"directsound", "DirectSound", DSOUND_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_DSOUND */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,46 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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 "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;
int num_buffers;
int mixlen;
DWORD lastchunk;
Uint8 *locked_buf;
};
#endif /* _SDL_directsound_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,102 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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 _directx_h
#define _directx_h
/* Include all of the DirectX 8.0 headers and adds any necessary tweaks */
#include "../../core/windows/SDL_windows.h"
#include <mmsystem.h>
#ifndef WIN32
#define WIN32
#endif
#undef WINNT
/* Far pointers don't exist in 32-bit code */
#ifndef FAR
#define FAR
#endif
/* Error codes not yet included in Win32 API header files */
#ifndef MAKE_HRESULT
#define MAKE_HRESULT(sev,fac,code) \
((HRESULT)(((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))))
#endif
#ifndef S_OK
#define S_OK (HRESULT)0x00000000L
#endif
#ifndef SUCCEEDED
#define SUCCEEDED(x) ((HRESULT)(x) >= 0)
#endif
#ifndef FAILED
#define FAILED(x) ((HRESULT)(x)<0)
#endif
#ifndef E_FAIL
#define E_FAIL (HRESULT)0x80000008L
#endif
#ifndef E_NOINTERFACE
#define E_NOINTERFACE (HRESULT)0x80004002L
#endif
#ifndef E_OUTOFMEMORY
#define E_OUTOFMEMORY (HRESULT)0x8007000EL
#endif
#ifndef E_INVALIDARG
#define E_INVALIDARG (HRESULT)0x80070057L
#endif
#ifndef E_NOTIMPL
#define E_NOTIMPL (HRESULT)0x80004001L
#endif
#ifndef REGDB_E_CLASSNOTREG
#define REGDB_E_CLASSNOTREG (HRESULT)0x80040154L
#endif
/* Severity codes */
#ifndef SEVERITY_ERROR
#define SEVERITY_ERROR 1
#endif
/* Error facility codes */
#ifndef FACILITY_WIN32
#define FACILITY_WIN32 7
#endif
#ifndef FIELD_OFFSET
#define FIELD_OFFSET(type, field) ((LONG)&(((type *)0)->field))
#endif
/* DirectX headers (if it isn't included, I haven't tested it yet)
*/
/* We need these defines to mark what version of DirectX API we use */
#define DIRECTDRAW_VERSION 0x0700
#define DIRECTSOUND_VERSION 0x0800
#define DIRECTINPUT_VERSION 0x0500
#include <ddraw.h>
#include <dsound.h>
#include <dinput.h>
#endif /* _directx_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,163 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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_audiomem.h"
#include "../SDL_audio_c.h"
#include "SDL_diskaudio.h"
/* environment variables and defaults. */
#define DISKENVR_OUTFILE "SDL_DISKAUDIOFILE"
#define DISKDEFAULT_OUTFILE "sdlaudio.raw"
#define DISKENVR_WRITEDELAY "SDL_DISKAUDIODELAY"
#define DISKDEFAULT_WRITEDELAY 150
static const char *
DISKAUD_GetOutputFilename(const char *devname)
{
if (devname == NULL) {
devname = SDL_getenv(DISKENVR_OUTFILE);
if (devname == NULL) {
devname = DISKDEFAULT_OUTFILE;
}
}
return devname;
}
/* This function waits until it is possible to write a full sound buffer */
static void
DISKAUD_WaitDevice(_THIS)
{
SDL_Delay(this->hidden->write_delay);
}
static void
DISKAUD_PlayDevice(_THIS)
{
size_t written;
/* Write the audio data */
written = SDL_RWwrite(this->hidden->output,
this->hidden->mixbuf, 1, this->hidden->mixlen);
/* If we couldn't write, assume fatal error for now */
if (written != this->hidden->mixlen) {
this->enabled = 0;
}
#ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
#endif
}
static Uint8 *
DISKAUD_GetDeviceBuf(_THIS)
{
return (this->hidden->mixbuf);
}
static void
DISKAUD_CloseDevice(_THIS)
{
if (this->hidden != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
if (this->hidden->output != NULL) {
SDL_RWclose(this->hidden->output);
this->hidden->output = NULL;
}
SDL_free(this->hidden);
this->hidden = NULL;
}
}
static int
DISKAUD_OpenDevice(_THIS, const char *devname, int iscapture)
{
const char *envr = SDL_getenv(DISKENVR_WRITEDELAY);
const char *fname = DISKAUD_GetOutputFilename(devname);
this->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc(sizeof(*this->hidden));
if (this->hidden == NULL) {
return SDL_OutOfMemory();
}
SDL_memset(this->hidden, 0, sizeof(*this->hidden));
this->hidden->mixlen = this->spec.size;
this->hidden->write_delay =
(envr) ? SDL_atoi(envr) : DISKDEFAULT_WRITEDELAY;
/* Open the audio device */
this->hidden->output = SDL_RWFromFile(fname, "wb");
if (this->hidden->output == NULL) {
DISKAUD_CloseDevice(this);
return -1;
}
/* Allocate mixing buffer */
this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
if (this->hidden->mixbuf == NULL) {
DISKAUD_CloseDevice(this);
return -1;
}
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
#if HAVE_STDIO_H
fprintf(stderr,
"WARNING: You are using the SDL disk writer audio driver!\n"
" Writing to file [%s].\n", fname);
#endif
/* We're ready to rock and roll. :-) */
return 0;
}
static int
DISKAUD_Init(SDL_AudioDriverImpl * impl)
{
/* Set the function pointers */
impl->OpenDevice = DISKAUD_OpenDevice;
impl->WaitDevice = DISKAUD_WaitDevice;
impl->PlayDevice = DISKAUD_PlayDevice;
impl->GetDeviceBuf = DISKAUD_GetDeviceBuf;
impl->CloseDevice = DISKAUD_CloseDevice;
return 1; /* this audio target is available. */
}
AudioBootStrap DISKAUD_bootstrap = {
"disk", "direct-to-disk audio", DISKAUD_Init, 1
};
#endif /* SDL_AUDIO_DRIVER_DISK */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,42 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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 *output;
Uint8 *mixbuf;
Uint32 mixlen;
Uint32 write_delay;
};
#endif /* _SDL_diskaudio_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,306 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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_audiomem.h"
#include "../SDL_audio_c.h"
#include "../SDL_audiodev_c.h"
#include "SDL_dspaudio.h"
static void
DSP_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
{
SDL_EnumUnixAudioDevices(iscapture, 0, NULL, addfn);
}
static void
DSP_CloseDevice(_THIS)
{
if (this->hidden != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
if (this->hidden->audio_fd >= 0) {
close(this->hidden->audio_fd);
this->hidden->audio_fd = -1;
}
SDL_free(this->hidden);
this->hidden = NULL;
}
}
static int
DSP_OpenDevice(_THIS, 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_memset(this->hidden, 0, (sizeof *this->hidden));
/* Open the audio device */
this->hidden->audio_fd = open(devname, flags, 0);
if (this->hidden->audio_fd < 0) {
DSP_CloseDevice(this);
return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
}
this->hidden->mixbuf = NULL;
/* Make the file descriptor use blocking writes 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) {
DSP_CloseDevice(this);
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");
DSP_CloseDevice(this);
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) {
DSP_CloseDevice(this);
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");
DSP_CloseDevice(this);
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");
DSP_CloseDevice(this);
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");
DSP_CloseDevice(this);
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) {
DSP_CloseDevice(this);
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 */
this->hidden->mixlen = this->spec.size;
this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
if (this->hidden->mixbuf == NULL) {
DSP_CloseDevice(this);
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)
{
const Uint8 *mixbuf = this->hidden->mixbuf;
const int mixlen = this->hidden->mixlen;
if (write(this->hidden->audio_fd, mixbuf, mixlen) == -1) {
perror("Audio write");
this->enabled = 0;
}
#ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen);
#endif
}
static Uint8 *
DSP_GetDeviceBuf(_THIS)
{
return (this->hidden->mixbuf);
}
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;
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: */

View File

@@ -0,0 +1,43 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifndef _SDL_dspaudio_h
#define _SDL_dspaudio_h
#include "../SDL_sysaudio.h"
/* Hidden "this" pointer for the audio functions */
#define _THIS SDL_AudioDevice *this
struct SDL_PrivateAudioData
{
/* The file descriptor for the audio device */
int audio_fd;
/* Raw mixing buffer */
Uint8 *mixbuf;
int mixlen;
};
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
#endif /* _SDL_dspaudio_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,48 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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_audio.h"
#include "../SDL_audio_c.h"
#include "SDL_dummyaudio.h"
static int
DUMMYAUD_OpenDevice(_THIS, const char *devname, int iscapture)
{
return 0; /* always succeeds. */
}
static int
DUMMYAUD_Init(SDL_AudioDriverImpl * impl)
{
/* Set the function pointers */
impl->OpenDevice = DUMMYAUD_OpenDevice;
impl->OnlyHasDefaultOutputDevice = 1;
return 1; /* this audio target is available. */
}
AudioBootStrap DUMMYAUD_bootstrap = {
"dummy", "SDL dummy audio driver", DUMMYAUD_Init, 1
};
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,41 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifndef _SDL_dummyaudio_h
#define _SDL_dummyaudio_h
#include "../SDL_sysaudio.h"
/* Hidden "this" pointer for the audio functions */
#define _THIS SDL_AudioDevice *this
struct SDL_PrivateAudioData
{
/* The file descriptor for the audio device */
Uint8 *mixbuf;
Uint32 mixlen;
Uint32 write_delay;
Uint32 initial_calls;
};
#endif /* _SDL_dummyaudio_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,345 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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_audiomem.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) {
this->enabled = 0;
}
}
}
/* 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) {
this->enabled = 0;
}
}
static Uint8 *
ESD_GetDeviceBuf(_THIS)
{
return (this->hidden->mixbuf);
}
static void
ESD_CloseDevice(_THIS)
{
if (this->hidden != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
if (this->hidden->audio_fd >= 0) {
SDL_NAME(esd_close) (this->hidden->audio_fd);
this->hidden->audio_fd = -1;
}
SDL_free(this->hidden);
this->hidden = NULL;
}
}
/* Try to get the name of the program */
static char *
get_progname(void)
{
char *progname = NULL;
#ifdef __LINUX__
FILE *fp;
static char temp[BUFSIZ];
SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid());
fp = fopen(temp, "r");
if (fp != NULL) {
if (fgets(temp, sizeof(temp) - 1, fp)) {
progname = SDL_strrchr(temp, '/');
if (progname == NULL) {
progname = temp;
} else {
progname = progname + 1;
}
}
fclose(fp);
}
#endif
return (progname);
}
static int
ESD_OpenDevice(_THIS, const char *devname, 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_memset(this->hidden, 0, (sizeof *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) {
ESD_CloseDevice(this);
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) {
ESD_CloseDevice(this);
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_AllocAudioMem(this->hidden->mixlen);
if (this->hidden->mixbuf == NULL) {
ESD_CloseDevice(this);
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: */

View File

@@ -0,0 +1,50 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifndef _SDL_esdaudio_h
#define _SDL_esdaudio_h
#include "../SDL_sysaudio.h"
/* Hidden "this" pointer for the audio functions */
#define _THIS SDL_AudioDevice *this
struct SDL_PrivateAudioData
{
/* The file descriptor for the audio device */
int audio_fd;
/* The parent process id, to detect when application quits */
pid_t parent;
/* Raw mixing buffer */
Uint8 *mixbuf;
int mixlen;
/* Support for audio timing using a timer */
float frame_ticks;
float next_frame;
};
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
#endif /* _SDL_esdaudio_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,345 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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
/* 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_audiomem.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) {
this->enabled = 0;
}
#ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
#endif
}
static void
SDL_FS_WaitDone(_THIS)
{
this->hidden->stream->Wait(this->hidden->stream,
this->hidden->mixsamples * FUSION_BUFFERS);
}
static Uint8 *
SDL_FS_GetDeviceBuf(_THIS)
{
return (this->hidden->mixbuf);
}
static void
SDL_FS_CloseDevice(_THIS)
{
if (this->hidden != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
if (this->hidden->stream) {
this->hidden->stream->Release(this->hidden->stream);
this->hidden->stream = NULL;
}
if (this->hidden->fs) {
this->hidden->fs->Release(this->hidden->fs);
this->hidden->fs = NULL;
}
SDL_free(this->hidden);
this->hidden = NULL;
}
}
static int
SDL_FS_OpenDevice(_THIS, 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_memset(this->hidden, 0, (sizeof *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) {
SDL_FS_CloseDevice(this);
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) {
SDL_FS_CloseDevice(this);
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) {
SDL_FS_CloseDevice(this);
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_AllocAudioMem(this->hidden->mixlen);
if (this->hidden->mixbuf == NULL) {
SDL_FS_CloseDevice(this);
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->WaitDone = SDL_FS_WaitDone;
impl->Deinitialize = SDL_FS_Deinitialize;
impl->OnlyHasDefaultOutputDevice = 1;
return 1; /* this audio target is available. */
}
AudioBootStrap FUSIONSOUND_bootstrap = {
"fusionsound", "FusionSound", SDL_FS_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_FUSIONSOUND */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,49 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifndef _SDL_fsaudio_h
#define _SDL_fsaudio_h
#include <fusionsound/fusionsound.h>
#include "../SDL_sysaudio.h"
/* Hidden "this" pointer for the audio functions */
#define _THIS SDL_AudioDevice *this
struct SDL_PrivateAudioData
{
/* Interface */
IFusionSound *fs;
/* The stream interface for the audio device */
IFusionSoundStream *stream;
/* Raw mixing buffer */
Uint8 *mixbuf;
int mixlen;
int mixsamples;
};
#endif /* _SDL_fsaudio_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,240 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#if SDL_AUDIO_DRIVER_HAIKU
/* Allow access to the audio stream on Haiku */
#include <SoundPlayer.h>
#include <signal.h>
#include "../../main/haiku/SDL_BeApp.h"
extern "C"
{
#include "SDL_audio.h"
#include "../SDL_audio_c.h"
#include "../SDL_sysaudio.h"
#include "SDL_haikuaudio.h"
}
/* !!! FIXME: have the callback call the higher level to avoid code dupe. */
/* The Haiku callback for handling the audio buffer */
static void
FillSound(void *device, void *stream, size_t len,
const media_raw_audio_format & format)
{
SDL_AudioDevice *audio = (SDL_AudioDevice *) device;
/* Only do soemthing if audio is enabled */
if (!audio->enabled)
return;
if (!audio->paused) {
if (audio->convert.needed) {
SDL_LockMutex(audio->mixer_lock);
(*audio->spec.callback) (audio->spec.userdata,
(Uint8 *) audio->convert.buf,
audio->convert.len);
SDL_UnlockMutex(audio->mixer_lock);
SDL_ConvertAudio(&audio->convert);
SDL_memcpy(stream, audio->convert.buf, audio->convert.len_cvt);
} else {
SDL_LockMutex(audio->mixer_lock);
(*audio->spec.callback) (audio->spec.userdata,
(Uint8 *) stream, len);
SDL_UnlockMutex(audio->mixer_lock);
}
}
}
static void
HAIKUAUDIO_CloseDevice(_THIS)
{
if (_this->hidden != NULL) {
if (_this->hidden->audio_obj) {
_this->hidden->audio_obj->Stop();
delete _this->hidden->audio_obj;
_this->hidden->audio_obj = NULL;
}
delete _this->hidden;
_this->hidden = NULL;
}
}
static const int sig_list[] = {
SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM, SIGTERM, SIGWINCH, 0
};
static inline void
MaskSignals(sigset_t * omask)
{
sigset_t mask;
int i;
sigemptyset(&mask);
for (i = 0; sig_list[i]; ++i) {
sigaddset(&mask, sig_list[i]);
}
sigprocmask(SIG_BLOCK, &mask, omask);
}
static inline void
UnmaskSignals(sigset_t * omask)
{
sigprocmask(SIG_SETMASK, omask, NULL);
}
static int
HAIKUAUDIO_OpenDevice(_THIS, const char *devname, 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_memset(_this->hidden, 0, (sizeof *_this->hidden));
/* Parse the audio format and fill the Be raw audio format */
SDL_memset(&format, '\0', sizeof(media_raw_audio_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... */
HAIKUAUDIO_CloseDevice(_this);
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 {
HAIKUAUDIO_CloseDevice(_this);
return SDL_SetError("Unable to start Be audio");
}
/* We're running! */
return 0;
}
static void
HAIKUAUDIO_Deinitialize(void)
{
SDL_QuitBeApp();
}
static int
HAIKUAUDIO_Init(SDL_AudioDriverImpl * impl)
{
/* Initialize the Be Application, if it's not already started */
if (SDL_InitBeApp() < 0) {
return 0;
}
/* Set the function pointers */
impl->OpenDevice = HAIKUAUDIO_OpenDevice;
impl->CloseDevice = HAIKUAUDIO_CloseDevice;
impl->Deinitialize = HAIKUAUDIO_Deinitialize;
impl->ProvidesOwnCallbackThread = 1;
impl->OnlyHasDefaultOutputDevice = 1;
return 1; /* this audio target is available. */
}
extern "C"
{
extern AudioBootStrap HAIKUAUDIO_bootstrap;
}
AudioBootStrap HAIKUAUDIO_bootstrap = {
"haiku", "Haiku BSoundPlayer", HAIKUAUDIO_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_HAIKU */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,38 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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_beaudio_h
#define _SDL_beaudio_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_beaudio_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,397 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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_audiomem.h"
#include "../SDL_audio_c.h"
#include "SDL_nasaudio.h"
static struct SDL_PrivateAudioData *this2 = NULL;
static void (*NAS_AuCloseServer) (AuServer *);
static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *);
static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *);
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 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(AuCreateFlow);
SDL_NAS_SYM(AuStartFlow);
SDL_NAS_SYM(AuSetElements);
SDL_NAS_SYM(AuWriteElement);
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\n",
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 void
NAS_CloseDevice(_THIS)
{
if (this->hidden != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
if (this->hidden->aud) {
NAS_AuCloseServer(this->hidden->aud);
this->hidden->aud = 0;
}
SDL_free(this->hidden);
this2 = this->hidden = NULL;
}
}
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)
{
switch (ev->type) {
case AuEventTypeElementNotify:
{
AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
switch (event->kind) {
case AuElementNotifyKindLowWater:
if (this2->buf_free >= 0) {
this2->really += event->num_bytes;
gettimeofday(&this2->last_tv, 0);
this2->buf_free += event->num_bytes;
} else {
this2->buf_free = event->num_bytes;
}
break;
case AuElementNotifyKindState:
switch (event->cur_state) {
case AuStatePause:
if (event->reason != AuReasonUser) {
if (this2->buf_free >= 0) {
this2->really += event->num_bytes;
gettimeofday(&this2->last_tv, 0);
this2->buf_free += event->num_bytes;
} else {
this2->buf_free = event->num_bytes;
}
}
break;
}
}
}
}
return AuTrue;
}
static AuDeviceID
find_device(_THIS, int nch)
{
/* These "Au" things are all macros, not functions... */
int i;
for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
AuComponentKindPhysicalOutput) &&
AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
}
}
return AuNone;
}
static int
NAS_OpenDevice(_THIS, 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_memset(this->hidden, 0, (sizeof *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) {
NAS_CloseDevice(this);
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) {
NAS_CloseDevice(this);
return SDL_SetError("NAS: Couldn't open connection to NAS server");
}
this->hidden->dev = find_device(this, this->spec.channels);
if ((this->hidden->dev == AuNone)
|| (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
NAS_CloseDevice(this);
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);
this2 = this->hidden;
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) NULL);
NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
/* Allocate mixing buffer */
this->hidden->mixlen = this->spec.size;
this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
if (this->hidden->mixbuf == NULL) {
NAS_CloseDevice(this);
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->CloseDevice = NAS_CloseDevice;
impl->Deinitialize = NAS_Deinitialize;
impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: is this 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: */

View File

@@ -0,0 +1,56 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifndef _SDL_nasaudio_h
#define _SDL_nasaudio_h
#ifdef __sgi
#include <nas/audiolib.h>
#else
#include <audio/audiolib.h>
#endif
#include <sys/time.h>
#include "../SDL_sysaudio.h"
/* Hidden "this" pointer for the audio functions */
#define _THIS SDL_AudioDevice *this
struct SDL_PrivateAudioData
{
AuServer *aud;
AuFlowID flow;
AuDeviceID dev;
/* Raw mixing buffer */
Uint8 *mixbuf;
int mixlen;
int written;
int really;
int bps;
struct timeval last_tv;
int buf_free;
};
#endif /* _SDL_nasaudio_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,541 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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_audiomem.h"
#include "../SDL_audio_c.h"
#include "SDL_paudio.h"
#define DEBUG_AUDIO 0
/* 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 {
audio_buffer paud_bufinfo;
/* Use select() for audio synchronization */
struct timeval timeout;
FD_ZERO(&fdset);
FD_SET(this->hidden->audio_fd, &fdset);
if (ioctl(this->hidden->audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0) {
#ifdef DEBUG_AUDIO
fprintf(stderr, "Couldn't get audio buffer information\n");
#endif
timeout.tv_sec = 10;
timeout.tv_usec = 0;
} else {
long ms_in_buf = paud_bufinfo.write_buf_time;
timeout.tv_sec = ms_in_buf / 1000;
ms_in_buf = ms_in_buf - timeout.tv_sec * 1000;
timeout.tv_usec = ms_in_buf * 1000;
#ifdef DEBUG_AUDIO
fprintf(stderr,
"Waiting for write_buf_time=%ld,%ld\n",
timeout.tv_sec, timeout.tv_usec);
#endif
}
#ifdef DEBUG_AUDIO
fprintf(stderr, "Waiting for audio to get ready\n");
#endif
if (select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, &timeout)
<= 0) {
const char *message =
"Audio timeout - buggy audio driver? (disabled)";
/*
* 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 - %s\n", strerror(errno), message);
this->enabled = 0;
/* 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) {
this->enabled = 0;
}
#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 != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
if (this->hidden->audio_fd >= 0) {
close(this->hidden->audio_fd);
this->hidden->audio_fd = -1;
}
SDL_free(this->hidden);
this->hidden = NULL;
}
}
static int
PAUDIO_OpenDevice(_THIS, 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_status paud_status;
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_memset(this->hidden, 0, (sizeof *this->hidden));
/* Open the audio device */
fd = OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
this->hidden->audio_fd = fd;
if (fd < 0) {
PAUDIO_CloseDevice(this);
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) {
PAUDIO_CloseDevice(this);
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
PAUDIO_CloseDevice(this);
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) {
PAUDIO_CloseDevice(this);
return SDL_SetError("Paudio: %s", err);
}
/* Allocate mixing buffer */
this->hidden->mixlen = this->spec.size;
this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
if (this->hidden->mixbuf == NULL) {
PAUDIO_CloseDevice(this);
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) {
PAUDIO_CloseDevice(this);
#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 select() 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 = DSP_OpenDevice;
impl->PlayDevice = DSP_PlayDevice;
impl->PlayDevice = DSP_WaitDevice;
impl->GetDeviceBuf = DSP_GetDeviceBuf;
impl->CloseDevice = DSP_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: */

View File

@@ -0,0 +1,47 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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_paudaudio_h
#define _SDL_paudaudio_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 select() */
float frame_ticks;
float next_frame;
};
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
#endif /* _SDL_paudaudio_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,195 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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 <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_audiomem.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 PSPAUD_DRIVER_NAME "psp"
static int
PSPAUD_OpenDevice(_THIS, 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_memset(this->hidden, 0, sizeof(*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); MOD */
switch (this->spec.format) {
case AUDIO_U8:
this->spec.silence = 0x80;
break;
default:
this->spec.silence = 0x00;
break;
}
this->spec.size = SDL_AUDIO_BITSIZE(this->spec.format) / 8;
this->spec.size *= this->spec.channels;
this->spec.size *= this->spec.samples;
/* ========================================== */
/* 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 {
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 PSPAUD_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 PSPAUD_WaitDevice(_THIS)
{
/* Because we block when sending audio, there's no need for this function to do anything. */
}
static Uint8 *PSPAUD_GetDeviceBuf(_THIS)
{
return this->hidden->mixbufs[this->hidden->next_buffer];
}
static void PSPAUD_CloseDevice(_THIS)
{
if (this->hidden->channel >= 0) {
sceAudioChRelease(this->hidden->channel);
this->hidden->channel = -1;
}
if (this->hidden->rawbuf != NULL) {
free(this->hidden->rawbuf);
this->hidden->rawbuf = NULL;
}
}
static void PSPAUD_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
PSPAUD_Init(SDL_AudioDriverImpl * impl)
{
/* Set the function pointers */
impl->OpenDevice = PSPAUD_OpenDevice;
impl->PlayDevice = PSPAUD_PlayDevice;
impl->WaitDevice = PSPAUD_WaitDevice;
impl->GetDeviceBuf = PSPAUD_GetDeviceBuf;
impl->WaitDone = PSPAUD_WaitDevice;
impl->CloseDevice = PSPAUD_CloseDevice;
impl->ThreadInit = PSPAUD_ThreadInit;
/* PSP audio device */
impl->OnlyHasDefaultOutputDevice = 1;
/*
impl->HasCaptureSupport = 1;
impl->OnlyHasDefaultInputDevice = 1;
*/
/*
impl->DetectDevices = DSOUND_DetectDevices;
impl->Deinitialize = DSOUND_Deinitialize;
*/
return 1; /* this audio target is available. */
}
AudioBootStrap PSPAUD_bootstrap = {
"psp", "PSP audio driver", PSPAUD_Init, 0
};
/* SDL_AUDI */

View File

@@ -0,0 +1,45 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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 video 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 */
/* vim: ts=4 sw=4
*/

View File

@@ -0,0 +1,554 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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"
#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 <errno.h>
#include <pulse/pulseaudio.h>
#include <pulse/simple.h>
#include "SDL_timer.h"
#include "SDL_audio.h"
#include "../SDL_audiomem.h"
#include "../SDL_audio_c.h"
#include "SDL_pulseaudio.h"
#include "SDL_loadso.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_simple *(*PULSEAUDIO_pa_simple_new) (const char *, const char *,
pa_stream_direction_t, const char *, const char *, const pa_sample_spec *,
const pa_channel_map *, const pa_buffer_attr *, int *);
static void (*PULSEAUDIO_pa_simple_free) (pa_simple *);
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 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_context_state_t (*PULSEAUDIO_pa_context_get_state) (pa_context *);
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 pa_stream_state_t (*PULSEAUDIO_pa_stream_get_state) (pa_stream *);
static size_t (*PULSEAUDIO_pa_stream_writable_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_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_simple_new);
SDL_PULSEAUDIO_SYM(pa_simple_free);
SDL_PULSEAUDIO_SYM(pa_mainloop_new);
SDL_PULSEAUDIO_SYM(pa_mainloop_get_api);
SDL_PULSEAUDIO_SYM(pa_mainloop_iterate);
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_state);
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_get_state);
SDL_PULSEAUDIO_SYM(pa_stream_writable_size);
SDL_PULSEAUDIO_SYM(pa_stream_write);
SDL_PULSEAUDIO_SYM(pa_stream_drain);
SDL_PULSEAUDIO_SYM(pa_stream_disconnect);
SDL_PULSEAUDIO_SYM(pa_stream_unref);
SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto);
SDL_PULSEAUDIO_SYM(pa_strerror);
return 0;
}
/* Check to see if we can connect to PulseAudio */
static SDL_bool
CheckPulseAudioAvailable()
{
pa_simple *s;
pa_sample_spec ss;
ss.format = PA_SAMPLE_S16NE;
ss.channels = 1;
ss.rate = 22050;
s = PULSEAUDIO_pa_simple_new(NULL, "SDL", PA_STREAM_PLAYBACK, NULL,
"Test", &ss, NULL, NULL, NULL);
if (s) {
PULSEAUDIO_pa_simple_free(s);
return SDL_TRUE;
} else {
return SDL_FALSE;
}
}
/* 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(1) {
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) {
this->enabled = 0;
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 (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, h->mixlen, NULL, 0LL,
PA_SEEK_RELATIVE) < 0) {
this->enabled = 0;
}
}
static void
stream_drain_complete(pa_stream *s, int success, void *userdata)
{
/* no-op for pa_stream_drain() to use for callback. */
}
static void
PULSEAUDIO_WaitDone(_THIS)
{
struct SDL_PrivateAudioData *h = this->hidden;
pa_operation *o;
o = PULSEAUDIO_pa_stream_drain(h->stream, stream_drain_complete, NULL);
if (!o) {
return;
}
while (PULSEAUDIO_pa_operation_get_state(o) != PA_OPERATION_DONE) {
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) {
PULSEAUDIO_pa_operation_cancel(o);
break;
}
}
PULSEAUDIO_pa_operation_unref(o);
}
static Uint8 *
PULSEAUDIO_GetDeviceBuf(_THIS)
{
return (this->hidden->mixbuf);
}
static void
PULSEAUDIO_CloseDevice(_THIS)
{
if (this->hidden != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
if (this->hidden->stream) {
PULSEAUDIO_pa_stream_disconnect(this->hidden->stream);
PULSEAUDIO_pa_stream_unref(this->hidden->stream);
this->hidden->stream = NULL;
}
if (this->hidden->context != NULL) {
PULSEAUDIO_pa_context_disconnect(this->hidden->context);
PULSEAUDIO_pa_context_unref(this->hidden->context);
this->hidden->context = NULL;
}
if (this->hidden->mainloop != NULL) {
PULSEAUDIO_pa_mainloop_free(this->hidden->mainloop);
this->hidden->mainloop = NULL;
}
SDL_free(this->hidden);
this->hidden = NULL;
}
}
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 int
PULSEAUDIO_OpenDevice(_THIS, 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;
/* 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_memset(this->hidden, 0, (sizeof *this->hidden));
h = 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) {
PULSEAUDIO_CloseDevice(this);
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 */
h->mixlen = this->spec.size;
h->mixbuf = (Uint8 *) SDL_AllocAudioMem(h->mixlen);
if (h->mixbuf == NULL) {
PULSEAUDIO_CloseDevice(this);
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
/* 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);
/* Set up a new main loop */
if (!(h->mainloop = PULSEAUDIO_pa_mainloop_new())) {
PULSEAUDIO_CloseDevice(this);
return SDL_SetError("pa_mainloop_new() failed");
}
h->mainloop_api = PULSEAUDIO_pa_mainloop_get_api(h->mainloop);
h->context = PULSEAUDIO_pa_context_new(h->mainloop_api, getAppName());
if (!h->context) {
PULSEAUDIO_CloseDevice(this);
return SDL_SetError("pa_context_new() failed");
}
/* Connect to the PulseAudio server */
if (PULSEAUDIO_pa_context_connect(h->context, NULL, 0, NULL) < 0) {
PULSEAUDIO_CloseDevice(this);
return SDL_SetError("Could not setup connection to PulseAudio");
}
do {
if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
PULSEAUDIO_CloseDevice(this);
return SDL_SetError("pa_mainloop_iterate() failed");
}
state = PULSEAUDIO_pa_context_get_state(h->context);
if (!PA_CONTEXT_IS_GOOD(state)) {
PULSEAUDIO_CloseDevice(this);
return SDL_SetError("Could not connect to PulseAudio");
}
} while (state != PA_CONTEXT_READY);
h->stream = PULSEAUDIO_pa_stream_new(
h->context,
"Simple DirectMedia Layer", /* stream description */
&paspec, /* sample format spec */
&pacmap /* channel map */
);
if (h->stream == NULL) {
PULSEAUDIO_CloseDevice(this);
return SDL_SetError("Could not set up PulseAudio stream");
}
if (PULSEAUDIO_pa_stream_connect_playback(h->stream, NULL, &paattr, flags,
NULL, NULL) < 0) {
PULSEAUDIO_CloseDevice(this);
return SDL_SetError("Could not connect PulseAudio stream");
}
do {
if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
PULSEAUDIO_CloseDevice(this);
return SDL_SetError("pa_mainloop_iterate() failed");
}
state = PULSEAUDIO_pa_stream_get_state(h->stream);
if (!PA_STREAM_IS_GOOD(state)) {
PULSEAUDIO_CloseDevice(this);
return SDL_SetError("Could not create to PulseAudio stream");
}
} while (state != PA_STREAM_READY);
/* We're ready to rock and roll. :-) */
return 0;
}
static void
PULSEAUDIO_Deinitialize(void)
{
UnloadPulseAudioLibrary();
}
static int
PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
{
if (LoadPulseAudioLibrary() < 0) {
return 0;
}
if (!CheckPulseAudioAvailable()) {
UnloadPulseAudioLibrary();
return 0;
}
/* Set the function pointers */
impl->OpenDevice = PULSEAUDIO_OpenDevice;
impl->PlayDevice = PULSEAUDIO_PlayDevice;
impl->WaitDevice = PULSEAUDIO_WaitDevice;
impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
impl->CloseDevice = PULSEAUDIO_CloseDevice;
impl->WaitDone = PULSEAUDIO_WaitDone;
impl->Deinitialize = PULSEAUDIO_Deinitialize;
impl->OnlyHasDefaultOutputDevice = 1;
return 1; /* this audio target is available. */
}
AudioBootStrap PULSEAUDIO_bootstrap = {
"pulseaudio", "PulseAudio", PULSEAUDIO_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_PULSEAUDIO */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,48 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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
{
/* pulseaudio structures */
pa_mainloop *mainloop;
pa_mainloop_api *mainloop_api;
pa_context *context;
pa_stream *stream;
/* Raw mixing buffer */
Uint8 *mixbuf;
int mixlen;
};
#endif /* _SDL_pulseaudio_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,857 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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_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 "../SDL_audiomem.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
#define QSA_NO_WORKAROUNDS 0x00000000
#define QSA_MMAP_WORKAROUND 0x00000001
struct BuggyCards
{
char *cardname;
unsigned long bugtype;
};
#define QSA_WA_CARDS 3
#define QSA_MAX_CARD_NAME_LENGTH 33
struct BuggyCards buggycards[QSA_WA_CARDS] = {
{"Sound Blaster Live!", QSA_MMAP_WORKAROUND},
{"Vortex 8820", QSA_MMAP_WORKAROUND},
{"Vortex 8830", QSA_MMAP_WORKAROUND},
};
/* 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));
}
/* card names check to apply the workarounds */
static int
QSA_CheckBuggyCards(_THIS, unsigned long checkfor)
{
char scardname[QSA_MAX_CARD_NAME_LENGTH];
int it;
if (snd_card_get_name
(this->hidden->cardno, scardname, QSA_MAX_CARD_NAME_LENGTH - 1) < 0) {
return 0;
}
for (it = 0; it < QSA_WA_CARDS; it++) {
if (SDL_strcmp(buggycards[it].cardname, scardname) == 0) {
if (buggycards[it].bugtype == checkfor) {
return 1;
}
}
}
return 0;
}
/* !!! FIXME: does this need to be here? Does the SDL version not work? */
static void
QSA_ThreadInit(_THIS)
{
struct sched_param param;
int status;
/* Increase default 10 priority to 25 to avoid jerky sound */
status = SchedGet(0, 0, &param);
param.sched_priority = param.sched_curpriority + 15;
status = SchedSet(0, 0, SCHED_NOCHANGE, &param);
}
/* PCM channel parameters initialize function */
static void
QSA_InitAudioParams(snd_pcm_channel_params_t * cpars)
{
SDL_memset(cpars, 0, sizeof(snd_pcm_channel_params_t));
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)
{
fd_set wfds;
fd_set rfds;
int selectret;
struct timeval timeout;
if (!this->hidden->iscapture) {
FD_ZERO(&wfds);
FD_SET(this->hidden->audio_fd, &wfds);
} else {
FD_ZERO(&rfds);
FD_SET(this->hidden->audio_fd, &rfds);
}
do {
/* 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 ! */
timeout.tv_sec = 2;
timeout.tv_usec = 0;
this->hidden->timeout_on_wait = 0;
if (!this->hidden->iscapture) {
selectret =
select(this->hidden->audio_fd + 1, NULL, &wfds, NULL,
&timeout);
} else {
selectret =
select(this->hidden->audio_fd + 1, &rfds, NULL, NULL,
&timeout);
}
switch (selectret) {
case -1:
{
SDL_SetError("QSA: select() failed: %s", strerror(errno));
return;
}
break;
case 0:
{
SDL_SetError("QSA: timeout on buffer waiting occured");
this->hidden->timeout_on_wait = 1;
return;
}
break;
default:
{
if (!this->hidden->iscapture) {
if (FD_ISSET(this->hidden->audio_fd, &wfds)) {
return;
}
} else {
if (FD_ISSET(this->hidden->audio_fd, &rfds)) {
return;
}
}
}
break;
}
} while (1);
}
static void
QSA_PlayDevice(_THIS)
{
snd_pcm_channel_status_t cstatus;
int written;
int status;
int towrite;
void *pcmbuffer;
if ((!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_memset(&cstatus, 0, sizeof(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) && (this->enabled));
/* If we couldn't write, assume fatal error for now */
if (towrite != 0) {
this->enabled = 0;
}
}
static Uint8 *
QSA_GetDeviceBuf(_THIS)
{
return this->hidden->pcm_buf;
}
static void
QSA_CloseDevice(_THIS)
{
if (this->hidden != NULL) {
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);
this->hidden->audio_handle = NULL;
}
SDL_FreeAudioMem(this->hidden->pcm_buf);
this->hidden->pcm_buf = NULL;
SDL_free(this->hidden);
this->hidden = NULL;
}
}
static int
QSA_OpenDevice(_THIS, const char *devname, int iscapture)
{
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();
}
SDL_memset(this->hidden, 0, sizeof(struct SDL_PrivateAudioData));
/* Initialize channel transfer parameters to default */
QSA_InitAudioParams(&cparams);
/* Initialize channel direction: capture or playback */
this->hidden->iscapture = iscapture;
/* Find deviceid and cardid by device name for playback */
if ((!this->hidden->iscapture) && (devname != NULL)) {
uint32_t device;
int32_t status;
/* Search in the playback devices */
device = 0;
do {
status = SDL_strcmp(qsa_playback_device[device].name, devname);
if (status == 0) {
/* Found requested device */
this->hidden->deviceno = qsa_playback_device[device].deviceno;
this->hidden->cardno = qsa_playback_device[device].cardno;
break;
}
device++;
if (device >= qsa_playback_devices) {
QSA_CloseDevice(this);
return SDL_SetError("No such playback device");
}
} while (1);
}
/* Find deviceid and cardid by device name for capture */
if ((this->hidden->iscapture) && (devname != NULL)) {
/* Search in the capture devices */
uint32_t device;
int32_t status;
/* Searching in the playback devices */
device = 0;
do {
status = SDL_strcmp(qsa_capture_device[device].name, devname);
if (status == 0) {
/* Found requested device */
this->hidden->deviceno = qsa_capture_device[device].deviceno;
this->hidden->cardno = qsa_capture_device[device].cardno;
break;
}
device++;
if (device >= qsa_capture_devices) {
QSA_CloseDevice(this);
return SDL_SetError("No such capture device");
}
} while (1);
}
/* Check if SDL requested default audio device */
if (devname == NULL) {
/* Open system default audio device */
if (!this->hidden->iscapture) {
status = snd_pcm_open_preferred(&this->hidden->audio_handle,
&this->hidden->cardno,
&this->hidden->deviceno,
SND_PCM_OPEN_PLAYBACK);
} else {
status = snd_pcm_open_preferred(&this->hidden->audio_handle,
&this->hidden->cardno,
&this->hidden->deviceno,
SND_PCM_OPEN_CAPTURE);
}
} else {
/* Open requested audio device */
if (!this->hidden->iscapture) {
status =
snd_pcm_open(&this->hidden->audio_handle,
this->hidden->cardno, this->hidden->deviceno,
SND_PCM_OPEN_PLAYBACK);
} else {
status =
snd_pcm_open(&this->hidden->audio_handle,
this->hidden->cardno, this->hidden->deviceno,
SND_PCM_OPEN_CAPTURE);
}
}
/* Check if requested device is opened */
if (status < 0) {
this->hidden->audio_handle = NULL;
QSA_CloseDevice(this);
return QSA_SetError("snd_pcm_open", status);
}
if (!QSA_CheckBuggyCards(this, QSA_MMAP_WORKAROUND)) {
/* Disable QSA MMAP plugin for buggy audio drivers */
status =
snd_pcm_plugin_set_disable(this->hidden->audio_handle,
PLUGIN_DISABLE_MMAP);
if (status < 0) {
QSA_CloseDevice(this);
return QSA_SetError("snd_pcm_plugin_set_disable", 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) {
QSA_CloseDevice(this);
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) {
QSA_CloseDevice(this);
return QSA_SetError("snd_pcm_channel_params", status);
}
/* Make sure channel is setup right one last time */
SDL_memset(&csetup, 0, sizeof(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) {
QSA_CloseDevice(this);
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_AllocAudioMem(this->hidden->pcm_len);
if (this->hidden->pcm_buf == NULL) {
QSA_CloseDevice(this);
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) {
QSA_CloseDevice(this);
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) {
QSA_CloseDevice(this);
return QSA_SetError("snd_pcm_plugin_prepare", status);
}
/* We're really ready to rock and roll. :-) */
return 0;
}
static void
QSA_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
{
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;
}
/* Find requested devices by type */
if (!iscapture) {
/* 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) {
addfn(qsa_playback_device[qsa_playback_devices].name);
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;
}
}
} else {
/* 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) {
addfn(qsa_capture_device[qsa_capture_devices].name);
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_WaitDone(_THIS)
{
if (!this->hidden->iscapture) {
if (this->hidden->audio_handle != NULL) {
/* Wait till last fragment is played and stop channel */
snd_pcm_plugin_flush(this->hidden->audio_handle,
SND_PCM_CHANNEL_PLAYBACK);
}
} else {
if (this->hidden->audio_handle != NULL) {
/* Discard all unread data and stop channel */
snd_pcm_plugin_flush(this->hidden->audio_handle,
SND_PCM_CHANNEL_CAPTURE);
}
}
}
static void
QSA_Deinitialize(void)
{
/* Clear devices array on shutdown */
SDL_memset(qsa_playback_device, 0x00,
sizeof(QSA_Device) * QSA_MAX_DEVICES);
SDL_memset(qsa_capture_device, 0x00,
sizeof(QSA_Device) * QSA_MAX_DEVICES);
qsa_playback_devices = 0;
qsa_capture_devices = 0;
}
static int
QSA_Init(SDL_AudioDriverImpl * impl)
{
snd_pcm_t *handle = NULL;
int32_t status = 0;
/* Clear devices array */
SDL_memset(qsa_playback_device, 0x00,
sizeof(QSA_Device) * QSA_MAX_DEVICES);
SDL_memset(qsa_capture_device, 0x00,
sizeof(QSA_Device) * QSA_MAX_DEVICES);
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->WaitDone = QSA_WaitDone;
impl->Deinitialize = QSA_Deinitialize;
impl->LockDevice = NULL;
impl->UnlockDevice = NULL;
impl->OnlyHasDefaultOutputDevice = 0;
impl->ProvidesOwnCallbackThread = 0;
impl->SkipMixerLock = 0;
impl->HasCaptureSupport = 1;
impl->OnlyHasDefaultOutputDevice = 0;
impl->OnlyHasDefaultInputDevice = 0;
/* Check if io-audio manager is running or not */
status = snd_cards();
if (status == 0) {
/* if no, return immediately */
return 1;
}
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: */

View File

@@ -0,0 +1,57 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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 */
int 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: */

760
src/audio/sdlgenaudiocvt.pl Executable file
View File

@@ -0,0 +1,760 @@
#!/usr/bin/perl -w
use warnings;
use strict;
my @audiotypes = qw(
U8
S8
U16LSB
S16LSB
U16MSB
S16MSB
S32LSB
S32MSB
F32LSB
F32MSB
);
my @channels = ( 1, 2, 4, 6, 8 );
my %funcs;
my $custom_converters = 0;
sub getTypeConvertHashId {
my ($from, $to) = @_;
return "TYPECONVERTER $from/$to";
}
sub getResamplerHashId {
my ($from, $channels, $upsample, $multiple) = @_;
return "RESAMPLER $from/$channels/$upsample/$multiple";
}
sub outputHeader {
print <<EOF;
/* DO NOT EDIT! This file is generated by sdlgenaudiocvt.pl */
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken\@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../SDL_internal.h"
#include "SDL_audio.h"
#include "SDL_audio_c.h"
#ifndef DEBUG_CONVERT
#define DEBUG_CONVERT 0
#endif
/* If you can guarantee your data and need space, you can eliminate code... */
/* Just build the arbitrary resamplers if you're saving code space. */
#ifndef LESS_RESAMPLERS
#define LESS_RESAMPLERS 0
#endif
/* Don't build any resamplers if you're REALLY saving code space. */
#ifndef NO_RESAMPLERS
#define NO_RESAMPLERS 0
#endif
/* Don't build any type converters if you're saving code space. */
#ifndef NO_CONVERTERS
#define NO_CONVERTERS 0
#endif
/* *INDENT-OFF* */
EOF
my @vals = ( 127, 32767, 2147483647 );
foreach (@vals) {
my $val = $_;
my $fval = 1.0 / $val;
print("#define DIVBY${val} ${fval}f\n");
}
print("\n");
}
sub outputFooter {
print <<EOF;
/* $custom_converters converters generated. */
/* *INDENT-ON* */
/* vi: set ts=4 sw=4 expandtab: */
EOF
}
sub splittype {
my $t = shift;
my ($signed, $size, $endian) = $t =~ /([USF])(\d+)([LM]SB|)/;
my $float = ($signed eq 'F') ? 1 : 0;
$signed = (($float) or ($signed eq 'S')) ? 1 : 0;
$endian = 'NONE' if ($endian eq '');
my $ctype = '';
if ($float) {
$ctype = (($size == 32) ? 'float' : 'double');
} else {
$ctype = (($signed) ? 'S' : 'U') . "int${size}";
}
return ($signed, $float, $size, $endian, $ctype);
}
sub getSwapFunc {
my ($size, $signed, $float, $endian, $val) = @_;
my $BEorLE = (($endian eq 'MSB') ? 'BE' : 'LE');
my $code = '';
if ($float) {
$code = "SDL_SwapFloat${BEorLE}($val)";
} else {
if ($size > 8) {
$code = "SDL_Swap${BEorLE}${size}($val)";
} else {
$code = $val;
}
if (($signed) and (!$float)) {
$code = "((Sint${size}) $code)";
}
}
return "${code}";
}
sub maxIntVal {
my $size = shift;
if ($size == 8) {
return 0x7F;
} elsif ($size == 16) {
return 0x7FFF;
} elsif ($size == 32) {
return 0x7FFFFFFF;
}
die("bug in script.\n");
}
sub getFloatToIntMult {
my $size = shift;
my $val = maxIntVal($size) . '.0';
$val .= 'f' if ($size < 32);
return $val;
}
sub getIntToFloatDivBy {
my $size = shift;
return 'DIVBY' . maxIntVal($size);
}
sub getSignFlipVal {
my $size = shift;
if ($size == 8) {
return '0x80';
} elsif ($size == 16) {
return '0x8000';
} elsif ($size == 32) {
return '0x80000000';
}
die("bug in script.\n");
}
sub buildCvtFunc {
my ($from, $to) = @_;
my ($fsigned, $ffloat, $fsize, $fendian, $fctype) = splittype($from);
my ($tsigned, $tfloat, $tsize, $tendian, $tctype) = splittype($to);
my $diffs = 0;
$diffs++ if ($fsize != $tsize);
$diffs++ if ($fsigned != $tsigned);
$diffs++ if ($ffloat != $tfloat);
$diffs++ if ($fendian ne $tendian);
return if ($diffs == 0);
my $hashid = getTypeConvertHashId($from, $to);
if (1) { # !!! FIXME: if ($diffs > 1) {
my $sym = "SDL_Convert_${from}_to_${to}";
$funcs{$hashid} = $sym;
$custom_converters++;
# Always unsigned for ints, for possible byteswaps.
my $srctype = (($ffloat) ? 'float' : "Uint${fsize}");
print <<EOF;
static void SDLCALL
${sym}(SDL_AudioCVT * cvt, SDL_AudioFormat format)
{
int i;
const $srctype *src;
$tctype *dst;
#if DEBUG_CONVERT
fprintf(stderr, "Converting AUDIO_${from} to AUDIO_${to}.\\n");
#endif
EOF
if ($fsize < $tsize) {
my $mult = $tsize / $fsize;
print <<EOF;
src = ((const $srctype *) (cvt->buf + cvt->len_cvt)) - 1;
dst = (($tctype *) (cvt->buf + cvt->len_cvt * $mult)) - 1;
for (i = cvt->len_cvt / sizeof ($srctype); i; --i, --src, --dst) {
EOF
} else {
print <<EOF;
src = (const $srctype *) cvt->buf;
dst = ($tctype *) cvt->buf;
for (i = cvt->len_cvt / sizeof ($srctype); i; --i, ++src, ++dst) {
EOF
}
# Have to convert to/from float/int.
# !!! FIXME: cast through double for int32<->float?
my $code = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, '*src');
if ($ffloat != $tfloat) {
if ($ffloat) {
my $mult = getFloatToIntMult($tsize);
if (!$tsigned) { # bump from -1.0f/1.0f to 0.0f/2.0f
$code = "($code + 1.0f)";
}
$code = "(($tctype) ($code * $mult))";
} else {
# $divby will be the reciprocal, to avoid pipeline stalls
# from floating point division...so multiply it.
my $divby = getIntToFloatDivBy($fsize);
$code = "(((float) $code) * $divby)";
if (!$fsigned) { # bump from 0.0f/2.0f to -1.0f/1.0f.
$code = "($code - 1.0f)";
}
}
} else {
# All integer conversions here.
if ($fsigned != $tsigned) {
my $signflipval = getSignFlipVal($fsize);
$code = "(($code) ^ $signflipval)";
}
my $shiftval = abs($fsize - $tsize);
if ($fsize < $tsize) {
$code = "((($tctype) $code) << $shiftval)";
} elsif ($fsize > $tsize) {
$code = "(($tctype) ($code >> $shiftval))";
}
}
my $swap = getSwapFunc($tsize, $tsigned, $tfloat, $tendian, 'val');
print <<EOF;
const $tctype val = $code;
*dst = ${swap};
}
EOF
if ($fsize > $tsize) {
my $divby = $fsize / $tsize;
print(" cvt->len_cvt /= $divby;\n");
} elsif ($fsize < $tsize) {
my $mult = $tsize / $fsize;
print(" cvt->len_cvt *= $mult;\n");
}
print <<EOF;
if (cvt->filters[++cvt->filter_index]) {
cvt->filters[cvt->filter_index] (cvt, AUDIO_$to);
}
}
EOF
} else {
if ($fsigned != $tsigned) {
$funcs{$hashid} = 'SDL_ConvertSigned';
} elsif ($ffloat != $tfloat) {
$funcs{$hashid} = 'SDL_ConvertFloat';
} elsif ($fsize != $tsize) {
$funcs{$hashid} = 'SDL_ConvertSize';
} elsif ($fendian ne $tendian) {
$funcs{$hashid} = 'SDL_ConvertEndian';
} else {
die("error in script.\n");
}
}
}
sub buildTypeConverters {
print "#if !NO_CONVERTERS\n\n";
foreach (@audiotypes) {
my $from = $_;
foreach (@audiotypes) {
my $to = $_;
buildCvtFunc($from, $to);
}
}
print "#endif /* !NO_CONVERTERS */\n\n\n";
print "const SDL_AudioTypeFilters sdl_audio_type_filters[] =\n{\n";
print "#if !NO_CONVERTERS\n";
foreach (@audiotypes) {
my $from = $_;
foreach (@audiotypes) {
my $to = $_;
if ($from ne $to) {
my $hashid = getTypeConvertHashId($from, $to);
my $sym = $funcs{$hashid};
print(" { AUDIO_$from, AUDIO_$to, $sym },\n");
}
}
}
print "#endif /* !NO_CONVERTERS */\n";
print(" { 0, 0, NULL }\n");
print "};\n\n\n";
}
sub getBiggerCtype {
my ($isfloat, $size) = @_;
if ($isfloat) {
if ($size == 32) {
return 'double';
}
die("bug in script.\n");
}
if ($size == 8) {
return 'Sint16';
} elsif ($size == 16) {
return 'Sint32'
} elsif ($size == 32) {
return 'Sint64'
}
die("bug in script.\n");
}
# These handle arbitrary resamples...44100Hz to 48000Hz, for example.
# Man, this code is skanky.
sub buildArbitraryResampleFunc {
# !!! FIXME: we do a lot of unnecessary and ugly casting in here, due to getSwapFunc().
my ($from, $channels, $upsample) = @_;
my ($fsigned, $ffloat, $fsize, $fendian, $fctype) = splittype($from);
my $bigger = getBiggerCtype($ffloat, $fsize);
my $interp = ($ffloat) ? '* 0.5' : '>> 1';
my $resample = ($upsample) ? 'Upsample' : 'Downsample';
my $hashid = getResamplerHashId($from, $channels, $upsample, 0);
my $sym = "SDL_${resample}_${from}_${channels}c";
$funcs{$hashid} = $sym;
$custom_converters++;
my $fudge = $fsize * $channels * 2; # !!! FIXME
my $eps_adjust = ($upsample) ? 'dstsize' : 'srcsize';
my $incr = '';
my $incr2 = '';
# !!! FIXME: DEBUG_CONVERT should report frequencies.
print <<EOF;
static void SDLCALL
${sym}(SDL_AudioCVT * cvt, SDL_AudioFormat format)
{
#if DEBUG_CONVERT
fprintf(stderr, "$resample arbitrary (x%f) AUDIO_${from}, ${channels} channels.\\n", cvt->rate_incr);
#endif
const int srcsize = cvt->len_cvt - $fudge;
const int dstsize = (int) (((double)cvt->len_cvt) * cvt->rate_incr);
register int eps = 0;
EOF
my $endcomparison = '!=';
# Upsampling (growing the buffer) needs to work backwards, since we
# overwrite the buffer as we go.
if ($upsample) {
$endcomparison = '>='; # dst > target
print <<EOF;
$fctype *dst = (($fctype *) (cvt->buf + dstsize)) - $channels;
const $fctype *src = (($fctype *) (cvt->buf + cvt->len_cvt)) - $channels;
const $fctype *target = ((const $fctype *) cvt->buf);
EOF
} else {
$endcomparison = '<'; # dst < target
print <<EOF;
$fctype *dst = ($fctype *) cvt->buf;
const $fctype *src = ($fctype *) cvt->buf;
const $fctype *target = (const $fctype *) (cvt->buf + dstsize);
EOF
}
for (my $i = 0; $i < $channels; $i++) {
my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
print <<EOF;
$fctype sample${idx} = $val;
EOF
}
for (my $i = 0; $i < $channels; $i++) {
my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
print <<EOF;
$fctype last_sample${idx} = sample${idx};
EOF
}
print <<EOF;
while (dst $endcomparison target) {
EOF
if ($upsample) {
for (my $i = 0; $i < $channels; $i++) {
# !!! FIXME: don't do this swap every write, just when the samples change.
my $idx = (($channels - $i) - 1);
my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "sample${idx}");
print <<EOF;
dst[$idx] = $val;
EOF
}
$incr = ($channels == 1) ? 'dst--' : "dst -= $channels";
$incr2 = ($channels == 1) ? 'src--' : "src -= $channels";
print <<EOF;
$incr;
eps += srcsize;
if ((eps << 1) >= dstsize) {
$incr2;
EOF
} else { # downsample.
$incr = ($channels == 1) ? 'src++' : "src += $channels";
print <<EOF;
$incr;
eps += dstsize;
if ((eps << 1) >= srcsize) {
EOF
for (my $i = 0; $i < $channels; $i++) {
my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "sample${i}");
print <<EOF;
dst[$i] = $val;
EOF
}
$incr = ($channels == 1) ? 'dst++' : "dst += $channels";
print <<EOF;
$incr;
EOF
}
for (my $i = 0; $i < $channels; $i++) {
my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
my $swapped = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
print <<EOF;
sample${idx} = ($fctype) (((($bigger) $swapped) + (($bigger) last_sample${idx})) $interp);
EOF
}
for (my $i = 0; $i < $channels; $i++) {
my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
print <<EOF;
last_sample${idx} = sample${idx};
EOF
}
print <<EOF;
eps -= $eps_adjust;
}
}
EOF
print <<EOF;
cvt->len_cvt = dstsize;
if (cvt->filters[++cvt->filter_index]) {
cvt->filters[cvt->filter_index] (cvt, format);
}
}
EOF
}
# These handle clean resamples...doubling and quadrupling the sample rate, etc.
sub buildMultipleResampleFunc {
# !!! FIXME: we do a lot of unnecessary and ugly casting in here, due to getSwapFunc().
my ($from, $channels, $upsample, $multiple) = @_;
my ($fsigned, $ffloat, $fsize, $fendian, $fctype) = splittype($from);
my $bigger = getBiggerCtype($ffloat, $fsize);
my $interp = ($ffloat) ? '* 0.5' : '>> 1';
my $interp2 = ($ffloat) ? '* 0.25' : '>> 2';
my $mult3 = ($ffloat) ? '3.0' : '3';
my $lencvtop = ($upsample) ? '*' : '/';
my $resample = ($upsample) ? 'Upsample' : 'Downsample';
my $hashid = getResamplerHashId($from, $channels, $upsample, $multiple);
my $sym = "SDL_${resample}_${from}_${channels}c_x${multiple}";
$funcs{$hashid} = $sym;
$custom_converters++;
# !!! FIXME: DEBUG_CONVERT should report frequencies.
print <<EOF;
static void SDLCALL
${sym}(SDL_AudioCVT * cvt, SDL_AudioFormat format)
{
#if DEBUG_CONVERT
fprintf(stderr, "$resample (x${multiple}) AUDIO_${from}, ${channels} channels.\\n");
#endif
const int dstsize = cvt->len_cvt $lencvtop $multiple;
EOF
my $endcomparison = '!=';
# Upsampling (growing the buffer) needs to work backwards, since we
# overwrite the buffer as we go.
if ($upsample) {
$endcomparison = '>='; # dst > target
print <<EOF;
$fctype *dst = (($fctype *) (cvt->buf + dstsize)) - $channels * $multiple;
const $fctype *src = (($fctype *) (cvt->buf + cvt->len_cvt)) - $channels;
const $fctype *target = ((const $fctype *) cvt->buf);
EOF
} else {
$endcomparison = '<'; # dst < target
print <<EOF;
$fctype *dst = ($fctype *) cvt->buf;
const $fctype *src = ($fctype *) cvt->buf;
const $fctype *target = (const $fctype *) (cvt->buf + dstsize);
EOF
}
for (my $i = 0; $i < $channels; $i++) {
my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
print <<EOF;
$bigger last_sample${idx} = ($bigger) $val;
EOF
}
print <<EOF;
while (dst $endcomparison target) {
EOF
for (my $i = 0; $i < $channels; $i++) {
my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
my $val = getSwapFunc($fsize, $fsigned, $ffloat, $fendian, "src[$idx]");
print <<EOF;
const $bigger sample${idx} = ($bigger) $val;
EOF
}
my $incr = '';
if ($upsample) {
$incr = ($channels == 1) ? 'src--' : "src -= $channels";
} else {
my $amount = $channels * $multiple;
$incr = "src += $amount"; # can't ever be 1, so no "++" version.
}
print <<EOF;
$incr;
EOF
# !!! FIXME: This really begs for some Altivec or SSE, etc.
if ($upsample) {
if ($multiple == 2) {
for (my $i = $channels-1; $i >= 0; $i--) {
my $dsti = $i + $channels;
print <<EOF;
dst[$dsti] = ($fctype) ((sample${i} + last_sample${i}) $interp);
EOF
}
for (my $i = $channels-1; $i >= 0; $i--) {
my $dsti = $i;
print <<EOF;
dst[$dsti] = ($fctype) sample${i};
EOF
}
} elsif ($multiple == 4) {
for (my $i = $channels-1; $i >= 0; $i--) {
my $dsti = $i + ($channels * 3);
print <<EOF;
dst[$dsti] = ($fctype) ((sample${i} + ($mult3 * last_sample${i})) $interp2);
EOF
}
for (my $i = $channels-1; $i >= 0; $i--) {
my $dsti = $i + ($channels * 2);
print <<EOF;
dst[$dsti] = ($fctype) ((sample${i} + last_sample${i}) $interp);
EOF
}
for (my $i = $channels-1; $i >= 0; $i--) {
my $dsti = $i + ($channels * 1);
print <<EOF;
dst[$dsti] = ($fctype) ((($mult3 * sample${i}) + last_sample${i}) $interp2);
EOF
}
for (my $i = $channels-1; $i >= 0; $i--) {
my $dsti = $i + ($channels * 0);
print <<EOF;
dst[$dsti] = ($fctype) sample${i};
EOF
}
} else {
die('bug in program.'); # we only handle x2 and x4.
}
} else { # downsample.
if ($multiple == 2) {
for (my $i = 0; $i < $channels; $i++) {
print <<EOF;
dst[$i] = ($fctype) ((sample${i} + last_sample${i}) $interp);
EOF
}
} elsif ($multiple == 4) {
# !!! FIXME: interpolate all 4 samples?
for (my $i = 0; $i < $channels; $i++) {
print <<EOF;
dst[$i] = ($fctype) ((sample${i} + last_sample${i}) $interp);
EOF
}
} else {
die('bug in program.'); # we only handle x2 and x4.
}
}
for (my $i = 0; $i < $channels; $i++) {
my $idx = ($upsample) ? (($channels - $i) - 1) : $i;
print <<EOF;
last_sample${idx} = sample${idx};
EOF
}
if ($upsample) {
my $amount = $channels * $multiple;
$incr = "dst -= $amount"; # can't ever be 1, so no "--" version.
} else {
$incr = ($channels == 1) ? 'dst++' : "dst += $channels";
}
print <<EOF;
$incr;
}
cvt->len_cvt = dstsize;
if (cvt->filters[++cvt->filter_index]) {
cvt->filters[cvt->filter_index] (cvt, format);
}
}
EOF
}
sub buildResamplers {
print "#if !NO_RESAMPLERS\n\n";
foreach (@audiotypes) {
my $from = $_;
foreach (@channels) {
my $channel = $_;
buildArbitraryResampleFunc($from, $channel, 1);
buildArbitraryResampleFunc($from, $channel, 0);
}
}
print "\n#if !LESS_RESAMPLERS\n\n";
foreach (@audiotypes) {
my $from = $_;
foreach (@channels) {
my $channel = $_;
for (my $multiple = 2; $multiple <= 4; $multiple += 2) {
buildMultipleResampleFunc($from, $channel, 1, $multiple);
buildMultipleResampleFunc($from, $channel, 0, $multiple);
}
}
}
print "#endif /* !LESS_RESAMPLERS */\n";
print "#endif /* !NO_RESAMPLERS */\n\n\n";
print "const SDL_AudioRateFilters sdl_audio_rate_filters[] =\n{\n";
print "#if !NO_RESAMPLERS\n";
foreach (@audiotypes) {
my $from = $_;
foreach (@channels) {
my $channel = $_;
for (my $upsample = 0; $upsample <= 1; $upsample++) {
my $hashid = getResamplerHashId($from, $channel, $upsample, 0);
my $sym = $funcs{$hashid};
print(" { AUDIO_$from, $channel, $upsample, 0, $sym },\n");
}
}
}
print "#if !LESS_RESAMPLERS\n";
foreach (@audiotypes) {
my $from = $_;
foreach (@channels) {
my $channel = $_;
for (my $multiple = 2; $multiple <= 4; $multiple += 2) {
for (my $upsample = 0; $upsample <= 1; $upsample++) {
my $hashid = getResamplerHashId($from, $channel, $upsample, $multiple);
my $sym = $funcs{$hashid};
print(" { AUDIO_$from, $channel, $upsample, $multiple, $sym },\n");
}
}
}
}
print "#endif /* !LESS_RESAMPLERS */\n";
print "#endif /* !NO_RESAMPLERS */\n";
print(" { 0, 0, 0, 0, NULL }\n");
print "};\n\n";
}
# mainline ...
outputHeader();
buildTypeConverters();
buildResamplers();
outputFooter();
exit 0;
# end of sdlgenaudiocvt.pl ...

View File

@@ -0,0 +1,327 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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 <unistd.h>
#include "SDL_audio.h"
#include "../SDL_audiomem.h"
#include "../SDL_audio_c.h"
#include "SDL_sndioaudio.h"
#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
#include "SDL_loadso.h"
#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 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_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 ) {
this->enabled = 0;
}
#ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
#endif
}
static Uint8 *
SNDIO_GetDeviceBuf(_THIS)
{
return this->hidden->mixbuf;
}
static void
SNDIO_WaitDone(_THIS)
{
SNDIO_sio_stop(this->hidden->dev);
}
static void
SNDIO_CloseDevice(_THIS)
{
if (this->hidden != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
if ( this->hidden->dev != NULL ) {
SNDIO_sio_close(this->hidden->dev);
this->hidden->dev = NULL;
}
SDL_free(this->hidden);
this->hidden = NULL;
}
}
static int
SNDIO_OpenDevice(_THIS, 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_memset(this->hidden, 0, sizeof(*this->hidden));
this->hidden->mixlen = this->spec.size;
/* !!! FIXME: SIO_DEVANY can be a specific device... */
if ((this->hidden->dev = SNDIO_sio_open(NULL, SIO_PLAY, 0)) == NULL) {
SNDIO_CloseDevice(this);
return SDL_SetError("sio_open() failed");
}
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) == 1) {
status = 0;
break;
}
}
test_format = SDL_NextAudioFormat();
}
if (status < 0) {
SNDIO_CloseDevice(this);
return SDL_SetError("sndio: Couldn't find any hardware audio formats");
}
if (SNDIO_sio_getpar(this->hidden->dev, &par) == 0) {
SNDIO_CloseDevice(this);
return SDL_SetError("sio_getpar() failed");
}
if ((par.bits == 32) && (par.sig) && (par.le))
this->spec.format = AUDIO_S32LSB;
else if ((par.bits == 32) && (par.sig) && (!par.le))
this->spec.format = AUDIO_S32MSB;
else if ((par.bits == 16) && (par.sig) && (par.le))
this->spec.format = AUDIO_S16LSB;
else if ((par.bits == 16) && (par.sig) && (!par.le))
this->spec.format = AUDIO_S16MSB;
else if ((par.bits == 16) && (!par.sig) && (par.le))
this->spec.format = AUDIO_U16LSB;
else if ((par.bits == 16) && (!par.sig) && (!par.le))
this->spec.format = AUDIO_U16MSB;
else if ((par.bits == 8) && (par.sig))
this->spec.format = AUDIO_S8;
else if ((par.bits == 8) && (!par.sig))
this->spec.format = AUDIO_U8;
else {
SNDIO_CloseDevice(this);
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_AllocAudioMem(this->hidden->mixlen);
if (this->hidden->mixbuf == NULL) {
SNDIO_CloseDevice(this);
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->WaitDone = SNDIO_WaitDone;
impl->CloseDevice = SNDIO_CloseDevice;
impl->Deinitialize = SNDIO_Deinitialize;
impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: sndio can handle multiple devices. */
return 1; /* this audio target is available. */
}
AudioBootStrap SNDIO_bootstrap = {
"sndio", "OpenBSD sndio", SNDIO_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_SNDIO */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,45 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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 <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;
};
#endif /* _SDL_sndioaudio_h */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,426 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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 "../SDL_audiomem.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(int iscapture, SDL_AddAudioDevice addfn)
{
SDL_EnumUnixAudioDevices(iscapture, 1, (int (*)(int fd)) NULL, addfn);
}
#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
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(this->hidden->audio_fd, &fdset);
select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, NULL);
#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 */
this->enabled = 0;
}
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 */
this->enabled = 0;
}
this->hidden->written += this->hidden->fragsize;
}
}
static Uint8 *
SUNAUDIO_GetDeviceBuf(_THIS)
{
return (this->hidden->mixbuf);
}
static void
SUNAUDIO_CloseDevice(_THIS)
{
if (this->hidden != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
SDL_free(this->hidden->ulaw_buf);
this->hidden->ulaw_buf = NULL;
if (this->hidden->audio_fd >= 0) {
close(this->hidden->audio_fd);
this->hidden->audio_fd = -1;
}
SDL_free(this->hidden);
this->hidden = NULL;
}
}
static int
SUNAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
{
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_memset(this->hidden, 0, (sizeof *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));
}
#ifdef AUDIO_SETINFO
int enc;
#endif
int 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_AllocAudioMem(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;
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: */

View File

@@ -0,0 +1,47 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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: */

400
src/audio/winmm/SDL_winmm.c Normal file
View File

@@ -0,0 +1,400 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#if SDL_AUDIO_DRIVER_WINMM
/* Allow access to a raw mixing buffer */
#include "../../core/windows/SDL_windows.h"
#include <mmsystem.h>
#include "SDL_timer.h"
#include "SDL_audio.h"
#include "../SDL_audio_c.h"
#include "SDL_winmm.h"
#ifndef WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif
#define DETECT_DEV_IMPL(typ, capstyp) \
static void DetectWave##typ##Devs(SDL_AddAudioDevice addfn) { \
const UINT devcount = wave##typ##GetNumDevs(); \
capstyp caps; \
UINT i; \
for (i = 0; i < devcount; i++) { \
if (wave##typ##GetDevCaps(i,&caps,sizeof(caps))==MMSYSERR_NOERROR) { \
char *name = WIN_StringToUTF8(caps.szPname); \
if (name != NULL) { \
addfn(name); \
SDL_free(name); \
} \
} \
} \
}
DETECT_DEV_IMPL(Out, WAVEOUTCAPS)
DETECT_DEV_IMPL(In, WAVEINCAPS)
static void
WINMM_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
{
if (iscapture) {
DetectWaveInDevs(addfn);
} else {
DetectWaveOutDevs(addfn);
}
}
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 void
WINMM_WaitDone(_THIS)
{
int i, left;
do {
left = NUM_BUFFERS;
for (i = 0; i < NUM_BUFFERS; ++i) {
if (this->hidden->wavebuf[i].dwFlags & WHDR_DONE) {
--left;
}
}
if (left > 0) {
SDL_Delay(100);
}
} while (left > 0);
}
static void
WINMM_CloseDevice(_THIS)
{
/* Close up audio */
if (this->hidden != NULL) {
int i;
if (this->hidden->audio_sem) {
CloseHandle(this->hidden->audio_sem);
this->hidden->audio_sem = 0;
}
/* 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]));
this->hidden->wavebuf[i].dwUser = 0xFFFF;
}
}
/* Free raw mixing buffer */
SDL_free(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
if (this->hidden->hin) {
waveInClose(this->hidden->hin);
this->hidden->hin = 0;
}
if (this->hidden->hout) {
waveOutClose(this->hidden->hout);
this->hidden->hout = 0;
}
SDL_free(this->hidden);
this->hidden = NULL;
}
}
static SDL_bool
PrepWaveFormat(_THIS, UINT devId, WAVEFORMATEX *pfmt, const int iscapture)
{
SDL_zerop(pfmt);
if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
pfmt->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
} else {
pfmt->wFormatTag = WAVE_FORMAT_PCM;
}
pfmt->wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
pfmt->nChannels = this->spec.channels;
pfmt->nSamplesPerSec = this->spec.freq;
pfmt->nBlockAlign = pfmt->nChannels * (pfmt->wBitsPerSample / 8);
pfmt->nAvgBytesPerSec = pfmt->nSamplesPerSec * pfmt->nBlockAlign;
if (iscapture) {
return (waveInOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
} else {
return (waveOutOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
}
}
static int
WINMM_OpenDevice(_THIS, const char *devname, 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 */
char *utf8 = NULL;
UINT i;
if (devname != NULL) { /* specific device requested? */
if (iscapture) {
const UINT devcount = waveInGetNumDevs();
WAVEINCAPS caps;
for (i = 0; (i < devcount) && (devId == WAVE_MAPPER); i++) {
result = waveInGetDevCaps(i, &caps, sizeof (caps));
if (result != MMSYSERR_NOERROR)
continue;
else if ((utf8 = WIN_StringToUTF8(caps.szPname)) == NULL)
continue;
else if (SDL_strcmp(devname, utf8) == 0)
devId = i;
SDL_free(utf8);
}
} else {
const UINT devcount = waveOutGetNumDevs();
WAVEOUTCAPS caps;
for (i = 0; (i < devcount) && (devId == WAVE_MAPPER); i++) {
result = waveOutGetDevCaps(i, &caps, sizeof (caps));
if (result != MMSYSERR_NOERROR)
continue;
else if ((utf8 = WIN_StringToUTF8(caps.szPname)) == NULL)
continue;
else if (SDL_strcmp(devname, utf8) == 0)
devId = i;
SDL_free(utf8);
}
}
if (devId == WAVE_MAPPER) {
return SDL_SetError("Requested device not found");
}
}
/* 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_memset(this->hidden, 0, (sizeof *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? */
/* Check the buffer size -- minimum of 1/4 second (word aligned) */
if (this->spec.samples < (this->spec.freq / 4))
this->spec.samples = ((this->spec.freq / 4) + 3) & ~3;
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) {
WINMM_CloseDevice(this);
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);
} else {
result = waveOutOpen(&this->hidden->hout, devId, &waveformat,
(DWORD_PTR) FillSound, (DWORD_PTR) this,
CALLBACK_FUNCTION);
}
if (result != MMSYSERR_NOERROR) {
WINMM_CloseDevice(this);
return SetMMerror("waveOutOpen()", result);
}
#ifdef SOUND_DEBUG
/* Check the sound device we retrieved */
{
WAVEOUTCAPS caps;
result = waveOutGetDevCaps((UINT) this->hidden->hout,
&caps, sizeof(caps));
if (result != MMSYSERR_NOERROR) {
WINMM_CloseDevice(this);
return SetMMerror("waveOutGetDevCaps()", result);
}
printf("Audio device: %s\n", caps.szPname);
}
#endif
/* Create the audio buffer semaphore */
this->hidden->audio_sem =
CreateSemaphore(NULL, NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
if (this->hidden->audio_sem == NULL) {
WINMM_CloseDevice(this);
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) {
WINMM_CloseDevice(this);
return SDL_OutOfMemory();
}
for (i = 0; i < NUM_BUFFERS; ++i) {
SDL_memset(&this->hidden->wavebuf[i], 0,
sizeof(this->hidden->wavebuf[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];
result = waveOutPrepareHeader(this->hidden->hout,
&this->hidden->wavebuf[i],
sizeof(this->hidden->wavebuf[i]));
if (result != MMSYSERR_NOERROR) {
WINMM_CloseDevice(this);
return SetMMerror("waveOutPrepareHeader()", 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->WaitDone = WINMM_WaitDone;
impl->GetDeviceBuf = WINMM_GetDeviceBuf;
impl->CloseDevice = WINMM_CloseDevice;
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: */

View File

@@ -0,0 +1,45 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as 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: */

View File

@@ -0,0 +1,542 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/* WinRT NOTICE:
A few changes to SDL's XAudio2 backend were warranted by API
changes to Windows. Many, but not all of these are documented by Microsoft
at:
http://blogs.msdn.com/b/chuckw/archive/2012/04/02/xaudio2-and-windows-8-consumer-preview.aspx
1. Windows' thread synchronization function, CreateSemaphore, was removed
from WinRT. SDL's semaphore API was substituted instead.
2. The method calls, IXAudio2::GetDeviceCount and IXAudio2::GetDeviceDetails
were removed from the XAudio2 API. Microsoft is telling developers to
use APIs in Windows::Foundation instead.
For SDL, the missing methods were reimplemented using the APIs Microsoft
said to use.
3. CoInitialize and CoUninitialize are not available in WinRT.
These calls were removed, as COM will have been initialized earlier,
at least by the call to the WinRT app's main function
(aka 'int main(Platform::Array<Platform::String^>^)). (DLudwig:
This was my understanding of how WinRT: the 'main' function uses
a tag of [MTAThread], which should initialize COM. My understanding
of COM is somewhat limited, and I may be incorrect here.)
4. IXAudio2::CreateMasteringVoice changed its integer-based 'DeviceIndex'
argument to a string-based one, 'szDeviceId'. In WinRT, the
string-based argument will be used.
*/
#include "../../SDL_internal.h"
#if SDL_AUDIO_DRIVER_XAUDIO2
#include "../../core/windows/SDL_windows.h"
#include "SDL_audio.h"
#include "../SDL_audio_c.h"
#include "../SDL_sysaudio.h"
#include "SDL_assert.h"
#ifdef __GNUC__
/* The configure script already did any necessary checking */
# define SDL_XAUDIO2_HAS_SDK 1
#elif defined(__WINRT__)
/* WinRT always has access to the .the XAudio 2 SDK */
# define SDL_XAUDIO2_HAS_SDK
#else
/* XAudio2 exists as of the March 2008 DirectX SDK
The XAudio2 implementation available in the Windows 8 SDK targets Windows 8 and newer.
If you want to build SDL with XAudio2 support you should install the DirectX SDK.
*/
#include <dxsdkver.h>
#if (!defined(_DXSDK_BUILD_MAJOR) || (_DXSDK_BUILD_MAJOR < 1284))
# pragma message("Your DirectX SDK is too old. Disabling XAudio2 support.")
#else
# define SDL_XAUDIO2_HAS_SDK 1
#endif
#endif
#ifdef SDL_XAUDIO2_HAS_SDK
/* Check to see if we're compiling for XAudio 2.8, or higher. */
#ifdef WINVER
#if WINVER >= 0x0602 /* Windows 8 SDK or higher? */
#define SDL_XAUDIO2_WIN8 1
#endif
#endif
/* The XAudio header file, when #include'd on WinRT, will only compile in C++
files, but not C. A few preprocessor-based hacks are defined below in order
to get xaudio2.h to compile in the C/non-C++ file, SDL_xaudio2.c.
*/
#ifdef __WINRT__
#define uuid(x)
#define DX_BUILD
#endif
#define INITGUID 1
#include <xaudio2.h>
/* Hidden "this" pointer for the audio functions */
#define _THIS SDL_AudioDevice *this
#ifdef __WINRT__
#include "SDL_xaudio2_winrthelpers.h"
#endif
/* Fixes bug 1210 where some versions of gcc need named parameters */
#ifdef __GNUC__
#ifdef THIS
#undef THIS
#endif
#define THIS INTERFACE *p
#ifdef THIS_
#undef THIS_
#endif
#define THIS_ INTERFACE *p,
#endif
struct SDL_PrivateAudioData
{
IXAudio2 *ixa2;
IXAudio2SourceVoice *source;
IXAudio2MasteringVoice *mastering;
SDL_sem * semaphore;
Uint8 *mixbuf;
int mixlen;
Uint8 *nextbuf;
};
static void
XAUDIO2_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
{
IXAudio2 *ixa2 = NULL;
UINT32 devcount = 0;
UINT32 i = 0;
if (iscapture) {
SDL_SetError("XAudio2: capture devices unsupported.");
return;
} else if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) {
SDL_SetError("XAudio2: XAudio2Create() failed at detection.");
return;
} else if (IXAudio2_GetDeviceCount(ixa2, &devcount) != S_OK) {
SDL_SetError("XAudio2: IXAudio2::GetDeviceCount() failed.");
IXAudio2_Release(ixa2);
return;
}
for (i = 0; i < devcount; i++) {
XAUDIO2_DEVICE_DETAILS details;
if (IXAudio2_GetDeviceDetails(ixa2, i, &details) == S_OK) {
char *str = WIN_StringToUTF8(details.DisplayName);
if (str != NULL) {
addfn(str);
SDL_free(str); /* addfn() made a copy of the string. */
}
}
}
IXAudio2_Release(ixa2);
}
static void STDMETHODCALLTYPE
VoiceCBOnBufferEnd(THIS_ void *data)
{
/* Just signal the SDL audio thread and get out of XAudio2's way. */
SDL_AudioDevice *this = (SDL_AudioDevice *) data;
SDL_SemPost(this->hidden->semaphore);
}
static void STDMETHODCALLTYPE
VoiceCBOnVoiceError(THIS_ void *data, HRESULT Error)
{
/* !!! FIXME: attempt to recover, or mark device disconnected. */
SDL_assert(0 && "write me!");
}
/* no-op callbacks... */
static void STDMETHODCALLTYPE VoiceCBOnStreamEnd(THIS) {}
static void STDMETHODCALLTYPE VoiceCBOnVoiceProcessPassStart(THIS_ UINT32 b) {}
static void STDMETHODCALLTYPE VoiceCBOnVoiceProcessPassEnd(THIS) {}
static void STDMETHODCALLTYPE VoiceCBOnBufferStart(THIS_ void *data) {}
static void STDMETHODCALLTYPE VoiceCBOnLoopEnd(THIS_ void *data) {}
static Uint8 *
XAUDIO2_GetDeviceBuf(_THIS)
{
return this->hidden->nextbuf;
}
static void
XAUDIO2_PlayDevice(_THIS)
{
XAUDIO2_BUFFER buffer;
Uint8 *mixbuf = this->hidden->mixbuf;
Uint8 *nextbuf = this->hidden->nextbuf;
const int mixlen = this->hidden->mixlen;
IXAudio2SourceVoice *source = this->hidden->source;
HRESULT result = S_OK;
if (!this->enabled) { /* shutting down? */
return;
}
/* Submit the next filled buffer */
SDL_zero(buffer);
buffer.AudioBytes = mixlen;
buffer.pAudioData = nextbuf;
buffer.pContext = this;
if (nextbuf == mixbuf) {
nextbuf += mixlen;
} else {
nextbuf = mixbuf;
}
this->hidden->nextbuf = nextbuf;
result = IXAudio2SourceVoice_SubmitSourceBuffer(source, &buffer, NULL);
if (result == XAUDIO2_E_DEVICE_INVALIDATED) {
/* !!! FIXME: possibly disconnected or temporary lost. Recover? */
}
if (result != S_OK) { /* uhoh, panic! */
IXAudio2SourceVoice_FlushSourceBuffers(source);
this->enabled = 0;
}
}
static void
XAUDIO2_WaitDevice(_THIS)
{
if (this->enabled) {
SDL_SemWait(this->hidden->semaphore);
}
}
static void
XAUDIO2_WaitDone(_THIS)
{
IXAudio2SourceVoice *source = this->hidden->source;
XAUDIO2_VOICE_STATE state;
SDL_assert(!this->enabled); /* flag that stops playing. */
IXAudio2SourceVoice_Discontinuity(source);
#if SDL_XAUDIO2_WIN8
IXAudio2SourceVoice_GetState(source, &state, 0);
#else
IXAudio2SourceVoice_GetState(source, &state);
#endif
while (state.BuffersQueued > 0) {
SDL_SemWait(this->hidden->semaphore);
#if SDL_XAUDIO2_WIN8
IXAudio2SourceVoice_GetState(source, &state, 0);
#else
IXAudio2SourceVoice_GetState(source, &state);
#endif
}
}
static void
XAUDIO2_CloseDevice(_THIS)
{
if (this->hidden != NULL) {
IXAudio2 *ixa2 = this->hidden->ixa2;
IXAudio2SourceVoice *source = this->hidden->source;
IXAudio2MasteringVoice *mastering = this->hidden->mastering;
if (source != NULL) {
IXAudio2SourceVoice_Stop(source, 0, XAUDIO2_COMMIT_NOW);
IXAudio2SourceVoice_FlushSourceBuffers(source);
IXAudio2SourceVoice_DestroyVoice(source);
}
if (ixa2 != NULL) {
IXAudio2_StopEngine(ixa2);
}
if (mastering != NULL) {
IXAudio2MasteringVoice_DestroyVoice(mastering);
}
if (ixa2 != NULL) {
IXAudio2_Release(ixa2);
}
SDL_free(this->hidden->mixbuf);
if (this->hidden->semaphore != NULL) {
SDL_DestroySemaphore(this->hidden->semaphore);
}
SDL_free(this->hidden);
this->hidden = NULL;
}
}
static int
XAUDIO2_OpenDevice(_THIS, const char *devname, int iscapture)
{
HRESULT result = S_OK;
WAVEFORMATEX waveformat;
int valid_format = 0;
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
IXAudio2 *ixa2 = NULL;
IXAudio2SourceVoice *source = NULL;
#if defined(SDL_XAUDIO2_WIN8)
LPCWSTR devId = NULL;
#else
UINT32 devId = 0; /* 0 == system default device. */
#endif
static IXAudio2VoiceCallbackVtbl callbacks_vtable = {
VoiceCBOnVoiceProcessPassStart,
VoiceCBOnVoiceProcessPassEnd,
VoiceCBOnStreamEnd,
VoiceCBOnBufferStart,
VoiceCBOnBufferEnd,
VoiceCBOnLoopEnd,
VoiceCBOnVoiceError
};
static IXAudio2VoiceCallback callbacks = { &callbacks_vtable };
if (iscapture) {
return SDL_SetError("XAudio2: capture devices unsupported.");
} else if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) {
return SDL_SetError("XAudio2: XAudio2Create() failed at open.");
}
/*
XAUDIO2_DEBUG_CONFIGURATION debugConfig;
debugConfig.TraceMask = XAUDIO2_LOG_ERRORS; //XAUDIO2_LOG_WARNINGS | XAUDIO2_LOG_DETAIL | XAUDIO2_LOG_FUNC_CALLS | XAUDIO2_LOG_TIMING | XAUDIO2_LOG_LOCKS | XAUDIO2_LOG_MEMORY | XAUDIO2_LOG_STREAMING;
debugConfig.BreakMask = XAUDIO2_LOG_ERRORS; //XAUDIO2_LOG_WARNINGS;
debugConfig.LogThreadID = TRUE;
debugConfig.LogFileline = TRUE;
debugConfig.LogFunctionName = TRUE;
debugConfig.LogTiming = TRUE;
ixa2->SetDebugConfiguration(&debugConfig);
*/
#if ! defined(__WINRT__)
if (devname != NULL) {
UINT32 devcount = 0;
UINT32 i = 0;
if (IXAudio2_GetDeviceCount(ixa2, &devcount) != S_OK) {
IXAudio2_Release(ixa2);
return SDL_SetError("XAudio2: IXAudio2_GetDeviceCount() failed.");
}
for (i = 0; i < devcount; i++) {
XAUDIO2_DEVICE_DETAILS details;
if (IXAudio2_GetDeviceDetails(ixa2, i, &details) == S_OK) {
char *str = WIN_StringToUTF8(details.DisplayName);
if (str != NULL) {
const int match = (SDL_strcmp(str, devname) == 0);
SDL_free(str);
if (match) {
devId = i;
break;
}
}
}
}
if (i == devcount) {
IXAudio2_Release(ixa2);
return SDL_SetError("XAudio2: Requested device not found.");
}
}
#endif
/* Initialize all variables that we clean on shutdown */
this->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc((sizeof *this->hidden));
if (this->hidden == NULL) {
IXAudio2_Release(ixa2);
return SDL_OutOfMemory();
}
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
this->hidden->ixa2 = ixa2;
this->hidden->semaphore = SDL_CreateSemaphore(1);
if (this->hidden->semaphore == NULL) {
XAUDIO2_CloseDevice(this);
return SDL_SetError("XAudio2: CreateSemaphore() failed!");
}
while ((!valid_format) && (test_format)) {
switch (test_format) {
case AUDIO_U8:
case AUDIO_S16:
case AUDIO_S32:
case AUDIO_F32:
this->spec.format = test_format;
valid_format = 1;
break;
}
test_format = SDL_NextAudioFormat();
}
if (!valid_format) {
XAUDIO2_CloseDevice(this);
return SDL_SetError("XAudio2: Unsupported audio format");
}
/* Update the fragment size as size in bytes */
SDL_CalculateAudioSpec(&this->spec);
/* We feed a Source, it feeds the Mastering, which feeds the device. */
this->hidden->mixlen = this->spec.size;
this->hidden->mixbuf = (Uint8 *) SDL_malloc(2 * this->hidden->mixlen);
if (this->hidden->mixbuf == NULL) {
XAUDIO2_CloseDevice(this);
return SDL_OutOfMemory();
}
this->hidden->nextbuf = this->hidden->mixbuf;
SDL_memset(this->hidden->mixbuf, 0, 2 * this->hidden->mixlen);
/* We use XAUDIO2_DEFAULT_CHANNELS instead of this->spec.channels. On
Xbox360, this means 5.1 output, but on Windows, it means "figure out
what the system has." It might be preferable to let XAudio2 blast
stereo output to appropriate surround sound configurations
instead of clamping to 2 channels, even though we'll configure the
Source Voice for whatever number of channels you supply. */
#if SDL_XAUDIO2_WIN8
result = IXAudio2_CreateMasteringVoice(ixa2, &this->hidden->mastering,
XAUDIO2_DEFAULT_CHANNELS,
this->spec.freq, 0, devId, NULL, AudioCategory_GameEffects);
#else
result = IXAudio2_CreateMasteringVoice(ixa2, &this->hidden->mastering,
XAUDIO2_DEFAULT_CHANNELS,
this->spec.freq, 0, devId, NULL);
#endif
if (result != S_OK) {
XAUDIO2_CloseDevice(this);
return SDL_SetError("XAudio2: Couldn't create mastering voice");
}
SDL_zero(waveformat);
if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
waveformat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
} else {
waveformat.wFormatTag = WAVE_FORMAT_PCM;
}
waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
waveformat.nChannels = this->spec.channels;
waveformat.nSamplesPerSec = this->spec.freq;
waveformat.nBlockAlign =
waveformat.nChannels * (waveformat.wBitsPerSample / 8);
waveformat.nAvgBytesPerSec =
waveformat.nSamplesPerSec * waveformat.nBlockAlign;
waveformat.cbSize = sizeof(waveformat);
#ifdef __WINRT__
// DLudwig: for now, make XAudio2 do sample rate conversion, just to
// get the loopwave test to work.
//
// TODO, WinRT: consider removing WinRT-specific source-voice creation code from SDL_xaudio2.c
result = IXAudio2_CreateSourceVoice(ixa2, &source, &waveformat,
0,
1.0f, &callbacks, NULL, NULL);
#else
result = IXAudio2_CreateSourceVoice(ixa2, &source, &waveformat,
XAUDIO2_VOICE_NOSRC |
XAUDIO2_VOICE_NOPITCH,
1.0f, &callbacks, NULL, NULL);
#endif
if (result != S_OK) {
XAUDIO2_CloseDevice(this);
return SDL_SetError("XAudio2: Couldn't create source voice");
}
this->hidden->source = source;
/* Start everything playing! */
result = IXAudio2_StartEngine(ixa2);
if (result != S_OK) {
XAUDIO2_CloseDevice(this);
return SDL_SetError("XAudio2: Couldn't start engine");
}
result = IXAudio2SourceVoice_Start(source, 0, XAUDIO2_COMMIT_NOW);
if (result != S_OK) {
XAUDIO2_CloseDevice(this);
return SDL_SetError("XAudio2: Couldn't start source voice");
}
return 0; /* good to go. */
}
static void
XAUDIO2_Deinitialize(void)
{
#if defined(__WIN32__)
WIN_CoUninitialize();
#endif
}
#endif /* SDL_XAUDIO2_HAS_SDK */
static int
XAUDIO2_Init(SDL_AudioDriverImpl * impl)
{
#ifndef SDL_XAUDIO2_HAS_SDK
SDL_SetError("XAudio2: SDL was built without XAudio2 support (old DirectX SDK).");
return 0; /* no XAudio2 support, ever. Update your SDK! */
#else
/* XAudio2Create() is a macro that uses COM; we don't load the .dll */
IXAudio2 *ixa2 = NULL;
#if defined(__WIN32__)
// TODO, WinRT: Investigate using CoInitializeEx here
if (FAILED(WIN_CoInitialize())) {
SDL_SetError("XAudio2: CoInitialize() failed");
return 0;
}
#endif
if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) {
#if defined(__WIN32__)
WIN_CoUninitialize();
#endif
SDL_SetError("XAudio2: XAudio2Create() failed at initialization");
return 0; /* not available. */
}
IXAudio2_Release(ixa2);
/* Set the function pointers */
impl->DetectDevices = XAUDIO2_DetectDevices;
impl->OpenDevice = XAUDIO2_OpenDevice;
impl->PlayDevice = XAUDIO2_PlayDevice;
impl->WaitDevice = XAUDIO2_WaitDevice;
impl->WaitDone = XAUDIO2_WaitDone;
impl->GetDeviceBuf = XAUDIO2_GetDeviceBuf;
impl->CloseDevice = XAUDIO2_CloseDevice;
impl->Deinitialize = XAUDIO2_Deinitialize;
return 1; /* this audio target is available. */
#endif
}
AudioBootStrap XAUDIO2_bootstrap = {
"xaudio2", "XAudio2", XAUDIO2_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_XAUDIO2 */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -0,0 +1,90 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#include <xaudio2.h>
#include "SDL_xaudio2_winrthelpers.h"
#if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
using Windows::Devices::Enumeration::DeviceClass;
using Windows::Devices::Enumeration::DeviceInformation;
using Windows::Devices::Enumeration::DeviceInformationCollection;
#endif
extern "C" HRESULT __cdecl IXAudio2_GetDeviceCount(IXAudio2 * ixa2, UINT32 * devcount)
{
#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
// There doesn't seem to be any audio device enumeration on Windows Phone.
// In lieu of this, just treat things as if there is one and only one
// audio device.
*devcount = 1;
return S_OK;
#else
// TODO, WinRT: make xaudio2 device enumeration only happen once, and in the background
auto operation = DeviceInformation::FindAllAsync(DeviceClass::AudioRender);
while (operation->Status != Windows::Foundation::AsyncStatus::Completed)
{
}
DeviceInformationCollection^ devices = operation->GetResults();
*devcount = devices->Size;
return S_OK;
#endif
}
extern "C" HRESULT IXAudio2_GetDeviceDetails(IXAudio2 * unused, UINT32 index, XAUDIO2_DEVICE_DETAILS * details)
{
#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
// Windows Phone doesn't seem to have the same device enumeration APIs that
// Windows 8/RT has, or it doesn't have them at all. In lieu of this,
// just treat things as if there is one, and only one, default device.
if (index != 0)
{
return XAUDIO2_E_INVALID_CALL;
}
if (details)
{
wcsncpy_s(details->DeviceID, ARRAYSIZE(details->DeviceID), L"default", _TRUNCATE);
wcsncpy_s(details->DisplayName, ARRAYSIZE(details->DisplayName), L"default", _TRUNCATE);
}
return S_OK;
#else
auto operation = DeviceInformation::FindAllAsync(DeviceClass::AudioRender);
while (operation->Status != Windows::Foundation::AsyncStatus::Completed)
{
}
DeviceInformationCollection^ devices = operation->GetResults();
if (index >= devices->Size)
{
return XAUDIO2_E_INVALID_CALL;
}
DeviceInformation^ d = devices->GetAt(index);
if (details)
{
wcsncpy_s(details->DeviceID, ARRAYSIZE(details->DeviceID), d->Id->Data(), _TRUNCATE);
wcsncpy_s(details->DisplayName, ARRAYSIZE(details->DisplayName), d->Name->Data(), _TRUNCATE);
}
return S_OK;
#endif
}

View File

@@ -0,0 +1,70 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
//
// Re-implementation of methods removed from XAudio2 (in WinRT):
//
typedef struct XAUDIO2_DEVICE_DETAILS
{
WCHAR DeviceID[256];
WCHAR DisplayName[256];
/* Other fields exist in the pre-Windows 8 version of this struct, however
they weren't used by SDL, so they weren't added.
*/
} XAUDIO2_DEVICE_DETAILS;
#ifdef __cplusplus
extern "C" {
#endif
HRESULT IXAudio2_GetDeviceCount(IXAudio2 * unused, UINT32 * devcount);
HRESULT IXAudio2_GetDeviceDetails(IXAudio2 * unused, UINT32 index, XAUDIO2_DEVICE_DETAILS * details);
#ifdef __cplusplus
}
#endif
//
// C-style macros to call XAudio2's methods in C++:
//
#ifdef __cplusplus
/*
#define IXAudio2_CreateMasteringVoice(A, B, C, D, E, F, G) (A)->CreateMasteringVoice((B), (C), (D), (E), (F), (G))
#define IXAudio2_CreateSourceVoice(A, B, C, D, E, F, G, H) (A)->CreateSourceVoice((B), (C), (D), (E), (F), (G), (H))
#define IXAudio2_QueryInterface(A, B, C) (A)->QueryInterface((B), (C))
#define IXAudio2_Release(A) (A)->Release()
#define IXAudio2_StartEngine(A) (A)->StartEngine()
#define IXAudio2_StopEngine(A) (A)->StopEngine()
#define IXAudio2MasteringVoice_DestroyVoice(A) (A)->DestroyVoice()
#define IXAudio2SourceVoice_DestroyVoice(A) (A)->DestroyVoice()
#define IXAudio2SourceVoice_Discontinuity(A) (A)->Discontinuity()
#define IXAudio2SourceVoice_FlushSourceBuffers(A) (A)->FlushSourceBuffers()
#define IXAudio2SourceVoice_GetState(A, B) (A)->GetState((B))
#define IXAudio2SourceVoice_Start(A, B, C) (A)->Start((B), (C))
#define IXAudio2SourceVoice_Stop(A, B, C) (A)->Stop((B), (C))
#define IXAudio2SourceVoice_SubmitSourceBuffer(A, B, C) (A)->SubmitSourceBuffer((B), (C))
*/
#endif // ifdef __cplusplus