You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
495 lines
14 KiB
495 lines
14 KiB
/*
|
|
* FLAC decoder module for K3b.
|
|
* Based on the Ogg Vorbis module for same.
|
|
* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
|
|
* Copyright (C) 2003-2004 John Steele Scott <toojays@toojays.net>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
* See the file "COPYING" for the exact licensing terms.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "k3bflacdecoder.h"
|
|
|
|
#include <k3bpluginfactory.h>
|
|
|
|
#include <tqbuffer.h>
|
|
#include <tqfile.h>
|
|
#include <tqstringlist.h>
|
|
|
|
#include <kurl.h>
|
|
#include <kdebug.h>
|
|
#include <klocale.h>
|
|
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <FLAC++/metadata.h>
|
|
#include <FLAC++/decoder.h>
|
|
|
|
#ifdef HAVE_TAGLIB
|
|
#include <taglib/tag.h>
|
|
#include <taglib/flacfile.h>
|
|
#endif
|
|
|
|
#if !defined FLACPP_API_VERSION_CURRENT || FLACPP_API_VERSION_CURRENT < 6
|
|
#define LEGACY_FLAC
|
|
#else
|
|
#undef LEGACY_FLAC
|
|
#endif
|
|
|
|
K_EXPORT_COMPONENT_FACTORY( libk3bflacdecoder, K3bPluginFactory<K3bFLACDecoderFactory>( "libk3bflacdecoder" ) )
|
|
|
|
|
|
class K3bFLACDecoder::Private
|
|
#ifdef LEGACY_FLAC
|
|
: public FLAC::Decoder::SeekableStream
|
|
#else
|
|
: public FLAC::Decoder::Stream
|
|
#endif
|
|
{
|
|
public:
|
|
void open(TQFile* f) {
|
|
file = f;
|
|
file->open(IO_ReadOnly);
|
|
|
|
TQT_TQIODEVICE(internalBuffer)->flush();
|
|
|
|
set_metadata_respond(FLAC__METADATA_TYPE_STREAMINFO);
|
|
set_metadata_respond(FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
|
|
|
init();
|
|
process_until_end_of_metadata();
|
|
}
|
|
|
|
void cleanup() {
|
|
file->close();
|
|
finish();
|
|
delete comments;
|
|
comments = 0;
|
|
}
|
|
|
|
Private(TQFile* f)
|
|
#ifdef LEGACY_FLAC
|
|
: FLAC::Decoder::SeekableStream(),
|
|
#else
|
|
: FLAC::Decoder::Stream(),
|
|
#endif
|
|
comments(0) {
|
|
internalBuffer = new TQBuffer();
|
|
internalBuffer->open(IO_ReadWrite);
|
|
|
|
open(f);
|
|
}
|
|
|
|
|
|
~Private() {
|
|
cleanup();
|
|
delete internalBuffer;
|
|
}
|
|
|
|
bool seekToFrame(int frame);
|
|
|
|
TQFile* file;
|
|
TQBuffer* internalBuffer;
|
|
FLAC::Metadata::VorbisComment* comments;
|
|
unsigned rate;
|
|
unsigned channels;
|
|
unsigned bitsPerSample;
|
|
unsigned maxFramesize;
|
|
unsigned maxBlocksize;
|
|
unsigned minFramesize;
|
|
unsigned minBlocksize;
|
|
FLAC__uint64 samples;
|
|
|
|
protected:
|
|
#ifdef LEGACY_FLAC
|
|
virtual FLAC__SeekableStreamDecoderReadStatus read_callback(FLAC__byte buffer[], unsigned *bytes);
|
|
virtual FLAC__SeekableStreamDecoderSeekStatus seek_callback(FLAC__uint64 absolute_byte_offset);
|
|
virtual FLAC__SeekableStreamDecoderTellStatus tell_callback(FLAC__uint64 *absolute_byte_offset);
|
|
virtual FLAC__SeekableStreamDecoderLengthStatus length_callback(FLAC__uint64 *stream_length);
|
|
#else
|
|
virtual FLAC__StreamDecoderReadStatus read_callback(FLAC__byte buffer[], size_t *bytes);
|
|
virtual FLAC__StreamDecoderSeekStatus seek_callback(FLAC__uint64 absolute_byte_offset);
|
|
virtual FLAC__StreamDecoderTellStatus tell_callback(FLAC__uint64 *absolute_byte_offset);
|
|
virtual FLAC__StreamDecoderLengthStatus length_callback(FLAC__uint64 *stream_length);
|
|
#endif
|
|
virtual bool eof_callback();
|
|
virtual void error_callback(FLAC__StreamDecoderErrorStatus){};
|
|
virtual void metadata_callback(const ::FLAC__StreamMetadata *metadata);
|
|
virtual ::FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
|
|
};
|
|
|
|
bool K3bFLACDecoder::Private::seekToFrame(int frame) {
|
|
FLAC__uint64 sample = frame * rate / 75;
|
|
return seek_absolute(sample);
|
|
}
|
|
|
|
bool K3bFLACDecoder::Private::eof_callback() {
|
|
return file->atEnd();
|
|
}
|
|
|
|
#ifdef LEGACY_FLAC
|
|
FLAC__SeekableStreamDecoderReadStatus K3bFLACDecoder::Private::read_callback(FLAC__byte buffer[], unsigned *bytes) {
|
|
long retval = file->readBlock((char *)buffer, (*bytes));
|
|
if(-1 == retval) {
|
|
return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR;
|
|
} else {
|
|
(*bytes) = retval;
|
|
return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
|
|
}
|
|
}
|
|
#else
|
|
FLAC__StreamDecoderReadStatus K3bFLACDecoder::Private::read_callback(FLAC__byte buffer[], size_t *bytes) {
|
|
long retval = file->readBlock((char *)buffer, (*bytes));
|
|
if(-1 == retval) {
|
|
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
|
|
} else {
|
|
(*bytes) = retval;
|
|
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef LEGACY_FLAC
|
|
FLAC__SeekableStreamDecoderSeekStatus
|
|
K3bFLACDecoder::Private::seek_callback(FLAC__uint64 absolute_byte_offset) {
|
|
if(!file->at(absolute_byte_offset))
|
|
return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
|
|
else
|
|
return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;
|
|
}
|
|
#else
|
|
FLAC__StreamDecoderSeekStatus
|
|
K3bFLACDecoder::Private::seek_callback(FLAC__uint64 absolute_byte_offset) {
|
|
if(file->at(absolute_byte_offset) == FALSE)
|
|
return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
|
|
else
|
|
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
|
|
}
|
|
#endif
|
|
|
|
#ifdef LEGACY_FLAC
|
|
FLAC__SeekableStreamDecoderTellStatus
|
|
K3bFLACDecoder::Private::tell_callback(FLAC__uint64 *absolute_byte_offset) {
|
|
(*absolute_byte_offset) = file->at();
|
|
return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
|
|
}
|
|
#else
|
|
FLAC__StreamDecoderTellStatus
|
|
K3bFLACDecoder::Private::tell_callback(FLAC__uint64 *absolute_byte_offset) {
|
|
(*absolute_byte_offset) = file->at();
|
|
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
|
|
}
|
|
#endif
|
|
|
|
#ifdef LEGACY_FLAC
|
|
FLAC__SeekableStreamDecoderLengthStatus
|
|
K3bFLACDecoder::Private::length_callback(FLAC__uint64 *stream_length) {
|
|
(*stream_length) = file->size();
|
|
return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
|
|
}
|
|
#else
|
|
FLAC__StreamDecoderLengthStatus
|
|
K3bFLACDecoder::Private::length_callback(FLAC__uint64 *stream_length) {
|
|
(*stream_length) = file->size();
|
|
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
|
|
}
|
|
#endif
|
|
|
|
|
|
void K3bFLACDecoder::Private::metadata_callback(const FLAC__StreamMetadata *metadata) {
|
|
switch (metadata->type) {
|
|
case FLAC__METADATA_TYPE_STREAMINFO:
|
|
channels = metadata->data.stream_info.channels;
|
|
rate = metadata->data.stream_info.sample_rate;
|
|
bitsPerSample = metadata->data.stream_info.bits_per_sample;
|
|
samples = metadata->data.stream_info.total_samples;
|
|
maxFramesize = metadata->data.stream_info.max_framesize;
|
|
minFramesize = metadata->data.stream_info.min_framesize;
|
|
maxBlocksize = metadata->data.stream_info.max_blocksize;
|
|
minBlocksize = metadata->data.stream_info.min_blocksize;
|
|
break;
|
|
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
|
comments = new FLAC::Metadata::VorbisComment((FLAC__StreamMetadata *)metadata, true);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
FLAC__StreamDecoderWriteStatus K3bFLACDecoder::Private::write_callback(const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) {
|
|
unsigned i, j;
|
|
// Note that in canDecode we made sure that the input is 1-16 bit stereo or mono.
|
|
unsigned samples = frame->header.blocksize;
|
|
|
|
for(i=0; i < samples; i++) {
|
|
// in FLAC channel 0 is left, 1 is right
|
|
for(j=0; j < this->channels; j++) {
|
|
FLAC__int32 value = (buffer[j][i])<<(16 - frame->header.bits_per_sample);
|
|
internalBuffer->putch(value >> 8); // msb
|
|
internalBuffer->putch(value & 0xFF); // lsb
|
|
}
|
|
}
|
|
|
|
// Rewind the buffer so the decode method will take data from the beginning.
|
|
internalBuffer->at(0);
|
|
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
|
}
|
|
|
|
K3bFLACDecoder::K3bFLACDecoder( TQObject* parent, const char* name )
|
|
: K3bAudioDecoder( parent, name )
|
|
{
|
|
d = 0;
|
|
}
|
|
|
|
|
|
K3bFLACDecoder::~K3bFLACDecoder()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void K3bFLACDecoder::cleanup()
|
|
{
|
|
if (d) {
|
|
d->cleanup();
|
|
d->open(new TQFile(filename()));
|
|
}
|
|
else
|
|
d = new Private(new TQFile(filename()));
|
|
}
|
|
|
|
bool K3bFLACDecoder::analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch )
|
|
{
|
|
cleanup();
|
|
|
|
frames = (unsigned long)ceil((d->samples * 75.0)/d->rate);
|
|
samplerate = d->rate;
|
|
ch = d->channels;
|
|
|
|
// add meta info
|
|
if( d->comments != 0 ) {
|
|
kdDebug() << "(K3bFLACDecoder) unpacking Vorbis tags" << endl;
|
|
for( unsigned int i = 0; i < d->comments->get_num_comments(); ++i ) {
|
|
TQString key = TQString::fromUtf8( d->comments->get_comment(i).get_field_name(),
|
|
d->comments->get_comment(i).get_field_name_length() );
|
|
TQString value = TQString::fromUtf8( d->comments->get_comment(i).get_field_value(),
|
|
d->comments->get_comment(i).get_field_value_length() );
|
|
|
|
if( key.upper() == "TITLE" )
|
|
addMetaInfo( META_TITLE, value );
|
|
else if( key.upper() == "ARTIST" )
|
|
addMetaInfo( META_ARTIST, value );
|
|
else if( key.upper() == "DESCRIPTION" )
|
|
addMetaInfo( META_COMMENT, value );
|
|
}
|
|
}
|
|
#ifdef HAVE_TAGLIB
|
|
if ((d->comments == 0) || (d->comments->get_num_comments() == 0)) {
|
|
// no Vorbis comments, check for ID3 tags
|
|
kdDebug() << "(K3bFLACDecoder) using taglib to read tag" << endl;
|
|
TagLib::FLAC::File f( TQFile::encodeName(filename()) );
|
|
if( f.isOpen() ) {
|
|
addMetaInfo( META_TITLE, TStringToQString( f.tag()->title() ) );
|
|
addMetaInfo( META_ARTIST, TStringToQString( f.tag()->artist() ) );
|
|
addMetaInfo( META_COMMENT, TStringToQString( f.tag()->comment() ) );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool K3bFLACDecoder::initDecoderInternal()
|
|
{
|
|
cleanup();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
int K3bFLACDecoder::decodeInternal( char* _data, int maxLen )
|
|
{
|
|
int bytesToCopy;
|
|
int bytesCopied;
|
|
int bytesAvailable;
|
|
|
|
#ifdef LEGACY_FLAC
|
|
if(d->internalBuffer->size() == 0) {
|
|
// want more data
|
|
switch(d->get_state()) {
|
|
case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM:
|
|
d->finish();
|
|
break;
|
|
case FLAC__SEEKABLE_STREAM_DECODER_OK:
|
|
if(! d->process_single())
|
|
return -1;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
#else
|
|
if(d->internalBuffer->size() == 0) {
|
|
// want more data
|
|
if(d->get_state() == FLAC__STREAM_DECODER_END_OF_STREAM)
|
|
d->finish();
|
|
else if(d->get_state() < FLAC__STREAM_DECODER_END_OF_STREAM) {
|
|
if(! d->process_single())
|
|
return -1;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
bytesAvailable = d->internalBuffer->size() - d->internalBuffer->at();
|
|
bytesToCopy = TQMIN(maxLen, bytesAvailable);
|
|
bytesCopied = (int)d->internalBuffer->readBlock(_data, bytesToCopy);
|
|
|
|
if(bytesCopied == bytesAvailable) {
|
|
// reset the buffer
|
|
d->internalBuffer->close();
|
|
d->internalBuffer->open(IO_ReadWrite|IO_Truncate);
|
|
}
|
|
|
|
return bytesCopied;
|
|
}
|
|
|
|
|
|
bool K3bFLACDecoder::seekInternal( const K3b::Msf& pos )
|
|
{
|
|
return d->seekToFrame(pos.totalFrames());
|
|
}
|
|
|
|
|
|
TQString K3bFLACDecoder::fileType() const
|
|
{
|
|
return i18n("FLAC");
|
|
}
|
|
|
|
|
|
TQStringList K3bFLACDecoder::supportedTechnicalInfos() const
|
|
{
|
|
return TQStringList::split( ";",
|
|
i18n("Channels") + ";" +
|
|
i18n("Sampling Rate") + ";" +
|
|
i18n("Sample Size") );
|
|
}
|
|
|
|
|
|
TQString K3bFLACDecoder::technicalInfo( const TQString& info ) const
|
|
{
|
|
if( d->comments != 0 ) {
|
|
if( info == i18n("Vendor") )
|
|
#ifdef FLAC_NEWER_THAN_1_1_1
|
|
return TQString::fromUtf8((char*)d->comments->get_vendor_string());
|
|
#else
|
|
return TQString::fromUtf8(d->comments->get_vendor_string().get_field());
|
|
#endif
|
|
else if( info == i18n("Channels") )
|
|
return TQString::number(d->channels);
|
|
else if( info == i18n("Sampling Rate") )
|
|
return i18n("%1 Hz").arg(d->rate);
|
|
else if( info == i18n("Sample Size") )
|
|
return i18n("%1 bits").arg(d->bitsPerSample);
|
|
}
|
|
|
|
return TQString();
|
|
}
|
|
|
|
|
|
|
|
K3bFLACDecoderFactory::K3bFLACDecoderFactory( TQObject* parent, const char* name )
|
|
: K3bAudioDecoderFactory( parent, name )
|
|
{
|
|
}
|
|
|
|
|
|
K3bFLACDecoderFactory::~K3bFLACDecoderFactory()
|
|
{
|
|
}
|
|
|
|
|
|
K3bAudioDecoder* K3bFLACDecoderFactory::createDecoder( TQObject* parent,
|
|
const char* name ) const
|
|
{
|
|
return new K3bFLACDecoder( parent, name );
|
|
}
|
|
|
|
|
|
bool K3bFLACDecoderFactory::canDecode( const KURL& url )
|
|
{
|
|
// buffer large enough to read an ID3 tag header
|
|
char buf[10];
|
|
|
|
// Note: since file is created on the stack it will be closed automatically
|
|
// by its destructor when this method (i.e. canDecode) returns.
|
|
TQFile file(url.path());
|
|
|
|
if(!file.open(IO_ReadOnly)) {
|
|
kdDebug() << "(K3bFLACDecoder) Could not open file " << url.path() << endl;
|
|
return false;
|
|
}
|
|
|
|
// look for a fLaC magic number or ID3 tag header
|
|
if(10 != file.readBlock(buf, 10)) {
|
|
kdDebug() << "(K3bFLACDecorder) File " << url.path()
|
|
<< " is too small to be a FLAC file" << endl;
|
|
return false;
|
|
}
|
|
|
|
if(0 == memcmp(buf, "ID3", 3)) {
|
|
// Found ID3 tag, try and seek past it.
|
|
kdDebug() << "(K3bFLACDecorder) File " << url.path() << ": found ID3 tag" << endl;
|
|
|
|
// See www.id3.org for details of the header, note that the size field
|
|
// unpacks to 7-bit bytes, then the +10 is for the header itself.
|
|
int pos;
|
|
pos = ((buf[6]<<21)|(buf[7]<<14)|(buf[8]<<7)|buf[9]) + 10;
|
|
|
|
kdDebug() << "(K3bFLACDecoder) " << url.path() << ": seeking to "
|
|
<< pos << endl;
|
|
if(!file.at(pos)) {
|
|
kdDebug() << "(K3bFLACDecoder) " << url.path() << ": couldn't seek to "
|
|
<< pos << endl;
|
|
return false;
|
|
}else{
|
|
// seek was okay, try and read magic number into buf
|
|
if(4 != file.readBlock(buf, 4)) {
|
|
kdDebug() << "(K3bFLACDecorder) File " << url.path()
|
|
<< " has ID3 tag but naught else!" << endl;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(memcmp(buf, "fLaC", 4) != 0) {
|
|
kdDebug() << "(K3bFLACDecoder) " << url.path() << ": not a FLAC file" << endl;
|
|
return false;
|
|
}
|
|
|
|
FLAC::Metadata::StreamInfo info = FLAC::Metadata::StreamInfo();
|
|
FLAC::Metadata::get_streaminfo(url.path().ascii(), info);
|
|
|
|
if((info.get_channels() <= 2) &&
|
|
(info.get_bits_per_sample() <= 16)) {
|
|
return true;
|
|
} else {
|
|
kdDebug() << "(K3bFLACDecoder) " << url.path() << ": wrong format:" << endl
|
|
<< " channels: "
|
|
<< TQString::number(info.get_channels()) << endl
|
|
<< " samplerate: "
|
|
<< TQString::number(info.get_sample_rate()) << endl
|
|
<< " bits/sample: "
|
|
<< TQString::number(info.get_bits_per_sample()) << endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#include "k3bflacdecoder.moc"
|