Index: dynamic_flac.c =================================================================== --- dynamic_flac.c (revision 0) +++ dynamic_flac.c (revision 0) @@ -0,0 +1,176 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org + + + Implementation of the dynamic loading functionality for libFLAC. + ~ Austen Dicken (admin@cvpcs.org) +*/ + +#ifdef FLAC_MUSIC + +#include "SDL_loadso.h" + +#include "dynamic_flac.h" + +flac_loader flac = { + 0, NULL +}; + +#ifdef FLAC_DYNAMIC + +int Mix_InitFLAC() { + if ( flac.loaded == 0 ) { + flac.handle = SDL_LoadObject(FLAC_DYNAMIC); + if ( flac.handle == NULL ) { + return -1; + } + flac.FLAC__stream_decoder_new = + (FLAC__StreamDecoder *(*)()) + SDL_LoadFunction(flac.handle, "FLAC__stream_decoder_new"); + if ( flac.FLAC__stream_decoder_new == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_delete = + (void (*)(FLAC__StreamDecoder *)) + SDL_LoadFunction(flac.handle, "FLAC__stream_decoder_delete"); + if ( flac.FLAC__stream_decoder_delete == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_init_stream = + (FLAC__StreamDecoderInitStatus (*)( + FLAC__StreamDecoder *, + FLAC__StreamDecoderReadCallback, + FLAC__StreamDecoderSeekCallback, + FLAC__StreamDecoderTellCallback, + FLAC__StreamDecoderLengthCallback, + FLAC__StreamDecoderEofCallback, + FLAC__StreamDecoderWriteCallback, + FLAC__StreamDecoderMetadataCallback, + FLAC__StreamDecoderErrorCallback, + void *)) + SDL_LoadFunction(flac.handle, "FLAC__stream_decoder_init_stream"); + if ( flac.FLAC__stream_decoder_init_stream == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_finish = + (FLAC__bool (*)(FLAC__StreamDecoder *)) + SDL_LoadFunction(flac.handle, "FLAC__stream_decoder_finish"); + if ( flac.FLAC__stream_decoder_finish == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_flush = + (FLAC__bool (*)(FLAC__StreamDecoder *)) + SDL_LoadFunction(flac.handle, "FLAC__stream_decoder_flush"); + if ( flac.FLAC__stream_decoder_flush == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_process_single = + (FLAC__bool (*)(FLAC__StreamDecoder *)) + SDL_LoadFunction(flac.handle, + "FLAC__stream_decoder_process_single"); + if ( flac.FLAC__stream_decoder_process_single == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_process_until_end_of_metadata = + (FLAC__bool (*)(FLAC__StreamDecoder *)) + SDL_LoadFunction(flac.handle, + "FLAC__stream_decoder_process_until_end_of_metadata"); + if ( flac.FLAC__stream_decoder_process_until_end_of_metadata == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_process_until_end_of_stream = + (FLAC__bool (*)(FLAC__StreamDecoder *)) + SDL_LoadFunction(flac.handle, + "FLAC__stream_decoder_process_until_end_of_stream"); + if ( flac.FLAC__stream_decoder_process_until_end_of_stream == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_seek_absolute = + (FLAC__bool (*)(FLAC__StreamDecoder *, FLAC__uint64)) + SDL_LoadFunction(flac.handle, "FLAC__stream_decoder_seek_absolute"); + if ( flac.FLAC__stream_decoder_seek_absolute == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + flac.FLAC__stream_decoder_get_state = + (FLAC__StreamDecoderState (*)(const FLAC__StreamDecoder *decoder)) + SDL_LoadFunction(flac.handle, "FLAC__stream_decoder_get_state"); + if ( flac.FLAC__stream_decoder_get_state == NULL ) { + SDL_UnloadObject(flac.handle); + return -1; + } + } + ++flac.loaded; + + return 0; +} +void Mix_QuitFLAC() { + if ( flac.loaded == 0 ) { + return; + } + if ( flac.loaded == 1 ) { + SDL_UnloadObject(flac.handle); + } + --flac.loaded; +} +#else +int Mix_InitFLAC() { + if ( flac.loaded == 0 ) { + flac.FLAC__stream_decoder_new = FLAC__stream_decoder_new; + flac.FLAC__stream_decoder_delete = FLAC__stream_decoder_delete; + flac.FLAC__stream_decoder_init_stream = + FLAC__stream_decoder_init_stream; + flac.FLAC__stream_decoder_finish = FLAC__stream_decoder_finish; + flac.FLAC__stream_decoder_flush = FLAC__stream_decoder_flush; + flac.FLAC__stream_decoder_process_single = + FLAC__stream_decoder_process_single; + flac.FLAC__stream_decoder_process_until_end_of_metadata = + FLAC__stream_decoder_process_until_end_of_metadata; + flac.FLAC__stream_decoder_process_until_end_of_stream = + FLAC__stream_decoder_process_until_end_of_stream; + flac.FLAC__stream_decoder_seek_absolute = + FLAC__stream_decoder_seek_absolute; + flac.FLAC__stream_decoder_get_state = + FLAC__stream_decoder_get_state; + } + ++flac.loaded; + + return 0; +} +void Mix_QuitFLAC() { + if ( flac.loaded == 0 ) { + return; + } + if ( flac.loaded == 1 ) { + } + --flac.loaded; +} +#endif // FLAC_DYNAMIC + +#endif // FLAC_MUSIC Index: dynamic_flac.h =================================================================== --- dynamic_flac.h (revision 0) +++ dynamic_flac.h (revision 0) @@ -0,0 +1,68 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org + + + The following file defines all of the functions/objects used to dynamically + link to the libFLAC library. + ~ Austen Dicken (admin@cvpcs.org) +*/ + +#ifdef FLAC_MUSIC + +#include + +typedef struct { + int loaded; + void *handle; + FLAC__StreamDecoder *(*FLAC__stream_decoder_new)(); + void (*FLAC__stream_decoder_delete)(FLAC__StreamDecoder *decoder); + FLAC__StreamDecoderInitStatus (*FLAC__stream_decoder_init_stream)( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data); + FLAC__bool (*FLAC__stream_decoder_finish)(FLAC__StreamDecoder *decoder); + FLAC__bool (*FLAC__stream_decoder_flush)(FLAC__StreamDecoder *decoder); + FLAC__bool (*FLAC__stream_decoder_process_single)( + FLAC__StreamDecoder *decoder); + FLAC__bool (*FLAC__stream_decoder_process_until_end_of_metadata)( + FLAC__StreamDecoder *decoder); + FLAC__bool (*FLAC__stream_decoder_process_until_end_of_stream)( + FLAC__StreamDecoder *decoder); + FLAC__bool (*FLAC__stream_decoder_seek_absolute)( + FLAC__StreamDecoder *decoder, + FLAC__uint64 sample); + FLAC__StreamDecoderState (*FLAC__stream_decoder_get_state)( + const FLAC__StreamDecoder *decoder); +} flac_loader; + +extern flac_loader flac; + +extern int Mix_InitFLAC(); +extern void Mix_QuitFLAC(); + +#endif // FLAC_MUSIC Index: SDL_mixer.h =================================================================== --- SDL_mixer.h (revision 3533) +++ SDL_mixer.h (working copy) @@ -103,6 +103,7 @@ MUS_MOD, MUS_MID, MUS_OGG, + MUS_FLAC, MUS_MP3, MUS_MP3_MAD } Mix_MusicType; Index: load_flac.c =================================================================== --- load_flac.c (revision 0) +++ load_flac.c (revision 0) @@ -0,0 +1,332 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + This is the source needed to decode a FLAC into a waveform. + ~ Austen Dicken (admin@cvpcs.org). +*/ + +#ifdef FLAC_MUSIC + +#include +#include +#include + +#include "SDL_mutex.h" +#include "SDL_endian.h" +#include "SDL_timer.h" + +#include "SDL_mixer.h" +#include "dynamic_flac.h" +#include "load_flac.h" + +#include + +typedef struct { + SDL_RWops* sdl_src; + SDL_AudioSpec* sdl_spec; + Uint8** sdl_audio_buf; + Uint32* sdl_audio_len; + int sdl_audio_read; + FLAC__uint64 flac_total_samples; + unsigned flac_bps; +} FLAC_SDL_Data; + +static FLAC__StreamDecoderReadStatus flac_read_load_cb( + const FLAC__StreamDecoder *decoder, + FLAC__byte buffer[], + size_t *bytes, + void *client_data) { + // make sure there is something to be reading + if (*bytes > 0) { + FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data; + + *bytes = SDL_RWread (data->sdl_src, buffer, sizeof (FLAC__byte), + *bytes); + + if(*bytes < 0) { // error in read + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } + else if(*bytes == 0) { // no data was read (EOF) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + } + else { // data was read, continue + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + } + else { + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } +} + +static FLAC__StreamDecoderSeekStatus flac_seek_load_cb( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 absolute_byte_offset, + void *client_data) { + FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data; + + if (SDL_RWseek (data->sdl_src, absolute_byte_offset, SEEK_SET) < 0) { + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + } + else { + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + } +} + +static FLAC__StreamDecoderTellStatus flac_tell_load_cb( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *absolute_byte_offset, + void *client_data) { + FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data; + + int pos = SDL_RWtell (data->sdl_src); + + if (pos < 0) { + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + } + else { + *absolute_byte_offset = (FLAC__uint64)pos; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } +} + +static FLAC__StreamDecoderLengthStatus flac_length_load_cb( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *stream_length, + void *client_data) { + FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data; + + int pos = SDL_RWtell (data->sdl_src); + int length = SDL_RWseek (data->sdl_src, 0, SEEK_END); + + if (SDL_RWseek (data->sdl_src, pos, SEEK_SET) != pos || length < 0) { + /* there was an error attempting to return the stream to the original + * position, or the length was invalid. */ + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + } + else { + *stream_length = (FLAC__uint64)length; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } +} + +static FLAC__bool flac_eof_load_cb(const FLAC__StreamDecoder *decoder, + void *client_data) { + FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data; + + int pos = SDL_RWtell (data->sdl_src); + int end = SDL_RWseek (data->sdl_src, 0, SEEK_END); + + // was the original position equal to the end (a.k.a. the seek didn't move)? + if (pos == end) { + // must be EOF + return true; + } + else { + // not EOF, return to the original position + SDL_RWseek (data->sdl_src, pos, SEEK_SET); + + return false; + } +} + +static FLAC__StreamDecoderWriteStatus flac_write_load_cb( + const FLAC__StreamDecoder *decoder, + const FLAC__Frame *frame, + const FLAC__int32 *const buffer[], + void *client_data) { + FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data; + size_t i; + + if (data->flac_total_samples == 0) { + SDL_SetError ("Given FLAC file does not specify its sample count."); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + if (data->sdl_spec->channels != 2 || data->flac_bps != 16) { + SDL_SetError ("Current FLAC support is only for 16 bit Stereo files."); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + // check if it is the first audio frame so we can initialize the output + // buffer + if (frame->header.number.sample_number == 0) { + *(data->sdl_audio_len) = data->sdl_spec->size; + data->sdl_audio_read = 0; + *(data->sdl_audio_buf) = malloc (*(data->sdl_audio_len)); + + if (*(data->sdl_audio_buf) == NULL) { + SDL_SetError + ("Unable to allocate memory to store the FLAC stream."); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + } + + Uint8 *buf = *(data->sdl_audio_buf); + + for (i = 0; i < frame->header.blocksize; i++) { + FLAC__int16 i16; + FLAC__uint16 ui16; + + i16 = (FLAC__int16)buffer[0][i]; + ui16 = (FLAC__uint16)i16; + + *(buf + (data->sdl_audio_read++)) = (char)(ui16); + *(buf + (data->sdl_audio_read++)) = (char)(ui16 >> 8); + + i16 = (FLAC__int16)buffer[1][i]; + ui16 = (FLAC__uint16)i16; + + *(buf + (data->sdl_audio_read++)) = (char)(ui16); + *(buf + (data->sdl_audio_read++)) = (char)(ui16 >> 8); + } + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +static void flac_metadata_load_cb( + const FLAC__StreamDecoder *decoder, + const FLAC__StreamMetadata *metadata, + void *client_data) { + FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data; + FLAC__uint64 total_samples; + unsigned bps; + + if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + // save the metadata right now for use later on + *(data->sdl_audio_buf) = NULL; + *(data->sdl_audio_len) = 0; + memset (data->sdl_spec, '\0', sizeof (SDL_AudioSpec)); + + data->sdl_spec->format = AUDIO_S16; + data->sdl_spec->freq = (int)(metadata->data.stream_info.sample_rate); + data->sdl_spec->channels = (Uint8)(metadata->data.stream_info.channels); + data->sdl_spec->samples = 8192; /* buffer size */ + + total_samples = metadata->data.stream_info.total_samples; + bps = metadata->data.stream_info.bits_per_sample; + + data->sdl_spec->size = total_samples * data->sdl_spec->channels * + (bps / 8); + data->flac_total_samples = total_samples; + data->flac_bps = bps; + } +} + +static void flac_error_load_cb( + const FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderErrorStatus status, + void *client_data) { + // print an SDL error based on the error status + switch (status) { + case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC: + SDL_SetError ("Error processing the FLAC file [LOST_SYNC]."); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER: + SDL_SetError ("Error processing the FLAC file [BAD_HEADER]."); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH: + SDL_SetError ("Error processing the FLAC file [CRC_MISMATCH]."); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM: + SDL_SetError ("Error processing the FLAC file [UNPARSEABLE]."); + break; + default: + SDL_SetError ("Error processing the FLAC file [UNKNOWN]."); + break; + } +} + +/* don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadFLAC_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) { + FLAC__StreamDecoder *decoder = 0; + FLAC__StreamDecoderInitStatus init_status; + int was_error = 1; + int was_init = 0; + Uint32 samplesize; + + // create the client data passing information + FLAC_SDL_Data* client_data; + client_data = (FLAC_SDL_Data *)malloc (sizeof (FLAC_SDL_Data)); + + if ((!src) || (!audio_buf) || (!audio_len)) /* sanity checks. */ + goto done; + + if (Mix_InitFLAC() < 0) + goto done; + + if ((decoder = flac.FLAC__stream_decoder_new ()) == NULL) { + SDL_SetError ("Unable to allocate FLAC decoder."); + goto done; + } + + init_status = flac.FLAC__stream_decoder_init_stream (decoder, + flac_read_load_cb, flac_seek_load_cb, + flac_tell_load_cb, flac_length_load_cb, + flac_eof_load_cb, flac_write_load_cb, + flac_metadata_load_cb, flac_error_load_cb, + client_data); + + if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + SDL_SetError ("Unable to initialize FLAC stream decoder."); + goto done; + } + + was_init = 1; + + client_data->sdl_src = src; + client_data->sdl_spec = spec; + client_data->sdl_audio_buf = audio_buf; + client_data->sdl_audio_len = audio_len; + + if (!flac.FLAC__stream_decoder_process_until_end_of_stream (decoder)) { + SDL_SetError ("Unable to process FLAC file."); + goto done; + } + + was_error = 0; + + /* Don't return a buffer that isn't a multiple of samplesize */ + samplesize = ((spec->format & 0xFF) / 8) * spec->channels; + *audio_len &= ~(samplesize - 1); + +done: + if(was_init && decoder) { + flac.FLAC__stream_decoder_finish (decoder); + } + + if(decoder) { + flac.FLAC__stream_decoder_delete (decoder); + } + + if (src) { + if (freesrc) + SDL_RWclose (src); + else + SDL_RWseek (src, 0, SEEK_SET); + } + + if (was_error) + spec = NULL; + + Mix_QuitFLAC (); + + return spec; +} + +#endif // FLAC_MUSIC Index: configure.in =================================================================== --- configure.in (revision 3533) +++ configure.in (working copy) @@ -318,6 +318,41 @@ fi fi fi +AC_ARG_ENABLE([music-flac], +AC_HELP_STRING([--enable-music-flac], [enable FLAC music [[default=yes]]]), + [], [enable_music_flac=yes]) +AC_ARG_ENABLE([music-flac-shared], +AC_HELP_STRING([--enable-music-flac-shared], + [dynamically load FLAC support [[default=yes]]]), + [], [enable_music_flac_shared=yes]) +if test x$enable_music_flac = xyes; then + AC_CHECK_HEADER([FLAC/stream_decoder.h], [have_flac_hdr=yes]) + AC_CHECK_LIB([FLAC], [FLAC__stream_decoder_new], [have_flac_lib=yes]) + if test x$have_flac_hdr = xyes -a x$have_flac_lib = xyes; then + case "$host" in + *-*-darwin*) + flac_lib=[`find_lib FLAC*.dylib`] + ;; + *-*-cygwin* | *-*-mingw32*) + flac_lib=[`find_lib "libFLAC*.dll"`] + ;; + *) + flac_lib=[`find_lib "libFLAC.so.[0-9]"`] + if test x$flac_lib = x; then + flac_lib=[`find_lib "libFLAC.so.[0-9]*"`] + fi + ;; + esac + SOURCES="$SOURCES $srcdir/*_flac.c" + EXTRA_CFLAGS="$EXTRA_CFLAGS -DFLAC_MUSIC" + if test x$enable_music_flac_shared = xyes && test x$flac_lib != x; then + echo "-- dynamic libFLAC -> $flac_lib" + EXTRA_CFLAGS="$EXTRA_CFLAGS -DFLAC_DYNAMIC=\\\"$flac_lib\\\"" + else + EXTRA_LDFLAGS="$EXTRA_LDFLAGS -lFLAC" + fi + fi +fi AC_ARG_ENABLE(music-mp3, [ --enable-music-mp3 enable MP3 music via smpeg [[default=yes]]], , enable_music_mp3=yes) Index: music_flac.c =================================================================== --- music_flac.c (revision 0) +++ music_flac.c (revision 0) @@ -0,0 +1,591 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org + + + This file is used to support SDL_LoadMUS playback of FLAC files. + ~ Austen Dicken (admin@cvpcs.org) +*/ + +#ifdef FLAC_MUSIC + +#include +#include +#include + +#include "SDL_mixer.h" +#include "dynamic_flac.h" +#include "music_flac.h" + +/* This is the format of the audio mixer data */ +static SDL_AudioSpec mixer; + +/* Initialize the FLAC player, with the given mixer settings + This function returns 0, or -1 if there was an error. + */ +int FLAC_init(SDL_AudioSpec *mixerfmt) { + mixer = *mixerfmt; + return(0); +} + +/* Set the volume for an FLAC stream */ +void FLAC_setvolume(FLAC_music *music, int volume) { + music->volume = volume; +} + +/* Load an FLAC stream from the given file */ +FLAC_music *FLAC_new(const char *file) { + SDL_RWops *rw; + + rw = SDL_RWFromFile (file, "rb"); + if (rw == NULL) { + SDL_SetError ("Couldn't open %s", file); + return NULL; + } + return FLAC_new_RW (rw); +} + +static FLAC__StreamDecoderReadStatus flac_read_music_cb( + const FLAC__StreamDecoder *decoder, + FLAC__byte buffer[], + size_t *bytes, + void *client_data) { + FLAC_music *data = (FLAC_music*)client_data; + + // make sure there is something to be reading + if (*bytes > 0) { + *bytes = SDL_RWread (data->rwops, buffer, sizeof (FLAC__byte), *bytes); + + if (*bytes < 0) { // error in read + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } + else if (*bytes == 0 ) { // no data was read (EOF) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + } + else { // data was read, continue + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + } + else { + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } +} + +static FLAC__StreamDecoderSeekStatus flac_seek_music_cb( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 absolute_byte_offset, + void *client_data) { + FLAC_music *data = (FLAC_music*)client_data; + + if (SDL_RWseek (data->rwops, absolute_byte_offset, SEEK_SET) < 0) { + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + } + else { + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + } +} + +static FLAC__StreamDecoderTellStatus flac_tell_music_cb( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *absolute_byte_offset, + void *client_data ) { + FLAC_music *data = (FLAC_music*)client_data; + + int pos = SDL_RWtell (data->rwops); + + if (pos < 0) { + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + } + else { + *absolute_byte_offset = (FLAC__uint64)pos; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } +} + +static FLAC__StreamDecoderLengthStatus flac_length_music_cb ( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *stream_length, + void *client_data) { + FLAC_music *data = (FLAC_music*)client_data; + + int pos = SDL_RWtell (data->rwops); + int length = SDL_RWseek (data->rwops, 0, SEEK_END); + + if (SDL_RWseek (data->rwops, pos, SEEK_SET) != pos || length < 0) { + /* there was an error attempting to return the stream to the original + * position, or the length was invalid. */ + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + } + else { + *stream_length = (FLAC__uint64)length; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } +} + +static FLAC__bool flac_eof_music_cb( + const FLAC__StreamDecoder *decoder, + void *client_data ) { + FLAC_music *data = (FLAC_music*)client_data; + + int pos = SDL_RWtell (data->rwops); + int end = SDL_RWseek (data->rwops, 0, SEEK_END); + + // was the original position equal to the end (a.k.a. the seek didn't move)? + if (pos == end) { + // must be EOF + return true; + } + else { + // not EOF, return to the original position + SDL_RWseek (data->rwops, pos, SEEK_SET); + + return false; + } +} + +static FLAC__StreamDecoderWriteStatus flac_write_music_cb( + const FLAC__StreamDecoder *decoder, + const FLAC__Frame *frame, + const FLAC__int32 *const buffer[], + void *client_data) { + FLAC_music *data = (FLAC_music *)client_data; + size_t i; + + if (data->flac_data.total_samples == 0) { + SDL_SetError ("Given FLAC file does not specify its sample count."); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + if (data->flac_data.channels != 2 || + data->flac_data.bits_per_sample != 16) { + SDL_SetError("Current FLAC support is only for 16 bit Stereo files."); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + for (i = 0; i < frame->header.blocksize; i++) { + // make sure we still have at least two bytes that can be read (one for + // each channel) + if (data->flac_data.max_to_read >= 4) { + // does the data block exist? + if (!data->flac_data.data) { + data->flac_data.data_len = data->flac_data.max_to_read; + data->flac_data.data_read = 0; + + // create it + data->flac_data.data = + (char *)malloc (data->flac_data.data_len); + + if (!data->flac_data.data) { + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + } + + FLAC__int16 i16; + FLAC__uint16 ui16; + + i16 = (FLAC__int16)buffer[0][i]; + ui16 = (FLAC__uint16)i16; + + *((data->flac_data.data) + (data->flac_data.data_read++)) = + (char)(ui16); + *((data->flac_data.data) + (data->flac_data.data_read++)) = + (char)(ui16 >> 8); + + i16 = (FLAC__int16)buffer[1][i]; + ui16 = (FLAC__uint16)i16; + + *((data->flac_data.data) + (data->flac_data.data_read++)) = + (char)(ui16); + *((data->flac_data.data) + (data->flac_data.data_read++)) = + (char)(ui16 >> 8); + + data->flac_data.max_to_read -= 4; + + if (data->flac_data.max_to_read < 4) { + // we need to set this so that the read halts from the + // FLAC_getsome function. + data->flac_data.max_to_read = 0; + } + } + else { + // we need to write to the overflow + if (!data->flac_data.overflow) { + data->flac_data.overflow_len = + 4 * (frame->header.blocksize - i); + data->flac_data.overflow_read = 0; + + // make it big enough for the rest of the block + data->flac_data.overflow = + (char *)malloc (data->flac_data.overflow_len); + + if (!data->flac_data.overflow) { + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + } + + FLAC__int16 i16; + FLAC__uint16 ui16; + + i16 = (FLAC__int16)buffer[0][i]; + ui16 = (FLAC__uint16)i16; + + *((data->flac_data.overflow) + (data->flac_data.overflow_read++)) = + (char)(ui16); + *((data->flac_data.overflow) + (data->flac_data.overflow_read++)) = + (char)(ui16 >> 8); + + i16 = (FLAC__int16)buffer[1][i]; + ui16 = (FLAC__uint16)i16; + + *((data->flac_data.overflow) + (data->flac_data.overflow_read++)) = + (char)(ui16); + *((data->flac_data.overflow) + (data->flac_data.overflow_read++)) = + (char)(ui16 >> 8); + } + } + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +static void flac_metadata_music_cb( + const FLAC__StreamDecoder *decoder, + const FLAC__StreamMetadata *metadata, + void *client_data) { + FLAC_music *data = (FLAC_music *)client_data; + + if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + data->flac_data.sample_rate = metadata->data.stream_info.sample_rate; + data->flac_data.channels = metadata->data.stream_info.channels; + data->flac_data.total_samples = + metadata->data.stream_info.total_samples; + data->flac_data.bits_per_sample = + metadata->data.stream_info.bits_per_sample; + data->flac_data.sample_size = data->flac_data.channels * + ((data->flac_data.bits_per_sample) / 8); + } +} + +static void flac_error_music_cb( + const FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderErrorStatus status, + void *client_data) { + // print an SDL error based on the error status + switch (status) { + case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC: + SDL_SetError ("Error processing the FLAC file [LOST_SYNC]."); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER: + SDL_SetError ("Error processing the FLAC file [BAD_HEADER]."); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH: + SDL_SetError ("Error processing the FLAC file [CRC_MISMATCH]."); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM: + SDL_SetError ("Error processing the FLAC file [UNPARSEABLE]."); + break; + default: + SDL_SetError ("Error processing the FLAC file [UNKNOWN]."); + break; + } +} + +/* Load an FLAC stream from an SDL_RWops object */ +FLAC_music *FLAC_new_RW(SDL_RWops *rw) { + FLAC_music *music; + int init_stage = 0; + int was_error = 1; + + music = (FLAC_music *)malloc ( sizeof (*music)); + if (music) { + /* Initialize the music structure */ + memset (music, 0, (sizeof (*music))); + FLAC_stop (music); + FLAC_setvolume (music, MIX_MAX_VOLUME); + music->section = -1; + music->rwops = rw; + music->flac_data.max_to_read = 0; + music->flac_data.overflow = NULL; + music->flac_data.overflow_len = 0; + music->flac_data.overflow_read = 0; + music->flac_data.data = NULL; + music->flac_data.data_len = 0; + music->flac_data.data_read = 0; + + if (Mix_InitFLAC () >= 0) { + init_stage++; // stage 1! + + music->flac_decoder = flac.FLAC__stream_decoder_new (); + + if (music->flac_decoder != NULL) { + init_stage++; // stage 2! + + if (flac.FLAC__stream_decoder_init_stream( + music->flac_decoder, + flac_read_music_cb, flac_seek_music_cb, + flac_tell_music_cb, flac_length_music_cb, + flac_eof_music_cb, flac_write_music_cb, + flac_metadata_music_cb, flac_error_music_cb, + music) == FLAC__STREAM_DECODER_INIT_STATUS_OK ) { + init_stage++; // stage 3! + + if (flac.FLAC__stream_decoder_process_until_end_of_metadata + (music->flac_decoder)) { + was_error = 0; + } + } + } + } + + if (was_error) { + switch (init_stage) { + case 3: + flac.FLAC__stream_decoder_finish( music->flac_decoder ); + case 2: + flac.FLAC__stream_decoder_delete( music->flac_decoder ); + case 1: + Mix_QuitFLAC(); + case 0: + free(music); + SDL_RWclose(rw); + break; + } + + SDL_SetError ("There was an error in stage [%d] of FLAC init.", + init_stage); + + return NULL; + } + } + else { + SDL_OutOfMemory(); + } + + return music; +} + +/* Start playback of a given FLAC stream */ +void FLAC_play(FLAC_music *music) { + music->playing = 1; +} + +/* Return non-zero if a stream is currently playing */ +int FLAC_playing(FLAC_music *music) { + return(music->playing); +} + +/* Read some FLAC stream data and convert it for output */ +static void FLAC_getsome(FLAC_music *music) { + int section; + SDL_AudioCVT *cvt; + + /* GET AUDIO wAVE DATA */ + // set the max number of characters to read + music->flac_data.max_to_read = 8192; + + // clear out the data buffer if it exists + if (music->flac_data.data) { + free (music->flac_data.data); + } + + music->flac_data.data_len = music->flac_data.max_to_read; + music->flac_data.data_read = 0; + music->flac_data.data = (char *)malloc (music->flac_data.data_len); + + // we have data to read + while(music->flac_data.max_to_read > 0) { + // first check if there is data in the overflow from before + if (music->flac_data.overflow) { + size_t overflow_len = music->flac_data.overflow_read; + + if (overflow_len > music->flac_data.max_to_read) { + size_t overflow_extra_len = overflow_len - + music->flac_data.max_to_read; + + char* new_overflow = (char *)malloc (overflow_extra_len); + memcpy (music->flac_data.data+music->flac_data.data_read, + music->flac_data.overflow, music->flac_data.max_to_read); + music->flac_data.data_read += music->flac_data.max_to_read; + memcpy (new_overflow, + music->flac_data.overflow + music->flac_data.max_to_read, + overflow_extra_len); + free (music->flac_data.overflow); + music->flac_data.overflow = new_overflow; + music->flac_data.overflow_len = overflow_extra_len; + music->flac_data.overflow_read = 0; + music->flac_data.max_to_read = 0; + } + else { + memcpy (music->flac_data.data+music->flac_data.data_read, + music->flac_data.overflow, overflow_len); + music->flac_data.data_read += overflow_len; + free (music->flac_data.overflow); + music->flac_data.overflow = NULL; + music->flac_data.overflow_len = 0; + music->flac_data.overflow_read = 0; + music->flac_data.max_to_read -= overflow_len; + } + } + else { + if (!flac.FLAC__stream_decoder_process_single ( + music->flac_decoder)) { + music->flac_data.max_to_read = 0; + } + + if (flac.FLAC__stream_decoder_get_state (music->flac_decoder) + == FLAC__STREAM_DECODER_END_OF_STREAM) { + music->flac_data.max_to_read = 0; + } + } + } + + if (music->flac_data.data_read <= 0) { + if (music->flac_data.data_read == 0) { + music->playing = 0; + } + return; + } + cvt = &music->cvt; + if (section != music->section) { + + SDL_BuildAudioCVT (cvt, AUDIO_S16, (Uint8)music->flac_data.channels, + (int)music->flac_data.sample_rate, mixer.format, + mixer.channels, mixer.freq); + if (cvt->buf) { + free (cvt->buf); + } + cvt->buf = (Uint8 *)malloc (music->flac_data.data_len * cvt->len_mult); + music->section = section; + } + if (cvt->buf) { + memcpy (cvt->buf, music->flac_data.data, music->flac_data.data_read); + if (cvt->needed) { + cvt->len = music->flac_data.data_read; + SDL_ConvertAudio (cvt); + } + else { + cvt->len_cvt = music->flac_data.data_read; + } + music->len_available = music->cvt.len_cvt; + music->snd_available = music->cvt.buf; + } + else { + SDL_SetError ("Out of memory"); + music->playing = 0; + } +} + +/* Play some of a stream previously started with FLAC_play() */ +int FLAC_playAudio(FLAC_music *music, Uint8 *snd, int len) { + int mixable; + + while ((len > 0) && music->playing) { + if (!music->len_available) { + FLAC_getsome (music); + } + mixable = len; + if (mixable > music->len_available) { + mixable = music->len_available; + } + if (music->volume == MIX_MAX_VOLUME) { + memcpy (snd, music->snd_available, mixable); + } + else { + SDL_MixAudio (snd, music->snd_available, mixable, music->volume); + } + music->len_available -= mixable; + music->snd_available += mixable; + len -= mixable; + snd += mixable; + } + + return len; +} + +/* Stop playback of a stream previously started with FLAC_play() */ +void FLAC_stop(FLAC_music *music) { + music->playing = 0; +} + +/* Close the given FLAC_music object */ +void FLAC_delete(FLAC_music *music) { + if (music) { + if (music->flac_decoder) { + flac.FLAC__stream_decoder_finish (music->flac_decoder); + flac.FLAC__stream_decoder_delete (music->flac_decoder); + } + + if (music->flac_data.data) { + free (music->flac_data.data); + } + + if (music->flac_data.overflow) { + free (music->flac_data.overflow); + } + + if (music->cvt.buf) { + free (music->cvt.buf); + } + + free (music); + + Mix_QuitFLAC (); + } +} + +/* Jump (seek) to a given position (time is in seconds) */ +void FLAC_jump_to_time(FLAC_music *music, double time) { + if (music) { + if (music->flac_decoder) { + double seek_sample = music->flac_data.sample_rate * time; + + // clear data if it has data + if (music->flac_data.data) { + free (music->flac_data.data); + music->flac_data.data = NULL; + } + + // clear overflow if it has data + if (music->flac_data.overflow) { + free (music->flac_data.overflow); + music->flac_data.overflow = NULL; + } + + if (!flac.FLAC__stream_decoder_seek_absolute (music->flac_decoder, + (FLAC__uint64)seek_sample)) { + if (flac.FLAC__stream_decoder_get_state (music->flac_decoder) + == FLAC__STREAM_DECODER_SEEK_ERROR) { + flac.FLAC__stream_decoder_flush (music->flac_decoder); + } + + SDL_SetError + ("Seeking of FLAC stream failed: libFLAC seek failed."); + } + } + else { + SDL_SetError + ("Seeking of FLAC stream failed: FLAC decoder was NULL."); + } + } + else { + SDL_SetError ("Seeking of FLAC stream failed: music was NULL."); + } +} + +#endif /* FLAC_MUSIC */ Index: load_flac.h =================================================================== --- load_flac.h (revision 0) +++ load_flac.h (revision 0) @@ -0,0 +1,29 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + This is the source needed to decode a FLAC into a waveform. + ~ Austen Dicken (admin@cvpcs.org). +*/ + +/* $Id: $ */ + +#ifdef FLAC_MUSIC +/* Don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadFLAC_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len); +#endif Index: music_flac.h =================================================================== --- music_flac.h (revision 0) +++ music_flac.h (revision 0) @@ -0,0 +1,93 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org + + Header to handle loading FLAC music files in SDL. + ~ Austen Dicken (admin@cvpcs.org) +*/ + +/* $Id: $ */ + +#ifdef FLAC_MUSIC + +#include + +typedef struct { + FLAC__uint64 sample_size; + unsigned sample_rate; + unsigned channels; + unsigned bits_per_sample; + FLAC__uint64 total_samples; + + // the following are used to handle the callback nature of the writer + int max_to_read; + char *data; // pointer to beginning of data array + int data_len; // size of data array + int data_read; // amount of data array used + char *overflow; // pointer to beginning of overflow array + int overflow_len; // size of overflow array + int overflow_read; // amount of overflow array used +} FLAC_Data; + +typedef struct { + int playing; + int volume; + int section; + FLAC__StreamDecoder *flac_decoder; + FLAC_Data flac_data; + SDL_RWops *rwops; + SDL_AudioCVT cvt; + int len_available; + Uint8 *snd_available; +} FLAC_music; + +/* Initialize the FLAC player, with the given mixer settings + This function returns 0, or -1 if there was an error. + */ +extern int FLAC_init(SDL_AudioSpec *mixer); + +/* Set the volume for a FLAC stream */ +extern void FLAC_setvolume(FLAC_music *music, int volume); + +/* Load a FLAC stream from the given file */ +extern FLAC_music *FLAC_new(const char *file); + +/* Load an FLAC stream from an SDL_RWops object */ +extern FLAC_music *FLAC_new_RW(SDL_RWops *rw); + +/* Start playback of a given FLAC stream */ +extern void FLAC_play(FLAC_music *music); + +/* Return non-zero if a stream is currently playing */ +extern int FLAC_playing(FLAC_music *music); + +/* Play some of a stream previously started with FLAC_play() */ +extern int FLAC_playAudio(FLAC_music *music, Uint8 *stream, int len); + +/* Stop playback of a stream previously started with FLAC_play() */ +extern void FLAC_stop(FLAC_music *music); + +/* Close the given FLAC stream */ +extern void FLAC_delete(FLAC_music *music); + +/* Jump (seek) to a given position (time is in seconds) */ +extern void FLAC_jump_to_time(FLAC_music *music, double time); + +#endif /* FLAC_MUSIC */ Index: music.c =================================================================== --- music.c (revision 3533) +++ music.c (working copy) @@ -89,6 +89,9 @@ #ifdef MP3_MAD_MUSIC #include "music_mad.h" #endif +#ifdef FLAC_MUSIC +#include "music_flac.h" +#endif #if defined(MP3_MUSIC) || defined(MP3_MAD_MUSIC) static SDL_AudioSpec used_mixer; @@ -133,6 +136,9 @@ #ifdef MP3_MAD_MUSIC mad_data *mp3_mad; #endif +#ifdef FLAC_MUSIC + FLAC_music *flac; +#endif } data; Mix_Fading fading; int fade_step; @@ -345,6 +351,13 @@ break; #endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + len = FLAC_playAudio(music_playing->data.flac, stream, len); + if (len > 0 && music_halt_or_loop()) + FLAC_playAudio(music_playing->data.flac, stream, len); + break; +#endif #ifdef MP3_MUSIC case MUS_MP3: smpeg.SMPEG_playAudio(music_playing->data.mp3, stream, len); @@ -469,6 +482,11 @@ ++music_error; } #endif +#ifdef FLAC_MUSIC + if ( FLAC_init(mixer) < 0 ) { + ++music_error; + } +#endif #if defined(MP3_MUSIC) || defined(MP3_MAD_MUSIC) /* Keep a copy of the mixer */ used_mixer = *mixer; @@ -602,6 +620,17 @@ } } else #endif +#ifdef FLAC_MUSIC + /* FLAC files have the magic four bytes "fLaC" */ + if ( (ext && MIX_string_equals(ext, "FLAC")) || + strcmp((char *)magic, "fLaC") == 0 ) { + music->type = MUS_FLAC; + music->data.flac = FLAC_new(file); + if ( music->data.flac == NULL ) { + music->error = 1; + } + } else +#endif #ifdef MP3_MUSIC if ( (ext && MIX_string_equals(ext, "MPG")) || (ext && MIX_string_equals(ext, "MP3")) || @@ -720,6 +749,11 @@ OGG_delete(music->data.ogg); break; #endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + FLAC_delete(music->data.flac); + break; +#endif #ifdef MP3_MUSIC case MUS_MP3: smpeg.SMPEG_delete(music->data.mp3); @@ -813,6 +847,11 @@ OGG_play(music->data.ogg); break; #endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + FLAC_play(music->data.flac); + break; +#endif #ifdef MP3_MUSIC case MUS_MP3: smpeg.SMPEG_enableaudio(music->data.mp3,1); @@ -908,6 +947,11 @@ OGG_jump_to_time(music_playing->data.ogg, position); break; #endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + FLAC_jump_to_time(music_playing->data.flac, position); + break; +#endif #ifdef MP3_MUSIC case MUS_MP3: if ( position > 0.0 ) { @@ -997,6 +1041,11 @@ OGG_setvolume(music_playing->data.ogg, volume); break; #endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + FLAC_setvolume(music_playing->data.flac, volume); + break; +#endif #ifdef MP3_MUSIC case MUS_MP3: smpeg.SMPEG_setvolume(music_playing->data.mp3,(int)(((float)volume/(float)MIX_MAX_VOLUME)*100.0)); @@ -1070,6 +1119,11 @@ OGG_stop(music_playing->data.ogg); break; #endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + FLAC_stop(music_playing->data.flac); + break; +#endif #ifdef MP3_MUSIC case MUS_MP3: smpeg.SMPEG_stop(music_playing->data.mp3); @@ -1217,6 +1271,13 @@ } break; #endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + if ( ! FLAC_playing(music_playing->data.flac) ) { + playing = 0; + } + break; +#endif #ifdef MP3_MUSIC case MUS_MP3: if ( smpeg.SMPEG_status(music_playing->data.mp3) != SMPEG_PLAYING ) @@ -1456,6 +1517,16 @@ } } else #endif +#ifdef FLAC_MUSIC + /* FLAC files have the magic four bytes "fLaC" */ + if ( strcmp((char *)magic, "fLaC") == 0 ) { + music->type = MUS_FLAC; + music->data.flac = FLAC_new_RW(rw); + if ( music->data.flac == NULL ) { + music->error = 1; + } + } else +#endif #ifdef MP3_MUSIC if ( magic[0] == 0xFF && (magic[1] & 0xF0) == 0xF0 ) { if ( Mix_InitMP3() == 0 ) { Index: mixer.c =================================================================== --- mixer.c (revision 3533) +++ mixer.c (working copy) @@ -34,6 +34,7 @@ #include "load_aiff.h" #include "load_voc.h" #include "load_ogg.h" +#include "load_flac.h" #define __MIX_INTERNAL_EFFECT__ #include "effects_internal.h" @@ -43,7 +44,8 @@ #define WAVE 0x45564157 /* "WAVE" */ #define FORM 0x4d524f46 /* "FORM" */ #define OGGS 0x5367674f /* "OggS" */ -#define CREA 0x61657243 /* "Crea" */ +#define CREA 0x61657243 /* "Crea" */ +#define FLAC 0x43614C66 /* "fLaC" */ static int audio_opened = 0; static SDL_AudioSpec mixer; @@ -455,6 +457,12 @@ (Uint8 **)&chunk->abuf, &chunk->alen); break; #endif +#ifdef FLAC_MUSIC + case FLAC: + loaded = Mix_LoadFLAC_RW(src, freesrc, &wavespec, + (Uint8 **)&chunk->abuf, &chunk->alen); + break; +#endif case CREA: loaded = Mix_LoadVOC_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);