blob: eb003dbed7db510f035a8faedac5fa0962d96f32 [file] [log] [blame]
/* libSoX file format: FLAC (c) 2006-7 robs@users.sourceforge.net
*
* 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 "third_party/sox/src/src/sox_i.h"
#include <string.h>
/* Next line for systems that don't define off_t when you #include
stdio.h; apparently OS/2 has this bug */
#include <sys/types.h>
#include "third_party/flac/src/include/FLAC/all.h"
#define MAX_COMPRESSION 8
typedef struct {
/* Info: */
unsigned bits_per_sample;
unsigned channels;
unsigned sample_rate;
uint64_t total_samples;
/* Decode buffer: */
sox_sample_t *req_buffer; /* this may be on the stack */
size_t number_of_requested_samples;
sox_sample_t *leftover_buf; /* heap */
unsigned number_of_leftover_samples;
FLAC__StreamDecoder * decoder;
FLAC__bool eof;
sox_bool seek_pending;
uint64_t seek_offset;
/* Encode buffer: */
FLAC__int32 * decoded_samples;
unsigned number_of_samples;
FLAC__StreamEncoder * encoder;
FLAC__StreamMetadata * metadata[2];
unsigned num_metadata;
} priv_t;
static FLAC__StreamDecoderReadStatus decoder_read_callback(FLAC__StreamDecoder const* decoder UNUSED, FLAC__byte buffer[], size_t* bytes, void* ft_data)
{
sox_format_t* ft = (sox_format_t*)ft_data;
if(*bytes > 0) {
*bytes = lsx_readbuf(ft, buffer, *bytes);
if(lsx_error(ft))
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
else if(*bytes == 0)
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
else
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}
else
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
}
static FLAC__StreamDecoderSeekStatus decoder_seek_callback(FLAC__StreamDecoder const* decoder UNUSED, FLAC__uint64 absolute_byte_offset, void* ft_data)
{
sox_format_t* ft = (sox_format_t*)ft_data;
if(lsx_seeki(ft, (off_t)absolute_byte_offset, SEEK_SET) < 0)
return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
else
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
}
static FLAC__StreamDecoderTellStatus decoder_tell_callback(FLAC__StreamDecoder const* decoder UNUSED, FLAC__uint64* absolute_byte_offset, void* ft_data)
{
sox_format_t* ft = (sox_format_t*)ft_data;
off_t pos;
if((pos = lsx_tell(ft)) < 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 decoder_length_callback(FLAC__StreamDecoder const* decoder UNUSED, FLAC__uint64* stream_length, void* ft_data)
{
sox_format_t* ft = (sox_format_t*)ft_data;
*stream_length = lsx_filelength(ft);
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
}
static FLAC__bool decoder_eof_callback(FLAC__StreamDecoder const* decoder UNUSED, void* ft_data)
{
sox_format_t* ft = (sox_format_t*)ft_data;
return lsx_eof(ft) ? 1 : 0;
}
static void decoder_metadata_callback(FLAC__StreamDecoder const * const flac, FLAC__StreamMetadata const * const metadata, void * const client_data)
{
sox_format_t * ft = (sox_format_t *) client_data;
priv_t * p = (priv_t *)ft->priv;
(void) flac;
if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
p->bits_per_sample = metadata->data.stream_info.bits_per_sample;
p->channels = metadata->data.stream_info.channels;
p->sample_rate = metadata->data.stream_info.sample_rate;
p->total_samples = metadata->data.stream_info.total_samples;
}
else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
size_t i;
if (metadata->data.vorbis_comment.num_comments == 0)
return;
if (ft->oob.comments != NULL) {
lsx_warn("multiple Vorbis comment block ignored");
return;
}
for (i = 0; i < metadata->data.vorbis_comment.num_comments; ++i)
sox_append_comment(&ft->oob.comments, (char const *) metadata->data.vorbis_comment.comments[i].entry);
}
}
static void decoder_error_callback(FLAC__StreamDecoder const * const flac, FLAC__StreamDecoderErrorStatus const status, void * const client_data)
{
sox_format_t * ft = (sox_format_t *) client_data;
(void) flac;
lsx_fail_errno(ft, SOX_EINVAL, "%s", FLAC__StreamDecoderErrorStatusString[status]);
}
static FLAC__StreamDecoderWriteStatus decoder_write_callback(FLAC__StreamDecoder const * const flac, FLAC__Frame const * const frame, FLAC__int32 const * const buffer[], void * const client_data)
{
sox_format_t * ft = (sox_format_t *) client_data;
priv_t * p = (priv_t *)ft->priv;
sox_sample_t * dst = p->req_buffer;
unsigned channel;
unsigned nsamples = frame->header.blocksize;
unsigned sample = 0;
size_t actual = nsamples * p->channels;
(void) flac;
if (frame->header.bits_per_sample != p->bits_per_sample || frame->header.channels != p->channels || frame->header.sample_rate != p->sample_rate) {
lsx_fail_errno(ft, SOX_EINVAL, "FLAC ERROR: parameters differ between frame and header");
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
if (dst == NULL) {
lsx_warn("FLAC ERROR: entered write callback without a buffer (SoX bug)");
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
/* FLAC may give us too much data, prepare the leftover buffer */
if (actual > p->number_of_requested_samples) {
size_t to_stash = actual - p->number_of_requested_samples;
p->leftover_buf = lsx_malloc(to_stash * sizeof(sox_sample_t));
p->number_of_leftover_samples = to_stash;
nsamples = p->number_of_requested_samples / p->channels;
p->req_buffer += p->number_of_requested_samples;
p->number_of_requested_samples = 0;
} else {
p->req_buffer += actual;
p->number_of_requested_samples -= actual;
}
leftover_copy:
for (; sample < nsamples; sample++) {
for (channel = 0; channel < p->channels; channel++) {
FLAC__int32 d = buffer[channel][sample];
switch (p->bits_per_sample) {
case 8: *dst++ = SOX_SIGNED_8BIT_TO_SAMPLE(d,); break;
case 16: *dst++ = SOX_SIGNED_16BIT_TO_SAMPLE(d,); break;
case 24: *dst++ = SOX_SIGNED_24BIT_TO_SAMPLE(d,); break;
case 32: *dst++ = SOX_SIGNED_32BIT_TO_SAMPLE(d,); break;
}
}
}
/* copy into the leftover buffer if we've prepared it */
if (sample < frame->header.blocksize) {
nsamples = frame->header.blocksize;
dst = p->leftover_buf;
goto leftover_copy;
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
static int start_read(sox_format_t * const ft)
{
priv_t * p = (priv_t *)ft->priv;
lsx_debug("API version %u", FLAC_API_VERSION_CURRENT);
p->decoder = FLAC__stream_decoder_new();
if (p->decoder == NULL) {
lsx_fail_errno(ft, SOX_ENOMEM, "FLAC ERROR creating the decoder instance");
return SOX_EOF;
}
FLAC__stream_decoder_set_md5_checking(p->decoder, sox_true);
FLAC__stream_decoder_set_metadata_respond_all(p->decoder);
if (FLAC__stream_decoder_init_stream(
p->decoder,
decoder_read_callback,
ft->seekable ? decoder_seek_callback : NULL,
ft->seekable ? decoder_tell_callback : NULL,
ft->seekable ? decoder_length_callback : NULL,
ft->seekable ? decoder_eof_callback : NULL,
decoder_write_callback,
decoder_metadata_callback,
decoder_error_callback,
ft) != FLAC__STREAM_DECODER_INIT_STATUS_OK){
lsx_fail_errno(ft, SOX_EHDR, "FLAC ERROR initialising decoder");
return SOX_EOF;
}
if (!FLAC__stream_decoder_process_until_end_of_metadata(p->decoder)) {
lsx_fail_errno(ft, SOX_EHDR, "FLAC ERROR whilst decoding metadata");
return SOX_EOF;
}
if (FLAC__stream_decoder_get_state(p->decoder) > FLAC__STREAM_DECODER_END_OF_STREAM) {
lsx_fail_errno(ft, SOX_EHDR, "FLAC ERROR during metadata decoding");
return SOX_EOF;
}
ft->encoding.encoding = SOX_ENCODING_FLAC;
ft->signal.rate = p->sample_rate;
ft->encoding.bits_per_sample = p->bits_per_sample;
ft->signal.channels = p->channels;
ft->signal.length = p->total_samples * p->channels;
return SOX_SUCCESS;
}
static size_t read_samples(sox_format_t * const ft, sox_sample_t * sampleBuffer, size_t const requested)
{
priv_t * p = (priv_t *)ft->priv;
size_t prev_requested;
if (p->seek_pending) {
p->seek_pending = sox_false;
/* discard leftover decoded data */
free(p->leftover_buf);
p->leftover_buf = NULL;
p->number_of_leftover_samples = 0;
p->req_buffer = sampleBuffer;
p->number_of_requested_samples = requested;
/* calls decoder_write_callback */
if (!FLAC__stream_decoder_seek_absolute(p->decoder, (FLAC__uint64)(p->seek_offset / ft->signal.channels))) {
p->req_buffer = NULL;
return 0;
}
} else if (p->number_of_leftover_samples > 0) {
/* small request, no need to decode more samples since we have leftovers */
if (requested < p->number_of_leftover_samples) {
size_t req_bytes = requested * sizeof(sox_sample_t);
memcpy(sampleBuffer, p->leftover_buf, req_bytes);
p->number_of_leftover_samples -= requested;
memmove(p->leftover_buf, (char *)p->leftover_buf + req_bytes,
(size_t)p->number_of_leftover_samples * sizeof(sox_sample_t));
return requested;
}
/* first, give them all of our leftover data: */
memcpy(sampleBuffer, p->leftover_buf,
p->number_of_leftover_samples * sizeof(sox_sample_t));
p->req_buffer = sampleBuffer + p->number_of_leftover_samples;
p->number_of_requested_samples = requested - p->number_of_leftover_samples;
free(p->leftover_buf);
p->leftover_buf = NULL;
p->number_of_leftover_samples = 0;
/* continue invoking decoder below */
} else {
p->req_buffer = sampleBuffer;
p->number_of_requested_samples = requested;
}
/* invoke the decoder, calls decoder_write_callback */
while ((prev_requested = p->number_of_requested_samples) && !p->eof) {
if (!FLAC__stream_decoder_process_single(p->decoder))
break; /* error, but maybe got earlier in the loop, though */
/* number_of_requested_samples decrements as the decoder progresses */
if (p->number_of_requested_samples == prev_requested)
p->eof = sox_true;
}
p->req_buffer = NULL;
return requested - p->number_of_requested_samples;
}
static int stop_read(sox_format_t * const ft)
{
priv_t * p = (priv_t *)ft->priv;
if (!FLAC__stream_decoder_finish(p->decoder) && p->eof)
lsx_warn("decoder MD5 checksum mismatch.");
FLAC__stream_decoder_delete(p->decoder);
free(p->leftover_buf);
p->leftover_buf = NULL;
p->number_of_leftover_samples = 0;
return SOX_SUCCESS;
}
static FLAC__StreamEncoderWriteStatus flac_stream_encoder_write_callback(FLAC__StreamEncoder const * const flac, const FLAC__byte buffer[], size_t const bytes, unsigned const samples, unsigned const current_frame, void * const client_data)
{
sox_format_t * const ft = (sox_format_t *) client_data;
(void) flac, (void) samples, (void) current_frame;
return lsx_writebuf(ft, buffer, bytes) == bytes ? FLAC__STREAM_ENCODER_WRITE_STATUS_OK : FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
}
static void flac_stream_encoder_metadata_callback(FLAC__StreamEncoder const * encoder, FLAC__StreamMetadata const * metadata, void * client_data)
{
(void) encoder, (void) metadata, (void) client_data;
}
static FLAC__StreamEncoderSeekStatus flac_stream_encoder_seek_callback(FLAC__StreamEncoder const * encoder, FLAC__uint64 absolute_byte_offset, void * client_data)
{
sox_format_t * const ft = (sox_format_t *) client_data;
(void) encoder;
if (!ft->seekable)
return FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED;
else if (lsx_seeki(ft, (off_t)absolute_byte_offset, SEEK_SET) != SOX_SUCCESS)
return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR;
else
return FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
}
static FLAC__StreamEncoderTellStatus flac_stream_encoder_tell_callback(FLAC__StreamEncoder const * encoder, FLAC__uint64 * absolute_byte_offset, void * client_data)
{
sox_format_t * const ft = (sox_format_t *) client_data;
off_t pos;
(void) encoder;
if (!ft->seekable)
return FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED;
else if ((pos = lsx_tell(ft)) < 0)
return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR;
else {
*absolute_byte_offset = (FLAC__uint64)pos;
return FLAC__STREAM_ENCODER_TELL_STATUS_OK;
}
}
static int start_write(sox_format_t * const ft)
{
priv_t * p = (priv_t *)ft->priv;
FLAC__StreamEncoderState status;
unsigned compression_level = MAX_COMPRESSION; /* Default to "best" */
if (ft->encoding.compression != HUGE_VAL) {
compression_level = ft->encoding.compression;
if (compression_level != ft->encoding.compression ||
compression_level > MAX_COMPRESSION) {
lsx_fail_errno(ft, SOX_EINVAL,
"FLAC compression level must be a whole number from 0 to %i",
MAX_COMPRESSION);
return SOX_EOF;
}
}
p->encoder = FLAC__stream_encoder_new();
if (p->encoder == NULL) {
lsx_fail_errno(ft, SOX_ENOMEM, "FLAC ERROR creating the encoder instance");
return SOX_EOF;
}
p->bits_per_sample = ft->encoding.bits_per_sample;
ft->signal.precision = ft->encoding.bits_per_sample;
lsx_report("encoding at %i bits per sample", p->bits_per_sample);
FLAC__stream_encoder_set_channels(p->encoder, ft->signal.channels);
FLAC__stream_encoder_set_bits_per_sample(p->encoder, p->bits_per_sample);
FLAC__stream_encoder_set_sample_rate(p->encoder, (unsigned)(ft->signal.rate + .5));
{ /* Check if rate is streamable: */
static const unsigned streamable_rates[] =
{8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000};
size_t i;
sox_bool streamable = sox_false;
for (i = 0; !streamable && i < array_length(streamable_rates); ++i)
streamable = (streamable_rates[i] == ft->signal.rate);
if (!streamable) {
lsx_report("non-standard rate; output may not be streamable");
FLAC__stream_encoder_set_streamable_subset(p->encoder, sox_false);
}
}
#if FLAC_API_VERSION_CURRENT >= 10
FLAC__stream_encoder_set_compression_level(p->encoder, compression_level);
#else
{
static struct {
unsigned blocksize;
FLAC__bool do_exhaustive_model_search;
FLAC__bool do_mid_side_stereo;
FLAC__bool loose_mid_side_stereo;
unsigned max_lpc_order;
unsigned max_residual_partition_order;
unsigned min_residual_partition_order;
} const options[MAX_COMPRESSION + 1] = {
{1152, sox_false, sox_false, sox_false, 0, 2, 2},
{1152, sox_false, sox_true, sox_true, 0, 2, 2},
{1152, sox_false, sox_true, sox_false, 0, 3, 0},
{4608, sox_false, sox_false, sox_false, 6, 3, 3},
{4608, sox_false, sox_true, sox_true, 8, 3, 3},
{4608, sox_false, sox_true, sox_false, 8, 3, 3},
{4608, sox_false, sox_true, sox_false, 8, 4, 0},
{4608, sox_true, sox_true, sox_false, 8, 6, 0},
{4608, sox_true, sox_true, sox_false, 12, 6, 0},
};
#define SET_OPTION(x) do {\
lsx_report(#x" = %i", options[compression_level].x); \
FLAC__stream_encoder_set_##x(p->encoder, options[compression_level].x);\
} while (0)
SET_OPTION(blocksize);
SET_OPTION(do_exhaustive_model_search);
SET_OPTION(max_lpc_order);
SET_OPTION(max_residual_partition_order);
SET_OPTION(min_residual_partition_order);
if (ft->signal.channels == 2) {
SET_OPTION(do_mid_side_stereo);
SET_OPTION(loose_mid_side_stereo);
}
#undef SET_OPTION
}
#endif
if (ft->signal.length != 0) {
FLAC__stream_encoder_set_total_samples_estimate(p->encoder, (FLAC__uint64)(ft->signal.length / ft->signal.channels));
p->metadata[p->num_metadata] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_SEEKTABLE);
if (p->metadata[p->num_metadata] == NULL) {
lsx_fail_errno(ft, SOX_ENOMEM, "FLAC ERROR creating the encoder seek table template");
return SOX_EOF;
}
{
if (!FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(p->metadata[p->num_metadata], (unsigned)(10 * ft->signal.rate + .5), (FLAC__uint64)(ft->signal.length/ft->signal.channels))) {
lsx_fail_errno(ft, SOX_ENOMEM, "FLAC ERROR creating the encoder seek table points");
return SOX_EOF;
}
}
p->metadata[p->num_metadata]->is_last = sox_false; /* the encoder will set this for us */
++p->num_metadata;
}
if (ft->oob.comments) { /* Make the comment structure */
FLAC__StreamMetadata_VorbisComment_Entry entry;
int i;
p->metadata[p->num_metadata] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
for (i = 0; ft->oob.comments[i]; ++i) {
static const char prepend[] = "Comment=";
char * text = lsx_calloc(strlen(prepend) + strlen(ft->oob.comments[i]) + 1, sizeof(*text));
/* Prepend `Comment=' if no field-name already in the comment */
if (!strchr(ft->oob.comments[i], '='))
strcpy(text, prepend);
entry.entry = (FLAC__byte *) strcat(text, ft->oob.comments[i]);
entry.length = strlen(text);
FLAC__metadata_object_vorbiscomment_append_comment(p->metadata[p->num_metadata], entry, /*copy= */ sox_true);
free(text);
}
++p->num_metadata;
}
if (p->num_metadata)
FLAC__stream_encoder_set_metadata(p->encoder, p->metadata, p->num_metadata);
status = FLAC__stream_encoder_init_stream(p->encoder, flac_stream_encoder_write_callback,
flac_stream_encoder_seek_callback, flac_stream_encoder_tell_callback, flac_stream_encoder_metadata_callback, ft);
if (status != FLAC__STREAM_ENCODER_OK) {
lsx_fail_errno(ft, SOX_EINVAL, "%s", FLAC__StreamEncoderStateString[status]);
return SOX_EOF;
}
return SOX_SUCCESS;
}
static size_t write_samples(sox_format_t * const ft, sox_sample_t const * const sampleBuffer, size_t const len)
{
priv_t * p = (priv_t *)ft->priv;
unsigned i;
/* allocate or grow buffer */
if (p->number_of_samples < len) {
p->number_of_samples = len;
free(p->decoded_samples);
p->decoded_samples = lsx_malloc(p->number_of_samples * sizeof(FLAC__int32));
}
for (i = 0; i < len; ++i) {
SOX_SAMPLE_LOCALS;
long pcm = SOX_SAMPLE_TO_SIGNED_32BIT(sampleBuffer[i], ft->clips);
p->decoded_samples[i] = pcm >> (32 - p->bits_per_sample);
switch (p->bits_per_sample) {
case 8: p->decoded_samples[i] =
SOX_SAMPLE_TO_SIGNED_8BIT(sampleBuffer[i], ft->clips);
break;
case 16: p->decoded_samples[i] =
SOX_SAMPLE_TO_SIGNED_16BIT(sampleBuffer[i], ft->clips);
break;
case 24: p->decoded_samples[i] = /* sign extension: */
SOX_SAMPLE_TO_SIGNED_24BIT(sampleBuffer[i],ft->clips) << 8;
p->decoded_samples[i] >>= 8;
break;
case 32: p->decoded_samples[i] =
SOX_SAMPLE_TO_SIGNED_32BIT(sampleBuffer[i],ft->clips);
break;
}
}
FLAC__stream_encoder_process_interleaved(p->encoder, p->decoded_samples, (unsigned) len / ft->signal.channels);
return FLAC__stream_encoder_get_state(p->encoder) == FLAC__STREAM_ENCODER_OK ? len : 0;
}
static int stop_write(sox_format_t * const ft)
{
priv_t * p = (priv_t *)ft->priv;
FLAC__StreamEncoderState state = FLAC__stream_encoder_get_state(p->encoder);
unsigned i;
FLAC__stream_encoder_finish(p->encoder);
FLAC__stream_encoder_delete(p->encoder);
for (i = 0; i < p->num_metadata; ++i)
FLAC__metadata_object_delete(p->metadata[i]);
free(p->decoded_samples);
if (state != FLAC__STREAM_ENCODER_OK) {
lsx_fail_errno(ft, SOX_EINVAL, "FLAC ERROR: failed to encode to end of stream");
return SOX_EOF;
}
return SOX_SUCCESS;
}
static int seek(sox_format_t * ft, uint64_t offset)
{
priv_t * p = (priv_t *)ft->priv;
p->seek_offset = offset;
p->seek_pending = sox_true;
return ft->mode == 'r' ? SOX_SUCCESS : SOX_EOF;
}
LSX_FORMAT_HANDLER(flac)
{
static char const * const names[] = {"flac", NULL};
static unsigned const encodings[] = {SOX_ENCODING_FLAC, 8, 16, 24, 0, 0};
static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
"Free Lossless Audio CODEC compressed audio", names, 0,
start_read, read_samples, stop_read,
start_write, write_samples, stop_write,
seek, encodings, NULL, sizeof(priv_t)
};
return &handler;
}