mixer: fade: Handle empty mood strings gracefully.
[paraslash.git] / flac_afh.c
1 /* Copyright (C) 2011 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
2
3 /** \file flac_afh.c Audio format handler for flac files. */
4
5 #include <regex.h>
6 #include <FLAC/stream_decoder.h>
7 #include <FLAC/metadata.h>
8
9 #include "para.h"
10 #include "error.h"
11 #include "afh.h"
12 #include "string.h"
13 #include "fd.h"
14
15 struct private_flac_afh_data {
16 const char *map;
17 size_t map_bytes;
18 size_t fpos;
19 struct afh_info *afhi;
20 unsigned blocksize;
21 };
22
23 static size_t copy_data(struct private_flac_afh_data *pfad, void *buf,
24 size_t want)
25 {
26 size_t copy, have = pfad->map_bytes - pfad->fpos;
27
28 if (have == 0)
29 return 0;
30 copy = have < want? have : want;
31 memcpy(buf, pfad->map + pfad->fpos, copy);
32 pfad->fpos += copy;
33 return copy;
34 }
35
36 static size_t meta_read_cb(void *ptr, size_t size, size_t nmemb,
37 FLAC__IOHandle handle)
38 {
39 struct private_flac_afh_data *pfad = handle;
40 return copy_data(pfad, ptr, nmemb * size);
41 }
42
43 static int meta_seek_cb(FLAC__IOHandle handle, FLAC__int64 offset, int whence)
44 {
45 struct private_flac_afh_data *pfad = handle;
46
47 if (offset < 0)
48 return -1;
49
50 switch (whence) {
51 case SEEK_SET:
52 if (offset >= pfad->map_bytes)
53 return -1;
54 pfad->fpos = offset;
55 return 0;
56 case SEEK_CUR:
57 if (pfad->fpos + offset >= pfad->map_bytes)
58 return -1;
59 pfad->fpos += offset;
60 return 0;
61 case SEEK_END:
62 if (offset >= pfad->map_bytes)
63 return -1;
64 pfad->fpos = offset;
65 return 0;
66 default:
67 return -1;
68 }
69 }
70
71 static FLAC__int64 meta_tell_cb(FLAC__IOHandle handle)
72 {
73 struct private_flac_afh_data *pfad = handle;
74 return pfad->fpos;
75 }
76
77 static int meta_eof_cb(FLAC__IOHandle handle)
78 {
79 struct private_flac_afh_data *pfad = handle;
80 return pfad->fpos == pfad->map_bytes;
81 }
82
83 static int meta_close_cb(FLAC__IOHandle __a_unused handle)
84 {
85 return 0;
86 }
87
88 static const FLAC__IOCallbacks meta_callbacks = {
89 .read = meta_read_cb,
90 .write = NULL,
91 .seek = meta_seek_cb,
92 .tell = meta_tell_cb,
93 .eof = meta_eof_cb,
94 .close = meta_close_cb
95 };
96
97 static void free_tags(struct taginfo *tags)
98 {
99 freep(&tags->artist);
100 freep(&tags->title);
101 freep(&tags->album);
102 freep(&tags->year);
103 freep(&tags->comment);
104 }
105
106 static bool copy_if_tag_type(const char *tag, int taglen, const char *type,
107 char **p)
108 {
109 char *q = key_value_copy(tag, taglen, type);
110 if (!q)
111 return false;
112 free(*p);
113 *p = q;
114 return true;
115 }
116
117 static void flac_read_vorbis_comments(FLAC__StreamMetadata_VorbisComment *vc,
118 struct taginfo *tags)
119 {
120 int i;
121 FLAC__StreamMetadata_VorbisComment_Entry *comment = vc->comments;
122
123 PARA_INFO_LOG("found %u vorbis comments\n", vc->num_comments);
124 for (i = 0; i < vc->num_comments; i++) {
125 char *e = (char *)comment[i].entry;
126 int len = comment[i].length;
127 if (copy_if_tag_type(e, len, "artist", &tags->artist))
128 continue;
129 if (copy_if_tag_type(e, len, "title", &tags->title))
130 continue;
131 if (copy_if_tag_type(e, len, "album", &tags->album))
132 continue;
133 if (copy_if_tag_type(e, len, "year", &tags->year))
134 continue;
135 if (copy_if_tag_type(e, len, "comment", &tags->comment))
136 continue;
137 }
138 }
139
140 /*
141 * FLAC__metadata_object_vorbiscomment_replace_comment() is buggy in some
142 * libFLAC versions (see commit e95399c1 in the flac git repository). Hence we
143 * use delete and add as a workaround.
144 */
145 static int flac_replace_vorbis_comment(FLAC__StreamMetadata *b,
146 const char *tag, const char* val)
147 {
148 FLAC__bool ok;
149 FLAC__StreamMetadata_VorbisComment_Entry entry;
150 int ret;
151
152 PARA_INFO_LOG("replacing %s\n", tag);
153 ret = FLAC__metadata_object_vorbiscomment_remove_entries_matching(
154 b, tag);
155 if (ret < 0)
156 return -E_FLAC_REPLACE_COMMENT;
157 ok = FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(
158 &entry, tag, val? val : "");
159 if (!ok)
160 return -E_FLAC_REPLACE_COMMENT;
161 ok = FLAC__metadata_object_vorbiscomment_append_comment(b, entry,
162 false /* no copy */);
163 if (!ok) {
164 free(entry.entry);
165 return -E_FLAC_REPLACE_COMMENT;
166 }
167 return 1;
168 }
169
170 static int flac_replace_vorbis_comments(FLAC__Metadata_Chain *chain,
171 FLAC__StreamMetadata *b, struct taginfo *tags)
172 {
173 FLAC__bool ok;
174 int ret;
175
176 ret = flac_replace_vorbis_comment(b, "artist", tags->artist);
177 if (ret < 0)
178 return ret;
179 ret = flac_replace_vorbis_comment(b, "title", tags->title);
180 if (ret < 0)
181 return ret;
182 ret = flac_replace_vorbis_comment(b, "album", tags->album);
183 if (ret < 0)
184 return ret;
185 ret = flac_replace_vorbis_comment(b, "year", tags->year);
186 if (ret < 0)
187 return ret;
188 ret = flac_replace_vorbis_comment(b, "comment", tags->comment);
189 if (ret < 0)
190 return ret;
191 /*
192 * Even if padding is disabled, libflac will try to modify the original
193 * file inplace if the metadata size has not changed. This won't work
194 * here though, because the original file is mapped read-only. Since
195 * there is no option to force the use of a temp file we work around
196 * this shortcoming by adding a dummy entry which increases the size of
197 * the meta data. If the entry already exists, we simply remove it.
198 */
199 ok = FLAC__metadata_chain_check_if_tempfile_needed(chain,
200 false /* no padding */);
201 if (!ok) {
202 PARA_INFO_LOG("adding/removing dummy comment\n");
203 ret = FLAC__metadata_object_vorbiscomment_remove_entries_matching(
204 b, "comment2");
205 if (ret < 0)
206 return -E_FLAC_REPLACE_COMMENT;
207 if (ret == 0) { /* nothing was removed */
208 ret = flac_replace_vorbis_comment(b, "comment2",
209 "avoid inplace write");
210 if (ret < 0)
211 return ret;
212 }
213 assert(FLAC__metadata_chain_check_if_tempfile_needed(chain,
214 false /* no padding */));
215 }
216 return 1;
217 }
218
219 static int flac_init_meta(struct private_flac_afh_data *pfad,
220 FLAC__Metadata_Chain **chainp, FLAC__Metadata_Iterator **iterp)
221 {
222 int ret;
223 FLAC__bool ok;
224 FLAC__Metadata_Chain *chain;
225 FLAC__Metadata_Iterator *iter;
226
227 *chainp = NULL;
228 *iterp = NULL;
229 chain = FLAC__metadata_chain_new();
230 if (!chain)
231 return -E_FLAC_CHAIN_ALLOC;
232 ret = -E_FLAC_CHAIN_READ;
233 ok = FLAC__metadata_chain_read_with_callbacks(chain, pfad,
234 meta_callbacks);
235 if (!ok)
236 goto free_chain;
237 ret = -E_FLAC_ITER_ALLOC;
238 iter = FLAC__metadata_iterator_new();
239 if (!iter)
240 goto free_chain;
241 FLAC__metadata_iterator_init(iter, chain);
242 *iterp = iter;
243 *chainp = chain;
244 return 1;
245 free_chain:
246 FLAC__metadata_chain_delete(chain);
247 return ret;
248 }
249
250 static int flac_read_meta(struct private_flac_afh_data *pfad)
251 {
252 int ret;
253 FLAC__Metadata_Chain *chain;
254 FLAC__Metadata_Iterator *iter;
255 FLAC__StreamMetadata_StreamInfo *info = NULL;
256 FLAC__bool ok;
257
258 ret = flac_init_meta(pfad, &chain, &iter);
259 if (ret < 0)
260 return ret;
261 for (;;) {
262 FLAC__StreamMetadata *b;
263 b = FLAC__metadata_iterator_get_block(iter);
264 if (!b)
265 break;
266 if (b->type == FLAC__METADATA_TYPE_STREAMINFO) {
267 info = &b->data.stream_info;
268 ret = -E_FLAC_VARBLOCK;
269 if (info->min_blocksize != info->max_blocksize)
270 goto free_iter;
271 pfad->afhi->frequency = info->sample_rate;
272 pfad->afhi->channels = info->channels;
273 pfad->blocksize = info->min_blocksize;
274 }
275 if (b->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
276 flac_read_vorbis_comments(&b->data.vorbis_comment,
277 &pfad->afhi->tags);
278 ok = FLAC__metadata_iterator_next(iter);
279 if (!ok)
280 break;
281 }
282 ret = info? 0: -E_FLAC_STREAMINFO;
283 free_iter:
284 FLAC__metadata_iterator_delete(iter);
285 FLAC__metadata_chain_delete(chain);
286 if (ret < 0)
287 free_tags(&pfad->afhi->tags);
288 return ret;
289 }
290
291 static FLAC__StreamDecoderReadStatus read_cb(
292 __a_unused const FLAC__StreamDecoder *decoder,
293 FLAC__byte buffer[], size_t *bytes, void *client_data)
294 {
295 struct private_flac_afh_data *pfad = client_data;
296
297 assert(*bytes > 0);
298 *bytes = copy_data(pfad, buffer, *bytes);
299 if (*bytes == 0)
300 return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
301 else
302 return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
303 }
304
305 static FLAC__StreamDecoderTellStatus tell_cb(
306 __a_unused const FLAC__StreamDecoder *decoder,
307 FLAC__uint64 *absolute_byte_offset, void *client_data)
308 {
309 struct private_flac_afh_data *pfad = client_data;
310
311 *absolute_byte_offset = pfad->fpos;
312 return FLAC__STREAM_DECODER_TELL_STATUS_OK;
313 }
314
315 /* libflac insists on this callback being present. */
316 static FLAC__StreamDecoderWriteStatus write_cb(
317 __a_unused const FLAC__StreamDecoder *decoder,
318 __a_unused const FLAC__Frame *frame,
319 __a_unused const FLAC__int32 *const buffer[],
320 __a_unused void *client_data)
321 {
322 return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
323 }
324
325 static void error_cb(
326 __a_unused const FLAC__StreamDecoder *decoder,
327 FLAC__StreamDecoderErrorStatus status,
328 __a_unused void *client_data)
329 {
330 PARA_ERROR_LOG("%s\n", FLAC__StreamDecoderErrorStatusString[status]);
331 }
332
333 static int flac_afh_read_chunks(struct private_flac_afh_data *pfad)
334 {
335 FLAC__StreamDecoder *decoder;
336 FLAC__StreamDecoderInitStatus init_status;
337 FLAC__bool ok;
338 FLAC__uint64 c;
339 unsigned chunk_table_size = 0;
340 int ret;
341 struct afh_info *afhi = pfad->afhi;
342
343 PARA_INFO_LOG("reading chunk table\n");
344 afhi->chunk_table = NULL;
345 decoder = FLAC__stream_decoder_new();
346 if (!decoder)
347 return -E_FLAC_AFH_DECODER_ALLOC;
348 ret = -E_FLAC_AFH_DECODER_INIT;
349 init_status = FLAC__stream_decoder_init_stream(
350 decoder,
351 read_cb,
352 NULL, /* seek */
353 tell_cb,
354 NULL, /* length */
355 NULL, /* eof */
356 write_cb,
357 NULL,
358 error_cb,
359 pfad
360 );
361 if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
362 goto free_decoder;
363 ret = -E_FLAC_SKIP_META;
364 ok = FLAC__stream_decoder_process_until_end_of_metadata(decoder);
365 if (!ok)
366 goto free_decoder;
367 for (c = 0;; c++) {
368 FLAC__uint64 pos;
369 FLAC__StreamDecoderState state;
370
371 ret = -E_FLAC_DECODE_POS;
372 ok = FLAC__stream_decoder_get_decode_position(decoder, &pos);
373 if (!ok)
374 goto free_decoder;
375 if (c >= chunk_table_size) {
376 chunk_table_size = 2 * chunk_table_size + 100;
377 afhi->chunk_table = para_realloc(afhi->chunk_table,
378 chunk_table_size * sizeof(uint32_t));
379 }
380 afhi->chunk_table[c] = pos;
381
382 ok = FLAC__stream_decoder_skip_single_frame(decoder);
383 if (!ok)
384 break;
385 state = FLAC__stream_decoder_get_state(decoder);
386 if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
387 break;
388 }
389 afhi->chunks_total = c;
390 set_max_chunk_size(afhi);
391 ret = 1;
392 free_decoder:
393 FLAC__stream_decoder_finish(decoder);
394 FLAC__stream_decoder_delete(decoder);
395 if (ret < 0)
396 freep(&afhi->chunk_table);
397 return ret;
398 }
399
400 static int flac_get_file_info(char *map, size_t map_bytes, __a_unused int fd,
401 struct afh_info *afhi)
402 {
403 struct private_flac_afh_data pfad_struct = {
404 .map = map,
405 .map_bytes = map_bytes,
406 .afhi = afhi
407 }, *pfad = &pfad_struct;
408 int ret;
409 double chunk_time;
410
411 afhi->header_len = 0;
412 ret = flac_read_meta(pfad);
413 if (ret < 0)
414 return ret;
415 pfad->fpos = 0;
416 ret = flac_afh_read_chunks(pfad);
417 if (ret < 0) {
418 free_tags(&afhi->tags);
419 return ret;
420 }
421 afhi->techinfo = make_message("blocksize: %u", pfad->blocksize);
422 afhi->seconds_total = DIV_ROUND_UP(afhi->chunks_total * pfad->blocksize,
423 afhi->frequency);
424 afhi->bitrate = pfad->map_bytes * 8 / afhi->seconds_total / 1024;
425 chunk_time = (double)pfad->blocksize / afhi->frequency;
426 afhi->chunk_tv.tv_sec = chunk_time;
427 chunk_time *= 1000 * 1000;
428 chunk_time -= afhi->chunk_tv.tv_sec * 1000 * 1000;
429 afhi->chunk_tv.tv_usec = chunk_time;
430 return 1;
431 }
432
433 static size_t temp_write_cb(const void *ptr, size_t size, size_t nmemb,
434 FLAC__IOHandle handle)
435 {
436 int ret, fd = *(int *)handle;
437 size_t n = size * nmemb; /* FIXME: possible overflow */
438
439 ret = write_all(fd, ptr, n);
440
441 /*
442 * libflac expects POSIX semantics: If an error occurs, or the end of
443 * the file is reached, the return value is a short item count or zero.
444 */
445 if (ret < 0) {
446 PARA_ERROR_LOG("%s\n", para_strerror(-ret));
447 return 0;
448 }
449 return nmemb;
450 }
451
452 /* only the write callback needs to be supplied for writing the temp file. */
453 static const FLAC__IOCallbacks temp_callbacks = {
454 .write = temp_write_cb,
455 };
456
457 static int flac_write_chain(FLAC__Metadata_Chain *chain,
458 struct private_flac_afh_data *pfad, int fd)
459 {
460 FLAC__bool ok;
461
462 ok = FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain,
463 false /* no padding*/, pfad,
464 meta_callbacks, &fd, temp_callbacks);
465 if (!ok) {
466 FLAC__Metadata_ChainStatus st;
467 st = FLAC__metadata_chain_status(chain);
468 PARA_ERROR_LOG("chain status: %u\n", st);
469 if (st == FLAC__METADATA_CHAIN_STATUS_READ_ERROR)
470 PARA_ERROR_LOG("read error\n");
471 return -E_FLAC_WRITE_CHAIN;
472 }
473 return 1;
474 }
475
476 static int flac_rewrite_tags(const char *map, size_t map_bytes,
477 struct taginfo *tags, int fd, __a_unused const char *filename)
478 {
479 int ret;
480 FLAC__Metadata_Chain *chain;
481 FLAC__Metadata_Iterator *iter;
482 FLAC__StreamMetadata *b = NULL;
483 FLAC__bool ok;
484 struct private_flac_afh_data *pfad = para_calloc(sizeof(*pfad));
485
486 pfad->map = map;
487 pfad->map_bytes = map_bytes;
488 pfad->fpos = 0;
489
490 ret = flac_init_meta(pfad, &chain, &iter);
491 if (ret < 0)
492 goto free_pfad;
493 for (ok = true; ok; ok = FLAC__metadata_iterator_next(iter)) {
494 b = FLAC__metadata_iterator_get_block(iter);
495 assert(b);
496 if (b->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
497 break;
498 b = NULL;
499 }
500 ret = -E_FLAC_REPLACE_COMMENT;
501 if (!b)
502 goto out;
503 ret = flac_replace_vorbis_comments(chain, b, tags);
504 if (ret < 0)
505 goto out;
506 ret = flac_write_chain(chain, pfad, fd);
507 out:
508 FLAC__metadata_iterator_delete(iter);
509 FLAC__metadata_chain_delete(chain);
510 free_pfad:
511 free(pfad);
512 return ret;
513 }
514
515 static const char * const flac_suffixes[] = {"flac", NULL};
516
517 /**
518 * The audio format handler for flac (free lossless audio decoder).
519 *
520 * It depends on libflac and on libogg.
521 */
522 const struct audio_format_handler flac_afh = {
523 .get_file_info = flac_get_file_info,
524 .suffixes = flac_suffixes,
525 .rewrite_tags = flac_rewrite_tags,
526 };