Implement the flac decoding filter.
[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 }