summaryrefslogtreecommitdiff
path: root/decoders
diff options
context:
space:
mode:
authorJon Bergli Heier <snakebite@jvnv.net>2010-09-05 20:39:52 +0200
committerJon Bergli Heier <snakebite@jvnv.net>2010-09-05 20:39:52 +0200
commit18399d8f41e0154af266f43fd4a05420cb335aa7 (patch)
treecfe2a795e1102f90fb44afc0cf597f09c6195eef /decoders
parenta6e733f5f43ced09a06fe2de569e5d6ab6d63604 (diff)
Added FLAC decoder.
Diffstat (limited to 'decoders')
-rw-r--r--decoders/decoder_flac.c125
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,
+};