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,
};
|