mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-03-28 14:56:22 +01:00
Experimental Xcode builds (dependencies included)
This commit is contained in:
parent
94c031b095
commit
7e21066a70
52 changed files with 24529 additions and 83 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -5,8 +5,6 @@
|
|||
*.frag.xxd
|
||||
*.ttf.xxd
|
||||
|
||||
Makefile
|
||||
|
||||
mkxp
|
||||
xxd+
|
||||
|
||||
|
|
795
SDL2_sound/SDL_sound.c
Normal file
795
SDL2_sound/SDL_sound.c
Normal 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
725
SDL2_sound/SDL_sound.h
Normal 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
537
SDL2_sound/SDL_sound_aiff.c
Normal 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
352
SDL2_sound/SDL_sound_au.c
Normal 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 ... */
|
||||
|
747
SDL2_sound/SDL_sound_coreaudio.c
Normal file
747
SDL2_sound/SDL_sound_coreaudio.c
Normal 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 application’s 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
182
SDL2_sound/SDL_sound_flac.c
Normal 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 ... */
|
||||
|
304
SDL2_sound/SDL_sound_internal.h
Normal file
304
SDL2_sound/SDL_sound_internal.h
Normal 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 ... */
|
||||
|
228
SDL2_sound/SDL_sound_modplug.c
Normal file
228
SDL2_sound/SDL_sound_modplug.c
Normal 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
184
SDL2_sound/SDL_sound_mp3.c
Normal 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
164
SDL2_sound/SDL_sound_raw.c
Normal 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
1309
SDL2_sound/SDL_sound_shn.c
Normal file
File diff suppressed because it is too large
Load diff
144
SDL2_sound/SDL_sound_skeleton.c
Normal file
144
SDL2_sound/SDL_sound_skeleton.c
Normal 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
550
SDL2_sound/SDL_sound_voc.c
Normal 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 ... */
|
223
SDL2_sound/SDL_sound_vorbis.c
Normal file
223
SDL2_sound/SDL_sound_vorbis.c
Normal 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
783
SDL2_sound/SDL_sound_wav.c
Normal 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
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
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
22
SDL2_sound/meson.build
Normal 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
5567
SDL2_sound/stb_vorbis.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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"))){
|
||||
|
|
|
@ -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
5
macos/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
DerivedData/
|
||||
*Dependencies/
|
||||
*Dependencies.dmg
|
||||
gls*
|
||||
*.dmg
|
20
macos/CocoaHelpers.hpp
Normal file
20
macos/CocoaHelpers.hpp
Normal 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
34
macos/CocoaHelpers.mm
Normal 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
55
macos/Config.xcconfig
Normal 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
23
macos/Deps.make
Normal 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"
|
||||
|
|
@ -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
15
macos/Makefile
Normal 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"
|
|
@ -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>
|
||||
|
|
1
macos/lib/.gitignore
vendored
1
macos/lib/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
MetalANGLE.framework/*
|
Binary file not shown.
|
@ -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
24
macos/misc/Assets.plist
Normal 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>
|
2438
macos/mkxp-z.xcodeproj/project.pbxproj
Normal file
2438
macos/mkxp-z.xcodeproj/project.pbxproj
Normal file
File diff suppressed because it is too large
Load diff
7
macos/mkxp-z.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
macos/mkxp-z.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -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>
|
BIN
macos/mkxp-z.xcodeproj/project.xcworkspace/xcuserdata/zoroark.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
BIN
macos/mkxp-z.xcodeproj/project.xcworkspace/xcuserdata/zoroark.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
Binary file not shown.
|
@ -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>
|
|
@ -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>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
uuid = "39122FB9-4F55-4590-9DCD-CE3ED9F5399A"
|
||||
type = "1"
|
||||
version = "2.0">
|
||||
</Bucket>
|
|
@ -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>
|
40
meson.build
40
meson.build
|
@ -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')
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
17
src/font.cpp
17
src/font.cpp
|
@ -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 */
|
||||
|
|
21
src/main.mm
21
src/main.mm
|
@ -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);
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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;
|
||||
|
|
11
src/shader.h
11
src/shader.h
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue