/* aKode: FLAC-Decoder Copyright (C) 2004-2005 Allan Sandfeld Jensen 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; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akodelib.h" #ifdef HAVE_LIBFLAC #include #include #include #include #ifdef HAVE_LIBOGGFLAC #include #endif #include "audioframe.h" #include "decoder.h" #include "file.h" #include "flac_decoder.h" namespace aKode { class FLACDecoder; bool FLACDecoderPlugin::canDecode(File* src) { char header[6]; bool res = false; src->openRO(); if(src->read(header, 4) == 4) { // skip id3v2 headers if (memcmp(header, "ID3", 3) == 0) { if (src->read(header, 6) != 6) goto end; int size = 0; if (header[1] & 0x10) size += 10; size += header[5]; size += header[4] << 7; size += header[3] << 14; size += header[2] << 21; src->seek(10+size); if (src->read(header, 4) != 4) goto end; } if (memcmp(header, "fLaC",4) == 0 ) res = true; } end: src->close(); return res; } extern "C" { FLACDecoderPlugin flac_decoder; } #ifdef HAVE_LIBOGGFLAC bool OggFLACDecoderPlugin::canDecode(File* src) { char header[34]; bool res = false; src->openRO(); if (src->read(header, 34) == 34) if (memcmp(header, "OggS",4) == 0 ) // old FLAC 1.1.0 format if (memcmp(header+28, "fLaC",4) == 0) res = true; else // new FLAC 1.1.1 format if (memcmp(header+29, "FLAC",4) == 0) res = true; src->close(); return res; } extern "C" { OggFLACDecoderPlugin oggflac_decoder; } #endif struct FLACDecoder::private_data { private_data() : decoder(0), valid(false), out(0), source(0), eof(false), error(false) {}; FLAC__SeekableStreamDecoder *decoder; const FLAC__StreamMetadata_StreamInfo* si; const FLAC__StreamMetadata_VorbisComment* vc; bool valid; AudioFrame *out; File *source; AudioConfiguration config; uint32_t max_block_size; uint64_t position, length; bool eof, error; }; #ifdef HAVE_LIBOGGFLAC struct OggFLACDecoder::private_data { private_data() : decoder(0), valid(false), out(0), source(0), eof(false), error(false) {}; OggFLAC__SeekableStreamDecoder *decoder; const FLAC__StreamMetadata_StreamInfo* si; const FLAC__StreamMetadata_VorbisComment* vc; bool valid; AudioFrame *out; File *source; AudioConfiguration config; uint32_t max_block_size; uint64_t position, length; bool eof, error; }; #endif static FLAC__SeekableStreamDecoderReadStatus flac_read_callback( const FLAC__SeekableStreamDecoder *, FLAC__byte buffer[], unsigned *bytes, void *client_data) { FLACDecoder::private_data *data = (FLACDecoder::private_data*)client_data; long res = data->source->read((char*)buffer, *bytes); if (res<=0) { if (data->source->eof()) data->eof = true; return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR; } else { *bytes = res; return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK; } } #ifdef HAVE_LIBOGGFLAC // God, I hate people doing OO in C! static OggFLAC__SeekableStreamDecoderReadStatus oggflac_read_callback( const OggFLAC__SeekableStreamDecoder *, FLAC__byte buffer[], unsigned *bytes, void *client_data) { OggFLACDecoder::private_data *data = (OggFLACDecoder::private_data*)client_data; long res = data->source->read((char*)buffer, *bytes); if (res<=0) { if (data->source->eof()) data->eof = true; return OggFLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR; } else { *bytes = res; return OggFLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK; } } #endif static FLAC__SeekableStreamDecoderSeekStatus flac_seek_callback( const FLAC__SeekableStreamDecoder *, FLAC__uint64 absolute_byte_offset, void *client_data) { FLACDecoder::private_data *data = (FLACDecoder::private_data*)client_data; if(data->source->seek(absolute_byte_offset)) return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK; else return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR; } #ifdef HAVE_LIBOGGFLAC static OggFLAC__SeekableStreamDecoderSeekStatus oggflac_seek_callback( const OggFLAC__SeekableStreamDecoder *, FLAC__uint64 absolute_byte_offset, void *client_data) { OggFLACDecoder::private_data *data = (OggFLACDecoder::private_data*)client_data; if(data->source->seek(absolute_byte_offset)) return OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK; else return OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR; } #endif static FLAC__SeekableStreamDecoderTellStatus flac_tell_callback( const FLAC__SeekableStreamDecoder *, FLAC__uint64 *absolute_byte_offset, void *client_data) { FLACDecoder::private_data *data = (FLACDecoder::private_data*)client_data; long res = data->source->position(); if (res<0) return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR; else { *absolute_byte_offset = res; return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK; } } #ifdef HAVE_LIBOGGFLAC static OggFLAC__SeekableStreamDecoderTellStatus oggflac_tell_callback( const OggFLAC__SeekableStreamDecoder *, FLAC__uint64 *absolute_byte_offset, void *client_data) { OggFLACDecoder::private_data *data = (OggFLACDecoder::private_data*)client_data; long res = data->source->position(); if (res<0) return OggFLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR; else { *absolute_byte_offset = res; return OggFLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK; } } #endif static FLAC__SeekableStreamDecoderLengthStatus flac_length_callback( const FLAC__SeekableStreamDecoder *, FLAC__uint64 *stream_length, void *client_data) { FLACDecoder::private_data *data = (FLACDecoder::private_data*)client_data; long res = data->source->length(); if (res<0) return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR; else { *stream_length = res; return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK; } } #ifdef HAVE_LIBOGGFLAC static OggFLAC__SeekableStreamDecoderLengthStatus oggflac_length_callback( const OggFLAC__SeekableStreamDecoder *, FLAC__uint64 *stream_length, void *client_data) { OggFLACDecoder::private_data *data = (OggFLACDecoder::private_data*)client_data; long res = data->source->length(); if (res<0) return OggFLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR; else { *stream_length = res; return OggFLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK; } } #endif template static FLAC__bool eof_callback( const S *, void *client_data) { T *data = (T*)client_data; return data->source->eof(); } template static FLAC__StreamDecoderWriteStatus write_callback( const S *, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void* client_data) { T *data = (T*)client_data; //FLACDecoder::private_data *data = (FLACDecoder::private_data*)client_data; if (!data->out) // Handle spurious callbacks (happens during seeks) data->out = new AudioFrame; const long frameSize = frame->header.blocksize; const char bits = frame->header.bits_per_sample; const char channels = frame->header.channels; AudioFrame* const outFrame = data->out; outFrame->reserveSpace(channels, frameSize, bits); outFrame->sample_rate = frame->header.sample_rate; if (channels == 1 || channels == 2) outFrame->channel_config = aKode::MonoStereo; else if (channels > 2 && channels < 8) outFrame->channel_config = aKode::Surround; else outFrame->channel_config = aKode::MultiChannel; for(int i = 0; idata[i] == 0) break; if (bits<=8) { int8_t** data = (int8_t**)outFrame->data; for(long j=0; jdata; for(long j=0; jdata; for(long j=0; jposition+=frameSize; data->valid = true; return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } template static void metadata_callback( const S *, //const FLAC__SeekableStreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void* client_data) { T *data = (T*)client_data; // FLACDecoder::private_data *data = (FLACDecoder::private_data*)client_data; if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { data->length = metadata->data.stream_info.total_samples; data->config.sample_rate = metadata->data.stream_info.sample_rate; data->config.sample_width = metadata->data.stream_info.bits_per_sample; data->config.channels = metadata->data.stream_info.channels; data->max_block_size = metadata->data.stream_info.max_blocksize; if (data->config.channels <= 2) data->config.channel_config = aKode::MonoStereo; else if (data->config.channels <= 7) data->config.channel_config = aKode::Surround; else data->config.channel_config = aKode::MultiChannel; data->si = &metadata->data.stream_info; data->position = 0; } else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { data->vc = &metadata->data.vorbis_comment; } } template static void error_callback( const S *, //const FLAC__SeekableStreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void* /* client_data */) { //T *data = (T*)client_data; std::cerr << "FLAC error: " << FLAC__StreamDecoderErrorStatusString[status] << "\n"; switch (status) { case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC: break; case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER: break; case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH: break; } //data->valid = false; } const AudioConfiguration* FLACDecoder::audioConfiguration() { return &m_data->config; } #ifdef HAVE_LIBOGGFLAC const AudioConfiguration* OggFLACDecoder::audioConfiguration() { return &m_data->config; } #endif FLACDecoder::FLACDecoder(File* src) { m_data = new private_data; m_data->out = 0; m_data->decoder = FLAC__seekable_stream_decoder_new(); m_data->source = src; m_data->source->openRO(); m_data->source->fadvise(); FLAC__seekable_stream_decoder_set_client_data(m_data->decoder, m_data); FLAC__seekable_stream_decoder_set_read_callback (m_data->decoder, flac_read_callback); FLAC__seekable_stream_decoder_set_seek_callback (m_data->decoder, flac_seek_callback); FLAC__seekable_stream_decoder_set_tell_callback (m_data->decoder, flac_tell_callback); FLAC__seekable_stream_decoder_set_length_callback (m_data->decoder, flac_length_callback); FLAC__seekable_stream_decoder_set_eof_callback (m_data->decoder, eof_callback); FLAC__seekable_stream_decoder_set_write_callback (m_data->decoder, write_callback); FLAC__seekable_stream_decoder_set_error_callback (m_data->decoder, error_callback); FLAC__seekable_stream_decoder_set_metadata_callback(m_data->decoder, metadata_callback); FLAC__seekable_stream_decoder_set_metadata_respond (m_data->decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); FLAC__seekable_stream_decoder_init(m_data->decoder); FLAC__seekable_stream_decoder_process_until_end_of_metadata(m_data->decoder); } FLACDecoder::~FLACDecoder() { FLAC__seekable_stream_decoder_finish(m_data->decoder); FLAC__seekable_stream_decoder_delete(m_data->decoder); m_data->source->close(); delete m_data; } #ifdef HAVE_LIBOGGFLAC OggFLACDecoder::OggFLACDecoder(File* src) { m_data = new private_data; m_data->out = 0; m_data->decoder = OggFLAC__seekable_stream_decoder_new(); m_data->source = src; m_data->source->openRO(); m_data->source->fadvise(); OggFLAC__seekable_stream_decoder_set_client_data(m_data->decoder, m_data); OggFLAC__seekable_stream_decoder_set_read_callback (m_data->decoder, oggflac_read_callback); OggFLAC__seekable_stream_decoder_set_seek_callback (m_data->decoder, oggflac_seek_callback); OggFLAC__seekable_stream_decoder_set_tell_callback (m_data->decoder, oggflac_tell_callback); OggFLAC__seekable_stream_decoder_set_length_callback (m_data->decoder, oggflac_length_callback); OggFLAC__seekable_stream_decoder_set_eof_callback (m_data->decoder, eof_callback); OggFLAC__seekable_stream_decoder_set_write_callback (m_data->decoder, write_callback); OggFLAC__seekable_stream_decoder_set_error_callback (m_data->decoder, error_callback); OggFLAC__seekable_stream_decoder_set_metadata_callback(m_data->decoder, metadata_callback); OggFLAC__seekable_stream_decoder_set_metadata_respond (m_data->decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); OggFLAC__seekable_stream_decoder_init(m_data->decoder); OggFLAC__seekable_stream_decoder_process_until_end_of_metadata(m_data->decoder); } OggFLACDecoder::~OggFLACDecoder() { OggFLAC__seekable_stream_decoder_finish(m_data->decoder); OggFLAC__seekable_stream_decoder_delete(m_data->decoder); m_data->source->close(); delete m_data; } #endif bool FLACDecoder::readFrame(AudioFrame* frame) { if (m_data->error || m_data->eof) return false; if (m_data->out) { // Handle spurious callbacks frame->freeSpace(); *frame = *m_data->out; // copy m_data->out->data = 0; // nullify, don't free! delete m_data->out; m_data->out = 0; return true; } m_data->valid = false; m_data->out = frame; bool ret = FLAC__seekable_stream_decoder_process_single(m_data->decoder); m_data->out = 0; if (ret && m_data->valid) { frame->pos = position(); return true; } else { FLAC__SeekableStreamDecoderState state = FLAC__seekable_stream_decoder_get_state(m_data->decoder); switch (state) { case FLAC__SEEKABLE_STREAM_DECODER_OK: break; case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM: m_data->eof = true; break; default: m_data->error = true; break; } return false; } } #ifdef HAVE_LIBOGGFLAC bool OggFLACDecoder::readFrame(AudioFrame* frame) { if (m_data->error || m_data->eof) return false; if (m_data->out) { // Handle spurious callbacks frame->freeSpace(); *frame = *m_data->out; // copy m_data->out->data = 0; // nullify, don't free! delete m_data->out; m_data->out = 0; return true; } m_data->valid = false; m_data->out = frame; bool ret = OggFLAC__seekable_stream_decoder_process_single(m_data->decoder); m_data->out = 0; if (ret && m_data->valid) { frame->pos = position(); return true; } else { OggFLAC__SeekableStreamDecoderState state = OggFLAC__seekable_stream_decoder_get_state(m_data->decoder); switch (state) { case OggFLAC__SEEKABLE_STREAM_DECODER_OK: break; case OggFLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM: m_data->eof = true; break; default: m_data->error = true; break; } return false; } } #endif long FLACDecoder::length() { float pos = ((float)m_data->length)/m_data->config.sample_rate; return (long)(pos*1000.0); } long FLACDecoder::position() { float pos = ((float)m_data->position)/m_data->config.sample_rate; return (long)(pos*1000.0); } bool FLACDecoder::eof() { return m_data->eof || m_data->source->eof(); } bool FLACDecoder::error() { return m_data->error; } bool FLACDecoder::seekable() { return m_data->source->seekable(); } bool FLACDecoder::seek(long pos) { if (m_data->error) return false; float samplePos = (float)pos * (float)m_data->config.sample_rate / 1000.0; m_data->position = (uint64_t)samplePos; return FLAC__seekable_stream_decoder_seek_absolute(m_data->decoder, m_data->position); } #ifdef HAVE_LIBOGGFLAC long OggFLACDecoder::length() { float pos = ((float)m_data->length)/m_data->config.sample_rate; return (long)(pos*1000.0); } long OggFLACDecoder::position() { float pos = ((float)m_data->position)/m_data->config.sample_rate; return (long)(pos*1000.0); } bool OggFLACDecoder::eof() { return m_data->eof || m_data->source->eof(); } bool OggFLACDecoder::error() { return m_data->error; } bool OggFLACDecoder::seekable() { return m_data->source->seekable(); } bool OggFLACDecoder::seek(long pos) { if (m_data->error) return false; float samplePos = (float)pos * (float)m_data->config.sample_rate / 1000.0; m_data->position = (uint64_t)samplePos; return OggFLAC__seekable_stream_decoder_seek_absolute(m_data->decoder, m_data->position); } #endif } // namespace #endif // HAVE_LIBFLAC