a855d62025
This ports Fabien Sanglard's Chocolate Duke to run on a version of SDL for Rockbox. Change-Id: I8f2c4c78af19de10c1633ed7bb7a997b43256dd9
593 lines
16 KiB
C
593 lines
16 KiB
C
/*
|
|
SDL_mixer: An audio mixer library based on the SDL library
|
|
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
This file is used to support SDL_LoadMUS playback of FLAC files.
|
|
~ Austen Dicken (admin@cvpcs.org)
|
|
*/
|
|
|
|
#ifdef FLAC_MUSIC
|
|
|
|
#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;
|
|
}
|
|
|
|
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, RW_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, RW_SEEK_END);
|
|
|
|
if (SDL_RWseek (data->rwops, pos, RW_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, RW_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, RW_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++) {
|
|
FLAC__int16 i16;
|
|
FLAC__uint16 ui16;
|
|
|
|
// 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 *)SDL_malloc (data->flac_data.data_len);
|
|
|
|
if (!data->flac_data.data) {
|
|
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
|
}
|
|
}
|
|
|
|
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 *)SDL_malloc (data->flac_data.overflow_len);
|
|
|
|
if (!data->flac_data.overflow) {
|
|
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
|
}
|
|
}
|
|
|
|
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, int freerw)
|
|
{
|
|
FLAC_music *music;
|
|
int init_stage = 0;
|
|
int was_error = 1;
|
|
|
|
if (!Mix_Init(MIX_INIT_FLAC)) {
|
|
if (freerw) {
|
|
SDL_RWclose(rw);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
music = (FLAC_music *)SDL_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->freerw = freerw;
|
|
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;
|
|
|
|
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;
|
|
} else {
|
|
SDL_SetError("FLAC__stream_decoder_process_until_end_of_metadata() failed");
|
|
}
|
|
} else {
|
|
SDL_SetError("FLAC__stream_decoder_init_stream() failed");
|
|
}
|
|
} else {
|
|
SDL_SetError("FLAC__stream_decoder_new() failed");
|
|
}
|
|
|
|
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:
|
|
case 0:
|
|
SDL_free(music);
|
|
if (freerw) {
|
|
SDL_RWclose(rw);
|
|
}
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
} else {
|
|
SDL_OutOfMemory();
|
|
if (freerw) {
|
|
SDL_RWclose(rw);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
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)
|
|
{
|
|
SDL_AudioCVT *cvt;
|
|
|
|
/* GET AUDIO WAVE DATA */
|
|
// set the max number of characters to read
|
|
music->flac_data.max_to_read = 8192;
|
|
music->flac_data.data_len = music->flac_data.max_to_read;
|
|
music->flac_data.data_read = 0;
|
|
if (!music->flac_data.data) {
|
|
music->flac_data.data = (char *)SDL_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;
|
|
|
|
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 (music->flac_data.overflow,
|
|
music->flac_data.overflow + music->flac_data.max_to_read,
|
|
overflow_extra_len);
|
|
music->flac_data.overflow_len = overflow_extra_len;
|
|
music->flac_data.overflow_read = overflow_extra_len;
|
|
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 (music->section < 0) {
|
|
|
|
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 *)SDL_malloc (music->flac_data.data_len * cvt->len_mult);
|
|
music->section = 0;
|
|
}
|
|
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);
|
|
}
|
|
|
|
if (music->freerw) {
|
|
SDL_RWclose(music->rwops);
|
|
}
|
|
free (music);
|
|
}
|
|
}
|
|
|
|
/* 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 */
|