Experimental Xcode builds (dependencies included)

This commit is contained in:
Struma 2020-11-19 03:56:35 -05:00 committed by zzoro
parent 94c031b095
commit 7e21066a70
52 changed files with 24529 additions and 83 deletions

2
.gitignore vendored
View file

@ -5,8 +5,6 @@
*.frag.xxd
*.ttf.xxd
Makefile
mkxp
xxd+

795
SDL2_sound/SDL_sound.c Normal file
View file

@ -0,0 +1,795 @@
/**
* SDL_sound; An abstract sound format decoding API.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
/**
* This file implements the core API, which is relatively simple.
* The real meat of SDL_sound is in the decoders.
*
* Documentation is in SDL_sound.h ... It's verbose, honest. :)
*/
#define __SDL_SOUND_INTERNAL__
#include "SDL_sound_internal.h"
/* The various decoder drivers... */
/* All these externs may be missing; we check SOUND_SUPPORTS_xxx before use. */
extern const Sound_DecoderFunctions __Sound_DecoderFunctions_MODPLUG;
extern const Sound_DecoderFunctions __Sound_DecoderFunctions_MP3;
extern const Sound_DecoderFunctions __Sound_DecoderFunctions_WAV;
extern const Sound_DecoderFunctions __Sound_DecoderFunctions_AIFF;
extern const Sound_DecoderFunctions __Sound_DecoderFunctions_AU;
extern const Sound_DecoderFunctions __Sound_DecoderFunctions_VORBIS;
extern const Sound_DecoderFunctions __Sound_DecoderFunctions_VOC;
extern const Sound_DecoderFunctions __Sound_DecoderFunctions_RAW;
extern const Sound_DecoderFunctions __Sound_DecoderFunctions_SHN;
extern const Sound_DecoderFunctions __Sound_DecoderFunctions_FLAC;
extern const Sound_DecoderFunctions __Sound_DecoderFunctions_QuickTime;
extern const Sound_DecoderFunctions __Sound_DecoderFunctions_CoreAudio;
typedef struct
{
int available;
const Sound_DecoderFunctions *funcs;
} decoder_element;
static decoder_element decoders[] =
{
#if SOUND_SUPPORTS_MODPLUG
{ 0, &__Sound_DecoderFunctions_MODPLUG },
#endif
#if SOUND_SUPPORTS_MP3
{ 0, &__Sound_DecoderFunctions_MP3 },
#endif
#if SOUND_SUPPORTS_WAV
{ 0, &__Sound_DecoderFunctions_WAV },
#endif
#if SOUND_SUPPORTS_AIFF
{ 0, &__Sound_DecoderFunctions_AIFF },
#endif
#if SOUND_SUPPORTS_AU
{ 0, &__Sound_DecoderFunctions_AU },
#endif
#if SOUND_SUPPORTS_VORBIS
{ 0, &__Sound_DecoderFunctions_VORBIS },
#endif
#if SOUND_SUPPORTS_VOC
{ 0, &__Sound_DecoderFunctions_VOC },
#endif
#if SOUND_SUPPORTS_RAW
{ 0, &__Sound_DecoderFunctions_RAW },
#endif
#if SOUND_SUPPORTS_SHN
{ 0, &__Sound_DecoderFunctions_SHN },
#endif
#if SOUND_SUPPORTS_FLAC
{ 0, &__Sound_DecoderFunctions_FLAC },
#endif
#if SOUND_SUPPORTS_COREAUDIO
{ 0, &__Sound_DecoderFunctions_CoreAudio },
#endif
{ 0, NULL }
};
/* General SDL_sound state ... */
typedef struct __SOUND_ERRMSGTYPE__
{
Uint32 tid;
int error_available;
char error_string[128];
struct __SOUND_ERRMSGTYPE__ *next;
} ErrMsg;
static ErrMsg *error_msgs = NULL;
static SDL_mutex *errorlist_mutex = NULL;
static Sound_Sample *sample_list = NULL; /* this is a linked list. */
static SDL_mutex *samplelist_mutex = NULL;
static const Sound_DecoderInfo **available_decoders = NULL;
static int initialized = 0;
/* functions ... */
void Sound_GetLinkedVersion(Sound_Version *ver)
{
if (ver != NULL)
{
ver->major = SOUND_VER_MAJOR;
ver->minor = SOUND_VER_MINOR;
ver->patch = SOUND_VER_PATCH;
} /* if */
} /* Sound_GetLinkedVersion */
int Sound_Init(void)
{
size_t i;
size_t pos = 0;
size_t total = sizeof (decoders) / sizeof (decoders[0]);
BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0);
sample_list = NULL;
error_msgs = NULL;
available_decoders = (const Sound_DecoderInfo **)
SDL_calloc(total, sizeof (Sound_DecoderInfo *));
BAIL_IF_MACRO(available_decoders == NULL, ERR_OUT_OF_MEMORY, 0);
SDL_InitSubSystem(SDL_INIT_AUDIO);
errorlist_mutex = SDL_CreateMutex();
samplelist_mutex = SDL_CreateMutex();
for (i = 0; decoders[i].funcs != NULL; i++)
{
decoders[i].available = decoders[i].funcs->init();
if (decoders[i].available)
{
available_decoders[pos] = &(decoders[i].funcs->info);
pos++;
} /* if */
} /* for */
initialized = 1;
return 1;
} /* Sound_Init */
int Sound_Quit(void)
{
ErrMsg *err;
ErrMsg *nexterr = NULL;
size_t i;
BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
while (((volatile Sound_Sample *) sample_list) != NULL)
Sound_FreeSample(sample_list);
initialized = 0;
SDL_DestroyMutex(samplelist_mutex);
samplelist_mutex = NULL;
sample_list = NULL;
for (i = 0; decoders[i].funcs != NULL; i++)
{
if (decoders[i].available)
{
decoders[i].funcs->quit();
decoders[i].available = 0;
} /* if */
} /* for */
if (available_decoders != NULL)
SDL_free((void *) available_decoders);
available_decoders = NULL;
/* clean up error state for each thread... */
SDL_LockMutex(errorlist_mutex);
for (err = error_msgs; err != NULL; err = nexterr)
{
nexterr = err->next;
SDL_free(err);
} /* for */
error_msgs = NULL;
SDL_UnlockMutex(errorlist_mutex);
SDL_DestroyMutex(errorlist_mutex);
errorlist_mutex = NULL;
return 1;
} /* Sound_Quit */
const Sound_DecoderInfo **Sound_AvailableDecoders(void)
{
return available_decoders; /* READ. ONLY. */
} /* Sound_AvailableDecoders */
static ErrMsg *findErrorForCurrentThread(void)
{
ErrMsg *i;
Uint32 tid;
if (error_msgs != NULL)
{
tid = SDL_ThreadID();
SDL_LockMutex(errorlist_mutex);
for (i = error_msgs; i != NULL; i = i->next)
{
if (i->tid == tid)
{
SDL_UnlockMutex(errorlist_mutex);
return i;
} /* if */
} /* for */
SDL_UnlockMutex(errorlist_mutex);
} /* if */
return NULL; /* no error available. */
} /* findErrorForCurrentThread */
const char *Sound_GetError(void)
{
const char *retval = NULL;
ErrMsg *err;
if (!initialized)
return ERR_NOT_INITIALIZED;
err = findErrorForCurrentThread();
if ((err != NULL) && (err->error_available))
{
retval = err->error_string;
err->error_available = 0;
} /* if */
return retval;
} /* Sound_GetError */
void Sound_ClearError(void)
{
ErrMsg *err;
if (!initialized)
return;
err = findErrorForCurrentThread();
if (err != NULL)
err->error_available = 0;
} /* Sound_ClearError */
/*
* This is declared in the internal header.
*/
void __Sound_SetError(const char *str)
{
ErrMsg *err;
if (str == NULL)
return;
SNDDBG(("__Sound_SetError(\"%s\");%s\n", str,
(initialized) ? "" : " [NOT INITIALIZED!]"));
if (!initialized)
return;
err = findErrorForCurrentThread();
if (err == NULL)
{
err = (ErrMsg *) SDL_calloc(1, sizeof (ErrMsg));
if (err == NULL)
return; /* uhh...? */
err->tid = SDL_ThreadID();
SDL_LockMutex(errorlist_mutex);
err->next = error_msgs;
error_msgs = err;
SDL_UnlockMutex(errorlist_mutex);
} /* if */
err->error_available = 1;
SDL_strlcpy(err->error_string, str, sizeof (err->error_string));
} /* __Sound_SetError */
Uint32 __Sound_convertMsToBytePos(Sound_AudioInfo *info, Uint32 ms)
{
/* "frames" == "sample frames" */
float frames_per_ms = ((float) info->rate) / 1000.0f;
Uint32 frame_offset = (Uint32) (frames_per_ms * ((float) ms));
Uint32 frame_size = (Uint32) ((info->format & 0xFF) / 8) * info->channels;
return frame_offset * frame_size;
} /* __Sound_convertMsToBytePos */
/*
* Allocate a Sound_Sample, and fill in most of its fields. Those that need
* to be filled in later, by a decoder, will be initialized to zero.
*/
static Sound_Sample *alloc_sample(SDL_RWops *rw, Sound_AudioInfo *desired,
Uint32 bufferSize)
{
/*
* !!! FIXME: We're going to need to pool samples, since the mixer
* !!! FIXME: might be allocating tons of these on a regular basis.
*/
Sound_Sample *retval = SDL_calloc(1, sizeof (Sound_Sample));
Sound_SampleInternal *internal = SDL_calloc(1, sizeof (Sound_SampleInternal));
if ((retval == NULL) || (internal == NULL))
{
__Sound_SetError(ERR_OUT_OF_MEMORY);
if (retval)
SDL_free(retval);
if (internal)
SDL_free(internal);
return NULL;
} /* if */
SDL_assert(bufferSize > 0);
retval->buffer = SDL_calloc(1, bufferSize); /* pure ugly. */
if (!retval->buffer)
{
__Sound_SetError(ERR_OUT_OF_MEMORY);
SDL_free(internal);
SDL_free(retval);
return NULL;
} /* if */
retval->buffer_size = bufferSize;
if (desired != NULL)
SDL_memcpy(&retval->desired, desired, sizeof (Sound_AudioInfo));
internal->rw = rw;
retval->opaque = internal;
return retval;
} /* alloc_sample */
#if (defined DEBUG_CHATTER)
static SDL_INLINE const char *fmt_to_str(Uint16 fmt)
{
switch(fmt)
{
case AUDIO_U8:
return "U8";
case AUDIO_S8:
return "S8";
case AUDIO_U16LSB:
return "U16LSB";
case AUDIO_S16LSB:
return "S16LSB";
case AUDIO_S32LSB:
return "S32LSB";
case AUDIO_F32LSB:
return "F32LSB";
case AUDIO_U16MSB:
return "U16MSB";
case AUDIO_S16MSB:
return "S16MSB";
case AUDIO_S32MSB:
return "S32MSB";
case AUDIO_F32MSB:
return "F32MSB";
} /* switch */
return "Unknown";
} /* fmt_to_str */
#endif
/*
* The bulk of the Sound_NewSample() work is done here...
* Ask the specified decoder to handle the data in (rw), and if
* so, construct the Sound_Sample. Otherwise, try to wind (rw)'s stream
* back to where it was, and return false.
*/
static int init_sample(const Sound_DecoderFunctions *funcs,
Sound_Sample *sample, const char *ext,
Sound_AudioInfo *_desired)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
Sound_AudioInfo desired;
int pos = SDL_RWtell(internal->rw);
/* fill in the funcs for this decoder... */
sample->decoder = &funcs->info;
internal->funcs = funcs;
if (!funcs->open(sample, ext))
{
SDL_RWseek(internal->rw, pos, SEEK_SET); /* set for next try... */
return 0;
} /* if */
/* success; we've got a decoder! */
/* Now we need to set up the conversion buffer... */
if (_desired == NULL)
SDL_memcpy(&desired, &sample->actual, sizeof (Sound_AudioInfo));
else
{
desired.format = _desired->format ? _desired->format : sample->actual.format;
desired.channels = _desired->channels ? _desired->channels : sample->actual.channels;
desired.rate = _desired->rate ? _desired->rate : sample->actual.rate;
} /* else */
if (SDL_BuildAudioCVT(&internal->sdlcvt,
sample->actual.format,
sample->actual.channels,
sample->actual.rate,
desired.format,
desired.channels,
desired.rate) == -1)
{
__Sound_SetError(SDL_GetError());
funcs->close(sample);
SDL_RWseek(internal->rw, pos, SEEK_SET); /* set for next try... */
return 0;
} /* if */
if (internal->sdlcvt.len_mult > 1)
{
void *rc = SDL_realloc(sample->buffer, sample->buffer_size * internal->sdlcvt.len_mult);
if (rc == NULL)
{
funcs->close(sample);
SDL_RWseek(internal->rw, pos, SEEK_SET); /* set for next try... */
return 0;
} /* if */
sample->buffer = rc;
} /* if */
/* these pointers are all one and the same. */
SDL_memcpy(&sample->desired, &desired, sizeof (Sound_AudioInfo));
internal->sdlcvt.buf = internal->buffer = sample->buffer;
internal->buffer_size = sample->buffer_size / internal->sdlcvt.len_mult;
internal->sdlcvt.len = internal->buffer_size;
/* Prepend our new Sound_Sample to the sample_list... */
SDL_LockMutex(samplelist_mutex);
internal->next = sample_list;
if (sample_list != NULL)
((Sound_SampleInternal *) sample_list->opaque)->prev = sample;
sample_list = sample;
SDL_UnlockMutex(samplelist_mutex);
SNDDBG(("New sample DESIRED format: %s format, %d rate, %d channels.\n",
fmt_to_str(sample->desired.format),
sample->desired.rate,
sample->desired.channels));
SNDDBG(("New sample ACTUAL format: %s format, %d rate, %d channels.\n",
fmt_to_str(sample->actual.format),
sample->actual.rate,
sample->actual.channels));
SNDDBG(("On-the-fly conversion: %s.\n",
internal->sdlcvt.needed ? "ENABLED" : "DISABLED"));
return 1;
} /* init_sample */
Sound_Sample *Sound_NewSample(SDL_RWops *rw, const char *ext,
Sound_AudioInfo *desired, Uint32 bSize)
{
Sound_Sample *retval;
decoder_element *decoder;
/* sanity checks. */
BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, NULL);
BAIL_IF_MACRO(rw == NULL, ERR_INVALID_ARGUMENT, NULL);
retval = alloc_sample(rw, desired, bSize);
if (!retval)
return NULL; /* alloc_sample() sets error message... */
if (ext != NULL)
{
for (decoder = &decoders[0]; decoder->funcs != NULL; decoder++)
{
if (decoder->available)
{
const char **decoderExt = decoder->funcs->info.extensions;
while (*decoderExt)
{
if (SDL_strcasecmp(*decoderExt, ext) == 0)
{
if (init_sample(decoder->funcs, retval, ext, desired))
return retval;
break; /* done with this decoder either way. */
} /* if */
decoderExt++;
} /* while */
} /* if */
} /* for */
} /* if */
/* no direct extension match? Try everything we've got... */
for (decoder = &decoders[0]; decoder->funcs != NULL; decoder++)
{
if (decoder->available)
{
int should_try = 1;
const char **decoderExt = decoder->funcs->info.extensions;
/* skip if we would have tried decoder above... */
while (*decoderExt)
{
if (SDL_strcasecmp(*decoderExt, ext) == 0)
{
should_try = 0;
break;
} /* if */
decoderExt++;
} /* while */
if (should_try)
{
if (init_sample(decoder->funcs, retval, ext, desired))
return retval;
} /* if */
} /* if */
} /* for */
/* nothing could handle the sound data... */
SDL_free(retval->opaque);
if (retval->buffer != NULL)
SDL_free(retval->buffer);
SDL_free(retval);
SDL_RWclose(rw);
__Sound_SetError(ERR_UNSUPPORTED_FORMAT);
return NULL;
} /* Sound_NewSample */
Sound_Sample *Sound_NewSampleFromFile(const char *filename,
Sound_AudioInfo *desired,
Uint32 bufferSize)
{
const char *ext;
SDL_RWops *rw;
BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, NULL);
BAIL_IF_MACRO(filename == NULL, ERR_INVALID_ARGUMENT, NULL);
ext = SDL_strrchr(filename, '.');
rw = SDL_RWFromFile(filename, "rb");
BAIL_IF_MACRO(rw == NULL, SDL_GetError(), NULL);
if (ext != NULL)
ext++;
return Sound_NewSample(rw, ext, desired, bufferSize);
} /* Sound_NewSampleFromFile */
Sound_Sample *Sound_NewSampleFromMem(const Uint8 *data,
Uint32 size,
const char *ext,
Sound_AudioInfo *desired,
Uint32 bufferSize)
{
SDL_RWops *rw;
BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, NULL);
BAIL_IF_MACRO(data == NULL, ERR_INVALID_ARGUMENT, NULL);
BAIL_IF_MACRO(size == 0, ERR_INVALID_ARGUMENT, NULL);
rw = SDL_RWFromConstMem(data, size);
BAIL_IF_MACRO(rw == NULL, SDL_GetError(), NULL);
return Sound_NewSample(rw, ext, desired, bufferSize);
} /* Sound_NewSampleFromMem */
void Sound_FreeSample(Sound_Sample *sample)
{
Sound_SampleInternal *internal;
if (!initialized)
{
__Sound_SetError(ERR_NOT_INITIALIZED);
return;
} /* if */
if (sample == NULL)
{
__Sound_SetError(ERR_INVALID_ARGUMENT);
return;
} /* if */
internal = (Sound_SampleInternal *) sample->opaque;
SDL_LockMutex(samplelist_mutex);
/* update the sample_list... */
if (internal->prev != NULL)
{
Sound_SampleInternal *prevInternal;
prevInternal = (Sound_SampleInternal *) internal->prev->opaque;
prevInternal->next = internal->next;
} /* if */
else
{
SDL_assert(sample_list == sample);
sample_list = internal->next;
} /* else */
if (internal->next != NULL)
{
Sound_SampleInternal *nextInternal;
nextInternal = (Sound_SampleInternal *) internal->next->opaque;
nextInternal->prev = internal->prev;
} /* if */
SDL_UnlockMutex(samplelist_mutex);
/* nuke it... */
internal->funcs->close(sample);
if (internal->rw != NULL) /* this condition is a "just in case" thing. */
SDL_RWclose(internal->rw);
if ((internal->buffer != NULL) && (internal->buffer != sample->buffer))
SDL_free(internal->buffer);
SDL_free(internal);
if (sample->buffer != NULL)
SDL_free(sample->buffer);
SDL_free(sample);
} /* Sound_FreeSample */
int Sound_SetBufferSize(Sound_Sample *sample, Uint32 newSize)
{
void *newBuf = NULL;
Sound_SampleInternal *internal = NULL;
BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
BAIL_IF_MACRO(sample == NULL, ERR_INVALID_ARGUMENT, 0);
internal = ((Sound_SampleInternal *) sample->opaque);
newBuf = SDL_realloc(sample->buffer, newSize * internal->sdlcvt.len_mult);
BAIL_IF_MACRO(newBuf == NULL, ERR_OUT_OF_MEMORY, 0);
internal->sdlcvt.buf = internal->buffer = sample->buffer = newBuf;
sample->buffer_size = newSize;
internal->buffer_size = newSize / internal->sdlcvt.len_mult;
internal->sdlcvt.len = internal->buffer_size;
return 1;
} /* Sound_SetBufferSize */
Uint32 Sound_Decode(Sound_Sample *sample)
{
Sound_SampleInternal *internal = NULL;
Uint32 retval = 0;
/* a boatload of sanity checks... */
BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
BAIL_IF_MACRO(sample == NULL, ERR_INVALID_ARGUMENT, 0);
BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_ERROR, ERR_PREV_ERROR, 0);
BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_EOF, ERR_PREV_EOF, 0);
internal = (Sound_SampleInternal *) sample->opaque;
SDL_assert(sample->buffer != NULL);
SDL_assert(sample->buffer_size > 0);
SDL_assert(internal->buffer != NULL);
SDL_assert(internal->buffer_size > 0);
/* reset EAGAIN. Decoder can flip it back on if it needs to. */
sample->flags &= ~SOUND_SAMPLEFLAG_EAGAIN;
retval = internal->funcs->read(sample);
if (retval > 0 && internal->sdlcvt.needed)
{
internal->sdlcvt.len = retval;
SDL_ConvertAudio(&internal->sdlcvt);
retval = internal->sdlcvt.len_cvt;
} /* if */
return retval;
} /* Sound_Decode */
Uint32 Sound_DecodeAll(Sound_Sample *sample)
{
Sound_SampleInternal *internal = NULL;
void *buf = NULL;
Uint32 newBufSize = 0;
BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_EOF, ERR_PREV_EOF, 0);
BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_ERROR, ERR_PREV_ERROR, 0);
internal = (Sound_SampleInternal *) sample->opaque;
while ( ((sample->flags & SOUND_SAMPLEFLAG_EOF) == 0) &&
((sample->flags & SOUND_SAMPLEFLAG_ERROR) == 0) )
{
Uint32 br = Sound_Decode(sample);
void *ptr = SDL_realloc(buf, newBufSize + br);
if (ptr == NULL)
{
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
__Sound_SetError(ERR_OUT_OF_MEMORY);
} /* if */
else
{
buf = ptr;
SDL_memcpy( ((char *) buf) + newBufSize, sample->buffer, br );
newBufSize += br;
} /* else */
} /* while */
if (buf == NULL) /* ...in case first call to SDL_realloc() fails... */
return sample->buffer_size;
if (internal->buffer != sample->buffer)
SDL_free(internal->buffer);
SDL_free(sample->buffer);
internal->sdlcvt.buf = internal->buffer = sample->buffer = buf;
sample->buffer_size = newBufSize;
internal->buffer_size = newBufSize / internal->sdlcvt.len_mult;
internal->sdlcvt.len = internal->buffer_size;
return newBufSize;
} /* Sound_DecodeAll */
int Sound_Rewind(Sound_Sample *sample)
{
Sound_SampleInternal *internal;
BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
internal = (Sound_SampleInternal *) sample->opaque;
if (!internal->funcs->rewind(sample))
{
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
return 0;
} /* if */
sample->flags &= ~SOUND_SAMPLEFLAG_EAGAIN;
sample->flags &= ~SOUND_SAMPLEFLAG_ERROR;
sample->flags &= ~SOUND_SAMPLEFLAG_EOF;
return 1;
} /* Sound_Rewind */
int Sound_Seek(Sound_Sample *sample, Uint32 ms)
{
Sound_SampleInternal *internal;
BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
if (!(sample->flags & SOUND_SAMPLEFLAG_CANSEEK))
BAIL_MACRO(ERR_CANNOT_SEEK, 0);
internal = (Sound_SampleInternal *) sample->opaque;
BAIL_IF_MACRO(!internal->funcs->seek(sample, ms), NULL, 0);
sample->flags &= ~SOUND_SAMPLEFLAG_EAGAIN;
sample->flags &= ~SOUND_SAMPLEFLAG_ERROR;
sample->flags &= ~SOUND_SAMPLEFLAG_EOF;
return 1;
} /* Sound_Rewind */
Sint32 Sound_GetDuration(Sound_Sample *sample)
{
Sound_SampleInternal *internal;
BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, -1);
internal = (Sound_SampleInternal *) sample->opaque;
return internal->total_time;
} /* Sound_GetDuration */
/* end of SDL_sound.c ... */

725
SDL2_sound/SDL_sound.h Normal file
View file

@ -0,0 +1,725 @@
/** \file SDL_sound.h */
/*
* SDL_sound; An abstract sound format decoding API.
*
* Please see the file LICENSE.txt in the source's root directory.
*/
/**
* \mainpage SDL_sound
*
* The latest version of SDL_sound can be found at:
* https://icculus.org/SDL_sound/
*
* The basic gist of SDL_sound is that you use an SDL_RWops to get sound data
* into this library, and SDL_sound will take that data, in one of several
* popular formats, and decode it into raw waveform data in the format of
* your choice. This gives you a nice abstraction for getting sound into your
* game or application; just feed it to SDL_sound, and it will handle
* decoding and converting, so you can just pass it to your SDL audio
* callback (or whatever). Since it gets data from an SDL_RWops, you can get
* the initial sound data from any number of sources: file, memory buffer,
* network connection, etc.
*
* As the name implies, this library depends on SDL: Simple Directmedia Layer,
* which is a powerful, free, and cross-platform multimedia library. It can
* be found at https://www.libsdl.org/
*
* Support is in place or planned for the following sound formats:
* - .WAV (Microsoft WAVfile RIFF data, internal.)
* - .VOC (Creative Labs' Voice format, internal.)
* - .MP3 (MPEG-1 Layer 3 support, via libmpg123.)
* - .MID (MIDI music converted to Waveform data, internal.)
* - .MOD (MOD files, via internal copy of libModPlug.)
* - .OGG (Ogg files, via internal copy of stb_vorbis.)
* - .SHN (Shorten files, internal.)
* - .RAW (Raw sound data in any format, internal.)
* - .AU (Sun's Audio format, internal.)
* - .AIFF (Audio Interchange format, internal.)
* - .FLAC (Lossless audio compression, via libFLAC.)
*
* (...and more to come...)
*
* Please see the file LICENSE.txt in the source's root directory.
*
* \author Ryan C. Gordon (icculus@icculus.org)
* \author many others, please see CREDITS in the source's root directory.
*/
#ifndef _INCLUDE_SDL_SOUND_H_
#define _INCLUDE_SDL_SOUND_H_
#include "SDL.h"
#if SDL_MAJOR_VERSION < 2
#error SDL2_sound requires SDL 2.0.0 or later.
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifndef DOXYGEN_SHOULD_IGNORE_THIS
#ifdef SDL_SOUND_DLL_EXPORTS
# define SNDDECLSPEC __declspec(dllexport)
#elif (__GNUC__ >= 3)
# define SNDDECLSPEC __attribute__((visibility("default")))
#else
# define SNDDECLSPEC
#endif
#define SOUND_VER_MAJOR 1
#define SOUND_VER_MINOR 9
#define SOUND_VER_PATCH 0
#endif
/**
* \enum Sound_SampleFlags
* \brief Flags that are used in a Sound_Sample to show various states.
*
* To use:
* \code
* if (sample->flags & SOUND_SAMPLEFLAG_ERROR) { dosomething(); }
* \endcode
*
* \sa Sound_SampleNew
* \sa Sound_SampleNewFromFile
* \sa Sound_SampleDecode
* \sa Sound_SampleDecodeAll
* \sa Sound_SampleSeek
*/
typedef enum
{
SOUND_SAMPLEFLAG_NONE = 0, /**< No special attributes. */
/* these are set at sample creation time... */
SOUND_SAMPLEFLAG_CANSEEK = 1, /**< Sample can seek to arbitrary points. */
/* these are set during decoding... */
SOUND_SAMPLEFLAG_EOF = 1 << 29, /**< End of input stream. */
SOUND_SAMPLEFLAG_ERROR = 1 << 30, /**< Unrecoverable error. */
SOUND_SAMPLEFLAG_EAGAIN = 1 << 31 /**< Function would block, or temp error. */
} Sound_SampleFlags;
/**
* \struct Sound_AudioInfo
* \brief Information about an existing sample's format.
*
* These are the basics of a decoded sample's data structure: data format
* (see AUDIO_U8 and friends in SDL_audio.h), number of channels, and sample
* rate. If you need more explanation than that, you should stop developing
* sound code right now.
*
* \sa Sound_SampleNew
* \sa Sound_SampleNewFromFile
*/
typedef struct
{
Uint16 format; /**< Equivalent of SDL_AudioSpec.format. */
Uint8 channels; /**< Number of sound channels. 1 == mono, 2 == stereo. */
Uint32 rate; /**< Sample rate; frequency of sample points per second. */
} Sound_AudioInfo;
/**
* \struct Sound_DecoderInfo
* \brief Information about available soudn decoders.
*
* Each decoder sets up one of these structs, which can be retrieved via
* the Sound_AvailableDecoders() function. EVERY FIELD IN THIS IS READ-ONLY.
*
* The extensions field is a NULL-terminated list of ASCIZ strings. You
* should read it like this:
*
* \code
* const char **ext;
* for (ext = info->extensions; *ext != NULL; ext++) {
* printf(" File extension \"%s\"\n", *ext);
* }
* \endcode
*
* \sa Sound_AvailableDecoders
*/
typedef struct
{
const char **extensions; /**< File extensions, list ends with NULL. */
const char *description; /**< Human readable description of decoder. */
const char *author; /**< "Name Of Author \<email@emailhost.dom\>" */
const char *url; /**< URL specific to this decoder. */
} Sound_DecoderInfo;
/**
* \struct Sound_Sample
* \brief Represents sound data in the process of being decoded.
*
* The Sound_Sample structure is the heart of SDL_sound. This holds
* information about a source of sound data as it is being decoded.
* EVERY FIELD IN THIS IS READ-ONLY. Please use the API functions to
* change them.
*/
typedef struct
{
void *opaque; /**< Internal use only. Don't touch. */
const Sound_DecoderInfo *decoder; /**< Decoder used for this sample. */
Sound_AudioInfo desired; /**< Desired audio format for conversion. */
Sound_AudioInfo actual; /**< Actual audio format of sample. */
void *buffer; /**< Decoded sound data lands in here. */
Uint32 buffer_size; /**< Current size of (buffer), in bytes (Uint8). */
Sound_SampleFlags flags; /**< Flags relating to this sample. */
} Sound_Sample;
/**
* \struct Sound_Version
* \brief Information the version of SDL_sound in use.
*
* Represents the library's version as three levels: major revision
* (increments with massive changes, additions, and enhancements),
* minor revision (increments with backwards-compatible changes to the
* major revision), and patchlevel (increments with fixes to the minor
* revision).
*
* \sa SOUND_VERSION
* \sa Sound_GetLinkedVersion
*/
typedef struct
{
int major; /**< major revision */
int minor; /**< minor revision */
int patch; /**< patchlevel */
} Sound_Version;
/* functions and macros... */
/**
* \def SOUND_VERSION(x)
* \brief Macro to determine SDL_sound version program was compiled against.
*
* This macro fills in a Sound_Version structure with the version of the
* library you compiled against. This is determined by what header the
* compiler uses. Note that if you dynamically linked the library, you might
* have a slightly newer or older version at runtime. That version can be
* determined with Sound_GetLinkedVersion(), which, unlike SOUND_VERSION,
* is not a macro.
*
* \param x A pointer to a Sound_Version struct to initialize.
*
* \sa Sound_Version
* \sa Sound_GetLinkedVersion
*/
#define SOUND_VERSION(x) \
{ \
(x)->major = SOUND_VER_MAJOR; \
(x)->minor = SOUND_VER_MINOR; \
(x)->patch = SOUND_VER_PATCH; \
}
/**
* \fn void Sound_GetLinkedVersion(Sound_Version *ver)
* \brief Get the version of SDL_sound that is linked against your program.
*
* If you are using a shared library (DLL) version of SDL_sound, then it is
* possible that it will be different than the version you compiled against.
*
* This is a real function; the macro SOUND_VERSION tells you what version
* of SDL_sound you compiled against:
*
* \code
* Sound_Version compiled;
* Sound_Version linked;
*
* SOUND_VERSION(&compiled);
* Sound_GetLinkedVersion(&linked);
* printf("We compiled against SDL_sound version %d.%d.%d ...\n",
* compiled.major, compiled.minor, compiled.patch);
* printf("But we linked against SDL_sound version %d.%d.%d.\n",
* linked.major, linked.minor, linked.patch);
* \endcode
*
* This function may be called safely at any time, even before Sound_Init().
*
* \param ver Sound_Version structure to fill with shared library's version.
*
* \sa Sound_Version
* \sa SOUND_VERSION
*/
SNDDECLSPEC void SDLCALL Sound_GetLinkedVersion(Sound_Version *ver);
/**
* \fn Sound_Init(void)
* \brief Initialize SDL_sound.
*
* This must be called before any other SDL_sound function (except perhaps
* Sound_GetLinkedVersion()). You should call SDL_Init() before calling this.
* Sound_Init() will attempt to call SDL_Init(SDL_INIT_AUDIO), just in case.
* This is a safe behaviour, but it may not configure SDL to your liking by
* itself.
*
* \return nonzero on success, zero on error. Specifics of the
* error can be gleaned from Sound_GetError().
*
* \sa Sound_Quit
*/
SNDDECLSPEC int SDLCALL Sound_Init(void);
/**
* \fn Sound_Quit(void)
* \brief Shutdown SDL_sound.
*
* This closes any SDL_RWops that were being used as sound sources, and frees
* any resources in use by SDL_sound.
*
* All Sound_Sample pointers you had prior to this call are INVALIDATED.
*
* Once successfully deinitialized, Sound_Init() can be called again to
* restart the subsystem. All default API states are restored at this
* point.
*
* You should call this BEFORE SDL_Quit(). This will NOT call SDL_Quit()
* for you!
*
* \return nonzero on success, zero on error. Specifics of the error
* can be gleaned from Sound_GetError(). If failure, state of
* SDL_sound is undefined, and probably badly screwed up.
*
* \sa Sound_Init
*/
SNDDECLSPEC int SDLCALL Sound_Quit(void);
/**
* \fn const Sound_DecoderInfo **Sound_AvailableDecoders(void)
* \brief Get a list of sound formats supported by this version of SDL_sound.
*
* This is for informational purposes only. Note that the extension listed is
* merely convention: if we list "MP3", you can open an MPEG-1 Layer 3 audio
* file with an extension of "XYZ", if you like. The file extensions are
* informational, and only required as a hint to choosing the correct
* decoder, since the sound data may not be coming from a file at all, thanks
* to the abstraction that an SDL_RWops provides.
*
* The returned value is an array of pointers to Sound_DecoderInfo structures,
* with a NULL entry to signify the end of the list:
*
* \code
* Sound_DecoderInfo **i;
*
* for (i = Sound_AvailableDecoders(); *i != NULL; i++)
* {
* printf("Supported sound format: [%s], which is [%s].\n",
* i->extension, i->description);
* // ...and other fields...
* }
* \endcode
*
* The return values are pointers to static internal memory, and should
* be considered READ ONLY, and never freed.
*
* \return READ ONLY Null-terminated array of READ ONLY structures.
*
* \sa Sound_DecoderInfo
*/
SNDDECLSPEC const Sound_DecoderInfo ** SDLCALL Sound_AvailableDecoders(void);
/**
* \fn const char *Sound_GetError(void)
* \brief Get the last SDL_sound error message as a null-terminated string.
*
* This will be NULL if there's been no error since the last call to this
* function. The pointer returned by this call points to an internal buffer,
* and should not be deallocated. Each thread has a unique error state
* associated with it, but each time a new error message is set, it will
* overwrite the previous one associated with that thread. It is safe to call
* this function at anytime, even before Sound_Init().
*
* \return READ ONLY string of last error message.
*
* \sa Sound_ClearError
*/
SNDDECLSPEC const char * SDLCALL Sound_GetError(void);
/**
* \fn void Sound_ClearError(void)
* \brief Clear the current error message.
*
* The next call to Sound_GetError() after Sound_ClearError() will return NULL.
*
* \sa Sound_GetError
*/
SNDDECLSPEC void SDLCALL Sound_ClearError(void);
/**
* \fn Sound_Sample *Sound_NewSample(SDL_RWops *rw, const char *ext, Sound_AudioInfo *desired, Uint32 bufferSize)
* \brief Start decoding a new sound sample.
*
* The data is read via an SDL_RWops structure (see SDL_rwops.h in the SDL
* include directory), so it may be coming from memory, disk, network stream,
* etc. The (ext) parameter is merely a hint to determining the correct
* decoder; if you specify, for example, "mp3" for an extension, and one of
* the decoders lists that as a handled extension, then that decoder is given
* first shot at trying to claim the data for decoding. If none of the
* extensions match (or the extension is NULL), then every decoder examines
* the data to determine if it can handle it, until one accepts it. In such a
* case your SDL_RWops will need to be capable of rewinding to the start of
* the stream.
*
* If no decoders can handle the data, a NULL value is returned, and a human
* readable error message can be fetched from Sound_GetError().
*
* Optionally, a desired audio format can be specified. If the incoming data
* is in a different format, SDL_sound will convert it to the desired format
* on the fly. Note that this can be an expensive operation, so it may be
* wise to convert data before you need to play it back, if possible, or
* make sure your data is initially in the format that you need it in.
* If you don't want to convert the data, you can specify NULL for a desired
* format. The incoming format of the data, preconversion, can be found
* in the Sound_Sample structure.
*
* Note that the raw sound data "decoder" needs you to specify both the
* extension "RAW" and a "desired" format, or it will refuse to handle
* the data. This is to prevent it from catching all formats unsupported
* by the other decoders.
*
* Finally, specify an initial buffer size; this is the number of bytes that
* will be allocated to store each read from the sound buffer. The more you
* can safely allocate, the more decoding can be done in one block, but the
* more resources you have to use up, and the longer each decoding call will
* take. Note that different data formats require more or less space to
* store. This buffer can be resized via Sound_SetBufferSize() ...
*
* The buffer size specified must be a multiple of the size of a single
* sample point. So, if you want 16-bit, stereo samples, then your sample
* point size is (2 channels * 16 bits), or 32 bits per sample, which is four
* bytes. In such a case, you could specify 128 or 132 bytes for a buffer,
* but not 129, 130, or 131 (although in reality, you'll want to specify a
* MUCH larger buffer).
*
* When you are done with this Sound_Sample pointer, you can dispose of it
* via Sound_FreeSample().
*
* You do not have to keep a reference to (rw) around. If this function
* suceeds, it stores (rw) internally (and disposes of it during the call
* to Sound_FreeSample()). If this function fails, it will dispose of the
* SDL_RWops for you.
*
* \param rw SDL_RWops with sound data.
* \param ext File extension normally associated with a data format.
* Can usually be NULL.
* \param desired Format to convert sound data into. Can usually be NULL,
* if you don't need conversion.
* \param bufferSize Size, in bytes, to allocate for the decoding buffer.
* \return Sound_Sample pointer, which is used as a handle to several other
* SDL_sound APIs. NULL on error. If error, use
* Sound_GetError() to see what went wrong.
*
* \sa Sound_NewSampleFromFile
* \sa Sound_SetBufferSize
* \sa Sound_Decode
* \sa Sound_DecodeAll
* \sa Sound_Seek
* \sa Sound_Rewind
* \sa Sound_FreeSample
*/
SNDDECLSPEC Sound_Sample * SDLCALL Sound_NewSample(SDL_RWops *rw,
const char *ext,
Sound_AudioInfo *desired,
Uint32 bufferSize);
/**
* \fn Sound_Sample *Sound_NewSampleFromMem(const Uint8 *data, Uint32 size, const char *ext, Sound_AudioInfo *desired, Uint32 bufferSize)
* \brief Start decoding a new sound sample from a file on disk.
*
* This is identical to Sound_NewSample(), but it creates an SDL_RWops for you
* from the (size) bytes of memory referenced by (data).
*
* This can pool RWops structures, so it may fragment the heap less over time
* than using SDL_RWFromMem().
*
* \param data Buffer of data holding contents of an audio file to decode.
* \param size Size, in bytes, of buffer pointed to by (data).
* \param ext File extension normally associated with a data format.
* Can usually be NULL.
* \param desired Format to convert sound data into. Can usually be NULL,
* if you don't need conversion.
* \param bufferSize size, in bytes, of initial read buffer.
* \return Sound_Sample pointer, which is used as a handle to several other
* SDL_sound APIs. NULL on error. If error, use
* Sound_GetError() to see what went wrong.
*
* \sa Sound_NewSample
* \sa Sound_SetBufferSize
* \sa Sound_Decode
* \sa Sound_DecodeAll
* \sa Sound_Seek
* \sa Sound_Rewind
* \sa Sound_FreeSample
*/
SNDDECLSPEC Sound_Sample * SDLCALL Sound_NewSampleFromMem(const Uint8 *data,
Uint32 size,
const char *ext,
Sound_AudioInfo *desired,
Uint32 bufferSize);
/**
* \fn Sound_Sample *Sound_NewSampleFromFile(const char *filename, Sound_AudioInfo *desired, Uint32 bufferSize)
* \brief Start decoding a new sound sample from a file on disk.
*
* This is identical to Sound_NewSample(), but it creates an SDL_RWops for you
* from the file located in (filename). Note that (filename) is specified in
* platform-dependent notation. ("C:\\music\\mysong.mp3" on windows, and
* "/home/icculus/music/mysong.mp3" or whatever on Unix, etc.)
* Sound_NewSample()'s "ext" parameter is gleaned from the contents of
* (filename).
*
* This can pool RWops structures, so it may fragment the heap less over time
* than using SDL_RWFromFile().
*
* \param filename file containing sound data.
* \param desired Format to convert sound data into. Can usually be NULL,
* if you don't need conversion.
* \param bufferSize size, in bytes, of initial read buffer.
* \return Sound_Sample pointer, which is used as a handle to several other
* SDL_sound APIs. NULL on error. If error, use
* Sound_GetError() to see what went wrong.
*
* \sa Sound_NewSample
* \sa Sound_SetBufferSize
* \sa Sound_Decode
* \sa Sound_DecodeAll
* \sa Sound_Seek
* \sa Sound_Rewind
* \sa Sound_FreeSample
*/
SNDDECLSPEC Sound_Sample * SDLCALL Sound_NewSampleFromFile(const char *fname,
Sound_AudioInfo *desired,
Uint32 bufferSize);
/**
* \fn void Sound_FreeSample(Sound_Sample *sample)
* \brief Dispose of a Sound_Sample.
*
* This will also close/dispose of the SDL_RWops that was used at creation
* time, so there's no need to keep a reference to that around.
* The Sound_Sample pointer is invalid after this call, and will almost
* certainly result in a crash if you attempt to keep using it.
*
* \param sample The Sound_Sample to delete.
*
* \sa Sound_NewSample
* \sa Sound_NewSampleFromFile
*/
SNDDECLSPEC void SDLCALL Sound_FreeSample(Sound_Sample *sample);
/**
* \fn Sint32 Sound_GetDuration(Sound_Sample *sample)
* \brief Retrieve total play time of sample, in milliseconds.
*
* Report total time length of sample, in milliseconds. This is a fast
* call. Duration is calculated during Sound_NewSample*, so this is just
* an accessor into otherwise opaque data.
*
* Please note that not all formats can determine a total time, some can't
* be exact without fully decoding the data, and thus will estimate the
* duration. Many decoders will require the ability to seek in the data
* stream to calculate this, so even if we can tell you how long an .ogg
* file will be, the same data set may fail if it's, say, streamed over an
* HTTP connection. Plan accordingly.
*
* Most people won't need this function to just decode and playback, but it
* can be useful for informational purposes in, say, a music player's UI.
*
* \param sample Sound_Sample from which to retrieve duration information.
* \return Sample length in milliseconds, or -1 if duration can't be
* determined for any reason.
*/
SNDDECLSPEC Sint32 SDLCALL Sound_GetDuration(Sound_Sample *sample);
/**
* \fn int Sound_SetBufferSize(Sound_Sample *sample, Uint32 new_size)
* \brief Change the current buffer size for a sample.
*
* If the buffer size could be changed, then the sample->buffer and
* sample->buffer_size fields will reflect that. If they could not be
* changed, then your original sample state is preserved. If the buffer is
* shrinking, the data at the end of buffer is truncated. If the buffer is
* growing, the contents of the new space at the end is undefined until you
* decode more into it or initialize it yourself.
*
* The buffer size specified must be a multiple of the size of a single
* sample point. So, if you want 16-bit, stereo samples, then your sample
* point size is (2 channels * 16 bits), or 32 bits per sample, which is four
* bytes. In such a case, you could specify 128 or 132 bytes for a buffer,
* but not 129, 130, or 131 (although in reality, you'll want to specify a
* MUCH larger buffer).
*
* \param sample The Sound_Sample whose buffer to modify.
* \param new_size The desired size, in bytes, of the new buffer.
* \return non-zero if buffer size changed, zero on failure.
*
* \sa Sound_Decode
* \sa Sound_DecodeAll
*/
SNDDECLSPEC int SDLCALL Sound_SetBufferSize(Sound_Sample *sample,
Uint32 new_size);
/**
* \fn Uint32 Sound_Decode(Sound_Sample *sample)
* \brief Decode more of the sound data in a Sound_Sample.
*
* It will decode at most sample->buffer_size bytes into sample->buffer in the
* desired format, and return the number of decoded bytes.
* If sample->buffer_size bytes could not be decoded, then please refer to
* sample->flags to determine if this was an end-of-stream or error condition.
*
* \param sample Do more decoding to this Sound_Sample.
* \return number of bytes decoded into sample->buffer. If it is less than
* sample->buffer_size, then you should check sample->flags to see
* what the current state of the sample is (EOF, error, read again).
*
* \sa Sound_DecodeAll
* \sa Sound_SetBufferSize
* \sa Sound_Seek
* \sa Sound_Rewind
*/
SNDDECLSPEC Uint32 SDLCALL Sound_Decode(Sound_Sample *sample);
/**
* \fn Uint32 Sound_DecodeAll(Sound_Sample *sample)
* \brief Decode the remainder of the sound data in a Sound_Sample.
*
* This will dynamically allocate memory for the ENTIRE remaining sample.
* sample->buffer_size and sample->buffer will be updated to reflect the
* new buffer. Please refer to sample->flags to determine if the decoding
* finished due to an End-of-stream or error condition.
*
* Be aware that sound data can take a large amount of memory, and that
* this function may block for quite awhile while processing. Also note
* that a streaming source (for example, from a SDL_RWops that is getting
* fed from an Internet radio feed that doesn't end) may fill all available
* memory before giving up...be sure to use this on finite sound sources
* only!
*
* When decoding the sample in its entirety, the work is done one buffer at a
* time. That is, sound is decoded in sample->buffer_size blocks, and
* appended to a continually-growing buffer until the decoding completes.
* That means that this function will need enough RAM to hold approximately
* sample->buffer_size bytes plus the complete decoded sample at most. The
* larger your buffer size, the less overhead this function needs, but beware
* the possibility of paging to disk. Best to make this user-configurable if
* the sample isn't specific and small.
*
* \param sample Do all decoding for this Sound_Sample.
* \return number of bytes decoded into sample->buffer. You should check
* sample->flags to see what the current state of the sample is
* (EOF, error, read again).
*
* \sa Sound_Decode
* \sa Sound_SetBufferSize
*/
SNDDECLSPEC Uint32 SDLCALL Sound_DecodeAll(Sound_Sample *sample);
/**
* \fn int Sound_Rewind(Sound_Sample *sample)
* \brief Rewind a sample to the start.
*
* Restart a sample at the start of its waveform data, as if newly
* created with Sound_NewSample(). If successful, the next call to
* Sound_Decode[All]() will give audio data from the earliest point
* in the stream.
*
* Beware that this function will fail if the SDL_RWops that feeds the
* decoder can not be rewound via it's seek method, but this can
* theoretically be avoided by wrapping it in some sort of buffering
* SDL_RWops.
*
* This function should ONLY fail if the RWops is not seekable, or
* SDL_sound is not initialized. Both can be controlled by the application,
* and thus, it is up to the developer's paranoia to dictate whether this
* function's return value need be checked at all.
*
* If this function fails, the state of the sample is undefined, but it
* is still safe to call Sound_FreeSample() to dispose of it.
*
* On success, ERROR, EOF, and EAGAIN are cleared from sample->flags. The
* ERROR flag is set on error.
*
* \param sample The Sound_Sample to rewind.
* \return nonzero on success, zero on error. Specifics of the
* error can be gleaned from Sound_GetError().
*
* \sa Sound_Seek
*/
SNDDECLSPEC int SDLCALL Sound_Rewind(Sound_Sample *sample);
/**
* \fn int Sound_Seek(Sound_Sample *sample, Uint32 ms)
* \brief Seek to a different point in a sample.
*
* Reposition a sample's stream. If successful, the next call to
* Sound_Decode[All]() will give audio data from the offset you
* specified.
*
* The offset is specified in milliseconds from the start of the
* sample.
*
* Beware that this function can fail for several reasons. If the
* SDL_RWops that feeds the decoder can not seek, this call will almost
* certainly fail, but this can theoretically be avoided by wrapping it
* in some sort of buffering SDL_RWops. Some decoders can never seek,
* others can only seek with certain files. The decoders will set a flag
* in the sample at creation time to help you determine this.
*
* You should check sample->flags & SOUND_SAMPLEFLAG_CANSEEK
* before attempting. Sound_Seek() reports failure immediately if this
* flag isn't set. This function can still fail for other reasons if the
* flag is set.
*
* This function can be emulated in the application with Sound_Rewind()
* and predecoding a specific amount of the sample, but this can be
* extremely inefficient. Sound_Seek() accelerates the seek on a
* with decoder-specific code.
*
* If this function fails, the sample should continue to function as if
* this call was never made. If there was an unrecoverable error,
* sample->flags & SOUND_SAMPLEFLAG_ERROR will be set, which you regular
* decoding loop can pick up.
*
* On success, ERROR, EOF, and EAGAIN are cleared from sample->flags.
*
* \param sample The Sound_Sample to seek.
* \param ms The new position, in milliseconds from start of sample.
* \return nonzero on success, zero on error. Specifics of the
* error can be gleaned from Sound_GetError().
*
* \sa Sound_Rewind
*/
SNDDECLSPEC int SDLCALL Sound_Seek(Sound_Sample *sample, Uint32 ms);
#ifdef __cplusplus
}
#endif
#endif /* !defined _INCLUDE_SDL_SOUND_H_ */
/* end of SDL_sound.h ... */

537
SDL2_sound/SDL_sound_aiff.c Normal file
View file

@ -0,0 +1,537 @@
/**
* SDL_sound; An abstract sound format decoding API.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Torbjörn Andersson.
*/
/*
* AIFF decoder for SDL_sound
*
* [Insert something profound about the AIFF file format here.]
*
* This code was ripped from a decoder I had written for SDL_mixer, which was
* based on SDL_mixer's old AIFF music loader. (This loader was unfortunately
* completely broken, but it was still useful because all the pieces were
* still there, so to speak.)
*
* When rewriting it for SDL_sound, I changed its structure to be more like
* the WAV loader Ryan wrote. Had they not both been part of the same project
* it would have been embarrassing how similar they are.
*
* It is not the most feature-complete AIFF loader the world has ever seen.
* For instance, it only makes a token attempt at implementing the AIFF-C
* standard; basically the parts of it that I can easily understand and test.
* It's a start, though.
*/
#define __SDL_SOUND_INTERNAL__
#include "SDL_sound_internal.h"
#if SOUND_SUPPORTS_AIFF
/*****************************************************************************
* aiff_t is what we store in our internal->decoder_private field... *
*****************************************************************************/
typedef struct S_AIFF_FMT_T
{
Uint32 type;
Uint32 total_bytes;
Uint32 data_starting_offset;
void (*free)(struct S_AIFF_FMT_T *fmt);
Uint32 (*read_sample)(Sound_Sample *sample);
int (*rewind_sample)(Sound_Sample *sample);
int (*seek_sample)(Sound_Sample *sample, Uint32 ms);
#if 0
/*
this is ripped from wav.c as ann example of format-specific data.
please replace with something more appropriate when the need arises.
*/
union
{
struct
{
Uint16 cbSize;
Uint16 wSamplesPerBlock;
Uint16 wNumCoef;
ADPCMCOEFSET *aCoeff;
} adpcm;
/* put other format-specific data here... */
} fmt;
#endif
} fmt_t;
typedef struct
{
fmt_t fmt;
Sint32 bytesLeft;
} aiff_t;
/* Chunk management code... */
#define formID 0x4D524F46 /* "FORM", in ascii. */
#define aiffID 0x46464941 /* "AIFF", in ascii. */
#define aifcID 0x43464941 /* "AIFC", in ascii. */
#define ssndID 0x444E5353 /* "SSND", in ascii. */
/*****************************************************************************
* The COMM chunk... *
*****************************************************************************/
#define commID 0x4D4D4F43 /* "COMM", in ascii. */
/* format/compression types... */
#define noneID 0x454E4F4E /* "NONE", in ascii. */
typedef struct
{
Uint32 ckID;
Uint32 ckDataSize;
Uint16 numChannels;
Uint32 numSampleFrames;
Uint16 sampleSize;
Uint32 sampleRate;
/*
* We don't handle AIFF-C compressed audio yet, but for those
* interested the allowed compression types are supposed to be
*
* compressionType compressionName meaning
* ---------------------------------------------------------------
* 'NONE' "not compressed" uncompressed, that is,
* straight digitized samples
* 'ACE2' "ACE 2-to-1" 2-to-1 IIGS ACE (Audio
* Compression / Expansion)
* 'ACE8' "ACE 8-to-3" 8-to-3 IIGS ACE (Audio
* Compression / Expansion)
* 'MAC3' "MACE 3-to-1" 3-to-1 Macintosh Audio
* Compression / Expansion
* 'MAC6' "MACE 6-to-1" 6-to-1 Macintosh Audio
* Compression / Expansion
*
* A pstring is a "Pascal-style string", that is, "one byte followed
* by test bytes followed when needed by one pad byte. The total
* number of bytes in a pstring must be even. The pad byte is
* included when the number of text bytes is even, so the total of
* text bytes + one count byte + one pad byte will be even. This pad
* byte is not reflected in the count."
*
* As for how these compression algorithms work, your guess is as
* good as mine.
*/
Uint32 compressionType;
#if 0
pstring compressionName;
#endif
} comm_t;
/*
* Sample rate is encoded as an "80 bit IEEE Standard 754 floating point
* number (Standard Apple Numeric Environment [SANE] data type Extended)".
* Whose bright idea was that?
*
* This function was adapted from libsndfile, and while I do know a little
* bit about the IEEE floating point standard I don't pretend to fully
* understand this.
*/
static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
{
/* Is the frequency outside of what we can represent with Uint32? */
if ( (sanebuf[0] & 0x80)
|| (sanebuf[0] <= 0x3F)
|| (sanebuf[0] > 0x40)
|| (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C) )
return 0;
return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7)
| (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
} /* SANE_to_Uint32 */
/*
* Read in a comm_t from disk. This makes this process safe regardless of
* the processor's byte order or how the comm_t structure is packed.
*/
static int read_comm_chunk(SDL_RWops *rw, comm_t *comm)
{
Uint8 sampleRate[10];
/* skip reading the chunk ID, since it was already read at this point... */
comm->ckID = commID;
if (SDL_RWread(rw, &comm->ckDataSize, sizeof (comm->ckDataSize), 1) != 1)
return 0;
comm->ckDataSize = SDL_SwapBE32(comm->ckDataSize);
if (SDL_RWread(rw, &comm->numChannels, sizeof (comm->numChannels), 1) != 1)
return 0;
comm->numChannels = SDL_SwapBE16(comm->numChannels);
if (SDL_RWread(rw, &comm->numSampleFrames,
sizeof (comm->numSampleFrames), 1) != 1)
return 0;
comm->numSampleFrames = SDL_SwapBE32(comm->numSampleFrames);
if (SDL_RWread(rw, &comm->sampleSize, sizeof (comm->sampleSize), 1) != 1)
return 0;
comm->sampleSize = SDL_SwapBE16(comm->sampleSize);
if (SDL_RWread(rw, sampleRate, sizeof (sampleRate), 1) != 1)
return 0;
comm->sampleRate = SANE_to_Uint32(sampleRate);
if (comm->ckDataSize > sizeof(comm->numChannels)
+ sizeof(comm->numSampleFrames)
+ sizeof(comm->sampleSize)
+ sizeof(sampleRate))
{
if (SDL_RWread(rw, &comm->compressionType,
sizeof (comm->compressionType), 1) != 1)
return 0;
comm->compressionType = SDL_SwapBE32(comm->compressionType);
} /* if */
else
{
comm->compressionType = noneID;
} /* else */
return 1;
} /* read_comm_chunk */
/*****************************************************************************
* The SSND chunk... *
*****************************************************************************/
typedef struct
{
Uint32 ckID;
Uint32 ckDataSize;
Uint32 offset;
Uint32 blockSize;
/*
* Then, comm->numSampleFrames sample frames. (It's better to get the
* length from numSampleFrames than from ckDataSize.)
*/
} ssnd_t;
static int read_ssnd_chunk(SDL_RWops *rw, ssnd_t *ssnd)
{
/* skip reading the chunk ID, since it was already read at this point... */
ssnd->ckID = ssndID;
if (SDL_RWread(rw, &ssnd->ckDataSize, sizeof (ssnd->ckDataSize), 1) != 1)
return 0;
ssnd->ckDataSize = SDL_SwapBE32(ssnd->ckDataSize);
if (SDL_RWread(rw, &ssnd->offset, sizeof (ssnd->offset), 1) != 1)
return 0;
ssnd->offset = SDL_SwapBE32(ssnd->offset);
if (SDL_RWread(rw, &ssnd->blockSize, sizeof (ssnd->blockSize), 1) != 1)
return 0;
ssnd->blockSize = SDL_SwapBE32(ssnd->blockSize);
/* Leave the SDL_RWops position indicator at the start of the samples */
if (SDL_RWseek(rw, (int) ssnd->offset, SEEK_CUR) == -1)
return 0;
return 1;
} /* read_ssnd_chunk */
/*****************************************************************************
* Normal, uncompressed aiff handler... *
*****************************************************************************/
static Uint32 read_sample_fmt_normal(Sound_Sample *sample)
{
Uint32 retval;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
aiff_t *a = (aiff_t *) internal->decoder_private;
Uint32 max = (internal->buffer_size < (Uint32) a->bytesLeft) ?
internal->buffer_size : (Uint32) a->bytesLeft;
SDL_assert(max > 0);
/*
* We don't actually do any decoding, so we read the AIFF data
* directly into the internal buffer...
*/
retval = SDL_RWread(internal->rw, internal->buffer, 1, max);
a->bytesLeft -= retval;
/* Make sure the read went smoothly... */
if ((retval == 0) || (a->bytesLeft == 0))
sample->flags |= SOUND_SAMPLEFLAG_EOF;
else if (retval == -1)
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
/* (next call this EAGAIN may turn into an EOF or error.) */
else if (retval < internal->buffer_size)
sample->flags |= SOUND_SAMPLEFLAG_EAGAIN;
return retval;
} /* read_sample_fmt_normal */
static int rewind_sample_fmt_normal(Sound_Sample *sample)
{
/* no-op. */
return 1;
} /* rewind_sample_fmt_normal */
static int seek_sample_fmt_normal(Sound_Sample *sample, Uint32 ms)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
aiff_t *a = (aiff_t *) internal->decoder_private;
fmt_t *fmt = &a->fmt;
int offset = __Sound_convertMsToBytePos(&sample->actual, ms);
int pos = (int) (fmt->data_starting_offset + offset);
int rc = SDL_RWseek(internal->rw, pos, SEEK_SET);
BAIL_IF_MACRO(rc != pos, ERR_IO_ERROR, 0);
a->bytesLeft = fmt->total_bytes - offset;
return 1; /* success. */
} /* seek_sample_fmt_normal */
static void free_fmt_normal(fmt_t *fmt)
{
/* it's a no-op. */
} /* free_fmt_normal */
static int read_fmt_normal(SDL_RWops *rw, fmt_t *fmt)
{
/* (don't need to read more from the RWops...) */
fmt->free = free_fmt_normal;
fmt->read_sample = read_sample_fmt_normal;
fmt->rewind_sample = rewind_sample_fmt_normal;
fmt->seek_sample = seek_sample_fmt_normal;
return 1;
} /* read_fmt_normal */
/*****************************************************************************
* Everything else... *
*****************************************************************************/
static int AIFF_init(void)
{
return 1; /* always succeeds. */
} /* AIFF_init */
static void AIFF_quit(void)
{
/* it's a no-op. */
} /* AIFF_quit */
static int find_chunk(SDL_RWops *rw, Uint32 id)
{
Sint32 siz = 0;
Uint32 _id = 0;
while (1)
{
BAIL_IF_MACRO(SDL_RWread(rw, &_id, sizeof (_id), 1) != 1, NULL, 0);
if (SDL_SwapLE32(_id) == id)
return 1;
BAIL_IF_MACRO(SDL_RWread(rw, &siz, sizeof (siz), 1) != 1, NULL, 0);
siz = SDL_SwapBE32(siz);
SDL_assert(siz > 0);
BAIL_IF_MACRO(SDL_RWseek(rw, siz, SEEK_CUR) == -1, NULL, 0);
} /* while */
return 0; /* shouldn't hit this, but just in case... */
} /* find_chunk */
static int read_fmt(SDL_RWops *rw, comm_t *c, fmt_t *fmt)
{
fmt->type = c->compressionType;
/* if it's in this switch statement, we support the format. */
switch (fmt->type)
{
case noneID:
SNDDBG(("AIFF: Appears to be uncompressed audio.\n"));
return read_fmt_normal(rw, fmt);
/* add other types here. */
default:
SNDDBG(("AIFF: Format %lu is unknown.\n",
(unsigned int) fmt->type));
BAIL_MACRO("AIFF: Unsupported format", 0);
} /* switch */
SDL_assert(0); /* shouldn't hit this point. */
return 0;
} /* read_fmt */
static int AIFF_open(Sound_Sample *sample, const char *ext)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
SDL_RWops *rw = internal->rw;
Uint32 chunk_id;
int bytes_per_sample;
long pos;
comm_t c;
ssnd_t s;
aiff_t *a;
BAIL_IF_MACRO(SDL_ReadLE32(rw) != formID, "AIFF: Not a FORM file.", 0);
SDL_ReadBE32(rw); /* throw the length away; we don't need it. */
chunk_id = SDL_ReadLE32(rw);
BAIL_IF_MACRO(chunk_id != aiffID && chunk_id != aifcID,
"AIFF: Not an AIFF or AIFC file.", 0);
/* Chunks may appear in any order, so we establish base camp here. */
pos = SDL_RWtell(rw);
BAIL_IF_MACRO(!find_chunk(rw, commID), "AIFF: No common chunk.", 0);
BAIL_IF_MACRO(!read_comm_chunk(rw, &c),
"AIFF: Can't read common chunk.", 0);
sample->actual.channels = (Uint8) c.numChannels;
sample->actual.rate = c.sampleRate;
/* Really, sample->total_time = (c.numSampleFrames*1000) c.sampleRate */
internal->total_time = (c.numSampleFrames / c.sampleRate) * 1000;
internal->total_time += (c.numSampleFrames % c.sampleRate)
* 1000 / c.sampleRate;
if (c.sampleSize <= 8)
{
sample->actual.format = AUDIO_S8;
bytes_per_sample = c.numChannels;
} /* if */
else if (c.sampleSize <= 16)
{
sample->actual.format = AUDIO_S16MSB;
bytes_per_sample = 2 * c.numChannels;
} /* if */
else
{
BAIL_MACRO("AIFF: Unsupported sample size.", 0);
} /* else */
BAIL_IF_MACRO(c.sampleRate == 0, "AIFF: Unsupported sample rate.", 0);
a = (aiff_t *) SDL_malloc(sizeof(aiff_t));
BAIL_IF_MACRO(a == NULL, ERR_OUT_OF_MEMORY, 0);
if (!read_fmt(rw, &c, &(a->fmt)))
{
SDL_free(a);
return 0;
} /* if */
SDL_RWseek(rw, pos, SEEK_SET); /* if the seek fails, let it go... */
if (!find_chunk(rw, ssndID))
{
SDL_free(a);
BAIL_MACRO("AIFF: No sound data chunk.", 0);
} /* if */
if (!read_ssnd_chunk(rw, &s))
{
SDL_free(a);
BAIL_MACRO("AIFF: Can't read sound data chunk.", 0);
} /* if */
a->fmt.total_bytes = a->bytesLeft = bytes_per_sample * c.numSampleFrames;
a->fmt.data_starting_offset = SDL_RWtell(rw);
internal->decoder_private = (void *) a;
sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
SNDDBG(("AIFF: Accepting data stream.\n"));
return 1; /* we'll handle this data. */
} /* AIFF_open */
static void AIFF_close(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
aiff_t *a = (aiff_t *) internal->decoder_private;
a->fmt.free(&(a->fmt));
SDL_free(a);
} /* AIFF_close */
static Uint32 AIFF_read(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
aiff_t *a = (aiff_t *) internal->decoder_private;
return a->fmt.read_sample(sample);
} /* AIFF_read */
static int AIFF_rewind(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
aiff_t *a = (aiff_t *) internal->decoder_private;
fmt_t *fmt = &a->fmt;
int rc = SDL_RWseek(internal->rw, fmt->data_starting_offset, SEEK_SET);
BAIL_IF_MACRO(rc != fmt->data_starting_offset, ERR_IO_ERROR, 0);
a->bytesLeft = fmt->total_bytes;
return fmt->rewind_sample(sample);
} /* AIFF_rewind */
static int AIFF_seek(Sound_Sample *sample, Uint32 ms)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
aiff_t *a = (aiff_t *) internal->decoder_private;
return a->fmt.seek_sample(sample, ms);
} /* AIFF_seek */
static const char *extensions_aiff[] = { "AIFF", "AIF", NULL };
const Sound_DecoderFunctions __Sound_DecoderFunctions_AIFF =
{
{
extensions_aiff,
"Audio Interchange File Format",
"Torbjörn Andersson <d91tan@Update.UU.SE>",
"https://icculus.org/SDL_sound/"
},
AIFF_init, /* init() method */
AIFF_quit, /* quit() method */
AIFF_open, /* open() method */
AIFF_close, /* close() method */
AIFF_read, /* read() method */
AIFF_rewind, /* rewind() method */
AIFF_seek /* seek() method */
};
#endif /* SOUND_SUPPORTS_AIFF */
/* end of SDL_sound_aiff.c ... */

352
SDL2_sound/SDL_sound_au.c Normal file
View file

@ -0,0 +1,352 @@
/**
* SDL_sound; An abstract sound format decoding API.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Mattias Engdegård.
*/
/*
* Sun/NeXT .au decoder for SDL_sound.
* Formats supported: 8 and 16 bit linear PCM, 8 bit µ-law.
* Files without valid header are assumed to be 8 bit µ-law, 8kHz, mono.
*/
#define __SDL_SOUND_INTERNAL__
#include "SDL_sound_internal.h"
#if SOUND_SUPPORTS_AU
/* no init/deinit needed */
static int AU_init(void)
{
return 1;
} /* AU_init */
static void AU_quit(void)
{
/* no-op. */
} /* AU_quit */
struct au_file_hdr
{
Uint32 magic;
Uint32 hdr_size;
Uint32 data_size;
Uint32 encoding;
Uint32 sample_rate;
Uint32 channels;
};
#define HDR_SIZE 24
enum
{
AU_ENC_ULAW_8 = 1, /* 8-bit ISDN µ-law */
AU_ENC_LINEAR_8 = 2, /* 8-bit linear PCM */
AU_ENC_LINEAR_16 = 3, /* 16-bit linear PCM */
/* the rest are unsupported (I have never seen them in the wild) */
AU_ENC_LINEAR_24 = 4, /* 24-bit linear PCM */
AU_ENC_LINEAR_32 = 5, /* 32-bit linear PCM */
AU_ENC_FLOAT = 6, /* 32-bit IEEE floating point */
AU_ENC_DOUBLE = 7, /* 64-bit IEEE floating point */
/* more Sun formats, not supported either */
AU_ENC_ADPCM_G721 = 23,
AU_ENC_ADPCM_G722 = 24,
AU_ENC_ADPCM_G723_3 = 25,
AU_ENC_ADPCM_G723_5 = 26,
AU_ENC_ALAW_8 = 27
};
struct audec
{
Uint32 total;
Uint32 remaining;
Uint32 start_offset;
int encoding;
};
/*
* Read in the AU header from disk. This makes this process safe
* regardless of the processor's byte order or how the au_file_hdr
* structure is packed.
*/
static int read_au_header(SDL_RWops *rw, struct au_file_hdr *hdr)
{
if (SDL_RWread(rw, &hdr->magic, sizeof (hdr->magic), 1) != 1)
return 0;
hdr->magic = SDL_SwapBE32(hdr->magic);
if (SDL_RWread(rw, &hdr->hdr_size, sizeof (hdr->hdr_size), 1) != 1)
return 0;
hdr->hdr_size = SDL_SwapBE32(hdr->hdr_size);
if (SDL_RWread(rw, &hdr->data_size, sizeof (hdr->data_size), 1) != 1)
return 0;
hdr->data_size = SDL_SwapBE32(hdr->data_size);
if (SDL_RWread(rw, &hdr->encoding, sizeof (hdr->encoding), 1) != 1)
return 0;
hdr->encoding = SDL_SwapBE32(hdr->encoding);
if (SDL_RWread(rw, &hdr->sample_rate, sizeof (hdr->sample_rate), 1) != 1)
return 0;
hdr->sample_rate = SDL_SwapBE32(hdr->sample_rate);
if (SDL_RWread(rw, &hdr->channels, sizeof (hdr->channels), 1) != 1)
return 0;
hdr->channels = SDL_SwapBE32(hdr->channels);
return 1;
} /* read_au_header */
#define AU_MAGIC 0x2E736E64 /* ".snd", in ASCII (bigendian number) */
static int AU_open(Sound_Sample *sample, const char *ext)
{
Sound_SampleInternal *internal = sample->opaque;
SDL_RWops *rw = internal->rw;
int skip, hsize, i, bytes_per_second;
struct au_file_hdr hdr;
struct audec *dec;
char c;
/* read_au_header() will do byte order swapping. */
BAIL_IF_MACRO(!read_au_header(rw, &hdr), "AU: bad header", 0);
dec = SDL_malloc(sizeof *dec);
BAIL_IF_MACRO(dec == NULL, ERR_OUT_OF_MEMORY, 0);
internal->decoder_private = dec;
if (hdr.magic == AU_MAGIC)
{
/* valid magic */
dec->encoding = hdr.encoding;
switch(dec->encoding)
{
case AU_ENC_ULAW_8:
/* Convert 8-bit µ-law to 16-bit linear on the fly. This is
slightly wasteful if the audio driver must convert them
back, but µ-law only devices are rare (mostly _old_ Suns) */
sample->actual.format = AUDIO_S16SYS;
break;
case AU_ENC_LINEAR_8:
sample->actual.format = AUDIO_S8;
break;
case AU_ENC_LINEAR_16:
sample->actual.format = AUDIO_S16MSB;
break;
default:
SDL_free(dec);
BAIL_MACRO("AU: Unsupported .au encoding", 0);
} /* switch */
sample->actual.rate = hdr.sample_rate;
sample->actual.channels = hdr.channels;
dec->remaining = hdr.data_size;
hsize = hdr.hdr_size;
/* skip remaining part of header (input may be unseekable) */
for (i = HDR_SIZE; i < hsize; i++)
{
if (SDL_RWread(rw, &c, 1, 1) != 1)
{
SDL_free(dec);
BAIL_MACRO(ERR_IO_ERROR, 0);
} /* if */
} /* for */
} /* if */
else if (SDL_strcasecmp(ext, "au") == 0)
{
/*
* A number of files in the wild have the .au extension but no valid
* header; these are traditionally assumed to be 8kHz µ-law. Handle
* them here only if the extension is recognized.
*/
SNDDBG(("AU: Invalid header, assuming raw 8kHz µ-law.\n"));
/* if seeking fails, we lose 24 samples. big deal */
SDL_RWseek(rw, -HDR_SIZE, SEEK_CUR);
dec->encoding = AU_ENC_ULAW_8;
dec->remaining = (Uint32)-1; /* no limit */
sample->actual.format = AUDIO_S16SYS;
sample->actual.rate = 8000;
sample->actual.channels = 1;
} /* else if */
else
{
SDL_free(dec);
BAIL_MACRO("AU: Not an .AU stream.", 0);
} /* else */
bytes_per_second = ( ( dec->encoding == AU_ENC_LINEAR_16 ) ? 2 : 1 )
* sample->actual.rate * sample->actual.channels ;
internal->total_time = ((dec->remaining == -1) ? (-1) :
( ( dec->remaining / bytes_per_second ) * 1000 ) +
( ( dec->remaining % bytes_per_second ) * 1000 /
bytes_per_second ) );
sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
dec->total = dec->remaining;
dec->start_offset = SDL_RWtell(rw);
SNDDBG(("AU: Accepting data stream.\n"));
return 1;
} /* AU_open */
static void AU_close(Sound_Sample *sample)
{
Sound_SampleInternal *internal = sample->opaque;
SDL_free(internal->decoder_private);
} /* AU_close */
/* table to convert from µ-law encoding to signed 16-bit samples,
generated by a throwaway perl script */
static Sint16 ulaw_to_linear[256] = {
-32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
-23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
-15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
-11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
-7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
-5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
-3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
-2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
-1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
-1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
-876, -844, -812, -780, -748, -716, -684, -652,
-620, -588, -556, -524, -492, -460, -428, -396,
-372, -356, -340, -324, -308, -292, -276, -260,
-244, -228, -212, -196, -180, -164, -148, -132,
-120, -112, -104, -96, -88, -80, -72, -64,
-56, -48, -40, -32, -24, -16, -8, 0,
32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
876, 844, 812, 780, 748, 716, 684, 652,
620, 588, 556, 524, 492, 460, 428, 396,
372, 356, 340, 324, 308, 292, 276, 260,
244, 228, 212, 196, 180, 164, 148, 132,
120, 112, 104, 96, 88, 80, 72, 64,
56, 48, 40, 32, 24, 16, 8, 0
};
static Uint32 AU_read(Sound_Sample *sample)
{
int ret;
Sound_SampleInternal *internal = sample->opaque;
struct audec *dec = internal->decoder_private;
int maxlen;
Uint8 *buf;
maxlen = internal->buffer_size;
buf = internal->buffer;
if (dec->encoding == AU_ENC_ULAW_8)
{
/* We read µ-law samples into the second half of the buffer, so
we can expand them to 16-bit samples afterwards */
maxlen >>= 1;
buf += maxlen;
} /* if */
if (maxlen > dec->remaining)
maxlen = dec->remaining;
ret = SDL_RWread(internal->rw, buf, 1, maxlen);
if (ret == 0)
sample->flags |= SOUND_SAMPLEFLAG_EOF;
else if (ret == -1)
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
else
{
dec->remaining -= ret;
if (ret < maxlen)
sample->flags |= SOUND_SAMPLEFLAG_EAGAIN;
if (dec->encoding == AU_ENC_ULAW_8)
{
int i;
Sint16 *dst = internal->buffer;
for (i = 0; i < ret; i++)
dst[i] = ulaw_to_linear[buf[i]];
ret <<= 1; /* return twice as much as read */
} /* if */
} /* else */
return ret;
} /* AU_read */
static int AU_rewind(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
struct audec *dec = (struct audec *) internal->decoder_private;
int rc = SDL_RWseek(internal->rw, dec->start_offset, SEEK_SET);
BAIL_IF_MACRO(rc != dec->start_offset, ERR_IO_ERROR, 0);
dec->remaining = dec->total;
return 1;
} /* AU_rewind */
static int AU_seek(Sound_Sample *sample, Uint32 ms)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
struct audec *dec = (struct audec *) internal->decoder_private;
int offset = __Sound_convertMsToBytePos(&sample->actual, ms);
int rc;
int pos;
if (dec->encoding == AU_ENC_ULAW_8)
offset >>= 1; /* halve the byte offset for compression. */
pos = (int) (dec->start_offset + offset);
rc = SDL_RWseek(internal->rw, pos, SEEK_SET);
BAIL_IF_MACRO(rc != pos, ERR_IO_ERROR, 0);
dec->remaining = dec->total - offset;
return 1;
} /* AU_seek */
/*
* Sometimes the extension ".snd" is used for these files (mostly on the NeXT),
* and the magic number comes from this. However it may clash with other
* formats and is somewhat of an anachronism, so only .au is used here.
*/
static const char *extensions_au[] = { "AU", NULL };
const Sound_DecoderFunctions __Sound_DecoderFunctions_AU =
{
{
extensions_au,
"Sun/NeXT audio file format",
"Mattias Engdegård <f91-men@nada.kth.se>",
"https://icculus.org/SDL_sound/"
},
AU_init, /* init() method */
AU_quit, /* quit() method */
AU_open, /* open() method */
AU_close, /* close() method */
AU_read, /* read() method */
AU_rewind, /* rewind() method */
AU_seek /* seek() method */
};
#endif /* SOUND_SUPPORTS_AU */
/* end of SDL_sound_au.c ... */

View file

@ -0,0 +1,747 @@
/**
* SDL_sound; An abstract sound format decoding API.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Eric Wing.
*/
#define __SDL_SOUND_INTERNAL__
#include "SDL_sound_internal.h"
#if SOUND_SUPPORTS_COREAUDIO
#include <stddef.h> /* NULL */
#include <arpa/inet.h> /* htonl */
#include <AudioToolbox/AudioToolbox.h>
typedef struct CoreAudioFileContainer
{
AudioFileID* audioFileID;
ExtAudioFileRef extAudioFileRef;
AudioStreamBasicDescription* outputFormat;
} CoreAudioFileContainer;
static int CoreAudio_init(void)
{
return 1; /* always succeeds. */
} /* CoreAudio_init */
static void CoreAudio_quit(void)
{
/* it's a no-op. */
} /* CoreAudio_quit */
/*
https://developer.apple.com/library/ios/#documentation/MusicAudio/Reference/AudioFileConvertRef/Reference/reference.html
kAudioFileAIFFType = 'AIFF',
kAudioFileAIFCType = 'AIFC',
kAudioFileWAVEType = 'WAVE',
kAudioFileSoundDesigner2Type = 'Sd2f',
kAudioFileNextType = 'NeXT',
kAudioFileMP3Type = 'MPG3',
kAudioFileMP2Type = 'MPG2',
kAudioFileMP1Type = 'MPG1',
kAudioFileAC3Type = 'ac-3',
kAudioFileAAC_ADTSType = 'adts',
kAudioFileMPEG4Type = 'mp4f',
kAudioFileM4AType = 'm4af',
kAudioFileCAFType = 'caff',
kAudioFile3GPType = '3gpp',
kAudioFile3GP2Type = '3gp2',
kAudioFileAMRType = 'amrf'
*/
static AudioFileTypeID CoreAudio_GetAudioTypeForExtension(const char* file_extension)
{
if( (SDL_strcasecmp(file_extension, "aif") == 0)
|| (SDL_strcasecmp(file_extension, "aiff") == 0)
|| (SDL_strcasecmp(file_extension, "aifc") == 0)
)
{
return kAudioFileAIFCType;
}
else if( (SDL_strcasecmp(file_extension, "wav") == 0)
|| (SDL_strcasecmp(file_extension, "wave") == 0)
)
{
return kAudioFileWAVEType;
}
else if( (SDL_strcasecmp(file_extension, "mp3") == 0)
)
{
return kAudioFileMP3Type;
}
else if( (SDL_strcasecmp(file_extension, "mp4") == 0)
)
{
return kAudioFileMPEG4Type;
}
else if( (SDL_strcasecmp(file_extension, "m4a") == 0)
)
{
return kAudioFileM4AType;
}
else if( (SDL_strcasecmp(file_extension, "aac") == 0)
)
{
return kAudioFileAAC_ADTSType;
}
else if( (SDL_strcasecmp(file_extension, "aac") == 0)
)
{
return kAudioFileAAC_ADTSType;
}
else if( (SDL_strcasecmp(file_extension, "caf") == 0)
|| (SDL_strcasecmp(file_extension, "caff") == 0)
)
{
return kAudioFileCAFType;
}
else if( (SDL_strcasecmp(file_extension, "Sd2f") == 0)
|| (SDL_strcasecmp(file_extension, "sd2") == 0)
)
{
return kAudioFileSoundDesigner2Type;
}
else if( (SDL_strcasecmp(file_extension, "au") == 0)
|| (SDL_strcasecmp(file_extension, "next") == 0)
)
{
return kAudioFileNextType;
}
else if( (SDL_strcasecmp(file_extension, "mp2") == 0)
)
{
return kAudioFileMP2Type;
}
else if( (SDL_strcasecmp(file_extension, "mp1") == 0)
)
{
return kAudioFileMP1Type;
}
else if( (SDL_strcasecmp(file_extension, "ac3") == 0)
)
{
return kAudioFileAC3Type;
}
else if( (SDL_strcasecmp(file_extension, "3gpp") == 0)
)
{
return kAudioFile3GPType;
}
else if( (SDL_strcasecmp(file_extension, "3gp2") == 0)
)
{
return kAudioFile3GP2Type;
}
else if( (SDL_strcasecmp(file_extension, "amrf") == 0)
|| (SDL_strcasecmp(file_extension, "amr") == 0)
)
{
return kAudioFileAMRType;
}
else if( (SDL_strcasecmp(file_extension, "ima4") == 0)
|| (SDL_strcasecmp(file_extension, "ima") == 0)
)
{
/* not sure about this one */
return kAudioFileCAFType;
}
else
{
return 0;
}
}
static const char* CoreAudio_FourCCToString(int32_t error_code)
{
static char return_string[16];
uint32_t big_endian_code = htonl(error_code);
char* big_endian_str = (char*)&big_endian_code;
// see if it appears to be a 4-char-code
if(isprint(big_endian_str[0])
&& isprint(big_endian_str[1])
&& isprint(big_endian_str[2])
&& isprint (big_endian_str[3]))
{
return_string[0] = '\'';
return_string[1] = big_endian_str[0];
return_string[2] = big_endian_str[1];
return_string[3] = big_endian_str[2];
return_string[4] = big_endian_str[3];
return_string[5] = '\'';
return_string[6] = '\0';
}
else if(error_code > -200000 && error_code < 200000)
{
// no, format it as an integer
snprintf(return_string, 16, "%d", error_code);
}
else
{
// no, format it as an integer but in hex
snprintf(return_string, 16, "0x%x", error_code);
}
return return_string;
}
SInt64 CoreAudio_SizeCallback(void* inClientData)
{
SDL_RWops* rw_ops = (SDL_RWops*)inClientData;
SInt64 current_position = SDL_RWtell(rw_ops);
SInt64 end_position = SDL_RWseek(rw_ops, 0, SEEK_END);
SDL_RWseek(rw_ops, current_position, SEEK_SET);
// fprintf(stderr, "CoreAudio_SizeCallback:%d\n", end_position);
return end_position;
}
OSStatus CoreAudio_ReadCallback(
void* inClientData,
SInt64 inPosition,
UInt32 requestCount,
void* data_buffer,
UInt32* actualCount
)
{
SDL_RWops* rw_ops = (SDL_RWops*)inClientData;
SDL_RWseek(rw_ops, inPosition, SEEK_SET);
size_t bytes_actually_read = SDL_RWread(rw_ops, data_buffer, 1, requestCount);
// Not sure how to test for a read error with SDL_RWops
// fprintf(stderr, "CoreAudio_ReadCallback:%d, %d\n", requestCount, bytes_actually_read);
*actualCount = bytes_actually_read;
return noErr;
}
static int CoreAudio_open(Sound_Sample *sample, const char *ext)
{
CoreAudioFileContainer* core_audio_file_container;
AudioFileID* audio_file_id;
OSStatus error_result;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
AudioStreamBasicDescription actual_format;
AudioStreamBasicDescription output_format;
Float64 estimated_duration;
UInt32 format_size;
core_audio_file_container = (CoreAudioFileContainer*)SDL_malloc(sizeof(CoreAudioFileContainer));
BAIL_IF_MACRO(core_audio_file_container == NULL, ERR_OUT_OF_MEMORY, 0);
audio_file_id = (AudioFileID*)SDL_malloc(sizeof(AudioFileID));
BAIL_IF_MACRO(audio_file_id == NULL, ERR_OUT_OF_MEMORY, 0);
error_result = AudioFileOpenWithCallbacks(
internal->rw,
CoreAudio_ReadCallback,
NULL,
CoreAudio_SizeCallback,
NULL,
CoreAudio_GetAudioTypeForExtension(ext),
audio_file_id
);
if (error_result != noErr)
{
AudioFileClose(*audio_file_id);
SDL_free(audio_file_id);
SDL_free(core_audio_file_container);
SNDDBG(("Core Audio: can't grok data. reason: [%s].\n", CoreAudio_FourCCToString(error_result)));
BAIL_MACRO("Core Audio: Not valid audio data.", 0);
} /* if */
format_size = sizeof(actual_format);
error_result = AudioFileGetProperty(
*audio_file_id,
kAudioFilePropertyDataFormat,
&format_size,
&actual_format
);
if (error_result != noErr)
{
AudioFileClose(*audio_file_id);
SDL_free(audio_file_id);
SDL_free(core_audio_file_container);
SNDDBG(("Core Audio: AudioFileGetProperty failed. reason: [%s]", CoreAudio_FourCCToString(error_result)));
BAIL_MACRO("Core Audio: Not valid audio data.", 0);
} /* if */
format_size = sizeof(estimated_duration);
error_result = AudioFileGetProperty(
*audio_file_id,
kAudioFilePropertyEstimatedDuration,
&format_size,
&estimated_duration
);
if (error_result != noErr)
{
AudioFileClose(*audio_file_id);
SDL_free(audio_file_id);
SDL_free(core_audio_file_container);
SNDDBG(("Core Audio: AudioFileGetProperty failed. reason: [%s].\n", CoreAudio_FourCCToString(error_result)));
BAIL_MACRO("Core Audio: Not valid audio data.", 0);
} /* if */
core_audio_file_container->audioFileID = audio_file_id;
internal->decoder_private = core_audio_file_container;
sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
sample->actual.rate = (UInt32) actual_format.mSampleRate;
sample->actual.channels = (UInt8)actual_format.mChannelsPerFrame;
internal->total_time = (SInt32)(estimated_duration * 1000.0 + 0.5);
#if 0
/* FIXME: Both Core Audio and SDL 1.3 support float and 32-bit formats */
if(actual_format.mFormatFlags & kAudioFormatFlagIsBigEndian)
{
if(16 == actual_format.mBitsPerChannel)
{
if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags)
{
sample->actual.format = AUDIO_S16MSB;
}
else
{
sample->actual.format = AUDIO_U16MSB;
}
}
else if(8 == actual_format.mBitsPerChannel)
{
if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags)
{
sample->actual.format = AUDIO_S8;
}
else
{
sample->actual.format = AUDIO_U8;
}
}
else // might be 0 for undefined?
{
// This case seems to come up a lot for me. Maybe for file types like .m4a?
sample->actual.format = AUDIO_S16SYS;
SNDDBG(("Core Audio: Unsupported actual_format.mBitsPerChannel: [%d].\n", actual_format.mBitsPerChannel));
}
}
else // little endian
{
if(16 == actual_format.mBitsPerChannel)
{
if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags)
{
sample->actual.format = AUDIO_S16LSB;
}
else
{
sample->actual.format = AUDIO_U16LSB;
}
}
else if(8 == actual_format.mBitsPerChannel)
{
if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags)
{
sample->actual.format = AUDIO_S8;
}
else
{
sample->actual.format = AUDIO_U8;
}
}
else // might be 0 for undefined?
{
sample->actual.format = AUDIO_S16SYS;
SNDDBG(("Core Audio: Unsupported actual_format.mBitsPerChannel: [%d].\n", actual_format.mBitsPerChannel));
}
}
#else
/*
* I want to use Core Audio to do conversion and decoding for performance reasons.
* This is particularly important on mobile devices like iOS.
* Taking from the Ogg Vorbis decode, I pretend the "actual" format is the same
* as the desired format.
*/
sample->actual.format = (sample->desired.format == 0) ?
AUDIO_S16SYS : sample->desired.format;
#endif
SNDDBG(("CoreAudio: channels == (%d).\n", sample->actual.channels));
SNDDBG(("CoreAudio: sampling rate == (%d).\n",sample->actual.rate));
SNDDBG(("CoreAudio: total seconds of sample == (%d).\n", internal->total_time));
SNDDBG(("CoreAudio: sample->actual.format == (%d).\n", sample->actual.format));
error_result = ExtAudioFileWrapAudioFileID(*audio_file_id,
false, // set to false for read-only
&core_audio_file_container->extAudioFileRef
);
if(error_result != noErr)
{
AudioFileClose(*audio_file_id);
SDL_free(audio_file_id);
SDL_free(core_audio_file_container);
SNDDBG(("Core Audio: can't wrap data. reason: [%s].\n", CoreAudio_FourCCToString(error_result)));
BAIL_MACRO("Core Audio: Failed to wrap data.", 0);
} /* if */
/* The output format must be linear PCM because that's the only type OpenAL knows how to deal with.
* Set the client format to 16 bit signed integer (native-endian) data because that is the most
* optimal format on iPhone/iPod Touch hardware.
* Maintain the channel count and sample rate of the original source format.
*/
output_format.mSampleRate = actual_format.mSampleRate; // preserve the original sample rate
output_format.mChannelsPerFrame = actual_format.mChannelsPerFrame; // preserve the number of channels
output_format.mFormatID = kAudioFormatLinearPCM; // We want linear PCM data
output_format.mFramesPerPacket = 1; // We know for linear PCM, the definition is 1 frame per packet
if(sample->desired.format == 0)
{
// do AUDIO_S16SYS
output_format.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; // I seem to read failures problems without kAudioFormatFlagIsPacked. From a mailing list post, this seems to be a Core Audio bug.
output_format.mBitsPerChannel = 16; // We know we want 16-bit
}
else
{
output_format.mFormatFlags = 0; // clear flags
output_format.mFormatFlags |= kAudioFormatFlagIsPacked; // I seem to read failures problems without kAudioFormatFlagIsPacked. From a mailing list post, this seems to be a Core Audio bug.
// Mask against bitsize
if(0xFF & sample->desired.format)
{
output_format.mBitsPerChannel = 16; /* 16-bit */
}
else
{
output_format.mBitsPerChannel = 8; /* 8-bit */
}
// Mask for signed/unsigned
if((1<<15) & sample->desired.format)
{
output_format.mFormatFlags = output_format.mFormatFlags | kAudioFormatFlagIsSignedInteger;
}
else
{
// no flag set for unsigned
}
// Mask for big/little endian
if((1<<12) & sample->desired.format)
{
output_format.mFormatFlags = output_format.mFormatFlags | kAudioFormatFlagIsBigEndian;
}
else
{
// no flag set for little endian
}
}
output_format.mBytesPerPacket = output_format.mBitsPerChannel/8 * output_format.mChannelsPerFrame; // e.g. 16-bits/8 * channels => so 2-bytes per channel per frame
output_format.mBytesPerFrame = output_format.mBitsPerChannel/8 * output_format.mChannelsPerFrame; // For PCM, since 1 frame is 1 packet, it is the same as mBytesPerPacket
/*
output_format.mSampleRate = actual_format.mSampleRate; // preserve the original sample rate
output_format.mChannelsPerFrame = actual_format.mChannelsPerFrame; // preserve the number of channels
output_format.mFormatID = kAudioFormatLinearPCM; // We want linear PCM data
// output_format.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
output_format.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger;
output_format.mFramesPerPacket = 1; // We know for linear PCM, the definition is 1 frame per packet
output_format.mBitsPerChannel = 16; // We know we want 16-bit
output_format.mBytesPerPacket = 2 * output_format.mChannelsPerFrame; // We know we are using 16-bit, so 2-bytes per channel per frame
output_format.mBytesPerFrame = 2 * output_format.mChannelsPerFrame; // For PCM, since 1 frame is 1 packet, it is the same as mBytesPerPacket
*/
SNDDBG(("output_format: mSampleRate: %lf\n", output_format.mSampleRate));
SNDDBG(("output_format: mChannelsPerFrame: %d\n", output_format.mChannelsPerFrame));
SNDDBG(("output_format: mFormatID: %d\n", output_format.mFormatID));
SNDDBG(("output_format: mFormatFlags: %d\n", output_format.mFormatFlags));
SNDDBG(("output_format: mFramesPerPacket: %d\n", output_format.mFramesPerPacket));
SNDDBG(("output_format: mBitsPerChannel: %d\n", output_format.mBitsPerChannel));
SNDDBG(("output_format: mBytesPerPacket: %d\n", output_format.mBytesPerPacket));
SNDDBG(("output_format: mBytesPerFrame: %d\n", output_format.mBytesPerFrame));
/* Set the desired client (output) data format */
error_result = ExtAudioFileSetProperty(core_audio_file_container->extAudioFileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(output_format), &output_format);
if(noErr != error_result)
{
ExtAudioFileDispose(core_audio_file_container->extAudioFileRef);
AudioFileClose(*audio_file_id);
SDL_free(audio_file_id);
SDL_free(core_audio_file_container);
SNDDBG(("Core Audio: ExtAudioFileSetProperty(kExtAudioFileProperty_ClientDataFormat) failed, reason: [%s].\n", CoreAudio_FourCCToString(error_result)));
BAIL_MACRO("Core Audio: Not valid audio data.", 0);
}
core_audio_file_container->outputFormat = (AudioStreamBasicDescription*)SDL_malloc(sizeof(AudioStreamBasicDescription));
BAIL_IF_MACRO(core_audio_file_container->outputFormat == NULL, ERR_OUT_OF_MEMORY, 0);
/* Copy the output format to the audio_description that was passed in so the
* info will be returned to the user.
*/
SDL_memcpy(core_audio_file_container->outputFormat, &output_format, sizeof(AudioStreamBasicDescription));
return 1;
} /* CoreAudio_open */
static void CoreAudio_close(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private;
SDL_free(core_audio_file_container->outputFormat);
ExtAudioFileDispose(core_audio_file_container->extAudioFileRef);
AudioFileClose(*core_audio_file_container->audioFileID);
SDL_free(core_audio_file_container->audioFileID);
SDL_free(core_audio_file_container);
} /* CoreAudio_close */
static Uint32 CoreAudio_read(Sound_Sample *sample)
{
OSStatus error_result = noErr;
/* Documentation/example shows SInt64, but is problematic for big endian
* on 32-bit cast for ExtAudioFileRead() because it takes the upper
* bits which turn to 0.
*/
UInt32 buffer_size_in_frames = 0;
UInt32 buffer_size_in_frames_remaining = 0;
UInt32 total_frames_read = 0;
UInt32 data_buffer_size = 0;
UInt32 bytes_remaining = 0;
size_t total_bytes_read = 0;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private;
UInt32 max_buffer_size = internal->buffer_size;
// printf("internal->buffer_size=%d, internal->buffer=0x%x, sample->buffer_size=%d\n", internal->buffer_size, internal->buffer, sample->buffer_size);
// printf("internal->max_buffer_size=%d\n", max_buffer_size);
/* Compute how many frames will fit into our max buffer size */
/* Warning: If this is not evenly divisible, the buffer will not be completely filled which violates the SDL_sound assumption. */
buffer_size_in_frames = max_buffer_size / core_audio_file_container->outputFormat->mBytesPerFrame;
// printf("buffer_size_in_frames=%ld, internal->buffer_size=%d, internal->buffer=0x%x outputFormat->mBytesPerFrame=%d, sample->buffer_size=%d\n", buffer_size_in_frames, internal->buffer_size, internal->buffer, core_audio_file_container->outputFormat->mBytesPerFrame, sample->buffer_size);
// void* temp_buffer = SDL_malloc(max_buffer_size);
AudioBufferList audio_buffer_list;
audio_buffer_list.mNumberBuffers = 1;
audio_buffer_list.mBuffers[0].mDataByteSize = max_buffer_size;
audio_buffer_list.mBuffers[0].mNumberChannels = core_audio_file_container->outputFormat->mChannelsPerFrame;
audio_buffer_list.mBuffers[0].mData = internal->buffer;
bytes_remaining = max_buffer_size;
buffer_size_in_frames_remaining = buffer_size_in_frames;
// oops. Due to the kAudioFormatFlagIsPacked bug,
// I was misled to believe that Core Audio
// was not always filling my entire requested buffer.
// So this while-loop might be unnecessary.
// However, I have not exhaustively tested all formats,
// so maybe it is possible this loop is useful.
// It might also handle the not-evenly disvisible case above.
while(buffer_size_in_frames_remaining > 0 && !(sample->flags & SOUND_SAMPLEFLAG_EOF))
{
data_buffer_size = (UInt32)(buffer_size_in_frames * core_audio_file_container->outputFormat->mBytesPerFrame);
// printf("data_buffer_size=%d\n", data_buffer_size);
buffer_size_in_frames = buffer_size_in_frames_remaining;
// printf("reading buffer_size_in_frames=%"PRId64"\n", buffer_size_in_frames);
audio_buffer_list.mBuffers[0].mDataByteSize = bytes_remaining;
audio_buffer_list.mBuffers[0].mData = &(((UInt8*)internal->buffer)[total_bytes_read]);
/* Read the data into an AudioBufferList */
error_result = ExtAudioFileRead(core_audio_file_container->extAudioFileRef, &buffer_size_in_frames, &audio_buffer_list);
if(error_result == noErr)
{
/* Success */
total_frames_read += buffer_size_in_frames;
buffer_size_in_frames_remaining = buffer_size_in_frames_remaining - buffer_size_in_frames;
// printf("read buffer_size_in_frames=%"PRId64", buffer_size_in_frames_remaining=%"PRId64"\n", buffer_size_in_frames, buffer_size_in_frames_remaining);
/* ExtAudioFileRead returns the number of frames actually read. Need to convert back to bytes. */
data_buffer_size = (UInt32)(buffer_size_in_frames * core_audio_file_container->outputFormat->mBytesPerFrame);
// printf("data_buffer_size=%d\n", data_buffer_size);
total_bytes_read += data_buffer_size;
bytes_remaining = bytes_remaining - data_buffer_size;
/* Note: 0 == buffer_size_in_frames is a legitimate value meaning we are EOF. */
if(0 == buffer_size_in_frames)
{
sample->flags |= SOUND_SAMPLEFLAG_EOF;
}
}
else
{
SNDDBG(("Core Audio: ExtAudioFileReadfailed, reason: [%s].\n", CoreAudio_FourCCToString(error_result)));
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
break;
}
}
if( (!(sample->flags & SOUND_SAMPLEFLAG_EOF)) && (total_bytes_read < max_buffer_size))
{
SNDDBG(("Core Audio: ExtAudioFileReadfailed SOUND_SAMPLEFLAG_EAGAIN, reason: [total_bytes_read < max_buffer_size], %d, %d.\n", total_bytes_read , max_buffer_size));
/* Don't know what to do here. */
sample->flags |= SOUND_SAMPLEFLAG_EAGAIN;
}
return total_bytes_read;
} /* CoreAudio_read */
static int CoreAudio_rewind(Sound_Sample *sample)
{
OSStatus error_result = noErr;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private;
error_result = ExtAudioFileSeek(core_audio_file_container->extAudioFileRef, 0);
if(error_result != noErr)
{
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
}
return 1;
} /* CoreAudio_rewind */
/* Note: I found this tech note:
https://developer.apple.com/library/mac/#qa/qa2009/qa1678.html
I don't know if this applies to us. So far, I haven't noticed the problem,
so I haven't applied any of the techniques.
*/
static int CoreAudio_seek(Sound_Sample *sample, Uint32 ms)
{
OSStatus error_result = noErr;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private;
SInt64 frame_offset = 0;
AudioStreamBasicDescription actual_format;
UInt32 format_size;
/* I'm confused. The Apple documentation says this:
"Seek position is specified in the sample rate and frame count of the file’s audio data format
not your applications audio data format."
My interpretation is that I want to get the "actual format of the file and compute the frame offset.
But when I try that, seeking goes to the wrong place.
When I use outputFormat, things seem to work correctly.
I must be misinterpreting the documentation or doing something wrong.
*/
#if 0 /* not working */
format_size = sizeof(AudioStreamBasicDescription);
error_result = AudioFileGetProperty(
*core_audio_file_container->audioFileID,
kAudioFilePropertyDataFormat,
&format_size,
&actual_format
);
if(error_result != noErr)
{
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
BAIL_MACRO("Core Audio: Could not GetProperty for kAudioFilePropertyDataFormat.", 0);
} /* if */
// packetIndex = (pos * sampleRate) / framesPerPacket
// frame_offset = (SInt64)((ms/1000.0 * actual_format.mSampleRate) / actual_format.mFramesPerPacket);
#else /* seems to work, but I'm confused */
// packetIndex = (pos * sampleRate) / framesPerPacket
frame_offset = (SInt64)((ms/1000.0 * core_audio_file_container->outputFormat->mSampleRate) / core_audio_file_container->outputFormat->mFramesPerPacket);
#endif
// computed against actual format and not the client format
error_result = ExtAudioFileSeek(core_audio_file_container->extAudioFileRef, frame_offset);
if(error_result != noErr)
{
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
}
return 1;
} /* CoreAudio_seek */
static const char *extensions_coreaudio[] =
{
"aif",
"aiff",
"aifc",
"wav",
"wave",
"mp3",
"mp4",
"m4a",
"aac",
"caf",
"Sd2f",
"Sd2",
"au",
"next",
"mp2",
"mp1",
"ac3",
"3gpp",
"3gp2",
"amrf",
"amr",
"ima4",
"ima",
NULL
};
const Sound_DecoderFunctions __Sound_DecoderFunctions_CoreAudio =
{
{
extensions_coreaudio,
"Decode audio through Core Audio through",
"Eric Wing <ewing . public @ playcontrol.net>",
"https://playcontrol.net"
},
CoreAudio_init, /* init() method */
CoreAudio_quit, /* quit() method */
CoreAudio_open, /* open() method */
CoreAudio_close, /* close() method */
CoreAudio_read, /* read() method */
CoreAudio_rewind, /* rewind() method */
CoreAudio_seek /* seek() method */
};
#endif /* SOUND_SUPPORTS_COREAUDIO */
/* end of SDL_sound_coreaudio.c ... */

182
SDL2_sound/SDL_sound_flac.c Normal file
View file

@ -0,0 +1,182 @@
/**
* SDL_sound; An abstract sound format decoding API.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
/*
* FLAC decoder for SDL_sound.
*
* FLAC stands for Free Lossless Audio Codec. It's lossless and
* high-resolution, so it's become popular with tapers and audiophiles, at
* the cost of a much larger filesize than lossy things like Ogg, MP3, AAC,
* etc. More details can be found at https://xiph.org/flac/ ...
*
* This doesn't use libFLAC from xiph.org, but rather dr_flac, a public
* domain, single-header library, for decoding. It handles both FLAC and
* "Ogg FLAC" (FLAC codec wrapped in an Ogg container) files.
*
* dr_flac is here: https://github.com/mackron/dr_libs/
*/
#define __SDL_SOUND_INTERNAL__
#include "SDL_sound_internal.h"
#if SOUND_SUPPORTS_FLAC
#define DR_FLAC_IMPLEMENTATION
#define DR_FLAC_NO_STDIO 1
#define DR_FLAC_NO_WIN32_IO 1
#define DR_FLAC_NO_CRC 1
#define DRFLAC_ASSERT(x) SDL_assert((x))
#define DRFLAC_MALLOC(sz) SDL_malloc((sz))
#define DRFLAC_REALLOC(p, sz) SDL_realloc((p), (sz))
#define DRFLAC_FREE(p) SDL_free((p))
#define DRFLAC_COPY_MEMORY(dst, src, sz) SDL_memcpy((dst), (src), (sz))
#define DRFLAC_ZERO_MEMORY(p, sz) SDL_memset((p), 0, (sz))
#include "dr_flac.h"
static size_t flac_read(void* pUserData, void* pBufferOut, size_t bytesToRead)
{
Uint8 *ptr = (Uint8 *) pBufferOut;
Sound_Sample *sample = (Sound_Sample *) pUserData;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
SDL_RWops *rwops = internal->rw;
size_t retval = 0;
/* !!! FIXME: dr_flac treats returning less than bytesToRead as EOF. So we can't EAGAIN. */
while (retval < bytesToRead)
{
const size_t rc = SDL_RWread(rwops, ptr, 1, bytesToRead);
if (rc == 0)
{
sample->flags |= SOUND_SAMPLEFLAG_EOF;
break;
} /* if */
else if (retval == -1)
{
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
break;
} /* else if */
else
{
retval += rc;
ptr += rc;
} /* else */
} /* while */
return retval;
} /* flac_read */
static drflac_bool32 flac_seek(void* pUserData, int offset, drflac_seek_origin origin)
{
const int whence = (origin == drflac_seek_origin_start) ? RW_SEEK_SET : RW_SEEK_CUR;
Sound_Sample *sample = (Sound_Sample *) pUserData;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
return (SDL_RWseek(internal->rw, offset, whence) != -1) ? DRFLAC_TRUE : DRFLAC_FALSE;
} /* flac_seek */
static int FLAC_init(void)
{
return 1; /* always succeeds. */
} /* FLAC_init */
static void FLAC_quit(void)
{
/* it's a no-op. */
} /* FLAC_quit */
static int FLAC_open(Sound_Sample *sample, const char *ext)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
drflac *dr = drflac_open(flac_read, flac_seek, sample);
if (!dr)
{
BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_ERROR, ERR_IO_ERROR, 0);
BAIL_MACRO("FLAC: Not a FLAC stream.", 0);
} /* if */
SNDDBG(("FLAC: Accepting data stream.\n"));
sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
sample->actual.channels = dr->channels;
sample->actual.rate = dr->sampleRate;
sample->actual.format = AUDIO_S32SYS; /* dr_flac only does Sint32. */
if (dr->totalSampleCount == 0)
internal->total_time = -1;
else
{
const Uint32 rate = (Uint32) dr->sampleRate;
const Uint64 frames = (Uint64) (dr->totalSampleCount / dr->channels);
internal->total_time = (frames / rate) * 1000;
internal->total_time += ((dr->totalSampleCount % dr->sampleRate) * 1000) / dr->sampleRate;
} /* else */
internal->decoder_private = dr;
return 1;
} /* FLAC_open */
static void FLAC_close(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
drflac *dr = (drflac *) internal->decoder_private;
drflac_close(dr);
} /* FLAC_close */
static Uint32 FLAC_read(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
drflac *dr = (drflac *) internal->decoder_private;
const drflac_uint64 rc = drflac_read_s32(dr, internal->buffer_size / sizeof (drflac_int32), (drflac_int32 *) internal->buffer);
/* !!! FIXME: the flac_read callback sets ERROR and EOF flags, but this only tells you about i/o errors, not corruption. */
return rc * sizeof (drflac_int32);
} /* FLAC_read */
static int FLAC_rewind(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
drflac *dr = (drflac *) internal->decoder_private;
return (drflac_seek_to_sample(dr, 0) == DRFLAC_TRUE);
} /* FLAC_rewind */
static int FLAC_seek(Sound_Sample *sample, Uint32 ms)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
drflac *dr = (drflac *) internal->decoder_private;
const float frames_per_ms = ((float) sample->actual.rate) / 1000.0f;
const drflac_uint64 frame_offset = (drflac_uint64) (frames_per_ms * ((float) ms));
const drflac_uint64 sampnum = frame_offset * sample->actual.channels;
return (drflac_seek_to_sample(dr, sampnum) == DRFLAC_TRUE);
} /* FLAC_seek */
static const char *extensions_flac[] = { "FLAC", "FLA", NULL };
const Sound_DecoderFunctions __Sound_DecoderFunctions_FLAC =
{
{
extensions_flac,
"Free Lossless Audio Codec",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/SDL_sound/"
},
FLAC_init, /* init() method */
FLAC_quit, /* quit() method */
FLAC_open, /* open() method */
FLAC_close, /* close() method */
FLAC_read, /* read() method */
FLAC_rewind, /* rewind() method */
FLAC_seek /* seek() method */
};
#endif /* SOUND_SUPPORTS_FLAC */
/* end of SDL_sound_flac.c ... */

View file

@ -0,0 +1,304 @@
/**
* SDL_sound; An abstract sound format decoding API.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
/*
* Internal function/structure declaration. Do NOT include in your
* application.
*/
#ifndef _INCLUDE_SDL_SOUND_INTERNAL_H_
#define _INCLUDE_SDL_SOUND_INTERNAL_H_
#ifndef __SDL_SOUND_INTERNAL__
#error Do not include this header from your applications.
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "SDL_sound.h"
#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
#define SOUND_HAVE_PRAGMA_VISIBILITY 1
#endif
#if SOUND_HAVE_PRAGMA_VISIBILITY
#pragma GCC visibility push(hidden)
#endif
#if (defined DEBUG_CHATTER)
#define SNDDBG(x) SDL_LogDebug x
#else
#define SNDDBG(x)
#endif
#ifndef SOUND_SUPPORTS_MP3
#define SOUND_SUPPORTS_MP3 1
#endif
#ifndef SOUND_SUPPORTS_MODPLUG
#define SOUND_SUPPORTS_MODPLUG 0
#endif
#ifndef SOUND_SUPPORTS_WAV
#define SOUND_SUPPORTS_WAV 1
#endif
#ifndef SOUND_SUPPORTS_AIFF
#define SOUND_SUPPORTS_AIFF 0
#endif
#ifndef SOUND_SUPPORTS_AU
#define SOUND_SUPPORTS_AU 0
#endif
#ifndef SOUND_SUPPORTS_VORBIS
#define SOUND_SUPPORTS_VORBIS 1
#endif
#ifndef SOUND_SUPPORTS_VOC
#define SOUND_SUPPORTS_VOC 0
#endif
#ifndef SOUND_SUPPORTS_RAW
#define SOUND_SUPPORTS_RAW 0
#endif
#ifndef SOUND_SUPPORTS_SHN
#define SOUND_SUPPORTS_SHN 0
#endif
#ifndef SOUND_SUPPORTS_FLAC
#define SOUND_SUPPORTS_FLAC 0
#endif
#ifndef SOUND_SUPPORTS_COREAUDIO
#define SOUND_SUPPORTS_COREAUDIO 1
#endif
/* only build CoreAudio support if on an Apple platform. */
#if SOUND_SUPPORTS_COREAUDIO && !defined(__APPLE__)
#undef SOUND_SUPPORTS_COREAUDIO
#define SOUND_SUPPORTS_COREAUDIO 0
#endif
/*
* SDL itself only supports mono and stereo output, but hopefully we can
* raise this value someday...there's probably a lot of assumptions in
* SDL_sound that rely on it, though.
* !!! FIXME: SDL2 supports more channels.
*/
#define MAX_CHANNELS 2
typedef struct __SOUND_DECODERFUNCTIONS__
{
/* This is a block of info about your decoder. See SDL_sound.h. */
const Sound_DecoderInfo info;
/*
* This is called during the Sound_Init() function. Use this to
* set up any global state that your decoder needs, such as
* initializing an external library, etc.
*
* Return non-zero if initialization is successful, zero if there's
* a fatal error. If this method fails, then this decoder is
* flagged as unavailable until SDL_sound() is shut down and
* reinitialized, in which case this method will be tried again.
*
* Note that the decoders quit() method won't be called if this
* method fails, so if you can't intialize, you'll have to clean
* up the half-initialized state in this method.
*/
int (*init)(void);
/*
* This is called during the Sound_Quit() function. Use this to
* clean up any global state that your decoder has used during its
* lifespan.
*/
void (*quit)(void);
/*
* Returns non-zero if (sample) has a valid fileformat that this
* driver can handle. Zero if this driver can NOT handle the data.
*
* Extension, which may be NULL, is just a hint as to the form of
* data that is being passed in. Most decoders should determine if
* they can handle the data by the data itself, but others, like
* the raw data handler, need this hint to know if they should
* accept the data in the first place.
*
* (sample)'s (opaque) field should be cast to a Sound_SampleInternal
* pointer:
*
* Sound_SampleInternal *internal;
* internal = (Sound_SampleInternal *) sample->opaque;
*
* Certain fields of sample will be filled in for the decoder before
* this call, and others should be filled in by the decoder. Some
* fields are offlimits, and should NOT be modified. The list:
*
* in Sound_SampleInternal section:
* Sound_Sample *next; (offlimits)
* Sound_Sample *prev; (offlimits)
* SDL_RWops *rw; (can use, but do NOT close it)
* const Sound_DecoderFunctions *funcs; (that's this structure)
* SDL_AudioCVT sdlcvt; (offlimits)
* void *buffer; (offlimits until read() method)
* Uint32 buffer_size; (offlimits until read() method)
* void *decoder_private; (read and write access)
*
* in rest of Sound_Sample:
* void *opaque; (this was internal section, above)
* const Sound_DecoderInfo *decoder; (read only)
* Sound_AudioInfo desired; (read only, usually not needed here)
* Sound_AudioInfo actual; (please fill this in)
* void *buffer; (offlimits)
* Uint32 buffer_size; (offlimits)
* Sound_SampleFlags flags; (set appropriately)
*/
int (*open)(Sound_Sample *sample, const char *ext);
/*
* Clean up. SDL_sound is done with this sample, so the decoder should
* clean up any resources it allocated. Anything that wasn't
* explicitly allocated by the decoder should be LEFT ALONE, since
* the higher-level SDL_sound layer will clean up its own mess.
*/
void (*close)(Sound_Sample *sample);
/*
* Get more data from (sample). The decoder should get a pointer to
* the internal structure...
*
* Sound_SampleInternal *internal;
* internal = (Sound_SampleInternal *) sample->opaque;
*
* ...and then start decoding. Fill in up to internal->buffer_size
* bytes of decoded sound in the space pointed to by
* internal->buffer. The encoded data is read in from internal->rw.
* Data should be decoded in the format specified during the
* decoder's open() method in the sample->actual field. The
* conversion to the desired format is done at a higher level.
*
* The return value is the number of bytes decoded into
* internal->buffer, which can be no more than internal->buffer_size,
* but can be less. If it is less, you should set a state flag:
*
* If there's just no more data (end of file, etc), then do:
* sample->flags |= SOUND_SAMPLEFLAG_EOF;
*
* If there's an unrecoverable error, then do:
* __Sound_SetError(ERR_EXPLAIN_WHAT_WENT_WRONG);
* sample->flags |= SOUND_SAMPLEFLAG_ERROR;
*
* If there's more data, but you'd have to block for considerable
* amounts of time to get at it, or there's a recoverable error,
* then do:
* __Sound_SetError(ERR_EXPLAIN_WHAT_WENT_WRONG);
* sample->flags |= SOUND_SAMPLEFLAG_EAGAIN;
*
* SDL_sound will not call your read() method for any samples with
* SOUND_SAMPLEFLAG_EOF or SOUND_SAMPLEFLAG_ERROR set. The
* SOUND_SAMPLEFLAG_EAGAIN flag is reset before each call to this
* method.
*/
Uint32 (*read)(Sound_Sample *sample);
/*
* Reset the decoding to the beginning of the stream. Nonzero on
* success, zero on failure.
*
* The purpose of this method is to allow for higher efficiency than
* an application could get by just recreating the sample externally;
* not only do they not have to reopen the RWops, reallocate buffers,
* and potentially pass the data through several rejecting decoders,
* but certain decoders will not have to recreate their existing
* state (search for metadata, etc) since they already know they
* have a valid audio stream with a given set of characteristics.
*
* The decoder is responsible for calling seek() on the associated
* SDL_RWops. A failing call to seek() should be the ONLY reason that
* this method should ever fail!
*/
int (*rewind)(Sound_Sample *sample);
/*
* Reposition the decoding to an arbitrary point. Nonzero on
* success, zero on failure.
*
* The purpose of this method is to allow for higher efficiency than
* an application could get by just rewinding the sample and
* decoding to a given point.
*
* The decoder is responsible for calling seek() on the associated
* SDL_RWops.
*
* If there is an error, try to recover so that the next read will
* continue as if nothing happened.
*/
int (*seek)(Sound_Sample *sample, Uint32 ms);
} Sound_DecoderFunctions;
typedef void (*MixFunc)(float *dst, void *src, Uint32 frames, float *gains);
typedef struct __SOUND_SAMPLEINTERNAL__
{
Sound_Sample *next;
Sound_Sample *prev;
SDL_RWops *rw;
const Sound_DecoderFunctions *funcs;
SDL_AudioCVT sdlcvt;
void *buffer;
Uint32 buffer_size;
void *decoder_private;
Sint32 total_time;
Uint32 mix_position;
MixFunc mix;
} Sound_SampleInternal;
/* error messages... */
#define ERR_IS_INITIALIZED "Already initialized"
#define ERR_NOT_INITIALIZED "Not initialized"
#define ERR_INVALID_ARGUMENT "Invalid argument"
#define ERR_OUT_OF_MEMORY "Out of memory"
#define ERR_NOT_SUPPORTED "Operation not supported"
#define ERR_UNSUPPORTED_FORMAT "Sound format unsupported"
#define ERR_NOT_A_HANDLE "Not a file handle"
#define ERR_NO_SUCH_FILE "No such file"
#define ERR_PAST_EOF "Past end of file"
#define ERR_IO_ERROR "I/O error"
#define ERR_COMPRESSION "(De)compression error"
#define ERR_PREV_ERROR "Previous decoding already caused an error"
#define ERR_PREV_EOF "Previous decoding already triggered EOF"
#define ERR_CANNOT_SEEK "Sample is not seekable"
/*
* Call this to set the message returned by Sound_GetError().
* Please only use the ERR_* constants above, or add new constants to the
* above group, but I want these all in one place.
*
* Calling this with a NULL argument is a safe no-op.
*/
void __Sound_SetError(const char *err);
/*
* Call this to convert milliseconds to an actual byte position, based on
* audio data characteristics.
*/
Uint32 __Sound_convertMsToBytePos(Sound_AudioInfo *info, Uint32 ms);
/* These get used all over for lessening code clutter. */
#define BAIL_MACRO(e, r) { __Sound_SetError(e); return r; }
#define BAIL_IF_MACRO(c, e, r) if (c) { __Sound_SetError(e); return r; }
#ifdef __cplusplus
extern "C" {
#endif
#endif /* defined _INCLUDE_SDL_SOUND_INTERNAL_H_ */
/* end of SDL_sound_internal.h ... */

View file

@ -0,0 +1,228 @@
/**
* SDL_sound; An abstract sound format decoding API.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Torbjörn Andersson.
*/
/*
* Module player for SDL_sound. This driver handles anything that ModPlug does.
*
* ModPlug can be found at https://sourceforge.net/projects/modplug-xmms
*
* An unofficial version of modplug with all C++ dependencies removed is also
* available: http://freecraft.net/snapshots/
* (Look for something like "libmodplug-johns-*.tar.gz")
* (update: this domain is gone. --ryan.)
*/
#define __SDL_SOUND_INTERNAL__
#include "SDL_sound_internal.h"
#if SOUND_SUPPORTS_MODPLUG
#include "libmodplug/modplug.h"
static const char *extensions_modplug[] =
{
/* The XMMS plugin is apparently able to load compressed modules as
* well, but libmodplug does not handle this.
*/
"669", /* Composer 669 / UNIS 669 module */
"AMF", /* ASYLUM Music Format / Advanced Music Format(DSM) */
"AMS", /* AMS module */
"DBM", /* DigiBooster Pro Module */
"DMF", /* DMF DELUSION DIGITAL MUSIC FILEFORMAT (X-Tracker) */
"DSM", /* DSIK Internal Format module */
"FAR", /* Farandole module */
"IT", /* Impulse Tracker IT file */
"MDL", /* DigiTracker module */
"MED", /* OctaMed MED file */
"MOD", /* ProTracker / NoiseTracker MOD/NST file */
"MT2", /* MadTracker 2.0 */
"MTM", /* MTM file */
"OKT", /* Oktalyzer module */
"PTM", /* PTM PolyTracker module */
"PSM", /* PSM module */
"S3M", /* ScreamTracker file */
"STM", /* ST 2.xx */
"ULT",
"UMX",
"XM", /* FastTracker II */
"ABC",
"MID",
"MIDI",
NULL
};
static int MODPLUG_init(void)
{
return ModPlug_Init(); /* success. */
} /* MODPLUG_init */
static void MODPLUG_quit(void)
{
/* it's a no-op. */
} /* MODPLUG_quit */
/*
* Most MOD files I've seen have tended to be a few hundred KB, even if some
* of them were much smaller than that.
*/
#define CHUNK_SIZE 65536
static int MODPLUG_open(Sound_Sample *sample, const char *ext)
{
ModPlug_Settings settings;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
ModPlugFile *module;
Uint8 *data;
size_t size;
Uint32 retval;
int has_extension = 0;
int i;
/*
* Apparently ModPlug's loaders are too forgiving. They gladly accept
* streams that they shouldn't. For now, rely on file extension instead.
*/
for (i = 0; extensions_modplug[i] != NULL; i++)
{
if (SDL_strcasecmp(ext, extensions_modplug[i]) == 0)
{
has_extension = 1;
break;
} /* if */
} /* for */
if (!has_extension)
{
SNDDBG(("MODPLUG: Unrecognized file type: %s\n", ext));
BAIL_MACRO("MODPLUG: Not a module file.", 0);
} /* if */
/* ModPlug needs the entire stream in one big chunk. I don't like it,
but I don't think there's any way around it. !!! FIXME: rework modplug? */
data = (Uint8 *) SDL_malloc(CHUNK_SIZE);
BAIL_IF_MACRO(data == NULL, ERR_OUT_OF_MEMORY, 0);
size = 0;
do
{
retval = SDL_RWread(internal->rw, &data[size], 1, CHUNK_SIZE);
size += retval;
if (retval == CHUNK_SIZE)
{
data = (Uint8 *) SDL_realloc(data, size + CHUNK_SIZE);
BAIL_IF_MACRO(data == NULL, ERR_OUT_OF_MEMORY, 0);
} /* if */
} while (retval > 0);
SDL_memcpy(&sample->actual, &sample->desired, sizeof (Sound_AudioInfo));
if (sample->actual.rate == 0) sample->actual.rate = 44100;
if (sample->actual.channels == 0) sample->actual.channels = 2;
if (sample->actual.format == 0) sample->actual.format = AUDIO_S16SYS;
settings.mChannels=sample->actual.channels;
settings.mFrequency=sample->actual.rate;
settings.mBits = sample->actual.format & 0xFF;
/* The settings will require some experimenting. I've borrowed some
of them from the XMMS ModPlug plugin. */
settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING;
settings.mFlags |= MODPLUG_ENABLE_NOISE_REDUCTION |
MODPLUG_ENABLE_MEGABASS |
MODPLUG_ENABLE_SURROUND;
settings.mReverbDepth = 30;
settings.mReverbDelay = 100;
settings.mBassAmount = 40;
settings.mBassRange = 30;
settings.mSurroundDepth = 20;
settings.mSurroundDelay = 20;
settings.mChannels = 2;
settings.mBits = 16;
settings.mFrequency = 44100;
settings.mResamplingMode = MODPLUG_RESAMPLE_FIR;
settings.mLoopCount = 0;
/* The buffer may be a bit too large, but that doesn't matter. I think
it's safe to free it as soon as ModPlug_Load() is finished anyway. */
module = ModPlug_Load((void *) data, size, &settings);
SDL_free(data);
BAIL_IF_MACRO(module == NULL, "MODPLUG: Not a module file.", 0);
internal->total_time = ModPlug_GetLength(module);
internal->decoder_private = (void *) module;
sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
SNDDBG(("MODPLUG: Accepting data stream\n"));
return 1; /* we'll handle this data. */
} /* MODPLUG_open */
static void MODPLUG_close(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
ModPlugFile *module = (ModPlugFile *) internal->decoder_private;
ModPlug_Unload(module);
} /* MODPLUG_close */
static Uint32 MODPLUG_read(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
ModPlugFile *module = (ModPlugFile *) internal->decoder_private;
int retval;
retval = ModPlug_Read(module, internal->buffer, internal->buffer_size);
if (retval == 0)
sample->flags |= SOUND_SAMPLEFLAG_EOF;
return retval;
} /* MODPLUG_read */
static int MODPLUG_rewind(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
ModPlugFile *module = (ModPlugFile *) internal->decoder_private;
ModPlug_Seek(module, 0);
return 1;
} /* MODPLUG_rewind */
static int MODPLUG_seek(Sound_Sample *sample, Uint32 ms)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
ModPlugFile *module = (ModPlugFile *) internal->decoder_private;
ModPlug_Seek(module, ms);
return 1;
} /* MODPLUG_seek */
const Sound_DecoderFunctions __Sound_DecoderFunctions_MODPLUG =
{
{
extensions_modplug,
"Play modules through ModPlug",
"Torbjörn Andersson <d91tan@Update.UU.SE>",
"http://modplug-xmms.sourceforge.net/"
},
MODPLUG_init, /* init() method */
MODPLUG_quit, /* quit() method */
MODPLUG_open, /* open() method */
MODPLUG_close, /* close() method */
MODPLUG_read, /* read() method */
MODPLUG_rewind, /* rewind() method */
MODPLUG_seek /* seek() method */
};
#endif /* SOUND_SUPPORTS_MODPLUG */
/* end of SDL_sound_modplug.c ... */

184
SDL2_sound/SDL_sound_mp3.c Normal file
View file

@ -0,0 +1,184 @@
/**
* SDL_sound; An abstract sound format decoding API.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
/*
* MP3 decoder for SDL_sound.
*
* !!! FIXME: write something here.
*
* dr_mp3 is here: https://github.com/mackron/dr_libs/
*/
#define __SDL_SOUND_INTERNAL__
#include "SDL_sound_internal.h"
#if SOUND_SUPPORTS_MP3
#define DR_MP3_IMPLEMENTATION
#define DR_MP3_NO_STDIO 1
#define DRMP3_ASSERT(x) SDL_assert((x))
#define DRMP3_MALLOC(sz) SDL_malloc((sz))
#define DRMP3_REALLOC(p, sz) SDL_realloc((p), (sz))
#define DRMP3_FREE(p) SDL_free((p))
#define DRMP3_COPY_MEMORY(dst, src, sz) SDL_memcpy((dst), (src), (sz))
#define DRMP3_ZERO_MEMORY(p, sz) SDL_memset((p), 0, (sz))
#if !defined(__clang_analyzer__)
#ifdef memset
#undef memset
#endif
#ifdef memcpy
#undef memcpy
#endif
#ifdef memmove
#undef memmove
#endif
#define memset SDL_memset
#define memcpy SDL_memcpy
#define memmove SDL_memmove
#endif
#include "dr_mp3.h"
static size_t mp3_read(void* pUserData, void* pBufferOut, size_t bytesToRead)
{
Uint8 *ptr = (Uint8 *) pBufferOut;
Sound_Sample *sample = (Sound_Sample *) pUserData;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
SDL_RWops *rwops = internal->rw;
size_t retval = 0;
/* !!! FIXME: dr_mp3 treats returning less than bytesToRead as EOF. So we can't EAGAIN. */
while (retval < bytesToRead)
{
const size_t rc = SDL_RWread(rwops, ptr, 1, bytesToRead);
if (rc == 0)
{
sample->flags |= SOUND_SAMPLEFLAG_EOF;
break;
} /* if */
else if (retval == -1)
{
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
break;
} /* else if */
else
{
retval += rc;
ptr += rc;
} /* else */
} /* while */
return retval;
} /* mp3_read */
static drmp3_bool32 mp3_seek(void* pUserData, int offset, drmp3_seek_origin origin)
{
const int whence = (origin == drmp3_seek_origin_start) ? RW_SEEK_SET : RW_SEEK_CUR;
Sound_Sample *sample = (Sound_Sample *) pUserData;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
return (SDL_RWseek(internal->rw, offset, whence) != -1) ? DRMP3_TRUE : DRMP3_FALSE;
} /* mp3_seek */
static int MP3_init(void)
{
return 1; /* always succeeds. */
} /* MP3_init */
static void MP3_quit(void)
{
/* it's a no-op. */
} /* MP3_quit */
static int MP3_open(Sound_Sample *sample, const char *ext)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
drmp3 *dr = (drmp3 *) SDL_calloc(1, sizeof (drmp3));
BAIL_IF_MACRO(!dr, ERR_OUT_OF_MEMORY, 0);
if (drmp3_init(dr, mp3_read, mp3_seek, sample, NULL) != DRMP3_TRUE)
{
SDL_free(dr);
BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_ERROR, ERR_IO_ERROR, 0);
BAIL_MACRO("MP3: Not an MPEG-1 layer 1-3 stream.", 0);
} /* if */
SNDDBG(("MP3: Accepting data stream.\n"));
sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
sample->actual.channels = dr->channels;
sample->actual.rate = dr->sampleRate;
sample->actual.format = AUDIO_F32SYS; /* dr_mp3 only does float. */
internal->total_time = -1; /* !!! FIXME? */
internal->decoder_private = dr;
return 1;
} /* MP3_open */
static void MP3_close(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
drmp3 *dr = (drmp3 *) internal->decoder_private;
drmp3_uninit(dr);
SDL_free(dr);
} /* MP3_close */
static Uint32 MP3_read(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
const int channels = (int) sample->actual.channels;
drmp3 *dr = (drmp3 *) internal->decoder_private;
const drmp3_uint64 frames_to_read = (internal->buffer_size / channels) / sizeof (float);
const drmp3_uint64 rc = drmp3_read_f32(dr, frames_to_read, (float *) internal->buffer);
/* !!! FIXME: the mp3_read callback sets ERROR and EOF flags, but this only tells you about i/o errors, not corruption. */
return rc * channels * sizeof (float);
} /* MP3_read */
static int MP3_rewind(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
drmp3 *dr = (drmp3 *) internal->decoder_private;
return (drmp3_seek_to_frame(dr, 0) == DRMP3_TRUE);
} /* MP3_rewind */
static int MP3_seek(Sound_Sample *sample, Uint32 ms)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
drmp3 *dr = (drmp3 *) internal->decoder_private;
const float frames_per_ms = ((float) sample->actual.rate) / 1000.0f;
const drmp3_uint64 frame_offset = (drmp3_uint64) (frames_per_ms * ((float) ms));
return (drmp3_seek_to_frame(dr, frame_offset) == DRMP3_TRUE);
} /* MP3_seek */
/* dr_mp3 will play layer 1 and 2 files, too */
static const char *extensions_mp3[] = { "MP3", "MP2", "MP1", NULL };
const Sound_DecoderFunctions __Sound_DecoderFunctions_MP3 =
{
{
extensions_mp3,
"MPEG-1 Audio Layer I-III",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/SDL_sound/"
},
MP3_init, /* init() method */
MP3_quit, /* quit() method */
MP3_open, /* open() method */
MP3_close, /* close() method */
MP3_read, /* read() method */
MP3_rewind, /* rewind() method */
MP3_seek /* seek() method */
};
#endif /* SOUND_SUPPORTS_MP3 */
/* end of SDL_sound_mp3.c ... */

164
SDL2_sound/SDL_sound_raw.c Normal file
View file

@ -0,0 +1,164 @@
/**
* SDL_sound; A sound processing toolkit.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
/*
* RAW decoder for SDL_sound. This is as simple as it gets.
*
* This driver handles raw audio data. You must, regardless of where the
* data is actually coming from, specify the string "RAW" in the extension
* parameter of Sound_NewSample() (or, alternately, open a file with the
* extension ".raw" in Sound_NewSampleFromFile()). The string is checked
* case-insensitive. We need this check, because raw data, being raw, has
* no headers or magic number we can use to determine if we should handle a
* given file, so we needed some way to have this "decoder" discriminate.
*
* When calling Sound_NewSample*(), you must also specify a "desired"
* audio format. The "actual" format will always match what you specify, so
* there will be no conversion overhead, but these routines need to know how
* to treat the bits, since it's all random garbage otherwise.
*/
#define __SDL_SOUND_INTERNAL__
#include "SDL_sound_internal.h"
#if SOUND_SUPPORTS_RAW
static int RAW_init(void)
{
return 1; /* always succeeds. */
} /* RAW_init */
static void RAW_quit(void)
{
/* it's a no-op. */
} /* RAW_quit */
static int RAW_open(Sound_Sample *sample, const char *ext)
{
Sound_SampleInternal *internal = sample->opaque;
SDL_RWops *rw = internal->rw;
Uint32 pos, sample_rate;
/*
* We check this explicitly, since we have no other way to
* determine whether we should handle this data or not.
*/
if (SDL_strcasecmp(ext, "RAW") != 0)
BAIL_MACRO("RAW: extension isn't explicitly \"RAW\".", 0);
/*
* You must also specify a desired format, so we know how to
* treat the bits that are otherwise binary garbage.
*/
if ( (sample->desired.channels < 1) ||
(sample->desired.channels > 2) ||
(sample->desired.rate == 0) ||
(sample->desired.format == 0) )
{
BAIL_MACRO("RAW: invalid desired format.", 0);
} /* if */
SNDDBG(("RAW: Accepting data stream.\n"));
/*
* We never convert raw samples; what you ask for is what you get.
*/
SDL_memcpy(&sample->actual, &sample->desired, sizeof (Sound_AudioInfo));
sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
if ( (pos = SDL_RWseek(internal->rw, 0, SEEK_END) ) <= 0) {
BAIL_MACRO("RAW: cannot seek the end of the file \"RAW\".", 0);
}
if ( SDL_RWseek(internal->rw, 0, SEEK_SET) ) {
BAIL_MACRO("RAW: cannot reset file \"RAW\".", 0);
}
sample_rate = (sample->actual.rate * sample->actual.channels
* ( (sample->actual.format & 0x0018) >> 3) );
internal->total_time = ( pos ) / sample_rate * 1000;
internal->total_time += (pos % sample_rate) * 1000 / sample_rate;
return 1; /* we'll handle this data. */
} /* RAW_open */
static void RAW_close(Sound_Sample *sample)
{
/* we don't allocate anything that we need to free. That's easy, eh? */
} /* RAW_close */
static Uint32 RAW_read(Sound_Sample *sample)
{
Uint32 retval;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
/*
* We don't actually do any decoding, so we read the raw data
* directly into the internal buffer...
*/
retval = SDL_RWread(internal->rw, internal->buffer,
1, internal->buffer_size);
/* Make sure the read went smoothly... */
if (retval == 0)
sample->flags |= SOUND_SAMPLEFLAG_EOF;
else if (retval == -1)
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
/* (next call this EAGAIN may turn into an EOF or error.) */
else if (retval < internal->buffer_size)
sample->flags |= SOUND_SAMPLEFLAG_EAGAIN;
return retval;
} /* RAW_read */
static int RAW_rewind(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
BAIL_IF_MACRO(SDL_RWseek(internal->rw, 0, SEEK_SET) != 0, ERR_IO_ERROR, 0);
return 1;
} /* RAW_rewind */
static int RAW_seek(Sound_Sample *sample, Uint32 ms)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
int pos = (int) __Sound_convertMsToBytePos(&sample->actual, ms);
int err = (SDL_RWseek(internal->rw, pos, SEEK_SET) != pos);
BAIL_IF_MACRO(err, ERR_IO_ERROR, 0);
return 1;
} /* RAW_seek */
static const char *extensions_raw[] = { "RAW", NULL };
const Sound_DecoderFunctions __Sound_DecoderFunctions_RAW =
{
{
extensions_raw,
"Raw audio",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/SDL_sound/"
},
RAW_init, /* init() method */
RAW_quit, /* quit() method */
RAW_open, /* open() method */
RAW_close, /* close() method */
RAW_read, /* read() method */
RAW_rewind, /* rewind() method */
RAW_seek /* seek() method */
};
#endif /* SOUND_SUPPORTS_RAW */
/* end of SDL_sound_raw.c ... */

1309
SDL2_sound/SDL_sound_shn.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,144 @@
/**
* SDL_sound; A sound processing toolkit.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
/*
* FMT decoder for SDL_sound.
*
* This driver handles FMT audio data. Blahblahblah... The author should
* have done a search and replace on "fmt" and "FMT" and changed this
* comment. This is the default comment in the skeleton decoder file...
*
* None of this code, even the parts that LOOK right, have been compiled,
* so you cut-and-paste at your own risk.
*/
#error DO NOT COMPILE THIS.
#error This is an example decoder skeleton.
#error You should base your code on this file, and remove these error lines
#error from your version.
#define __SDL_SOUND_INTERNAL__
#include "SDL_sound_internal.h"
#if SOUND_SUPPORTS_FMT
static int FMT_init(void)
{
/* do any global decoder/library initialization you need here. */
return 1; /* initialization successful. */
} /* FMT_init */
static void FMT_quit(void)
{
/* do any global decoder/library cleanup you need here. */
} /* FMT_quit */
static int FMT_open(Sound_Sample *sample, const char *ext)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
SDL_RWops *rw = internal->rw;
if (can NOT accept the data)
BAIL_MACRO("FMT: expected X, got Y.", 0);
SNDDBG(("FMT: Accepting data stream.\n"));
set up sample->actual;
sample->flags = SOUND_SAMPLEFLAG_NONE;
return 1; /* we'll handle this data. */
} /* FMT_open */
static void FMT_close(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
clean up anything you put into internal->decoder_private;
} /* FMT_close */
static Uint32 FMT_read(Sound_Sample *sample)
{
Uint32 retval;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
/*
* We don't actually do any decoding, so we read the fmt data
* directly into the internal buffer...
*/
retval = SDL_RWread(internal->rw, internal->buffer,
1, internal->buffer_size);
(or whatever. Do some decoding here...)
/* Make sure the read went smoothly... */
if (retval == 0)
sample->flags |= SOUND_SAMPLEFLAG_EOF;
else if (retval == -1)
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
/* (next call this EAGAIN may turn into an EOF or error.) */
else if (retval < internal->buffer_size)
sample->flags |= SOUND_SAMPLEFLAG_EAGAIN;
(or whatever. retval == number of bytes you put in internal->buffer).
return retval;
} /* FMT_read */
static int FMT_rewind(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
/* seek to the appropriate place... */
BAIL_IF_MACRO(SDL_RWseek(internal->rw, 0, SEEK_SET) != 0, ERR_IO_ERROR, 0);
(reset state as necessary.)
return 1; /* success. */
} /* FMT_rewind */
static int FMT_seek(Sound_Sample *sample, Uint32 ms)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
/* seek to the appropriate place... */
BAIL_IF_MACRO(SDL_RWseek(internal->rw, 0, SEEK_SET) != 0, ERR_IO_ERROR, 0);
(set state as necessary.)
return 1; /* success. */
} /* FMT_seek */
static const char *extensions_fmt[] = { "FMT", NULL };
const Sound_DecoderFunctions __Sound_DecoderFunctions_FMT =
{
{
extensions_fmt,
"FMT audio format description",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/SDL_sound/"
},
FMT_init, /* init() method */
FMT_quit, /* quit() method */
FMT_open, /* open() method */
FMT_close, /* close() method */
FMT_read, /* read() method */
FMT_rewind, /* rewind() method */
FMT_seek /* seek() method */
};
#endif /* SOUND_SUPPORTS_FMT */
/* end of SDL_sound_fmt.c ... */

550
SDL2_sound/SDL_sound_voc.c Normal file
View file

@ -0,0 +1,550 @@
/**
* SDL_sound; A sound processing toolkit.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
/*
* VOC decoder for SDL_sound.
*
* This driver handles Creative Labs VOC audio data...this is a legacy format,
* but there's some game ports that could make use of such a decoder. Plus,
* VOC is fairly straightforward to decode, so this is a more complex, but
* still palatable example of an SDL_sound decoder. Y'know, in case the
* RAW decoder didn't do it for you. :)
*
* This code was ripped from a decoder I had written for SDL_mixer, which was
* largely ripped from sox v12.17.1's voc.c.
*
* SDL_mixer: https://www.libsdl.org/projects/SDL_mixer/
* sox: http://www.freshmeat.net/projects/sox/
*/
#define __SDL_SOUND_INTERNAL__
#include "SDL_sound_internal.h"
#if SOUND_SUPPORTS_VOC
/* Private data for VOC file */
typedef struct vocstuff {
Uint32 rest; /* bytes remaining in current block */
Uint32 rate; /* rate code (byte) of this chunk */
int silent; /* sound or silence? */
Uint32 srate; /* rate code (byte) of silence */
Uint32 blockseek; /* start of current output block */
Uint32 samples; /* number of samples output */
Uint32 size; /* word length of data */
Uint8 channels; /* number of sound channels */
int extended; /* Has an extended block been read? */
Uint32 bufpos; /* byte position in internal->buffer. */
Uint32 start_pos; /* offset to seek to in stream when rewinding. */
int error; /* error condition (as opposed to EOF). */
} vs_t;
/* Size field */
/* SJB: note that the 1st 3 are sometimes used as sizeof(type) */
#define ST_SIZE_BYTE 1
#define ST_SIZE_8BIT 1
#define ST_SIZE_WORD 2
#define ST_SIZE_16BIT 2
#define ST_SIZE_DWORD 4
#define ST_SIZE_32BIT 4
#define ST_SIZE_FLOAT 5
#define ST_SIZE_DOUBLE 6
#define ST_SIZE_IEEE 7 /* IEEE 80-bit floats. */
/* Style field */
#define ST_ENCODING_UNSIGNED 1 /* unsigned linear: Sound Blaster */
#define ST_ENCODING_SIGN2 2 /* signed linear 2's comp: Mac */
#define ST_ENCODING_ULAW 3 /* U-law signed logs: US telephony, SPARC */
#define ST_ENCODING_ALAW 4 /* A-law signed logs: non-US telephony */
#define ST_ENCODING_ADPCM 5 /* Compressed PCM */
#define ST_ENCODING_IMA_ADPCM 6 /* Compressed PCM */
#define ST_ENCODING_GSM 7 /* GSM 6.10 33-byte frame lossy compression */
#define VOC_TERM 0
#define VOC_DATA 1
#define VOC_CONT 2
#define VOC_SILENCE 3
#define VOC_MARKER 4
#define VOC_TEXT 5
#define VOC_LOOP 6
#define VOC_LOOPEND 7
#define VOC_EXTENDED 8
#define VOC_DATA_16 9
static int VOC_init(void)
{
return 1; /* always succeeds. */
} /* VOC_init */
static void VOC_quit(void)
{
/* it's a no-op. */
} /* VOC_quit */
static SDL_INLINE int voc_readbytes(SDL_RWops *src, vs_t *v, void *p, int size)
{
if (SDL_RWread(src, p, size, 1) != 1)
{
v->error = 1;
BAIL_MACRO("VOC: i/o error", 0);
} /* if */
return 1;
} /* voc_readbytes */
static SDL_INLINE int voc_check_header(SDL_RWops *src)
{
/* VOC magic header */
Uint8 signature[20]; /* "Creative Voice File\032" */
Uint16 datablockofs;
vs_t v; /* dummy struct for voc_readbytes */
if (!voc_readbytes(src, &v, signature, sizeof (signature)))
return 0;
if (SDL_memcmp(signature, "Creative Voice File\032", sizeof (signature)) != 0)
{
BAIL_MACRO("VOC: Wrong signature; not a VOC file.", 0);
} /* if */
/* get the offset where the first datablock is located */
if (!voc_readbytes(src, &v, &datablockofs, sizeof (Uint16)))
return 0;
datablockofs = SDL_SwapLE16(datablockofs);
if (SDL_RWseek(src, datablockofs, SEEK_SET) != datablockofs)
{
BAIL_MACRO("VOC: Failed to seek to data block.", 0);
} /* if */
return 1; /* success! */
} /* voc_check_header */
/* Read next block header, save info, leave position at start of data */
static int voc_get_block(Sound_Sample *sample, vs_t *v)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
SDL_RWops *src = internal->rw;
Uint8 bits24[3];
Uint8 uc, block;
Uint32 sblen;
Uint16 new_rate_short;
Uint32 new_rate_long;
Uint8 trash[6];
Uint16 period;
Uint32 bytes_per_second;
int i;
v->silent = 0;
while (v->rest == 0)
{
if (SDL_RWread(src, &block, sizeof (block), 1) != 1)
return 1; /* assume that's the end of the file. */
if (block == VOC_TERM)
return 1;
if (SDL_RWread(src, bits24, sizeof (bits24), 1) != 1)
return 1; /* assume that's the end of the file. */
/* Size is an 24-bit value. Ugh. */
sblen = ( (bits24[0]) | (bits24[1] << 8) | (bits24[2] << 16) );
switch(block)
{
case VOC_DATA:
if (!voc_readbytes(src, v, &uc, sizeof (uc)))
return 0;
/* When DATA block preceeded by an EXTENDED */
/* block, the DATA blocks rate value is invalid */
if (!v->extended)
{
BAIL_IF_MACRO(uc == 0, "VOC: Sample rate is zero?", 0);
if ((v->rate != -1) && (uc != v->rate))
BAIL_MACRO("VOC sample rate codes differ", 0);
v->rate = uc;
sample->actual.rate = 1000000.0/(256 - v->rate);
sample->actual.channels = 1;
v->channels = 1;
} /* if */
if (!voc_readbytes(src, v, &uc, sizeof (uc)))
return 0;
BAIL_IF_MACRO(uc != 0, "VOC: only supports 8-bit data", 0);
v->extended = 0;
v->rest = sblen - 2;
v->size = ST_SIZE_BYTE;
bytes_per_second = sample->actual.rate
* sample->actual.channels;
internal->total_time += ( v->rest ) / bytes_per_second * 1000;
internal->total_time += (v->rest % bytes_per_second) * 1000
/ bytes_per_second;
return 1;
case VOC_DATA_16:
if (!voc_readbytes(src, v, &new_rate_long, sizeof (Uint32)))
return 0;
new_rate_long = SDL_SwapLE32(new_rate_long);
BAIL_IF_MACRO(!new_rate_long, "VOC: Sample rate is zero?", 0);
if ((v->rate != -1) && (new_rate_long != v->rate))
BAIL_MACRO("VOC: sample rate codes differ", 0);
v->rate = new_rate_long;
sample->actual.rate = new_rate_long;
if (!voc_readbytes(src, v, &uc, sizeof (uc)))
return 0;
switch (uc)
{
case 8: v->size = ST_SIZE_BYTE; break;
case 16: v->size = ST_SIZE_WORD; break;
default:
BAIL_MACRO("VOC: unknown data size", 0);
} /* switch */
if (!voc_readbytes(src, v, &v->channels, sizeof (Uint8)))
return 0;
if (!voc_readbytes(src, v, trash, sizeof (Uint8) * 6))
return 0;
v->rest = sblen - 12;
bytes_per_second = ((v->size == ST_SIZE_WORD) ? (2) : (1)) *
sample->actual.rate * v->channels;
internal->total_time += v->rest / bytes_per_second * 1000;
internal->total_time += ( v->rest % bytes_per_second ) * 1000
/ bytes_per_second;
return 1;
case VOC_CONT:
v->rest = sblen;
return 1;
case VOC_SILENCE:
if (!voc_readbytes(src, v, &period, sizeof (period)))
return 0;
period = SDL_SwapLE16(period);
if (!voc_readbytes(src, v, &uc, sizeof (uc)))
return 0;
BAIL_IF_MACRO(uc == 0, "VOC: silence sample rate is zero", 0);
/*
* Some silence-packed files have gratuitously
* different sample rate codes in silence.
* Adjust period.
*/
if ((v->rate != -1) && (uc != v->rate))
period = (period * (256 - uc))/(256 - v->rate);
else
v->rate = uc;
v->rest = period;
v->silent = 1;
internal->total_time += (period) / (v->rate) * 1000;
internal->total_time += (period % v->rate) * 1000 / v->rate;
return 1;
case VOC_LOOP:
case VOC_LOOPEND:
for(i = 0; i < sblen; i++) /* skip repeat loops. */
{
if (!voc_readbytes(src, v, trash, sizeof (Uint8)))
return 0;
} /* for */
break;
case VOC_EXTENDED:
/* An Extended block is followed by a data block */
/* Set this byte so we know to use the rate */
/* value from the extended block and not the */
/* data block. */
v->extended = 1;
if (!voc_readbytes(src, v, &new_rate_short, sizeof (Uint16)))
return 0;
new_rate_short = SDL_SwapLE16(new_rate_short);
BAIL_IF_MACRO(!new_rate_short, "VOC: sample rate is zero", 0);
if ((v->rate != -1) && (new_rate_short != v->rate))
BAIL_MACRO("VOC: sample rate codes differ", 0);
v->rate = new_rate_short;
if (!voc_readbytes(src, v, &uc, sizeof (uc)))
return 0;
BAIL_IF_MACRO(uc != 0, "VOC: only supports 8-bit data", 0);
if (!voc_readbytes(src, v, &uc, sizeof (uc)))
return 0;
if (uc)
sample->actual.channels = 2; /* Stereo */
/* Needed number of channels before finishing
compute for rate */
sample->actual.rate =
(256000000L/(65536L - v->rate)) / sample->actual.channels;
/* An extended block must be followed by a data */
/* block to be valid so loop back to top so it */
/* can be grabed. */
continue;
case VOC_MARKER:
if (!voc_readbytes(src, v, trash, sizeof (Uint8) * 2))
return 0;
/* Falling! Falling! */
default: /* text block or other krapola. */
for(i = 0; i < sblen; i++) /* skip repeat loops. */
{
if (!voc_readbytes(src, v, trash, sizeof (Uint8)))
return 0;
} /* for */
if (block == VOC_TEXT)
continue; /* get next block */
} /* switch */
} /* while */
return 1;
} /* voc_get_block */
static int voc_read_waveform(Sound_Sample *sample, int fill_buf, Uint32 max)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
SDL_RWops *src = internal->rw;
vs_t *v = (vs_t *) internal->decoder_private;
int done = 0;
Uint8 silence = 0x80;
Uint8 *buf = internal->buffer;
if (v->rest == 0)
{
if (!voc_get_block(sample, v))
return 0;
} /* if */
if (v->rest == 0)
return 0;
max = (v->rest < max) ? v->rest : max;
if (v->silent)
{
if (v->size == ST_SIZE_WORD)
silence = 0x00;
/* Fill in silence */
if (fill_buf)
SDL_memset(buf + v->bufpos, silence, max);
done = max;
v->rest -= done;
} /* if */
else
{
if (fill_buf)
{
done = SDL_RWread(src, buf + v->bufpos, 1, max);
if (done < max)
{
__Sound_SetError("VOC: i/o error");
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
} /* if */
} /* if */
else
{
int cur, rc;
cur = SDL_RWtell(src);
if (cur >= 0)
{
rc = SDL_RWseek(src, max, SEEK_CUR);
if (rc >= 0)
done = rc - cur;
else
{
__Sound_SetError("VOC: seek error");
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
} /* else */
} /* if */
} /* else */
v->rest -= done;
v->bufpos += done;
} /* else */
return done;
} /* voc_read_waveform */
static int VOC_open(Sound_Sample *sample, const char *ext)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
vs_t *v = NULL;
if (!voc_check_header(internal->rw))
return 0;
v = (vs_t *) SDL_calloc(1, sizeof (vs_t));
BAIL_IF_MACRO(v == NULL, ERR_OUT_OF_MEMORY, 0);
v->start_pos = SDL_RWtell(internal->rw);
v->rate = -1;
if (!voc_get_block(sample, v))
{
SDL_free(v);
return 0;
} /* if */
if (v->rate == -1)
{
SDL_free(v);
BAIL_MACRO("VOC: data had no sound!", 0);
} /* if */
SNDDBG(("VOC: Accepting data stream.\n"));
sample->actual.format = (v->size == ST_SIZE_WORD) ? AUDIO_S16LSB:AUDIO_U8;
sample->actual.channels = v->channels;
sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
internal->decoder_private = v;
return 1;
} /* VOC_open */
static void VOC_close(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
SDL_free(internal->decoder_private);
} /* VOC_close */
static Uint32 VOC_read(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
vs_t *v = (vs_t *) internal->decoder_private;
v->bufpos = 0;
while (v->bufpos < internal->buffer_size)
{
Uint32 rc = voc_read_waveform(sample, 1, internal->buffer_size);
if (rc == 0)
{
sample->flags |= (v->error) ?
SOUND_SAMPLEFLAG_ERROR :
SOUND_SAMPLEFLAG_EOF;
break;
} /* if */
if (!voc_get_block(sample, v))
{
sample->flags |= (v->error) ?
SOUND_SAMPLEFLAG_ERROR :
SOUND_SAMPLEFLAG_EOF;
break;
} /* if */
} /* while */
return v->bufpos;
} /* VOC_read */
static int VOC_rewind(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
vs_t *v = (vs_t *) internal->decoder_private;
int rc = SDL_RWseek(internal->rw, v->start_pos, SEEK_SET);
BAIL_IF_MACRO(rc != v->start_pos, ERR_IO_ERROR, 0);
v->rest = 0;
return 1;
} /* VOC_rewind */
static int VOC_seek(Sound_Sample *sample, Uint32 ms)
{
/*
* VOCs don't lend themselves well to seeking, since you have to
* parse each section, which is an arbitrary size. The best we can do
* is rewind, set a flag saying not to write the waveforms to a buffer,
* and decode to the point that we want. Ugh. Fortunately, there's
* really no such thing as a large VOC, due to the era and hardware that
* spawned them, so even though this is inefficient, this is still a
* relatively fast operation in most cases.
*/
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
vs_t *v = (vs_t *) internal->decoder_private;
int offset = __Sound_convertMsToBytePos(&sample->actual, ms);
int origpos = SDL_RWtell(internal->rw);
int origrest = v->rest;
BAIL_IF_MACRO(!VOC_rewind(sample), NULL, 0);
v->bufpos = 0;
while (offset > 0)
{
Uint32 rc = voc_read_waveform(sample, 0, offset);
if ( (rc == 0) || (!voc_get_block(sample, v)) )
{
SDL_RWseek(internal->rw, origpos, SEEK_SET);
v->rest = origrest;
return 0;
} /* if */
offset -= rc;
} /* while */
return 1;
} /* VOC_seek */
static const char *extensions_voc[] = { "VOC", NULL };
const Sound_DecoderFunctions __Sound_DecoderFunctions_VOC =
{
{
extensions_voc,
"Creative Labs Voice format",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/SDL_sound/"
},
VOC_init, /* init() method */
VOC_quit, /* quit() method */
VOC_open, /* open() method */
VOC_close, /* close() method */
VOC_read, /* read() method */
VOC_rewind, /* rewind() method */
VOC_seek /* seek() method */
};
#endif /* SOUND_SUPPORTS_VOC */
/* end of SDL_sound_voc.c ... */

View file

@ -0,0 +1,223 @@
/**
* SDL_sound; A sound processing toolkit.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
/*
* Ogg Vorbis decoder for SDL_sound.
*
* This driver handles .OGG audio files, and uses Sean Barrett's excellent
* stb_vorbis for the heavy lifting. Since stb_vorbis is a single,
* public domain C header file, it's just compiled into this source and
* needs no external dependencies.
*
* stb_vorbis homepage: https://nothings.org/stb_vorbis/
*/
#define __SDL_SOUND_INTERNAL__
#include "SDL_sound_internal.h"
#if SOUND_SUPPORTS_VORBIS
/* Configure and include stb_vorbis for compiling... */
#define STB_VORBIS_NO_STDIO 1
#define STB_VORBIS_NO_CRT 1
#define STB_VORBIS_NO_PUSHDATA_API 1
#define STB_VORBIS_MAX_CHANNELS 6
#define STBV_CDECL
#define STB_FORCEINLINE SDL_FORCE_INLINE
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define STB_VORBIS_BIG_ENDIAN 1
#endif
#if !defined(__clang_analyzer__)
#ifdef assert
#undef assert
#endif
#ifdef memset
#undef memset
#endif
#ifdef memcpy
#undef memcpy
#endif
#ifdef alloca
#undef alloca
#endif
#define assert SDL_assert
#define memset SDL_memset
#define memcmp SDL_memcmp
#define memcpy SDL_memcpy
#define qsort SDL_qsort
#define pow SDL_pow
#define floor SDL_floor
#define malloc SDL_malloc
#define realloc SDL_realloc
#define free SDL_free
#define alloca(x) ((void *) SDL_stack_alloc(Uint8, (x)))
#define dealloca(x) SDL_stack_free((x))
#define ldexp(v, e) SDL_scalbn((v), (e))
#define abs(x) SDL_abs(x)
#define cos(x) SDL_cos(x)
#define sin(x) SDL_sin(x)
#define log(x) SDL_log(x)
#endif
#include "stb_vorbis.h"
static const char *vorbis_error_string(const int err)
{
switch (err)
{
case VORBIS__no_error: return NULL;
case VORBIS_need_more_data: return "VORBIS: need more data";
case VORBIS_invalid_api_mixing: return "VORBIS: can't mix API modes";
case VORBIS_outofmem: return "VORBIS: out of memory";
case VORBIS_feature_not_supported: return "VORBIS: feature not supported";
case VORBIS_too_many_channels: return "VORBIS: too many channels";
case VORBIS_seek_without_length: return "VORBIS: can't seek in unknown length stream";
case VORBIS_unexpected_eof: return "VORBIS: unexpected eof";
case VORBIS_seek_invalid: return "VORBIS: invalid seek";
case VORBIS_invalid_setup: return "VORBIS: invalid setup";
case VORBIS_invalid_stream: return "VORBIS: invalid stream";
case VORBIS_missing_capture_pattern: return "VORBIS: missing capture pattern";
case VORBIS_invalid_stream_structure_version: return "VORBIS: invalid stream structure version";
case VORBIS_continued_packet_flag_invalid: return "VORBIS: continued packet flag invalid";
case VORBIS_incorrect_stream_serial_number: return "VORBIS: incorrect stream serial number";
case VORBIS_invalid_first_page: return "VORBIS: invalid first page";
case VORBIS_bad_packet_type: return "VORBIS: bad packet type";
case VORBIS_cant_find_last_page: return "VORBIS: can't find last page";
case VORBIS_seek_failed: return "VORBIS: seek failed";
default: break;
} /* switch */
return "VORBIS: unknown error";
} /* vorbis_error_string */
static int VORBIS_init(void)
{
return 1; /* always succeeds. */
} /* VORBIS_init */
static void VORBIS_quit(void)
{
/* it's a no-op. */
} /* VORBIS_quit */
static int VORBIS_open(Sound_Sample *sample, const char *ext)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
SDL_RWops *rw = internal->rw;
int err = 0;
stb_vorbis *stb = stb_vorbis_open_rwops(rw, 0, &err, NULL);
unsigned int num_frames;
BAIL_IF_MACRO(!stb, vorbis_error_string(err), 0);
SNDDBG(("VORBIS: Accepting data stream.\n"));
internal->decoder_private = stb;
sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
sample->actual.format = AUDIO_F32SYS;
sample->actual.channels = stb->channels;
sample->actual.rate = stb->sample_rate;
num_frames = stb_vorbis_stream_length_in_samples(stb);
if (!num_frames)
internal->total_time = -1;
else
{
const unsigned int rate = stb->sample_rate;
internal->total_time = (num_frames / rate) * 1000;
internal->total_time += (num_frames % rate) * 1000 / rate;
} /* else */
return 1; /* we'll handle this data. */
} /* VORBIS_open */
static void VORBIS_close(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
stb_vorbis *stb = (stb_vorbis *) internal->decoder_private;
stb_vorbis_close(stb);
} /* VORBIS_close */
static Uint32 VORBIS_read(Sound_Sample *sample)
{
Uint32 retval;
int rc;
int err;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
stb_vorbis *stb = (stb_vorbis *) internal->decoder_private;
const int channels = (int) sample->actual.channels;
const int want_samples = (int) (internal->buffer_size / sizeof (float));
stb_vorbis_get_error(stb); /* clear any error state */
rc = stb_vorbis_get_samples_float_interleaved(stb, channels, (float *) internal->buffer, want_samples);
retval = (Uint32) (rc * channels * sizeof (float)); /* rc == number of sample frames read */
err = stb_vorbis_get_error(stb);
if (retval == 0)
{
if (!err)
sample->flags |= SOUND_SAMPLEFLAG_EOF;
else
{
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
/* !!! FIXME: should I set this? __Sound_SetError(vorbis_error_string(err)); */
} /* else */
} /* if */
else if (retval < internal->buffer_size)
sample->flags |= SOUND_SAMPLEFLAG_EAGAIN;
return retval;
} /* VORBIS_read */
static int VORBIS_rewind(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
stb_vorbis *stb = (stb_vorbis *) internal->decoder_private;
BAIL_IF_MACRO(!stb_vorbis_seek_start(stb), vorbis_error_string(stb_vorbis_get_error(stb)), 0);
return 1;
} /* VORBIS_rewind */
static int VORBIS_seek(Sound_Sample *sample, Uint32 ms)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
stb_vorbis *stb = (stb_vorbis *) internal->decoder_private;
const float frames_per_ms = ((float) sample->actual.rate) / 1000.0f;
const Uint32 frame_offset = (Uint32) (frames_per_ms * ((float) ms));
const unsigned int sampnum = (unsigned int) frame_offset;
BAIL_IF_MACRO(!stb_vorbis_seek(stb, sampnum), vorbis_error_string(stb_vorbis_get_error(stb)), 0);
return 1;
} /* VORBIS_seek */
static const char *extensions_vorbis[] = { "OGG", NULL };
const Sound_DecoderFunctions __Sound_DecoderFunctions_VORBIS =
{
{
extensions_vorbis,
"Ogg Vorbis audio",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/SDL_sound/"
},
VORBIS_init, /* init() method */
VORBIS_quit, /* quit() method */
VORBIS_open, /* open() method */
VORBIS_close, /* close() method */
VORBIS_read, /* read() method */
VORBIS_rewind, /* rewind() method */
VORBIS_seek /* seek() method */
};
#endif /* SOUND_SUPPORTS_VORBIS */
/* end of SDL_sound_vorbis.c ... */

783
SDL2_sound/SDL_sound_wav.c Normal file
View file

@ -0,0 +1,783 @@
/**
* SDL_sound; A sound processing toolkit.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
/*
* WAV decoder for SDL_sound.
*
* This driver handles Microsoft .WAVs, in as many of the thousands of
* variations as we can.
*/
#define __SDL_SOUND_INTERNAL__
#include "SDL_sound_internal.h"
#if SOUND_SUPPORTS_WAV
/* Better than SDL_ReadLE16, since you can detect i/o errors... */
static SDL_INLINE int read_le16(SDL_RWops *rw, Uint16 *ui16)
{
int rc = SDL_RWread(rw, ui16, sizeof (Uint16), 1);
BAIL_IF_MACRO(rc != 1, ERR_IO_ERROR, 0);
*ui16 = SDL_SwapLE16(*ui16);
return 1;
} /* read_le16 */
/* Better than SDL_ReadLE32, since you can detect i/o errors... */
static SDL_INLINE int read_le32(SDL_RWops *rw, Uint32 *ui32)
{
int rc = SDL_RWread(rw, ui32, sizeof (Uint32), 1);
BAIL_IF_MACRO(rc != 1, ERR_IO_ERROR, 0);
*ui32 = SDL_SwapLE32(*ui32);
return 1;
} /* read_le32 */
static SDL_INLINE int read_le16s(SDL_RWops *rw, Sint16 *si16)
{
return read_le16(rw, (Uint16 *) si16);
} /* read_le16s */
static SDL_INLINE int read_le32s(SDL_RWops *rw, Sint32 *si32)
{
return read_le32(rw, (Uint32 *) si32);
} /* read_le32s */
/* This is just cleaner on the caller's end... */
static SDL_INLINE int read_uint8(SDL_RWops *rw, Uint8 *ui8)
{
int rc = SDL_RWread(rw, ui8, sizeof (Uint8), 1);
BAIL_IF_MACRO(rc != 1, ERR_IO_ERROR, 0);
return 1;
} /* read_uint8 */
/* Chunk management code... */
#define riffID 0x46464952 /* "RIFF", in ascii. */
#define waveID 0x45564157 /* "WAVE", in ascii. */
#define factID 0x74636166 /* "fact", in ascii. */
/*****************************************************************************
* The FORMAT chunk... *
*****************************************************************************/
#define fmtID 0x20746D66 /* "fmt ", in ascii. */
#define FMT_NORMAL 0x0001 /* Uncompressed waveform data. */
#define FMT_ADPCM 0x0002 /* ADPCM compressed waveform data. */
typedef struct
{
Sint16 iCoef1;
Sint16 iCoef2;
} ADPCMCOEFSET;
typedef struct
{
Uint8 bPredictor;
Uint16 iDelta;
Sint16 iSamp1;
Sint16 iSamp2;
} ADPCMBLOCKHEADER;
typedef struct S_WAV_FMT_T
{
Uint32 chunkID;
Sint32 chunkSize;
Sint16 wFormatTag;
Uint16 wChannels;
Uint32 dwSamplesPerSec;
Uint32 dwAvgBytesPerSec;
Uint16 wBlockAlign;
Uint16 wBitsPerSample;
Uint32 next_chunk_offset;
Uint32 sample_frame_size;
Uint32 data_starting_offset;
Uint32 total_bytes;
void (*free)(struct S_WAV_FMT_T *fmt);
Uint32 (*read_sample)(Sound_Sample *sample);
int (*rewind_sample)(Sound_Sample *sample);
int (*seek_sample)(Sound_Sample *sample, Uint32 ms);
union
{
struct
{
Uint16 cbSize;
Uint16 wSamplesPerBlock;
Uint16 wNumCoef;
ADPCMCOEFSET *aCoef;
ADPCMBLOCKHEADER *blockheaders;
Uint32 samples_left_in_block;
int nibble_state;
Sint8 nibble;
} adpcm;
/* put other format-specific data here... */
} fmt;
} fmt_t;
/*
* Read in a fmt_t from disk. This makes this process safe regardless of
* the processor's byte order or how the fmt_t structure is packed.
* Note that the union "fmt" is not read in here; that is handled as
* needed in the read_fmt_* functions.
*/
static int read_fmt_chunk(SDL_RWops *rw, fmt_t *fmt)
{
/* skip reading the chunk ID, since it was already read at this point... */
fmt->chunkID = fmtID;
BAIL_IF_MACRO(!read_le32s(rw, &fmt->chunkSize), NULL, 0);
BAIL_IF_MACRO(fmt->chunkSize < 16, "WAV: Invalid chunk size", 0);
fmt->next_chunk_offset = SDL_RWtell(rw) + fmt->chunkSize;
BAIL_IF_MACRO(!read_le16s(rw, &fmt->wFormatTag), NULL, 0);
BAIL_IF_MACRO(!read_le16(rw, &fmt->wChannels), NULL, 0);
BAIL_IF_MACRO(!read_le32(rw, &fmt->dwSamplesPerSec), NULL, 0);
BAIL_IF_MACRO(!read_le32(rw, &fmt->dwAvgBytesPerSec), NULL, 0);
BAIL_IF_MACRO(!read_le16(rw, &fmt->wBlockAlign), NULL, 0);
BAIL_IF_MACRO(!read_le16(rw, &fmt->wBitsPerSample), NULL, 0);
return 1;
} /* read_fmt_chunk */
/*****************************************************************************
* The DATA chunk... *
*****************************************************************************/
#define dataID 0x61746164 /* "data", in ascii. */
typedef struct
{
Uint32 chunkID;
Sint32 chunkSize;
/* Then, (chunkSize) bytes of waveform data... */
} data_t;
/*
* Read in a data_t from disk. This makes this process safe regardless of
* the processor's byte order or how the fmt_t structure is packed.
*/
static int read_data_chunk(SDL_RWops *rw, data_t *data)
{
/* skip reading the chunk ID, since it was already read at this point... */
data->chunkID = dataID;
BAIL_IF_MACRO(!read_le32s(rw, &data->chunkSize), NULL, 0);
return 1;
} /* read_data_chunk */
/*****************************************************************************
* this is what we store in our internal->decoder_private field... *
*****************************************************************************/
typedef struct
{
fmt_t *fmt;
Sint32 bytesLeft;
} wav_t;
/*****************************************************************************
* Normal, uncompressed waveform handler... *
*****************************************************************************/
/*
* Sound_Decode() lands here for uncompressed WAVs...
*/
static Uint32 read_sample_fmt_normal(Sound_Sample *sample)
{
Uint32 retval;
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
wav_t *w = (wav_t *) internal->decoder_private;
Uint32 max = (internal->buffer_size < (Uint32) w->bytesLeft) ?
internal->buffer_size : (Uint32) w->bytesLeft;
SDL_assert(max > 0);
/*
* We don't actually do any decoding, so we read the wav data
* directly into the internal buffer...
*/
retval = SDL_RWread(internal->rw, internal->buffer, 1, max);
w->bytesLeft -= retval;
/* Make sure the read went smoothly... */
if ((retval == 0) || (w->bytesLeft == 0))
sample->flags |= SOUND_SAMPLEFLAG_EOF;
else if (retval == -1)
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
/* (next call this EAGAIN may turn into an EOF or error.) */
else if (retval < internal->buffer_size)
sample->flags |= SOUND_SAMPLEFLAG_EAGAIN;
return retval;
} /* read_sample_fmt_normal */
static int seek_sample_fmt_normal(Sound_Sample *sample, Uint32 ms)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
wav_t *w = (wav_t *) internal->decoder_private;
fmt_t *fmt = w->fmt;
int offset = __Sound_convertMsToBytePos(&sample->actual, ms);
int pos = (int) (fmt->data_starting_offset + offset);
int rc = SDL_RWseek(internal->rw, pos, SEEK_SET);
BAIL_IF_MACRO(rc != pos, ERR_IO_ERROR, 0);
w->bytesLeft = fmt->total_bytes - offset;
return 1; /* success. */
} /* seek_sample_fmt_normal */
static int rewind_sample_fmt_normal(Sound_Sample *sample)
{
/* no-op. */
return 1;
} /* rewind_sample_fmt_normal */
static int read_fmt_normal(SDL_RWops *rw, fmt_t *fmt)
{
/* (don't need to read more from the RWops...) */
fmt->free = NULL;
fmt->read_sample = read_sample_fmt_normal;
fmt->rewind_sample = rewind_sample_fmt_normal;
fmt->seek_sample = seek_sample_fmt_normal;
return 1;
} /* read_fmt_normal */
/*****************************************************************************
* ADPCM compression handler... *
*****************************************************************************/
#define FIXED_POINT_COEF_BASE 256
#define FIXED_POINT_ADAPTION_BASE 256
#define SMALLEST_ADPCM_DELTA 16
static SDL_INLINE int read_adpcm_block_headers(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
SDL_RWops *rw = internal->rw;
wav_t *w = (wav_t *) internal->decoder_private;
fmt_t *fmt = w->fmt;
ADPCMBLOCKHEADER *headers = fmt->fmt.adpcm.blockheaders;
int i;
int max = fmt->wChannels;
if (w->bytesLeft < fmt->wBlockAlign)
{
sample->flags |= SOUND_SAMPLEFLAG_EOF;
return 0;
} /* if */
w->bytesLeft -= fmt->wBlockAlign;
for (i = 0; i < max; i++)
BAIL_IF_MACRO(!read_uint8(rw, &headers[i].bPredictor), NULL, 0);
for (i = 0; i < max; i++)
BAIL_IF_MACRO(!read_le16(rw, &headers[i].iDelta), NULL, 0);
for (i = 0; i < max; i++)
BAIL_IF_MACRO(!read_le16s(rw, &headers[i].iSamp1), NULL, 0);
for (i = 0; i < max; i++)
BAIL_IF_MACRO(!read_le16s(rw, &headers[i].iSamp2), NULL, 0);
fmt->fmt.adpcm.samples_left_in_block = fmt->fmt.adpcm.wSamplesPerBlock;
fmt->fmt.adpcm.nibble_state = 0;
return 1;
} /* read_adpcm_block_headers */
static SDL_INLINE void do_adpcm_nibble(Uint8 nib,
ADPCMBLOCKHEADER *header,
Sint32 lPredSamp)
{
static const Sint32 max_audioval = ((1<<(16-1))-1);
static const Sint32 min_audioval = -(1<<(16-1));
static const Sint32 AdaptionTable[] =
{
230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230
};
Sint32 lNewSamp;
Sint32 delta;
if (nib & 0x08)
lNewSamp = lPredSamp + (header->iDelta * (nib - 0x10));
else
lNewSamp = lPredSamp + (header->iDelta * nib);
/* clamp value... */
if (lNewSamp < min_audioval)
lNewSamp = min_audioval;
else if (lNewSamp > max_audioval)
lNewSamp = max_audioval;
delta = ((Sint32) header->iDelta * AdaptionTable[nib]) /
FIXED_POINT_ADAPTION_BASE;
if (delta < SMALLEST_ADPCM_DELTA)
delta = SMALLEST_ADPCM_DELTA;
header->iDelta = delta;
header->iSamp2 = header->iSamp1;
header->iSamp1 = lNewSamp;
} /* do_adpcm_nibble */
static SDL_INLINE int decode_adpcm_sample_frame(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
wav_t *w = (wav_t *) internal->decoder_private;
fmt_t *fmt = w->fmt;
ADPCMBLOCKHEADER *headers = fmt->fmt.adpcm.blockheaders;
SDL_RWops *rw = internal->rw;
int i;
int max = fmt->wChannels;
Sint32 delta;
Uint8 nib = fmt->fmt.adpcm.nibble;
for (i = 0; i < max; i++)
{
Uint8 byte;
Sint16 iCoef1 = fmt->fmt.adpcm.aCoef[headers[i].bPredictor].iCoef1;
Sint16 iCoef2 = fmt->fmt.adpcm.aCoef[headers[i].bPredictor].iCoef2;
Sint32 lPredSamp = ((headers[i].iSamp1 * iCoef1) +
(headers[i].iSamp2 * iCoef2)) /
FIXED_POINT_COEF_BASE;
if (fmt->fmt.adpcm.nibble_state == 0)
{
BAIL_IF_MACRO(!read_uint8(rw, &nib), NULL, 0);
fmt->fmt.adpcm.nibble_state = 1;
do_adpcm_nibble(nib >> 4, &headers[i], lPredSamp);
} /* if */
else
{
fmt->fmt.adpcm.nibble_state = 0;
do_adpcm_nibble(nib & 0x0F, &headers[i], lPredSamp);
} /* else */
} /* for */
fmt->fmt.adpcm.nibble = nib;
return 1;
} /* decode_adpcm_sample_frame */
static SDL_INLINE void put_adpcm_sample_frame1(void *_buf, fmt_t *fmt)
{
Uint16 *buf = (Uint16 *) _buf;
ADPCMBLOCKHEADER *headers = fmt->fmt.adpcm.blockheaders;
int i;
for (i = 0; i < fmt->wChannels; i++)
*(buf++) = headers[i].iSamp1;
} /* put_adpcm_sample_frame1 */
static SDL_INLINE void put_adpcm_sample_frame2(void *_buf, fmt_t *fmt)
{
Uint16 *buf = (Uint16 *) _buf;
ADPCMBLOCKHEADER *headers = fmt->fmt.adpcm.blockheaders;
int i;
for (i = 0; i < fmt->wChannels; i++)
*(buf++) = headers[i].iSamp2;
} /* put_adpcm_sample_frame2 */
/*
* Sound_Decode() lands here for ADPCM-encoded WAVs...
*/
static Uint32 read_sample_fmt_adpcm(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
wav_t *w = (wav_t *) internal->decoder_private;
fmt_t *fmt = w->fmt;
Uint32 bw = 0;
while (bw < internal->buffer_size)
{
/* write ongoing sample frame before reading more data... */
switch (fmt->fmt.adpcm.samples_left_in_block)
{
case 0: /* need to read a new block... */
if (!read_adpcm_block_headers(sample))
{
if ((sample->flags & SOUND_SAMPLEFLAG_EOF) == 0)
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
return bw;
} /* if */
/* only write first sample frame for now. */
put_adpcm_sample_frame2((Uint8 *) internal->buffer + bw, fmt);
fmt->fmt.adpcm.samples_left_in_block--;
bw += fmt->sample_frame_size;
break;
case 1: /* output last sample frame of block... */
put_adpcm_sample_frame1((Uint8 *) internal->buffer + bw, fmt);
fmt->fmt.adpcm.samples_left_in_block--;
bw += fmt->sample_frame_size;
break;
default: /* output latest sample frame and read a new one... */
put_adpcm_sample_frame1((Uint8 *) internal->buffer + bw, fmt);
fmt->fmt.adpcm.samples_left_in_block--;
bw += fmt->sample_frame_size;
if (!decode_adpcm_sample_frame(sample))
{
sample->flags |= SOUND_SAMPLEFLAG_ERROR;
return bw;
} /* if */
} /* switch */
} /* while */
return bw;
} /* read_sample_fmt_adpcm */
/*
* Sound_FreeSample() lands here for ADPCM-encoded WAVs...
*/
static void free_fmt_adpcm(fmt_t *fmt)
{
if (fmt->fmt.adpcm.aCoef != NULL)
SDL_free(fmt->fmt.adpcm.aCoef);
if (fmt->fmt.adpcm.blockheaders != NULL)
SDL_free(fmt->fmt.adpcm.blockheaders);
} /* free_fmt_adpcm */
static int rewind_sample_fmt_adpcm(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
wav_t *w = (wav_t *) internal->decoder_private;
w->fmt->fmt.adpcm.samples_left_in_block = 0;
return 1;
} /* rewind_sample_fmt_adpcm */
static int seek_sample_fmt_adpcm(Sound_Sample *sample, Uint32 ms)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
wav_t *w = (wav_t *) internal->decoder_private;
fmt_t *fmt = w->fmt;
Uint32 origsampsleft = fmt->fmt.adpcm.samples_left_in_block;
int origpos = SDL_RWtell(internal->rw);
int offset = __Sound_convertMsToBytePos(&sample->actual, ms);
int bpb = (fmt->fmt.adpcm.wSamplesPerBlock * fmt->sample_frame_size);
int skipsize = (offset / bpb) * fmt->wBlockAlign;
int pos = skipsize + fmt->data_starting_offset;
int rc = SDL_RWseek(internal->rw, pos, SEEK_SET);
BAIL_IF_MACRO(rc != pos, ERR_IO_ERROR, 0);
/* The offset we need is in this block, so we need to decode to there. */
skipsize += (offset % bpb);
rc = (offset % bpb); /* bytes into this block we need to decode */
if (!read_adpcm_block_headers(sample))
{
SDL_RWseek(internal->rw, origpos, SEEK_SET); /* try to make sane. */
return 0;
} /* if */
/* first sample frame of block is a freebie. :) */
fmt->fmt.adpcm.samples_left_in_block--;
rc -= fmt->sample_frame_size;
while (rc > 0)
{
if (!decode_adpcm_sample_frame(sample))
{
SDL_RWseek(internal->rw, origpos, SEEK_SET);
fmt->fmt.adpcm.samples_left_in_block = origsampsleft;
return 0;
} /* if */
fmt->fmt.adpcm.samples_left_in_block--;
rc -= fmt->sample_frame_size;
} /* while */
w->bytesLeft = fmt->total_bytes - skipsize;
return 1; /* success. */
} /* seek_sample_fmt_adpcm */
/*
* Read in the adpcm-specific info from disk. This makes this process
* safe regardless of the processor's byte order or how the fmt_t
* structure is packed.
*/
static int read_fmt_adpcm(SDL_RWops *rw, fmt_t *fmt)
{
size_t i;
SDL_memset(&fmt->fmt.adpcm, '\0', sizeof (fmt->fmt.adpcm));
fmt->free = free_fmt_adpcm;
fmt->read_sample = read_sample_fmt_adpcm;
fmt->rewind_sample = rewind_sample_fmt_adpcm;
fmt->seek_sample = seek_sample_fmt_adpcm;
BAIL_IF_MACRO(!read_le16(rw, &fmt->fmt.adpcm.cbSize), NULL, 0);
BAIL_IF_MACRO(!read_le16(rw, &fmt->fmt.adpcm.wSamplesPerBlock), NULL, 0);
BAIL_IF_MACRO(!read_le16(rw, &fmt->fmt.adpcm.wNumCoef), NULL, 0);
/* fmt->free() is always called, so these malloc()s will be cleaned up. */
i = sizeof (ADPCMCOEFSET) * fmt->fmt.adpcm.wNumCoef;
fmt->fmt.adpcm.aCoef = (ADPCMCOEFSET *) SDL_malloc(i);
BAIL_IF_MACRO(fmt->fmt.adpcm.aCoef == NULL, ERR_OUT_OF_MEMORY, 0);
for (i = 0; i < fmt->fmt.adpcm.wNumCoef; i++)
{
BAIL_IF_MACRO(!read_le16s(rw, &fmt->fmt.adpcm.aCoef[i].iCoef1), NULL, 0);
BAIL_IF_MACRO(!read_le16s(rw, &fmt->fmt.adpcm.aCoef[i].iCoef2), NULL, 0);
} /* for */
i = sizeof (ADPCMBLOCKHEADER) * fmt->wChannels;
fmt->fmt.adpcm.blockheaders = (ADPCMBLOCKHEADER *) SDL_malloc(i);
BAIL_IF_MACRO(fmt->fmt.adpcm.blockheaders == NULL, ERR_OUT_OF_MEMORY, 0);
return 1;
} /* read_fmt_adpcm */
/*****************************************************************************
* Everything else... *
*****************************************************************************/
static int WAV_init(void)
{
return 1; /* always succeeds. */
} /* WAV_init */
static void WAV_quit(void)
{
/* it's a no-op. */
} /* WAV_quit */
static int read_fmt(SDL_RWops *rw, fmt_t *fmt)
{
/* if it's in this switch statement, we support the format. */
switch (fmt->wFormatTag)
{
case FMT_NORMAL:
SNDDBG(("WAV: Appears to be uncompressed audio.\n"));
return read_fmt_normal(rw, fmt);
case FMT_ADPCM:
SNDDBG(("WAV: Appears to be ADPCM compressed audio.\n"));
return read_fmt_adpcm(rw, fmt);
/* add other types here. */
default:
SNDDBG(("WAV: Format 0x%X is unknown.\n",
(unsigned int) fmt->wFormatTag));
BAIL_MACRO("WAV: Unsupported format", 0);
} /* switch */
SDL_assert(0); /* shouldn't hit this point. */
return 0;
} /* read_fmt */
/*
* Locate a specific chunk in the WAVE file by ID...
*/
static int find_chunk(SDL_RWops *rw, Uint32 id)
{
Sint32 siz = 0;
Uint32 _id = 0;
Uint32 pos = SDL_RWtell(rw);
while (1)
{
BAIL_IF_MACRO(!read_le32(rw, &_id), NULL, 0);
if (_id == id)
return 1;
/* skip ahead and see what next chunk is... */
BAIL_IF_MACRO(!read_le32s(rw, &siz), NULL, 0);
SDL_assert(siz >= 0);
pos += (sizeof (Uint32) * 2) + siz;
if (siz > 0)
BAIL_IF_MACRO(SDL_RWseek(rw, pos, SEEK_SET) != pos, NULL, 0);
} /* while */
return 0; /* shouldn't hit this, but just in case... */
} /* find_chunk */
static int WAV_open_internal(Sound_Sample *sample, const char *ext, fmt_t *fmt)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
SDL_RWops *rw = internal->rw;
data_t d;
wav_t *w;
Uint32 pos;
BAIL_IF_MACRO(SDL_ReadLE32(rw) != riffID, "WAV: Not a RIFF file.", 0);
SDL_ReadLE32(rw); /* throw the length away; we get this info later. */
BAIL_IF_MACRO(SDL_ReadLE32(rw) != waveID, "WAV: Not a WAVE file.", 0);
BAIL_IF_MACRO(!find_chunk(rw, fmtID), "WAV: No format chunk.", 0);
BAIL_IF_MACRO(!read_fmt_chunk(rw, fmt), "WAV: Can't read format chunk.", 0);
/* !!! FIXME: need float32 format stuff, since it's not just wBitsPerSample. */
sample->actual.channels = (Uint8) fmt->wChannels;
sample->actual.rate = fmt->dwSamplesPerSec;
if (fmt->wBitsPerSample == 4)
sample->actual.format = AUDIO_S16SYS;
else if (fmt->wBitsPerSample == 8)
sample->actual.format = AUDIO_U8;
else if (fmt->wBitsPerSample == 16)
sample->actual.format = AUDIO_S16LSB;
else if (fmt->wBitsPerSample == 32)
sample->actual.format = AUDIO_S32LSB;
else
{
SNDDBG(("WAV: %d bits per sample!?\n", (int) fmt->wBitsPerSample));
BAIL_MACRO("WAV: Unsupported sample size.", 0);
} /* else */
BAIL_IF_MACRO(!read_fmt(rw, fmt), NULL, 0);
SDL_RWseek(rw, fmt->next_chunk_offset, SEEK_SET);
BAIL_IF_MACRO(!find_chunk(rw, dataID), "WAV: No data chunk.", 0);
BAIL_IF_MACRO(!read_data_chunk(rw, &d), "WAV: Can't read data chunk.", 0);
w = (wav_t *) SDL_malloc(sizeof(wav_t));
BAIL_IF_MACRO(w == NULL, ERR_OUT_OF_MEMORY, 0);
w->fmt = fmt;
fmt->total_bytes = w->bytesLeft = d.chunkSize;
fmt->data_starting_offset = SDL_RWtell(rw);
fmt->sample_frame_size = ( ((sample->actual.format & 0xFF) / 8) *
sample->actual.channels );
internal->decoder_private = (void *) w;
internal->total_time = (fmt->total_bytes / fmt->dwAvgBytesPerSec) * 1000;
internal->total_time += (fmt->total_bytes % fmt->dwAvgBytesPerSec)
* 1000 / fmt->dwAvgBytesPerSec;
sample->flags = SOUND_SAMPLEFLAG_NONE;
if (fmt->seek_sample != NULL)
sample->flags |= SOUND_SAMPLEFLAG_CANSEEK;
SNDDBG(("WAV: Accepting data stream.\n"));
return 1; /* we'll handle this data. */
} /* WAV_open_internal */
static int WAV_open(Sound_Sample *sample, const char *ext)
{
int rc;
fmt_t *fmt = (fmt_t *) SDL_calloc(1, sizeof (fmt_t));
BAIL_IF_MACRO(fmt == NULL, ERR_OUT_OF_MEMORY, 0);
rc = WAV_open_internal(sample, ext, fmt);
if (!rc)
{
if (fmt->free != NULL)
fmt->free(fmt);
SDL_free(fmt);
} /* if */
return rc;
} /* WAV_open */
static void WAV_close(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
wav_t *w = (wav_t *) internal->decoder_private;
if (w->fmt->free != NULL)
w->fmt->free(w->fmt);
SDL_free(w->fmt);
SDL_free(w);
} /* WAV_close */
static Uint32 WAV_read(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
wav_t *w = (wav_t *) internal->decoder_private;
return w->fmt->read_sample(sample);
} /* WAV_read */
static int WAV_rewind(Sound_Sample *sample)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
wav_t *w = (wav_t *) internal->decoder_private;
fmt_t *fmt = w->fmt;
int rc = SDL_RWseek(internal->rw, fmt->data_starting_offset, SEEK_SET);
BAIL_IF_MACRO(rc != fmt->data_starting_offset, ERR_IO_ERROR, 0);
w->bytesLeft = fmt->total_bytes;
return fmt->rewind_sample(sample);
} /* WAV_rewind */
static int WAV_seek(Sound_Sample *sample, Uint32 ms)
{
Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
wav_t *w = (wav_t *) internal->decoder_private;
return w->fmt->seek_sample(sample, ms);
} /* WAV_seek */
static const char *extensions_wav[] = { "WAV", NULL };
const Sound_DecoderFunctions __Sound_DecoderFunctions_WAV =
{
{
extensions_wav,
"Microsoft WAVE audio format",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/SDL_sound/"
},
WAV_init, /* init() method */
WAV_quit, /* quit() method */
WAV_open, /* open() method */
WAV_close, /* close() method */
WAV_read, /* read() method */
WAV_rewind, /* rewind() method */
WAV_seek /* seek() method */
};
#endif /* SOUND_SUPPORTS_WAV */
/* end of SDL_sound_wav.c ... */

5906
SDL2_sound/dr_flac.h Normal file

File diff suppressed because it is too large Load diff

2832
SDL2_sound/dr_mp3.h Normal file

File diff suppressed because it is too large Load diff

22
SDL2_sound/meson.build Normal file
View file

@ -0,0 +1,22 @@
global_include_dirs += include_directories('.')
global_dependencies += dependency('AudioToolbox')
global_sources += files(
'dr_flac.h',
'dr_mp3.h',
'SDL_sound_aiff.c',
'SDL_sound_au.c',
'SDL_sound_coreaudio.c',
'SDL_sound_flac.c',
'SDL_sound_internal.h',
'SDL_sound_modplug.c',
'SDL_sound_mp3.c',
'SDL_sound_raw.c',
'SDL_sound_shn.c',
'SDL_sound_voc.c',
'SDL_sound_vorbis.c',
'SDL_sound_wav.c',
'SDL_sound.c',
'SDL_sound.h',
'stb_vorbis.h'
)

5567
SDL2_sound/stb_vorbis.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -64,7 +64,11 @@ extern const char module_rpg3[];
// Scripts to run at some point during Ruby execution
#ifdef EASY_POKE
#ifndef MKXPZ_BUILD_XCODE
#include "EssentialsCompatibility.rb.xxd"
#else
#include "CocoaHelpers.hpp"
#endif
#endif
static void mriBindingExecute();
@ -625,8 +629,15 @@ static void runRMXPScripts(BacktraceData &btData) {
// make sure to check whether it's the last one or not and run
// any extra stuff before the end (primarily stupid Essentials fixes)
// Will be placed within a build option later if I decide to add more
#ifndef MKXPZ_BUILD_XCODE
#define SCRIPT(name) rb_str_new((const char*)&___scripts_##name##_rb, ___scripts_##name##_rb_len), #name " (Internal)"
#define EVALFILE(name) if (!evalScript(SCRIPT(name))) break;
#else
#define EVALFILE(name) { \
std::string s = Cocoa::getFile("BindingScripts/" #name, "rb"); \
if (!evalScript(rb_str_new_cstr(s.c_str()), #name)) break; \
}
#endif
if (i + 2 == scriptCount){
#ifdef EASY_POKE
if (minimonsters > 0 && !RTEST(rb_gv_get("Z_NOPOKEFIX"))){

View file

@ -1,3 +1,4 @@
#ifdef HAVE_STEAMSHIM
#include "binding-util.h"
#include "steamshim_child.h"
@ -217,3 +218,4 @@ void CUSLBindingInit() {
_rb_define_module_function(mSteamLite, "reset_all_stats", CUSLResetAllStats);
}
#endif

5
macos/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
DerivedData/
*Dependencies/
*Dependencies.dmg
gls*
*.dmg

20
macos/CocoaHelpers.hpp Normal file
View file

@ -0,0 +1,20 @@
//
// CocoaHelpers.hpp
// PlayerLegacy
//
// Created by ゾロアーク on 11/18/20.
//
#ifndef CocoaHelpers_hpp
#define CocoaHelpers_hpp
#include <string>
#include <vector>
class Cocoa {
public:
static std::string getFile(const char *baseName, const char *ext);
static std::string getFilePath(const char *baseName, const char *ext);
};
#endif /* CocoaHelpers_hpp */

34
macos/CocoaHelpers.mm Normal file
View file

@ -0,0 +1,34 @@
//
// CocoaHelpers.cpp
// PlayerLegacy
//
// Created by ゾロアーク on 11/18/20.
//
#import "CocoaHelpers.hpp"
#import <Foundation/Foundation.h>
#import <ObjFW/ObjFW.h>
// ObjFW is a bit of a dick to strings when Cocoa gets involved
// Can't use literals when using both Apple and ObjFW headers, meh
// This is a pretty lazy header, but it'll do for the moment while
// I work on Apple stuff, lots to do
#define NSSTR(ptr) [NSString stringWithUTF8String: ptr]
std::string Cocoa::getFile(const char* baseName, const char* ext) {
return std::string([[NSString stringWithContentsOfFile: NSSTR(getFilePath(baseName, ext).c_str())] UTF8String]);
}
std::string Cocoa::getFilePath(const char *baseName, const char *ext) {
NSBundle *assetBundle = [NSBundle bundleWithPath:
[NSString stringWithFormat:
NSSTR("%@/%s"),
NSBundle.mainBundle.resourcePath,
"Assets.bundle"
]
];
return std::string([assetBundle pathForResource: NSSTR(baseName) ofType: NSSTR(ext)].UTF8String);
}

55
macos/Config.xcconfig Normal file
View file

@ -0,0 +1,55 @@
//
// Config.xcconfig
// PlayerLegacy
//
// Created by ゾロアーク on 11/17/20.
//
// Uncomment this for the Essentials crap
// GCC_PREPROCESSOR_DEFINITIONS = $(inherited) EASY_POKE
// The path to your build dependencies, or the included (and mounted) .dmg
DEPENDENCY_SEARCH_PATH = $(PROJECT_DIR)/MKXPZ-Dependencies/mac
// The version of Ruby to use
// It's really only important for the disk image,
// if you install Ruby yourself just change paths in the project on your own
RUBY_INSTALL_PREFIX = $(DEPENDENCY_SEARCH_PATH)/prefix/opt/ruby$(MRI_VERSION)
// Default, don't change this here, change it in the target settings
MRI_VERSION=2.7.0
INFOPLIST_FILE = $(PROJECT_DIR)/Info.plist
CODE_SIGN_ENTITLEMENTS = $(PROJECT_DIR)/entitlements.plist
OTHER_CFLAGS = -fconstant-string-class=OFConstantString -fno-constant-cfstrings $(inherited)
CLANG_ENABLE_MODULES = NO
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) MKXPZ_BUILD_XCODE GLES2_HEADER ALCDEVICE_STRUCT=ALCdevice_struct SHARED_FLUID
GCC_C_LANGUAGE_STANDARD = gnu11
GCC_CXX_LANGUAGE_STANDARD = c++11
LIBRARY_SEARCH_PATHS = "$(RUBY_INSTALL_PREFIX)/lib" "$(DEPENDENCY_SEARCH_PATH)/prefix/lib" "$(DEPENDENCY_SEARCH_PATH)/prefix/lib64"
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../**" "$(DEPENDENCY_SEARCH_PATH)/prefix/include" "$(DEPENDENCY_SEARCH_PATH)/prefix/lib/**"
// Project includes
EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES = $(inherited) MKXPZ-Dependencies ../build
// sigc++
HEADER_SEARCH_PATHS = $(inherited) "$(DEPENDENCY_SEARCH_PATH)/prefix/include/sigc++-2.0"
// pixman
HEADER_SEARCH_PATHS = $(inherited) "$(DEPENDENCY_SEARCH_PATH)/prefix/include/pixman-1"
// Usually you need to access Framework headers like "SDL2/SDL.h"
// MKXP's source isn't written with macOS in mind though,
// so gotta search the framework headers
HEADER_SEARCH_PATHS = $(inherited) "$(DEPENDENCY_SEARCH_PATH)/frameworks/SDL2.framework/Headers/**"
HEADER_SEARCH_PATHS = $(inherited) "$(DEPENDENCY_SEARCH_PATH)/frameworks/SDL2_ttf.framework/Headers/**"
HEADER_SEARCH_PATHS = $(inherited) "$(DEPENDENCY_SEARCH_PATH)/frameworks/SDL2_image.framework/Headers/**"
HEADER_SEARCH_PATHS = $(inherited) "$(DEPENDENCY_SEARCH_PATH)/frameworks/MetalANGLE.framework/Headers/**"
FRAMEWORK_SEARCH_PATHS = "$(DEPENDENCY_SEARCH_PATH)/frameworks" $(inherited)
// Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974

23
macos/Deps.make Normal file
View file

@ -0,0 +1,23 @@
GOODLS_URL := https://github.com/tanaikech/goodls/releases/download/v1.2.7/goodls_darwin_amd64
DMG_URL := https://drive.google.com/file/d/1-9Pt5bxvFUuO_yPFzC_h3WvQ9LDA6Q5K/view?usp=sharing
DMG_NAME := Dependencies
GOODLS := gls
DEP_FOLDER := MKXPZ-Dependencies
deps: $(DMG_NAME).dmg
hdiutil attach "$(DMG_NAME).dmg" -mountroot . -quiet
$(DMG_NAME).dmg: $(GOODLS)
"./$(GOODLS)" -u "$(DMG_URL)" -f "$(DMG_NAME).dmg"
$(GOODLS):
curl -L "$(GOODLS_URL)" > "$(GOODLS)"
chmod +x "$(GOODLS)"
unmount:
-hdiutil detach $(DEP_FOLDER) -quiet
clean: unmount
rm -rf "$(GOODLS)" "$(DMG_NAME).dmg"

View file

@ -2,26 +2,28 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleGetInfoString</key>
<string>mkxp-z</string>
<key>CFBundleExecutable</key>
<string>mkxp-z</string>
<key>CFBundleIdentifier</key>
<string>com.zoro.mkxpz</string>
<key>CFBundleName</key>
<key>CFBundleGetInfoString</key>
<string>mkxp-z</string>
<key>CFBundleIconFile</key>
<string>icon.icns</string>
<key>CFBundleShortVersionString</key>
<string>1.3.0</string>
<key>CFBundleIdentifier</key>
<string>com.zoro.mkxpz</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>mkxp-z</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.3.0</string>
<key>IFMajorVersion</key>
<integer>0</integer>
<key>IFMinorVersion</key>
<integer>1</integer>
<key>LSApplicationCategoryType</key>
<string>public.app-category.role-playing-games</string>
<key>NSRequiresAquaSystemAppearance</key>
<string>False</string>
<key>SDL_FILESYSTEM_BASE_DIR_TYPE</key>

15
macos/Makefile Normal file
View file

@ -0,0 +1,15 @@
CONFIG := Config.xcconfig
XCARGS := -xcconfig $(CONFIG) -scheme Player
debug: deps
xcodebuild -configuration Debug $(XCARGS)
release: deps
xcodebuild -configuration Release $(XCARGS)
deps:
@make -f Deps.make
clean:
@make -f Deps.make clean
rm -rf "DerivedData"

View file

@ -1,26 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!--App sandbox entitlements: https://developer.apple.com/documentation/security/app_sandbox?language=objc -->
<!--Hardened runtime entitlements: https://developer.apple.com/documentation/security/hardened_runtime?language=objc-->
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<false/>
<key>com.apple.security.network.server</key>
<false/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.assets.pictures.read-write</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<false/>
<key>com.apple.security.cs.allow-jit</key>
<false/>
</dict>
</plist>
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<false/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<false/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>

View file

@ -1 +0,0 @@
MetalANGLE.framework/*

Binary file not shown.

View file

@ -2,16 +2,8 @@
EXE=${MESON_INSTALL_PREFIX}/Contents/MacOS/$2
ANGLE="${MESON_SOURCE_ROOT}/macos/lib/MetalANGLE.framework"
FRAMEWORKS="${MESON_INSTALL_PREFIX}/Contents/Frameworks"
if [ -n "$(otool -L $EXE | grep ANGLE)" ] && [ -d $ANGLE ]; then
if [ ! -d $FRAMEWORKS ]; then
mkdir -p $FRAMEWORKS
fi
cp -a $ANGLE $FRAMEWORKS
fi
if [ -n "$1" ]; then
echo "Setting up steam_api manually..."
if [ ! -d $FRAMEWORKS ]; then

24
macos/misc/Assets.plist Normal file
View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1220"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3BD2B64B2565AEC0003DAD8A"
BuildableName = "mkxp-z.app"
BlueprintName = "Player"
ReferencedContainer = "container:mkxp-z.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3BD2B64B2565AEC0003DAD8A"
BuildableName = "mkxp-z.app"
BlueprintName = "Player"
ReferencedContainer = "container:mkxp-z.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3BD2B64B2565AEC0003DAD8A"
BuildableName = "mkxp-z.app"
BlueprintName = "Player"
ReferencedContainer = "container:mkxp-z.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1220"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3BDB25AF2565175000C4A63D"
BuildableName = "mkxp-z.app"
BlueprintName = "PlayerLegacy"
ReferencedContainer = "container:mkxp-z.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3BDB25AF2565175000C4A63D"
BuildableName = "mkxp-z.app"
BlueprintName = "PlayerLegacy"
ReferencedContainer = "container:mkxp-z.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3BDB25AF2565175000C4A63D"
BuildableName = "mkxp-z.app"
BlueprintName = "PlayerLegacy"
ReferencedContainer = "container:mkxp-z.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
uuid = "39122FB9-4F55-4590-9DCD-CE3ED9F5399A"
type = "1"
version = "2.0">
</Bucket>

View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Obtain Dependencies.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
</dict>
<key>Player.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
<key>PlayerLegacy.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
<key>Remove Dependencies.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>3</integer>
</dict>
<key>gen.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
</dict>
<key>mkxpz-resources.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>4</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>3BD2B64B2565AEC0003DAD8A</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>3BDB22F52564501400C4A63D</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View file

@ -25,12 +25,16 @@ win64 = (sizeof['void*'] != sizeof['long'])
minimum_macos_version = ''
current_macos_version = ''
if host_system == 'darwin'
current_macos_version = run_command('sw_vers', '-productVersion').stdout()
current_macos_version = run_command('sw_vers', '-productVersion').stdout().strip()
macosx_version_min = get_option('macos_min_version')
if macosx_version_min != ''
minimum_macos_version = macosx_version_min
else
minimum_macos_version = current_macos_version
endif
endif
global_args += '-DMKXPZ_BUILD_MESON'
# ====================
# Ext libs
# ====================
@ -72,29 +76,12 @@ endif
# BOOST UNORDERED
global_include_dirs += include_directories('boost-unordered')
# METAL/GLES
# SDL2_SOUND
subdir('SDL2_sound')
# GLES
gfx_backend = get_option('gfx_backend')
if gfx_backend == 'metal'
# Verify that we can use metal in the first place,
# so 10.13 SDK or higher
message('Verifying that Metal is available...')
if not dependency('Metal', required: false).found()
error('Could not find Metal. Ensure that you are building against the macOS 10.13+ or higher SDK.')
endif
global_args += '-DGLES2_HEADER'
global_args += '-F' + meson.source_root() + '/macos/lib'
mangle = dependency('MetalANGLE', required: false)
if not mangle.found()
warning('There is a pre-built archive of MetalANGLE located in macos/lib.')
warning('Simply extract it to the same directory, or build it yourself.')
error('Could not locate the MetalANGLE framework.')
endif
global_args += '-I' + meson.source_root() + '/macos/lib/MetalANGLE.framework/Headers'
global_link_args += '-F' + meson.source_root() + '/macos/lib'
global_dependencies += mangle
elif gfx_backend == 'gles'
if gfx_backend == 'gles'
# Needs to be manually set up for now
global_args += '-DGLES2_HEADER'
elif gfx_backend == 'gl'
@ -147,10 +134,6 @@ if get_option('force32') == true
global_args += '-m32'
endif
if get_option('mk')
global_args += '-DMARIN'
endif
build_static = false
if get_option('static_executable') == true and host_system == 'windows'
build_static = true
@ -199,6 +182,9 @@ elif host_system == 'darwin'
add_project_arguments('-mmacosx-version-min='+minimum_macos_version, language: ['cpp', 'objc', 'objcpp'])
add_project_link_arguments('-mmacosx-version-min='+minimum_macos_version, language: ['cpp', 'objc', 'objcpp'])
endif
if minimum_macos_version.version_compare('>=10.13') == true
warning('\nIt seems like you are trying to build for macOS versions that include Metal support.\nPlease consider using the Xcode project located in the `macos` directory instead.')
endif
else
subdir('linux')
add_project_arguments('-std=c++11', language: 'objcpp')

View file

@ -22,7 +22,12 @@
#ifndef ALUTIL_H
#define ALUTIL_H
#ifdef MKXPZ_BUILD_XCODE
#include <OpenAL/al.h>
#else
#include <al.h>
#endif
#include <SDL_audio.h>
#include <assert.h>

View file

@ -29,11 +29,17 @@
#include <SDL_touch.h>
#include <SDL_rect.h>
#ifdef MKXPZ_BUILD_XCODE
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <al.h>
#include <alc.h>
#ifndef USE_MAC_OPENAL
#include <alext.h>
#endif
#endif
#include "sharedstate.h"
#include "graphics.h"

View file

@ -1,3 +1,4 @@
#ifdef EASY_POKE
#import <SDL.h>
#ifdef __APPLE__
#import <Foundation/Foundation.h>
@ -381,3 +382,4 @@ PREFABI LONG MKXP_SetWindowLong(HWND hWnd, int nIndex, LONG dwNewLong) {
};
#endif
#endif

View file

@ -31,8 +31,13 @@
#include <string>
#include <utility>
#ifdef MKXPZ_BUILD_XCODE
#include "CocoaHelpers.hpp"
#endif
#include <SDL_ttf.h>
#ifndef MKXPZ_BUILD_XCODE
#ifndef CJK_FALLBACK
#include "liberation.ttf.xxd"
#else
@ -59,13 +64,21 @@ BUNDLED_FONT_DECL(liberation)
#define BNDL_F_D(f) BUNDLED_FONT_D(f)
#define BNDL_F_L(f) BUNDLED_FONT_L(f)
typedef std::pair<std::string, int> FontKey;
#endif
static SDL_RWops *openBundledFont()
{
return SDL_RWFromConstMem(BNDL_F_D(BUNDLED_FONT), BNDL_F_L(BUNDLED_FONT));
#ifndef MKXPZ_BUILD_XCODE
return SDL_RWFromConstMem(BNDL_F_D(BUNDLED_FONT), BNDL_F_L(BUNDLED_FONT));
#else
return SDL_RWFromFile(Cocoa::getFilePath("liberation", "ttf").c_str(), "rb");
#endif
}
typedef std::pair<std::string, int> FontKey;
struct FontSet
{
/* 'Regular' style */

View file

@ -19,7 +19,13 @@
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
*/
#import <alc.h>
#ifdef MKXPZ_BUILD_XCODE
#include <OpenAL/alc.h>
#include "CocoaHelpers.hpp"
#else
#include <alc.h>
#import "icon.png.xxd"
#endif
#import <SDL.h>
#import <SDL_image.h>
@ -40,7 +46,7 @@
#import "binding.h"
#ifdef __WINDOWS__
#if defined(__WINDOWS__)
#import "resource.h"
#import <Winsock2.h>
#endif
@ -49,7 +55,6 @@
#ifdef HAVE_STEAMSHIM
#import "steamshim_child.h"
#endif
#import "icon.png.xxd"
#ifndef THREADED_GLINIT
#define GLINIT_SHOWERROR(s) showInitError(s)
@ -146,7 +151,11 @@ static void setupWindowIcon(const Config &conf, SDL_Window *win) {
SDL_RWops *iconSrc;
if (conf.iconPath.empty())
#ifndef MKXPZ_BUILD_XCODE
iconSrc = SDL_RWFromConstMem(___assets_icon_png, ___assets_icon_png_len);
#else
iconSrc = SDL_RWFromFile(Cocoa::getFilePath("icon", "png").c_str(), "rb");
#endif
else
iconSrc = SDL_RWFromFile(conf.iconPath.c_str(), "rb");
@ -211,18 +220,18 @@ int main(int argc, char *argv[]) {
}
}
@try {
[fm changeCurrentDirectoryPath:@(dataDir)];
[fm changeCurrentDirectoryPath: [OFString stringWithUTF8String:dataDir ]];
} @catch (...) {
}
#endif
/* now we load the config */
Config conf;
conf.read(argc, argv);
if (!conf.gameFolder.empty()) {
@try {
[fm changeCurrentDirectoryPath:@(conf.gameFolder.c_str())];
[fm changeCurrentDirectoryPath: [OFString stringWithUTF8String: conf.gameFolder.c_str()]];
} @catch (...) {
showInitError(std::string("Unable to switch into gameFolder ") +
conf.gameFolder);

View file

@ -5,7 +5,6 @@ vorbisfile = dependency('vorbisfile', static: build_static)
sdl2 = dependency('sdl2', static: build_static)
sdl2_ttf = dependency('SDL2_ttf', static: build_static)
sdl2_image = dependency('SDL2_image', static: build_static)
sdl_sound = dependency('SDL_sound', static: build_static)
openal = dependency('openal', static: build_static)
zlib = dependency('zlib', static: build_static)
@ -38,7 +37,7 @@ endif
global_args += '-DALCDEVICE_STRUCT=' + alcdev_struct
global_include_dirs += include_directories('.')
global_dependencies += [sigcxx, openal, zlib, pixman, physfs, vorbisfile, sdl2, sdl2_ttf, sdl2_image, sdl_sound]
global_dependencies += [sigcxx, openal, zlib, pixman, physfs, vorbisfile, sdl2, sdl2_ttf, sdl2_image]
if host_system == 'darwin'
global_dependencies += compilers['cpp'].find_library('iconv')
global_dependencies += dependency('Foundation')

View file

@ -28,6 +28,7 @@
#include <string.h>
#include <iostream>
#ifndef MKXPZ_BUILD_XCODE
#include "common.h.xxd"
#include "sprite.frag.xxd"
#include "hue.frag.xxd"
@ -53,16 +54,30 @@
#include "blurH.vert.xxd"
#include "blurV.vert.xxd"
#include "tilemapvx.vert.xxd"
#endif
#ifdef MKXPZ_BUILD_XCODE
#include "CocoaHelpers.hpp"
#define INIT_SHADER(vert, frag, name) \
{ \
std::string v = Cocoa::getFile("Shaders/" #vert, "vert"); \
std::string f = Cocoa::getFile("Shaders/" #frag, "frag"); \
Shader::init((const unsigned char*)v.c_str(), v.length(), (const unsigned char*)f.c_str(), f.length(), #vert, #frag, #name); \
}
#else
#define INIT_SHADER(vert, frag, name) \
{ \
Shader::init(___shader_##vert##_vert, ___shader_##vert##_vert_len, ___shader_##frag##_frag, ___shader_##frag##_frag_len, \
#vert, #frag, #name); \
}
#endif
#define GET_U(name) u_##name = gl.GetUniformLocation(program, #name)
#ifdef MKXPZ_BUILD_XCODE
std::string Shader::shaderCommon = "";
#endif
static void printShaderLog(GLuint shader)
{
GLint logLength;
@ -87,6 +102,10 @@ static void printProgramLog(GLuint program)
Shader::Shader()
{
#ifdef MKXPZ_BUILD_XCODE
if (Shader::shaderCommon.empty())
Shader::shaderCommon = Cocoa::getFile("Shaders/common", "h");
#endif
vertShader = gl.CreateShader(GL_VERTEX_SHADER);
fragShader = gl.CreateShader(GL_FRAGMENT_SHADER);
@ -112,6 +131,12 @@ void Shader::unbind()
glState.program.set(0);
}
#ifdef MKXPZ_BUILD_XCODE
std::string &Shader::commonHeader() {
return Shader::shaderCommon;
}
#endif
static void setupShaderSource(GLuint shader, GLenum type,
const unsigned char *body, int bodySize)
{
@ -136,8 +161,13 @@ static void setupShaderSource(GLuint shader, GLenum type,
++i;
}
#ifndef MKXPZ_BUILD_XCODE
shaderSrc[i] = (const GLchar*) ___shader_common_h;
shaderSrcSize[i] = ___shader_common_h_len;
#else
shaderSrc[i] = (const GLchar*) Shader::commonHeader().c_str();
shaderSrcSize[i] = Shader::commonHeader().length();
#endif
++i;
shaderSrc[i] = (const GLchar*) body;

View file

@ -38,13 +38,15 @@ public:
TexCoord = 1,
Color = 2
};
static std::string &commonHeader();
protected:
Shader();
~Shader();
void init(const unsigned char *vert, int vertSize,
const unsigned char *frag, int fragSize,
void init(const unsigned char *vert, int vertSize,
const unsigned char *frag, int fragSize,
const char *vertName, const char *fragName,
const char *programName);
void initFromFile(const char *vertFile, const char *fragFile,
@ -55,6 +57,11 @@ protected:
GLuint vertShader, fragShader;
GLuint program;
private:
#ifdef MKXPZ_BUILD_XCODE
static std::string shaderCommon;
#endif
};
class ShaderBase : public Shader