#include "decoder.h" #include #include #include 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, };