1 /*
2  * A FLAC decoder plugin for the Audacious Media Player
3  * Copyright (C) 2005 Ralf Ertzinger
4  * Copyright (C) 2010 John Lindgren
5  * Copyright (C) 2010 Michał Lipski <tallica@o2.pl>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #define WANT_VFS_STDIO_COMPAT
26 #include <libaudcore/runtime.h>
27 #include <libaudcore/i18n.h>
28 #include <libaudcore/audstrings.h>
29 
30 #include "flacng.h"
31 
read_cb(void * ptr,size_t size,size_t nmemb,FLAC__IOHandle handle)32 static size_t read_cb(void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle)
33 {
34     int64_t read;
35 
36     if (handle == nullptr)
37     {
38         AUDERR("Trying to read data from an uninitialized file!\n");
39         return -1;
40     }
41 
42     read = ((VFSFile *) handle)->fread (ptr, size, nmemb);
43 
44     switch (read)
45     {
46         case -1:
47             AUDERR("Error while reading from stream!\n");
48             return -1;
49 
50         case 0:
51             AUDDBG("Stream reached EOF\n");
52             return 0;
53 
54         default:
55             return read;
56     }
57 }
58 
write_cb(const void * ptr,size_t size,size_t nmemb,FLAC__IOHandle handle)59 static size_t write_cb(const void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle)
60 {
61     return ((VFSFile *) handle)->fwrite (ptr, size, nmemb);
62 }
63 
seek_cb(FLAC__IOHandle handle,FLAC__int64 offset,int whence)64 static int seek_cb(FLAC__IOHandle handle, FLAC__int64 offset, int whence)
65 {
66     if (((VFSFile *) handle)->fseek (offset, to_vfs_seek_type (whence)) != 0)
67     {
68         AUDERR("Could not seek to %ld!\n", (long)offset);
69         return -1;
70     }
71 
72     return 0;
73 }
74 
tell_cb(FLAC__IOHandle handle)75 static FLAC__int64 tell_cb(FLAC__IOHandle handle)
76 {
77     int64_t offset;
78 
79     if ((offset = ((VFSFile *) handle)->ftell ()) < 0)
80     {
81         AUDERR("Could not tell current position!\n");
82         return -1;
83     }
84 
85     AUDDBG ("Current position: %d\n", (int) offset);
86     return offset;
87 }
88 
eof_cb(FLAC__IOHandle handle)89 static int eof_cb(FLAC__IOHandle handle)
90 {
91     return ((VFSFile *) handle)->feof ();
92 }
93 
94 static FLAC__IOCallbacks io_callbacks = {
95     read_cb,
96     write_cb,
97     seek_cb,
98     tell_cb,
99     eof_cb,
100     nullptr
101 };
102 
insert_str_tuple_to_vc(FLAC__StreamMetadata * vc_block,const Tuple & tuple,Tuple::Field field,const char * field_name)103 static void insert_str_tuple_to_vc (FLAC__StreamMetadata * vc_block,
104  const Tuple & tuple, Tuple::Field field, const char * field_name)
105 {
106     FLAC__StreamMetadata_VorbisComment_Entry entry;
107     String val = tuple.get_str (field);
108 
109     if (! val)
110         return;
111 
112     StringBuf str = str_printf ("%s=%s", field_name, (const char *) val);
113     entry.entry = (FLAC__byte *) (char *) str;
114     entry.length = strlen(str);
115     FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,
116         vc_block->data.vorbis_comment.num_comments, entry, true);
117 }
118 
insert_int_tuple_to_vc(FLAC__StreamMetadata * vc_block,const Tuple & tuple,Tuple::Field field,const char * field_name)119 static void insert_int_tuple_to_vc (FLAC__StreamMetadata * vc_block,
120  const Tuple & tuple, Tuple::Field field, const char * field_name)
121 {
122     FLAC__StreamMetadata_VorbisComment_Entry entry;
123     int val = tuple.get_int (field);
124 
125     if (val <= 0)
126         return;
127 
128     StringBuf str = str_printf ("%s=%d", field_name, val);
129     entry.entry = (FLAC__byte *) (char *) str;
130     entry.length = strlen(str);
131     FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,
132         vc_block->data.vorbis_comment.num_comments, entry, true);
133 }
134 
write_tuple(const char * filename,VFSFile & file,const Tuple & tuple)135 bool FLACng::write_tuple(const char *filename, VFSFile &file, const Tuple &tuple)
136 {
137     AUDDBG("Update song tuple.\n");
138 
139     FLAC__Metadata_Iterator *iter;
140     FLAC__Metadata_Chain *chain;
141     FLAC__StreamMetadata *vc_block;
142     FLAC__Metadata_ChainStatus status;
143 
144     chain = FLAC__metadata_chain_new();
145 
146     if (!FLAC__metadata_chain_read_with_callbacks(chain, &file, io_callbacks))
147         goto ERR;
148 
149     iter = FLAC__metadata_iterator_new();
150 
151     FLAC__metadata_iterator_init(iter, chain);
152 
153     while (FLAC__metadata_iterator_next(iter))
154     {
155         if (FLAC__metadata_iterator_get_block_type(iter) == FLAC__METADATA_TYPE_VORBIS_COMMENT)
156         {
157             FLAC__metadata_iterator_delete_block(iter, true);
158             break;
159         }
160     }
161 
162     vc_block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
163 
164     insert_str_tuple_to_vc(vc_block, tuple, Tuple::Title, "TITLE");
165     insert_str_tuple_to_vc(vc_block, tuple, Tuple::Artist, "ARTIST");
166     insert_str_tuple_to_vc(vc_block, tuple, Tuple::Album, "ALBUM");
167     insert_str_tuple_to_vc(vc_block, tuple, Tuple::AlbumArtist, "ALBUMARTIST");
168     insert_str_tuple_to_vc(vc_block, tuple, Tuple::Genre, "GENRE");
169     insert_str_tuple_to_vc(vc_block, tuple, Tuple::Comment, "COMMENT");
170     insert_str_tuple_to_vc(vc_block, tuple, Tuple::Description, "DESCRIPTION");
171     insert_str_tuple_to_vc(vc_block, tuple, Tuple::MusicBrainzID, "musicbrainz_trackid");
172 
173     insert_int_tuple_to_vc(vc_block, tuple, Tuple::Year, "DATE");
174     insert_int_tuple_to_vc(vc_block, tuple, Tuple::Track, "TRACKNUMBER");
175 
176     FLAC__metadata_iterator_insert_block_after(iter, vc_block);
177 
178     FLAC__metadata_iterator_delete(iter);
179     FLAC__metadata_chain_sort_padding(chain);
180 
181     if (FLAC__metadata_chain_check_if_tempfile_needed(chain, true))
182     {
183         auto temp = VFSFile::tmpfile();
184         if (!temp)
185             goto ERR_RETURN;
186 
187         if (!FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain, true,
188          &file, io_callbacks, &temp, io_callbacks))
189             goto ERR;
190 
191         if (!file.replace_with(temp))
192             goto ERR_RETURN;
193     }
194     else /* no tempfile needed */
195     {
196         if (!FLAC__metadata_chain_write_with_callbacks(chain, true, &file, io_callbacks))
197             goto ERR;
198     }
199 
200     FLAC__metadata_chain_delete(chain);
201     return true;
202 
203 ERR:
204     status = FLAC__metadata_chain_status(chain);
205     AUDERR("An error occurred: %s\n", FLAC__Metadata_ChainStatusString[status]);
206 ERR_RETURN:
207     FLAC__metadata_chain_delete(chain);
208     return false;
209 }
210 
add_text(Tuple & tuple,Tuple::Field field,const char * value)211 static void add_text (Tuple & tuple, Tuple::Field field, const char * value)
212 {
213     String cur = tuple.get_str (field);
214     if (cur)
215         tuple.set_str (field, str_concat ({cur, ", ", value}));
216     else
217         tuple.set_str (field, value);
218 }
219 
parse_comment(Tuple & tuple,const char * key,const char * value)220 static void parse_comment (Tuple & tuple, const char * key, const char * value)
221 {
222     AUDDBG ("Found key %s <%s>\n", key, value);
223 
224     static const struct {
225         const char * key;
226         Tuple::Field field;
227     } tfields[] = {
228         {"ARTIST", Tuple::Artist},
229         {"ALBUM", Tuple::Album},
230         {"ALBUMARTIST", Tuple::AlbumArtist},
231         {"TITLE", Tuple::Title},
232         {"COMMENT", Tuple::Comment},
233         {"GENRE", Tuple::Genre},
234         {"DESCRIPTION", Tuple::Description},
235         {"musicbrainz_trackid", Tuple::MusicBrainzID},
236     };
237 
238     for (auto & tfield : tfields)
239     {
240         if (!strcmp_nocase(key, tfield.key))
241         {
242             add_text (tuple, tfield.field, value);
243             return;
244         }
245     }
246 
247     if (!strcmp_nocase(key, "TRACKNUMBER"))
248         tuple.set_int(Tuple::Track, atoi(value));
249     else if (!strcmp_nocase(key, "DATE"))
250         tuple.set_int(Tuple::Year, atoi(value));
251     else if (!strcmp_nocase(key, "REPLAYGAIN_TRACK_GAIN"))
252         tuple.set_gain(Tuple::TrackGain, Tuple::GainDivisor, value);
253     else if (!strcmp_nocase(key, "REPLAYGAIN_TRACK_PEAK"))
254         tuple.set_gain(Tuple::TrackPeak, Tuple::PeakDivisor, value);
255     else if (!strcmp_nocase(key, "REPLAYGAIN_ALBUM_GAIN"))
256         tuple.set_gain(Tuple::AlbumGain, Tuple::GainDivisor, value);
257     else if (!strcmp_nocase(key, "REPLAYGAIN_ALBUM_PEAK"))
258         tuple.set_gain(Tuple::AlbumPeak, Tuple::PeakDivisor, value);
259 }
260 
read_tag(const char * filename,VFSFile & file,Tuple & tuple,Index<char> * image)261 bool FLACng::read_tag (const char * filename, VFSFile & file, Tuple & tuple, Index<char> * image)
262 {
263     AUDDBG("Probe for tuple.\n");
264 
265     FLAC__Metadata_Iterator *iter;
266     FLAC__Metadata_Chain *chain;
267     FLAC__StreamMetadata *metadata = nullptr;
268     FLAC__Metadata_ChainStatus status;
269     FLAC__StreamMetadata_VorbisComment_Entry *entry;
270     char *key;
271     char *value;
272 
273     tuple.set_str (Tuple::Codec, "Free Lossless Audio Codec (FLAC)");
274     tuple.set_str (Tuple::Quality, _("lossless"));
275 
276     chain = FLAC__metadata_chain_new();
277 
278     if (!FLAC__metadata_chain_read_with_callbacks(chain, &file, io_callbacks))
279         goto ERR;
280 
281     iter = FLAC__metadata_iterator_new();
282 
283     FLAC__metadata_iterator_init(iter, chain);
284 
285     do
286     {
287         switch (FLAC__metadata_iterator_get_block_type(iter))
288         {
289             case FLAC__METADATA_TYPE_VORBIS_COMMENT:
290             {
291                 metadata = FLAC__metadata_iterator_get_block(iter);
292 
293                 AUDDBG("Vorbis comment contains %d fields\n", metadata->data.vorbis_comment.num_comments);
294                 AUDDBG("Vendor string: %s\n", metadata->data.vorbis_comment.vendor_string.entry);
295 
296                 entry = metadata->data.vorbis_comment.comments;
297 
298                 for (unsigned i = 0; i < metadata->data.vorbis_comment.num_comments; i++, entry++)
299                 {
300                     if (FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(*entry, &key, &value) == false)
301                         AUDDBG("Could not parse comment\n");
302                     else
303                     {
304                         parse_comment(tuple, key, value);
305                         free(key);
306                         free(value);
307                     }
308                 }
309                 break;
310             }
311 
312             case FLAC__METADATA_TYPE_STREAMINFO:
313             {
314                 metadata = FLAC__metadata_iterator_get_block(iter);
315 
316                 /* Calculate the stream length (milliseconds) */
317                 if (metadata->data.stream_info.sample_rate == 0)
318                 {
319                     AUDERR("Invalid sample rate for stream!\n");
320                     tuple.set_int (Tuple::Length, -1);
321                 }
322                 else
323                 {
324                     tuple.set_int (Tuple::Length,
325                         (metadata->data.stream_info.total_samples / metadata->data.stream_info.sample_rate) * 1000);
326                     AUDDBG("Stream length: %d seconds\n", tuple.get_int (Tuple::Length));
327                 }
328 
329                 int64_t size = file.fsize ();
330 
331                 if (size < 0 || metadata->data.stream_info.total_samples == 0)
332                     tuple.set_int (Tuple::Bitrate, 0);
333                 else
334                 {
335                     int bitrate = 8 * size *
336                         (int64_t) metadata->data.stream_info.sample_rate / metadata->data.stream_info.total_samples;
337 
338                     tuple.set_int (Tuple::Bitrate, (bitrate + 500) / 1000);
339                 }
340 
341                 if (metadata->data.stream_info.channels > 0)
342                     tuple.set_int(Tuple::Channels, metadata->data.stream_info.channels);
343                 break;
344             }
345 
346             case FLAC__METADATA_TYPE_PICTURE:
347             {
348                 if (image && !image->len())
349                 {
350                     metadata = FLAC__metadata_iterator_get_block(iter);
351 
352                     if (metadata->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER)
353                     {
354                         AUDDBG("FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER found.");
355                         image->insert((const char *) metadata->data.picture.data, 0,
356                          metadata->data.picture.data_length);
357                     }
358                 }
359                 break;
360             }
361 
362             default:
363                 ;
364         }
365     } while (FLAC__metadata_iterator_next(iter));
366 
367     FLAC__metadata_iterator_delete(iter);
368     FLAC__metadata_chain_delete(chain);
369 
370     return true;
371 
372 ERR:
373     status = FLAC__metadata_chain_status(chain);
374     FLAC__metadata_chain_delete(chain);
375 
376     AUDERR("An error occurred: %s\n", FLAC__Metadata_ChainStatusString[status]);
377     return false;
378 }
379