511 lines
13 KiB
C++
511 lines
13 KiB
C++
/*
|
|
Audio File Library
|
|
Copyright (C) 2013 Michael Pruett <michael@68k.org>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 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
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the
|
|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "FLAC.h"
|
|
|
|
#include "Compiler.h"
|
|
#include "FileModule.h"
|
|
#include "Features.h"
|
|
#include "Track.h"
|
|
#include "byteorder.h"
|
|
|
|
#if ENABLE(FLAC)
|
|
|
|
#include <FLAC/stream_decoder.h>
|
|
#include <FLAC/stream_encoder.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <vector>
|
|
|
|
class FLACDecoder : public FileModule
|
|
{
|
|
public:
|
|
static FLACDecoder *create(Track *track, File *file, bool canSeek,
|
|
bool headerless, AFframecount *chunkFrames);
|
|
|
|
virtual ~FLACDecoder();
|
|
|
|
virtual const char *name() const OVERRIDE { return "flac_decompress"; }
|
|
|
|
virtual void describe() OVERRIDE;
|
|
virtual void runPull() OVERRIDE;
|
|
virtual void reset1() OVERRIDE;
|
|
virtual void reset2() OVERRIDE;
|
|
|
|
virtual bool handlesSeeking() const OVERRIDE { return true; }
|
|
|
|
private:
|
|
FLACDecoder(Track *track, File *file, bool canSeek);
|
|
|
|
FLAC__StreamDecoder *m_decoder;
|
|
std::vector<int32_t *> m_buffer;
|
|
int m_bufferedFrames, m_bufferedOffset;
|
|
|
|
void convertAndInterleave(int offset, int frameCount);
|
|
|
|
static FLAC__StreamDecoderReadStatus readCallback(const FLAC__StreamDecoder *, FLAC__byte buffer[], size_t *bytes, void *clientData)
|
|
{
|
|
FLACDecoder *flac = static_cast<FLACDecoder *>(clientData);
|
|
ssize_t result = flac->read(buffer, *bytes);
|
|
if (result > 0)
|
|
{
|
|
*bytes = result;
|
|
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
|
}
|
|
|
|
*bytes = 0;
|
|
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
|
|
}
|
|
|
|
static FLAC__StreamDecoderSeekStatus seekCallback(const FLAC__StreamDecoder *, FLAC__uint64 absoluteByteOffset, void *clientData)
|
|
{
|
|
FLACDecoder *flac = static_cast<FLACDecoder *>(clientData);
|
|
if (flac->seek(absoluteByteOffset) < 0)
|
|
return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
|
|
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
|
|
}
|
|
|
|
static FLAC__StreamDecoderTellStatus tellCallback(const FLAC__StreamDecoder *, FLAC__uint64 *absoluteByteOffset, void *clientData)
|
|
{
|
|
FLACDecoder *flac = static_cast<FLACDecoder *>(clientData);
|
|
off_t result = flac->tell();
|
|
if (result < 0)
|
|
return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
|
|
*absoluteByteOffset = static_cast<FLAC__uint64>(result);
|
|
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
|
|
}
|
|
|
|
static FLAC__StreamDecoderLengthStatus lengthCallback(const FLAC__StreamDecoder *, FLAC__uint64 *length, void *clientData)
|
|
{
|
|
FLACDecoder *flac = static_cast<FLACDecoder *>(clientData);
|
|
off_t result = flac->length();
|
|
if (result < 0)
|
|
return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
|
|
*length = result;
|
|
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
|
|
}
|
|
|
|
static FLAC__bool eofCallback(const FLAC__StreamDecoder *, void *clientData)
|
|
{
|
|
FLACDecoder *flac = static_cast<FLACDecoder *>(clientData);
|
|
return flac->tell() == flac->length();
|
|
}
|
|
|
|
static FLAC__StreamDecoderWriteStatus writeCallback(const FLAC__StreamDecoder *, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *clientData)
|
|
{
|
|
FLACDecoder *flac = static_cast<FLACDecoder *>(clientData);
|
|
flac->didDecodeFrame(frame, buffer);
|
|
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
|
}
|
|
|
|
static void metadataCallback(const FLAC__StreamDecoder *, const FLAC__StreamMetadata *metadata, void *clientData)
|
|
{
|
|
}
|
|
|
|
static void errorCallback(const FLAC__StreamDecoder *, FLAC__StreamDecoderErrorStatus status, void *clientData)
|
|
{
|
|
_af_error(AF_BAD_CODEC_CONFIG, "FLAC decoding error %d", status);
|
|
}
|
|
|
|
void didDecodeFrame(const FLAC__Frame *frame, const FLAC__int32 * const buffer[])
|
|
{
|
|
m_bufferedFrames = frame->header.blocksize;
|
|
m_bufferedOffset = 0;
|
|
for (unsigned c=0; c<frame->header.channels; c++)
|
|
memcpy(m_buffer[c], buffer[c], frame->header.blocksize * sizeof (int32_t));
|
|
|
|
m_track->nextfframe += frame->header.blocksize;
|
|
}
|
|
};
|
|
|
|
FLACDecoder *FLACDecoder::create(Track *track, File *file, bool canSeek,
|
|
bool headerless, AFframecount *chunkFrames)
|
|
{
|
|
return new FLACDecoder(track, file, canSeek);
|
|
}
|
|
|
|
FLACDecoder::FLACDecoder(Track *track, File *file, bool canSeek) :
|
|
FileModule(Decompress, track, file, canSeek),
|
|
m_decoder(NULL),
|
|
m_bufferedFrames(0),
|
|
m_bufferedOffset(0)
|
|
{
|
|
m_decoder = FLAC__stream_decoder_new();
|
|
|
|
if (FLAC__stream_decoder_init_stream(m_decoder,
|
|
readCallback,
|
|
seekCallback,
|
|
tellCallback,
|
|
lengthCallback,
|
|
eofCallback,
|
|
writeCallback,
|
|
metadataCallback,
|
|
errorCallback,
|
|
this) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
|
|
{
|
|
_af_error(AF_BAD_CODEC_CONFIG, "could not initialize FLAC decoder");
|
|
return;
|
|
}
|
|
|
|
m_buffer.resize(m_track->f.channelCount);
|
|
for (int c=0; c<m_track->f.channelCount; c++)
|
|
m_buffer[c] = new int32_t[FLAC__MAX_BLOCK_SIZE];
|
|
}
|
|
|
|
FLACDecoder::~FLACDecoder()
|
|
{
|
|
if (m_decoder)
|
|
{
|
|
FLAC__stream_decoder_delete(m_decoder);
|
|
m_decoder = NULL;
|
|
}
|
|
|
|
for (size_t i=0; i<m_buffer.size(); i++)
|
|
delete [] m_buffer[i];
|
|
}
|
|
|
|
void FLACDecoder::describe()
|
|
{
|
|
m_outChunk->f.byteOrder = _AF_BYTEORDER_NATIVE;
|
|
m_outChunk->f.compressionType = AF_COMPRESSION_NONE;
|
|
m_outChunk->f.compressionParams = AU_NULL_PVLIST;
|
|
}
|
|
|
|
void FLACDecoder::convertAndInterleave(int offset, int frameCount)
|
|
{
|
|
int channelCount = m_outChunk->f.channelCount;
|
|
|
|
if (m_track->f.sampleWidth == 16)
|
|
{
|
|
int16_t *out = static_cast<int16_t *>(m_outChunk->buffer);
|
|
for (int i=0; i<frameCount; i++)
|
|
{
|
|
for (int c=0; c<channelCount; c++)
|
|
{
|
|
int outIndex = (offset+i) * channelCount + c;
|
|
out[outIndex] = m_buffer[c][m_bufferedOffset + i];
|
|
}
|
|
}
|
|
}
|
|
else if (m_track->f.sampleWidth == 24)
|
|
{
|
|
uint8_t *out = static_cast<uint8_t *>(m_outChunk->buffer);
|
|
for (int i=0; i<frameCount; i++)
|
|
{
|
|
for (int c=0; c<channelCount; c++)
|
|
{
|
|
int32_t in = m_buffer[c][m_bufferedOffset + i];
|
|
uint8_t c0, c1, c2;
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
c0 = (in >> 16) & 0xff;
|
|
c1 = (in >> 8) & 0xff;
|
|
c2 = in & 0xff;
|
|
#else
|
|
c2 = (in >> 16) & 0xff;
|
|
c1 = (in >> 8) & 0xff;
|
|
c0 = in & 0xff;
|
|
#endif
|
|
|
|
int outIndex = (offset+i) * channelCount + c;
|
|
out[3*outIndex] = c0;
|
|
out[3*outIndex+1] = c1;
|
|
out[3*outIndex+2] = c2;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_bufferedOffset += frameCount;
|
|
}
|
|
|
|
void FLACDecoder::runPull()
|
|
{
|
|
int framesToRead = m_outChunk->frameCount;
|
|
int offset = 0;
|
|
while (framesToRead > 0)
|
|
{
|
|
int bufferedFramesToRead = std::min<int>(framesToRead,
|
|
m_bufferedFrames - m_bufferedOffset);
|
|
convertAndInterleave(offset, bufferedFramesToRead);
|
|
offset += bufferedFramesToRead;
|
|
framesToRead -= bufferedFramesToRead;
|
|
|
|
if (framesToRead > 0)
|
|
{
|
|
if (!FLAC__stream_decoder_process_single(m_decoder))
|
|
break;
|
|
if (FLAC__stream_decoder_get_state(m_decoder) >= FLAC__STREAM_DECODER_END_OF_STREAM)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLACDecoder::reset1()
|
|
{
|
|
}
|
|
|
|
void FLACDecoder::reset2()
|
|
{
|
|
if (!FLAC__stream_decoder_seek_absolute(m_decoder, m_track->nextfframe))
|
|
{
|
|
_af_error(AF_BAD_CODEC_CONFIG, "could not seek to frame %jd",
|
|
static_cast<intmax_t>(m_track->nextfframe));
|
|
}
|
|
}
|
|
|
|
class FLACEncoder : public FileModule
|
|
{
|
|
public:
|
|
static FLACEncoder *create(Track *track, File *file, bool canSeek,
|
|
bool headerless, AFframecount *chunkFrames);
|
|
|
|
virtual ~FLACEncoder();
|
|
|
|
virtual const char *name() const OVERRIDE { return "flac_compress"; }
|
|
|
|
virtual void describe() OVERRIDE;
|
|
virtual void runPush() OVERRIDE;
|
|
virtual void sync1() OVERRIDE;
|
|
virtual void sync2() OVERRIDE;
|
|
|
|
virtual bool handlesSeeking() const OVERRIDE { return true; }
|
|
|
|
private:
|
|
FLAC__StreamEncoder *m_encoder;
|
|
FLAC__int32 *m_buffer;
|
|
|
|
FLACEncoder(Track *track, File *file, bool canSeek);
|
|
|
|
void convert16To32();
|
|
void convert24To32();
|
|
|
|
static FLAC__StreamEncoderSeekStatus seekCallback(const FLAC__StreamEncoder *, FLAC__uint64 absoluteByteOffset, void *clientData)
|
|
{
|
|
FLACEncoder *flac = static_cast<FLACEncoder *>(clientData);
|
|
off_t result = flac->seek(absoluteByteOffset);
|
|
if (result < 0)
|
|
return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR;
|
|
return FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
|
|
}
|
|
|
|
static FLAC__StreamEncoderTellStatus tellCallback(const FLAC__StreamEncoder *, FLAC__uint64 *absoluteByteOffset, void *clientData)
|
|
{
|
|
FLACEncoder *flac = static_cast<FLACEncoder *>(clientData);
|
|
off_t offset = flac->tell();
|
|
if (offset < 0)
|
|
return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR;
|
|
*absoluteByteOffset = static_cast<FLAC__uint64>(offset);
|
|
return FLAC__STREAM_ENCODER_TELL_STATUS_OK;
|
|
}
|
|
|
|
static FLAC__StreamEncoderWriteStatus writeCallback(const FLAC__StreamEncoder *, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned currentFrame, void *clientData)
|
|
{
|
|
FLACEncoder *flac = static_cast<FLACEncoder *>(clientData);
|
|
ssize_t result = flac->write(buffer, bytes);
|
|
if (result == static_cast<ssize_t>(bytes))
|
|
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
|
|
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
|
|
}
|
|
};
|
|
|
|
FLACEncoder::FLACEncoder(Track *track, File *file, bool canSeek) :
|
|
FileModule(Compress, track, file, canSeek),
|
|
m_encoder(NULL),
|
|
m_buffer(NULL)
|
|
{
|
|
m_encoder = FLAC__stream_encoder_new();
|
|
if (!m_encoder)
|
|
{
|
|
_af_error(AF_BAD_CODEC_CONFIG, "could not create encoder");
|
|
return;
|
|
}
|
|
|
|
if (!FLAC__stream_encoder_set_channels(m_encoder, m_track->f.channelCount))
|
|
{
|
|
_af_error(AF_BAD_CODEC_CONFIG, "could not set channel count");
|
|
return;
|
|
}
|
|
|
|
if (!FLAC__stream_encoder_set_sample_rate(m_encoder, m_track->f.sampleRate))
|
|
{
|
|
_af_error(AF_BAD_CODEC_CONFIG, "could not set sample rate");
|
|
return;
|
|
}
|
|
|
|
if (!FLAC__stream_encoder_set_bits_per_sample(m_encoder, m_track->f.sampleWidth))
|
|
{
|
|
_af_error(AF_BAD_CODEC_CONFIG, "could not set sample width");
|
|
return;
|
|
}
|
|
|
|
if (FLAC__stream_encoder_init_stream(m_encoder,
|
|
writeCallback,
|
|
seekCallback,
|
|
tellCallback,
|
|
NULL,
|
|
this) != FLAC__STREAM_ENCODER_INIT_STATUS_OK)
|
|
{
|
|
_af_error(AF_BAD_CODEC_CONFIG, "could not initialize FLAC encoder");
|
|
return;
|
|
}
|
|
|
|
m_buffer = new int32_t[FLAC__MAX_BLOCK_SIZE * m_track->f.channelCount];
|
|
}
|
|
|
|
FLACEncoder::~FLACEncoder()
|
|
{
|
|
if (m_encoder)
|
|
{
|
|
FLAC__stream_encoder_delete(m_encoder);
|
|
m_encoder = NULL;
|
|
}
|
|
|
|
delete [] m_buffer;
|
|
}
|
|
|
|
FLACEncoder *FLACEncoder::create(Track *track, File *file, bool canSeek,
|
|
bool headerless, AFframecount *chunkFrames)
|
|
{
|
|
return new FLACEncoder(track, file, canSeek);
|
|
}
|
|
|
|
void FLACEncoder::describe()
|
|
{
|
|
m_outChunk->f.byteOrder = _AF_BYTEORDER_NATIVE;
|
|
m_outChunk->f.compressionType = AF_COMPRESSION_FLAC;
|
|
}
|
|
|
|
void FLACEncoder::runPush()
|
|
{
|
|
if (m_track->f.sampleWidth == 16)
|
|
convert16To32();
|
|
else if (m_track->f.sampleWidth == 24)
|
|
convert24To32();
|
|
|
|
if (!FLAC__stream_encoder_process_interleaved(m_encoder, m_buffer,
|
|
m_inChunk->frameCount))
|
|
{
|
|
_af_error(AF_BAD_CODEC_CONFIG, "could not encode data into FLAC stream");
|
|
}
|
|
|
|
m_track->nextfframe += m_inChunk->frameCount;
|
|
m_track->totalfframes = m_track->nextfframe;
|
|
}
|
|
|
|
void FLACEncoder::sync1()
|
|
{
|
|
}
|
|
|
|
void FLACEncoder::sync2()
|
|
{
|
|
if (!FLAC__stream_encoder_finish(m_encoder))
|
|
{
|
|
_af_error(AF_BAD_CODEC_CONFIG, "could not finish encoding");
|
|
}
|
|
}
|
|
|
|
void FLACEncoder::convert16To32()
|
|
{
|
|
int channelCount = m_track->f.channelCount;
|
|
const int16_t *src = static_cast<const int16_t *>(m_inChunk->buffer);
|
|
for (unsigned i=0; i<m_inChunk->frameCount; i++)
|
|
for (int c=0; c<channelCount; c++)
|
|
m_buffer[channelCount * i + c] = src[channelCount * i + c];
|
|
}
|
|
|
|
void FLACEncoder::convert24To32()
|
|
{
|
|
int channelCount = m_track->f.channelCount;
|
|
const uint8_t *src = static_cast<const uint8_t *>(m_inChunk->buffer);
|
|
for (unsigned i=0; i<m_inChunk->frameCount; i++)
|
|
for (int c=0; c<channelCount; c++)
|
|
{
|
|
int srcIndex = channelCount * i + c;
|
|
int32_t t =
|
|
#ifdef WORDS_BIGENDIAN
|
|
(src[3*srcIndex] << 24) |
|
|
(src[3*srcIndex+1] << 16) |
|
|
src[3*srcIndex+2] << 8;
|
|
#else
|
|
(src[3*srcIndex+2] << 24) |
|
|
(src[3*srcIndex+1] << 16) |
|
|
src[3*srcIndex] << 8;
|
|
#endif
|
|
m_buffer[channelCount * i + c] = t >> 8;
|
|
}
|
|
}
|
|
|
|
bool _af_flac_format_ok(AudioFormat *f)
|
|
{
|
|
if (f->channelCount > static_cast<int>(FLAC__MAX_CHANNELS))
|
|
{
|
|
_af_error(AF_BAD_COMPRESSION,
|
|
"FLAC compression supports a maximum of 8 channels");
|
|
return false;
|
|
}
|
|
|
|
if (f->sampleFormat != AF_SAMPFMT_TWOSCOMP ||
|
|
(f->sampleWidth != 16 && f->sampleWidth != 24))
|
|
{
|
|
_af_error(AF_BAD_COMPRESSION,
|
|
"FLAC compression requires 16- or 24-bit signed integer format");
|
|
return false;
|
|
}
|
|
|
|
if (f->byteOrder != _AF_BYTEORDER_NATIVE)
|
|
{
|
|
_af_error(AF_BAD_COMPRESSION,
|
|
"FLAC compression requires native byte order");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#else
|
|
|
|
bool _af_flac_format_ok(AudioFormat *)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#endif
|
|
|
|
FileModule *_af_flac_init_compress(Track *track, File *file, bool canSeek,
|
|
bool headerless, AFframecount *chunkFrames)
|
|
{
|
|
#if ENABLE(FLAC)
|
|
return FLACEncoder::create(track, file, canSeek, headerless, chunkFrames);
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
FileModule *_af_flac_init_decompress(Track *track, File *file, bool canSeek,
|
|
bool headerless, AFframecount *chunkFrames)
|
|
{
|
|
#if ENABLE(FLAC)
|
|
return FLACDecoder::create(track, file, canSeek, headerless, chunkFrames);
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|