diff options
Diffstat (limited to 'akode/plugins/xiph_decoder/flac_decoder.cpp')
-rw-r--r-- | akode/plugins/xiph_decoder/flac_decoder.cpp | 587 |
1 files changed, 587 insertions, 0 deletions
diff --git a/akode/plugins/xiph_decoder/flac_decoder.cpp b/akode/plugins/xiph_decoder/flac_decoder.cpp new file mode 100644 index 0000000..130e2a1 --- /dev/null +++ b/akode/plugins/xiph_decoder/flac_decoder.cpp @@ -0,0 +1,587 @@ +/* aKode: FLAC-Decoder + + Copyright (C) 2004-2005 Allan Sandfeld Jensen <kde@carewolf.com> + + 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" + +#if defined(HAVE_LIBFLAC) && !defined(HAVE_LIBFLAC113) + +#include <string.h> +#include <iostream> + +#include <FLAC/format.h> +#include <FLAC/seekable_stream_decoder.h> + +#ifdef HAVE_LIBOGGFLAC + #include <OggFLAC/seekable_stream_decoder.h> +#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) { + 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) { + 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<class S, class T> +static FLAC__bool eof_callback( + const S *, + void *client_data) +{ + T *data = (T*)client_data; + + return data->source->eof(); +} + +template<class S, class T> +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; i<channels; i++) { + if (outFrame->data[i] == 0) break; + if (bits<=8) { + int8_t** data = (int8_t**)outFrame->data; + for(long j=0; j<frameSize; j++) + data[i][j] = buffer[i][j]; + } else + if (bits<=16) { + int16_t** data = (int16_t**)outFrame->data; + for(long j=0; j<frameSize; j++) + data[i][j] = buffer[i][j]; + } else { + int32_t** data = (int32_t**)outFrame->data; + for(long j=0; j<frameSize; j++) + data[i][j] = buffer[i][j]; + } + } + data->position+=frameSize; + data->valid = true; + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +template<class S, class T> +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<class S, class T> +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__SeekableStreamDecoder, private_data>); + FLAC__seekable_stream_decoder_set_write_callback (m_data->decoder, + write_callback<FLAC__SeekableStreamDecoder, private_data>); + FLAC__seekable_stream_decoder_set_error_callback (m_data->decoder, + error_callback<FLAC__SeekableStreamDecoder, private_data>); + FLAC__seekable_stream_decoder_set_metadata_callback(m_data->decoder, + metadata_callback<FLAC__SeekableStreamDecoder, private_data>); + 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__SeekableStreamDecoder, private_data>); + OggFLAC__seekable_stream_decoder_set_write_callback (m_data->decoder, + write_callback<OggFLAC__SeekableStreamDecoder, private_data>); + OggFLAC__seekable_stream_decoder_set_error_callback (m_data->decoder, + error_callback<OggFLAC__SeekableStreamDecoder, private_data>); + OggFLAC__seekable_stream_decoder_set_metadata_callback(m_data->decoder, + metadata_callback<OggFLAC__SeekableStreamDecoder, private_data>); + 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; +} + +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; +} + +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 |