summaryrefslogtreecommitdiff
path: root/decoders/decoder_flac.c
blob: fd85f4870021de7a223a9bb73c2dab67722a70dd (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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#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 decoder *_decoder = client_data;
	struct flac_decoder_data *decoder_data = _decoder->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 decoder *_decoder = client_data;
	struct flac_decoder_data *decoder_data = _decoder->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 metadata_callback(const FLAC__StreamDecoder *_decoder, const FLAC__StreamMetadata *metadata, void *client_data) {
	struct decoder *decoder = client_data;
	decoder->rate = metadata->data.stream_info.sample_rate;
	switch(metadata->data.stream_info.bits_per_sample) {
		case 16:
			decoder->format = AUDIO_FORMAT_S16LE;
			break;
	}
	decoder->channels = metadata->data.stream_info.channels;
	g_debug("flac: %d Hz, %dch, %d bps", decoder->rate, decoder->channels, metadata->data.stream_info.bits_per_sample);
}

static void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) {
	g_warning(FLAC__StreamDecoderErrorStatusString[status]);
}

static gboolean flac_decoder_init(struct decoder *decoder) {
	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, metadata_callback, error_callback, decoder);
	decoder->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(struct decoder *decoder, GInputStream *input,
		GOutputStream *output, GError **error) {
	struct flac_decoder_data *decoder_data = decoder->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(struct decoder *decoder) {
	struct flac_decoder_data *decoder_data = decoder->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,
};