Implement the flac audio format handler.
[paraslash.git] / flac_afh.c
1 /*
2 * Copyright (C) 2011 Andre Noll <maan@systemlinux.org>
3 *
4 * Licensed under the GPL v2. For licencing details see COPYING.
5 */
6
7 /** \file flac_afh.c Audio format handler for flac files. */
8
9 #include <regex.h>
10 #include <stdbool.h>
11 #include <FLAC/stream_decoder.h>
12 #include <FLAC/metadata.h>
13
14 #include "para.h"
15 #include "error.h"
16 #include "afh.h"
17 #include "string.h"
18
19 struct private_flac_afh_data {
20 char *map;
21 size_t map_bytes;
22 size_t fpos;
23 struct afh_info *afhi;
24 unsigned blocksize;
25 };
26
27 static size_t copy_data(struct private_flac_afh_data *pfad, void *buf,
28 size_t want)
29 {
30 size_t copy, have = pfad->map_bytes - pfad->fpos;
31
32 if (have == 0)
33 return 0;
34 copy = have < want? have : want;
35 memcpy(buf, pfad->map + pfad->fpos, copy);
36 pfad->fpos += copy;
37 return copy;
38 }
39
40 static size_t meta_read_cb(void *ptr, size_t size, size_t nmemb,
41 FLAC__IOHandle handle)
42 {
43 struct private_flac_afh_data *pfad = handle;
44 return copy_data(pfad, ptr, nmemb * size);
45 }
46
47 static int meta_seek_cb(FLAC__IOHandle handle, FLAC__int64 offset, int whence)
48 {
49 struct private_flac_afh_data *pfad = handle;
50
51 if (offset < 0)
52 return -1;
53
54 switch (whence) {
55 case SEEK_SET:
56 if (offset >= pfad->map_bytes)
57 return -1;
58 pfad->fpos = offset;
59 return 0;
60 case SEEK_CUR:
61 if (pfad->fpos + offset >= pfad->map_bytes)
62 return -1;
63 pfad->fpos += offset;
64 return 0;
65 case SEEK_END:
66 if (offset >= pfad->map_bytes)
67 return -1;
68 pfad->fpos = offset;
69 return 0;
70 default:
71 return -1;
72 }
73 }
74
75 static FLAC__int64 meta_tell_cb(FLAC__IOHandle handle)
76 {
77 struct private_flac_afh_data *pfad = handle;
78 return pfad->fpos;
79 }
80
81 static int meta_eof_cb(FLAC__IOHandle handle)
82 {
83 struct private_flac_afh_data *pfad = handle;
84 return pfad->fpos == pfad->map_bytes - 1;
85 }
86
87 static int meta_close_cb(FLAC__IOHandle __a_unused handle)
88 {
89 return 0;
90 }
91
92 static void free_tags(struct taginfo *tags)
93 {
94 freep(&tags->artist);
95 freep(&tags->title);
96 freep(&tags->album);
97 freep(&tags->year);
98 freep(&tags->comment);
99 }
100
101 static bool copy_if_tag_type(const char *tag, int taglen, const char *type,
102 char **p)
103 {
104 char *q = key_value_copy(tag, taglen, type);
105 if (!q)
106 return false;
107 free(*p);
108 *p = q;
109 return true;
110 }
111
112 static void flac_read_vorbis_comments(FLAC__StreamMetadata_VorbisComment *vc,
113 struct taginfo *tags)
114 {
115 int i;
116 FLAC__StreamMetadata_VorbisComment_Entry *comment = vc->comments;
117
118 PARA_INFO_LOG("found %u vorbis comments\n", vc->num_comments);
119 for (i = 0; i < vc->num_comments; i++) {
120 char *e = (char *)comment[i].entry;
121 int len = comment[i].length;
122 if (copy_if_tag_type(e, len, "artist", &tags->artist))
123 continue;
124 if (copy_if_tag_type(e, len, "title", &tags->title))
125 continue;
126 if (copy_if_tag_type(e, len, "album", &tags->album))
127 continue;
128 if (copy_if_tag_type(e, len, "year", &tags->year))
129 continue;
130 if (copy_if_tag_type(e, len, "comment", &tags->comment))
131 continue;
132 }
133 }
134
135 static int flac_read_meta(struct private_flac_afh_data *pfad)
136 {
137 int ret;
138 FLAC__IOCallbacks meta_callbacks = {
139 .read = meta_read_cb,
140 .write = NULL,
141 .seek = meta_seek_cb,
142 .tell = meta_tell_cb,
143 .eof = meta_eof_cb,
144 .close = meta_close_cb
145 };
146 FLAC__Metadata_Chain *chain;
147 FLAC__Metadata_Iterator *iter;
148 FLAC__StreamMetadata_StreamInfo *info = NULL;
149 FLAC__bool ok;
150
151 chain = FLAC__metadata_chain_new();
152 if (!chain)
153 return -E_FLAC_CHAIN_ALLOC;
154 ret = -E_FLAC_CHAIN_READ;
155 ok = FLAC__metadata_chain_read_with_callbacks(chain, pfad,
156 meta_callbacks);
157 if (!ok)
158 goto free_chain;
159 ret = -E_FLAC_ITER_ALLOC;
160 iter = FLAC__metadata_iterator_new();
161 if (!iter)
162 goto free_chain;
163 FLAC__metadata_iterator_init(iter, chain);
164 for (;;) {
165 FLAC__StreamMetadata *b;
166 b = FLAC__metadata_iterator_get_block(iter);
167 if (!b)
168 break;
169 if (b->type == FLAC__METADATA_TYPE_STREAMINFO) {
170 info = &b->data.stream_info;
171 ret = -E_FLAC_VARBLOCK;
172 if (info->min_blocksize != info->max_blocksize)
173 goto free_iter;
174 pfad->afhi->frequency = info->sample_rate;
175 pfad->afhi->channels = info->channels;
176 pfad->blocksize = info->min_blocksize;
177 }
178 if (b->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
179 flac_read_vorbis_comments(&b->data.vorbis_comment,
180 &pfad->afhi->tags);
181 ok = FLAC__metadata_iterator_next(iter);
182 if (!ok)
183 break;
184 }
185 ret = info? 0: -E_FLAC_STREAMINFO;
186 free_iter:
187 FLAC__metadata_iterator_delete(iter);
188 free_chain:
189 FLAC__metadata_chain_delete(chain);
190 if (ret < 0)
191 free_tags(&pfad->afhi->tags);
192 return ret;
193 }
194
195 static FLAC__StreamDecoderReadStatus read_cb(
196 __a_unused const FLAC__StreamDecoder *decoder,
197 FLAC__byte buffer[], size_t *bytes, void *client_data)
198 {
199 struct private_flac_afh_data *pfad = client_data;
200
201 assert(*bytes > 0);
202 *bytes = copy_data(pfad, buffer, *bytes);
203 if (*bytes == 0)
204 return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
205 else
206 return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
207 }
208
209 static FLAC__StreamDecoderTellStatus tell_cb(
210 __a_unused const FLAC__StreamDecoder *decoder,
211 FLAC__uint64 *absolute_byte_offset, void *client_data)
212 {
213 struct private_flac_afh_data *pfad = client_data;
214
215 *absolute_byte_offset = pfad->fpos;
216 return FLAC__STREAM_DECODER_TELL_STATUS_OK;
217 }
218
219 /* libflac insits on this callback being present. */
220 static FLAC__StreamDecoderWriteStatus write_cb(
221 __a_unused const FLAC__StreamDecoder *decoder,
222 __a_unused const FLAC__Frame *frame,
223 __a_unused const FLAC__int32 *const buffer[],
224 __a_unused void *client_data)
225 {
226 return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
227 }
228
229 static void error_cb(
230 __a_unused const FLAC__StreamDecoder *decoder,
231 FLAC__StreamDecoderErrorStatus status,
232 __a_unused void *client_data)
233 {
234 PARA_ERROR_LOG("%s\n", FLAC__StreamDecoderErrorStatusString[status]);
235 }
236
237 static int flac_afh_read_chunks(struct private_flac_afh_data *pfad)
238 {
239 FLAC__StreamDecoder *decoder;
240 FLAC__StreamDecoderInitStatus init_status;
241 FLAC__bool ok;
242 FLAC__uint64 c;
243 unsigned chunk_table_size = 0;
244 int ret;
245 struct afh_info *afhi = pfad->afhi;
246
247 PARA_INFO_LOG("reading chunk table\n");
248 afhi->chunk_table = NULL;
249 decoder = FLAC__stream_decoder_new();
250 if (!decoder)
251 return -E_FLAC_AFH_DECODER_ALLOC;
252 ret = -E_FLAC_AFH_DECODER_INIT;
253 init_status = FLAC__stream_decoder_init_stream(
254 decoder,
255 read_cb,
256 NULL, /* seek */
257 tell_cb,
258 NULL, /* length */
259 NULL, /* eof */
260 write_cb,
261 NULL,
262 error_cb,
263 pfad
264 );
265 if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
266 goto free_decoder;
267 ret = -E_FLAC_SKIP_META;
268 ok = FLAC__stream_decoder_process_until_end_of_metadata(decoder);
269 if (!ok)
270 goto free_decoder;
271 for (c = 0;; c++) {
272 FLAC__uint64 pos;
273 FLAC__StreamDecoderState state;
274
275 ret = -E_FLAC_DECODE_POS;
276 ok = FLAC__stream_decoder_get_decode_position(decoder, &pos);
277 if (!ok)
278 goto free_decoder;
279 if (c >= chunk_table_size) {
280 chunk_table_size = 2 * chunk_table_size + 100;
281 afhi->chunk_table = para_realloc(afhi->chunk_table,
282 chunk_table_size * sizeof(uint32_t));
283 }
284 afhi->chunk_table[c] = pos;
285
286 ok = FLAC__stream_decoder_skip_single_frame(decoder);
287 if (!ok)
288 break;
289 state = FLAC__stream_decoder_get_state(decoder);
290 if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
291 break;
292 }
293 afhi->chunks_total = c;
294 ret = 1;
295 free_decoder:
296 FLAC__stream_decoder_finish(decoder);
297 FLAC__stream_decoder_delete(decoder);
298 if (ret < 0)
299 freep(&afhi->chunk_table);
300 return ret;
301 }
302
303 static int flac_get_file_info(char *map, size_t map_bytes, __a_unused int fd,
304 struct afh_info *afhi)
305 {
306 struct private_flac_afh_data pfad_struct = {
307 .map = map,
308 .map_bytes = map_bytes,
309 .afhi = afhi
310 }, *pfad = &pfad_struct;
311 int ret;
312 double chunk_time;
313
314 afhi->header_len = 0;
315 ret = flac_read_meta(pfad);
316 if (ret < 0)
317 return ret;
318 pfad->fpos = 0;
319 ret = flac_afh_read_chunks(pfad);
320 if (ret < 0) {
321 free_tags(&afhi->tags);
322 return ret;
323 }
324 afhi->techinfo = make_message("blocksize: %u", pfad->blocksize);
325 afhi->seconds_total = DIV_ROUND_UP(afhi->chunks_total * pfad->blocksize,
326 afhi->frequency);
327 afhi->bitrate = pfad->map_bytes * 8 / afhi->seconds_total / 1024;
328 chunk_time = (double)pfad->blocksize / afhi->frequency;
329 afhi->chunk_tv.tv_sec = chunk_time;
330 chunk_time *= 1000 * 1000;
331 chunk_time -= afhi->chunk_tv.tv_sec * 1000 * 1000;
332 afhi->chunk_tv.tv_usec = chunk_time;
333 return 1;
334 }
335
336 static const char* flac_suffixes[] = {"flac", NULL};
337
338 /**
339 * The init function of the flac audio format handler.
340 *
341 * \param afh pointer to the struct to initialize
342 */
343 void flac_afh_init(struct audio_format_handler *afh)
344 {
345 afh->get_file_info = flac_get_file_info,
346 afh->suffixes = flac_suffixes;
347 }