summaryrefslogtreecommitdiff
path: root/decoders/decoder_flac.c
blob: 805ab789b0dd1a9c7ede39bf822e27add083294e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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,
};