Files
commandergenius/project/jni/sdl_mixer/music_flac.c

607 lines
16 KiB
C

/*
SDL_mixer: An audio mixer library based on the SDL library
Copyright (C) 1997-2009 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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, 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 *)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 *)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)
{
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_Init(MIX_INIT_FLAC)) {
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:
free(music);
SDL_RWclose(rw);
break;
}
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);
}
}
/* 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 */