diff options
author | Jon Bergli Heier <snakebite@jvnv.net> | 2010-09-05 20:39:52 +0200 |
---|---|---|
committer | Jon Bergli Heier <snakebite@jvnv.net> | 2010-09-05 20:39:52 +0200 |
commit | 18399d8f41e0154af266f43fd4a05420cb335aa7 (patch) | |
tree | cfe2a795e1102f90fb44afc0cf597f09c6195eef /decoders | |
parent | a6e733f5f43ced09a06fe2de569e5d6ab6d63604 (diff) |
Added FLAC decoder.
Diffstat (limited to 'decoders')
-rw-r--r-- | decoders/decoder_flac.c | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/decoders/decoder_flac.c b/decoders/decoder_flac.c new file mode 100644 index 0000000..805ab78 --- /dev/null +++ b/decoders/decoder_flac.c @@ -0,0 +1,125 @@ +#include "decoder.h" + +#include <FLAC/stream_decoder.h> + +#include <string.h> +#include <stdlib.h> + +struct flac_decoder_data { + FLAC__StreamDecoder *decoder; + GInputStream *input; + GOutputStream *output; + gsize output_written; +}; + +static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) { + struct flac_decoder_data *decoder_data = client_data; + + *bytes = g_input_stream_read(decoder_data->input, buffer, *bytes, NULL, NULL); + + switch(*bytes) { + case -1: + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + case 0: + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + default: + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } +} + +static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, + const FLAC__int32 * const buffer[], void *client_data) { + struct flac_decoder_data *decoder_data = client_data; + + unsigned channels = frame->header.channels; + //unsigned sample_rate = frame->header.sample_rate; + unsigned bits_per_sample = frame->header.bits_per_sample; + unsigned blocksize = frame->header.blocksize; + guint16 *outbuf = g_malloc(blocksize * channels * (bits_per_sample/8)); + guint16 *pos = outbuf; + + /* TODO: support other formats */ + for(unsigned i = 0; i < blocksize; i++) { + for(unsigned c = 0; c < channels; c++) { + *(pos++) = buffer[c][i]; + } + } + + gint written = g_output_stream_write(decoder_data->output, outbuf, blocksize * channels * (bits_per_sample/8), NULL, NULL); + + g_free(outbuf); + + if(written >= 0) { + decoder_data->output_written += written; + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } else { + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } +} + +static void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { + g_warning(FLAC__StreamDecoderErrorStatusString[status]); +} + +static gboolean flac_decoder_init(gpointer *data) { + struct flac_decoder_data *decoder_data = g_new0(struct flac_decoder_data, 1); + decoder_data->decoder = FLAC__stream_decoder_new(); + FLAC__StreamDecoderInitStatus status = FLAC__stream_decoder_init_stream(decoder_data->decoder, + read_callback, NULL, NULL, NULL, NULL, write_callback, NULL, error_callback, decoder_data); + *data = decoder_data; + + if(status != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + g_warning(FLAC__StreamDecoderInitStatusString[status]); + FLAC__stream_decoder_delete(decoder_data->decoder); + g_free(decoder_data); + return FALSE; + } + + return TRUE; +} + +static gssize flac_decoder_decode(gpointer data, GInputStream *input, + GOutputStream *output, GError **error) { + struct flac_decoder_data *decoder_data = data; + decoder_data->input = input; + decoder_data->output = output; + decoder_data->output_written = 0; + + FLAC__stream_decoder_process_single(decoder_data->decoder); + FLAC__StreamDecoderState state = FLAC__stream_decoder_get_state(decoder_data->decoder); + + switch(state) { + case FLAC__STREAM_DECODER_END_OF_STREAM: + *error = g_error_new(decoder_quark(), DECODER_CODE_DONE, "eof"); + break; + case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR: + *error = g_error_new(decoder_quark(), DECODER_CODE_ERROR, "memory allocation error"); + break; + case FLAC__STREAM_DECODER_ABORTED: + *error = g_error_new(decoder_quark(), DECODER_CODE_ERROR, "aborted by read callback"); + break; + default: + break; + } + + return decoder_data->output_written; +} + +static void flac_decoder_close(gpointer data) { + struct flac_decoder_data *decoder_data = data; + FLAC__stream_decoder_finish(decoder_data->decoder); + FLAC__stream_decoder_delete(decoder_data->decoder); + g_free(decoder_data); +} + +static const gchar * const decoder_flac_extensions[] = { + "flac", NULL, +}; + +const struct decoder_plugin decoder_flac_decoder = { + .name = "flac", + .extensions = decoder_flac_extensions, + .init = flac_decoder_init, + .decode = flac_decoder_decode, + .close = flac_decoder_close, +}; |