diff options
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, +}; | 
