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