comparison src/libaudtag/aac/aac.c @ 4887:0ddbd0025174 default tip

added libaudtag. (not used yet.)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Wed, 05 May 2010 18:26:06 +0900
parents
children
comparison
equal deleted inserted replaced
4886:54b4f7aaca24 4887:0ddbd0025174
1 /*
2 * Copyright 2009 Paula Stanciu
3 *
4 * This file is part of Audacious.
5 *
6 * Audacious is free software: you can redistribute it and/or modify it under
7 * the terms of the GNU General Public License as published by the Free Software
8 * Foundation, version 3 of the License.
9 *
10 * Audacious is distributed in the hope that it will be useful, but WITHOUT ANY
11 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * Audacious. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * The Audacious team does not consider modular code linking to Audacious or
18 * using our public API to be a derived work.
19 */
20
21 #include <glib/gstdio.h>
22 #include <audlegacy/tuple.h>
23 #include <audlegacy/vfs.h>
24 #include "../tag_module.h"
25 #include "aac.h"
26 #include "../util.h"
27
28 static const char *ID3v1GenreList[] = {
29 "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
30 "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies",
31 "Other", "Pop", "R&B", "Rap", "Reggae", "Rock",
32 "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks",
33 "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk",
34 "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House",
35 "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
36 "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock",
37 "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk",
38 "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta",
39 "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
40 "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
41 "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
42 "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing",
43 "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde",
44 "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
45 "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
46 "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
47 "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
48 "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet",
49 "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall",
50 "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror",
51 "Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat",
52 "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", "Contemporary C",
53 "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop",
54 "SynthPop",
55 };
56
57 gchar *atom_types[] = { "\251alb", "\251nam", "cprt", "\251art", "\251ART", "trkn", "\251day", "gnre", "desc" };
58
59 Atom *readAtom(VFSFile * fd)
60 {
61 guint32 size = read_BEuint32(fd);
62 if (size > vfs_fsize(fd))
63 return NULL;
64
65 Atom *atom = g_new0(Atom, 1);
66 atom->size = size;
67 atom->name = read_char_data(fd, 4);
68 atom->body = read_char_data(fd, atom->size - 8);
69 atom->type = 0;
70 return atom;
71 }
72
73 void writeAtom(VFSFile * fd, Atom * atom)
74 {
75 write_BEuint32(fd, atom->size);
76 vfs_fwrite(atom->name, 4, 1, fd);
77 vfs_fwrite(atom->body, atom->size - 8, 1, fd);
78 }
79
80 void printAtom(Atom * atom)
81 {
82 AUDDBG("size = %x\n", atom->size);
83 AUDDBG("name = %s\n", atom->name);
84 }
85
86 StrDataAtom *readStrDataAtom(VFSFile * fd)
87 {
88 StrDataAtom *atom = g_new0(StrDataAtom, 1);
89 atom->atomsize = read_BEuint32(fd);
90 atom->name = read_char_data(fd, 4);
91 atom->datasize = read_BEuint32(fd);
92 atom->dataname = read_char_data(fd, 4);
93 atom->vflag = read_BEuint32(fd);
94 atom->nullData = read_BEuint32(fd);
95 atom->data = read_char_data(fd, atom->datasize - 16);
96 atom->type = 1;
97 return atom;
98 }
99
100 void writeStrDataAtom(VFSFile * fd, StrDataAtom * atom)
101 {
102 write_BEuint32(fd, atom->atomsize);
103 vfs_fwrite(atom->name, 4, 1, fd);
104 write_BEuint32(fd, atom->datasize);
105 vfs_fwrite(atom->dataname, 4, 1, fd);
106 write_BEuint32(fd, atom->vflag);
107 write_BEuint32(fd, atom->nullData);
108 vfs_fwrite(atom->data, atom->datasize - 16, 1, fd);
109 }
110
111 Atom *findAtom(VFSFile * fd, gchar * name)
112 {
113 Atom *atom = readAtom(fd);
114 while (strcmp(atom->name, name) && !vfs_feof(fd))
115 {
116 g_free(atom);
117 atom = readAtom(fd);
118 }
119 if (vfs_feof(fd))
120 {
121 g_free(atom);
122 AUDDBG("The atom %s could not be found\n", name);
123 return NULL;
124 }
125 return atom;
126 }
127
128 Atom *getilstAtom(VFSFile * fd)
129 {
130 Atom *moov = findAtom(fd, MOOV);
131
132 // search atom childs
133 vfs_fseek(fd, -(moov->size - 7), SEEK_CUR);
134 Atom *udta = findAtom(fd, UDTA);
135
136
137 vfs_fseek(fd, -(udta->size - 7), SEEK_CUR);
138 Atom *meta = findAtom(fd, META);
139
140 vfs_fseek(fd, -(meta->size - 11), SEEK_CUR);
141 Atom *ilst = findAtom(fd, ILST);
142
143 int zz = vfs_ftell(fd);
144 AUDDBG("zzz = %d\n", zz);
145 ilstFileOffset = vfs_ftell(fd) - ilst->size;
146 vfs_fseek(fd, -(ilst->size - 7), SEEK_CUR);
147
148 return ilst;
149
150 }
151
152 int getAtomID(gchar * name)
153 {
154 g_return_val_if_fail(name != NULL, -1);
155 int i = 0;
156 for (i = 0; i < MP4_ITEMS_NO; i++)
157 {
158 if (!strcmp(name, atom_types[i]))
159 return i;
160 }
161 return -1;
162 }
163
164 StrDataAtom *makeAtomWithData(const gchar * data, StrDataAtom * atom, int field)
165 {
166 guint32 charsize = strlen(data);
167 atom->atomsize = charsize + 24;
168 atom->name = atom_types[field];
169 atom->datasize = charsize + 16;
170 atom->dataname = "data";
171 atom->vflag = 0x0;
172 atom->nullData = 0x0;
173 atom->data = (gchar *) data;
174 atom->type = 1;
175 return atom;
176
177 }
178
179 void writeAtomListToFile(VFSFile * from, VFSFile * to, int offset, mowgli_list_t * list)
180 {
181 //read free atom if we have any :D
182 guint32 oset = ilstFileOffset + ilstAtom->size;
183 vfs_fseek(from, oset, SEEK_SET);
184 mowgli_list_t *atoms_before_free = mowgli_list_create();
185 Atom *atom = readAtom(from);
186 while (strcmp(atom->name, "free") && !vfs_feof(from))
187 {
188 mowgli_node_add(atom, mowgli_node_create(), atoms_before_free);
189 g_free(atom);
190 atom = readAtom(from);
191 }
192 g_free(atom);
193 if (vfs_feof(from))
194 {
195 AUDDBG("No free atoms\n");
196 g_free(atom);
197 atom = NULL;
198 }
199
200 //write ilst atom header
201 gchar il[4] = ILST;
202 vfs_fwrite(&newilstSize, 4, 1, to);
203 vfs_fwrite(il, 4, 1, to);
204 //write ilst
205
206 mowgli_node_t *n, *tn;
207
208 MOWGLI_LIST_FOREACH_SAFE(n, tn, list->head)
209 {
210 if (((Atom *) (n->data))->type == 0)
211 {
212 writeAtom(to, (Atom *) (n->data));
213 }
214 else
215 {
216 writeStrDataAtom(to, (StrDataAtom *) (n->data));
217 }
218 }
219
220 //write all atoms before free
221 if (atoms_before_free->count != 0)
222 {
223
224 MOWGLI_LIST_FOREACH_SAFE(n, tn, list->head)
225 {
226 writeAtom(to, (Atom *) (n->data));
227 }
228 }
229 if (atom != NULL)
230 {
231 atom->size -= newilstSize - ilstAtom->size;
232 }
233 writeAtom(to, atom);
234 }
235
236 gboolean aac_can_handle_file(VFSFile * f)
237 {
238 Atom *first_atom = readAtom(f);
239 if (first_atom == NULL)
240 return FALSE;
241 if (!strcmp(first_atom->name, FTYP))
242 return TRUE;
243 return FALSE;
244 }
245
246 Tuple *aac_populate_tuple_from_file(Tuple * tuple, VFSFile * f)
247 {
248 if (ilstAtom)
249 g_free(ilstAtom);
250 ilstAtom = getilstAtom(f);
251 int size_read = 0;
252
253 if (dataAtoms != NULL)
254 {
255 mowgli_node_t *n, *tn;
256
257 MOWGLI_LIST_FOREACH_SAFE(n, tn, dataAtoms->head)
258 {
259 mowgli_node_delete(n, dataAtoms);
260 }
261 }
262 dataAtoms = mowgli_list_create();
263
264 while (size_read < ilstAtom->size)
265 {
266 Atom *at = readAtom(f);
267 mowgli_node_add(at, mowgli_node_create(), dataAtoms);
268 int atomtype = getAtomID(at->name);
269 if (atomtype == -1)
270 {
271 size_read += at->size;
272 continue;
273 }
274 g_free(at);
275 vfs_fseek(f, -(at->size), SEEK_CUR);
276 StrDataAtom *a = readStrDataAtom(f);
277 size_read += a->atomsize;
278
279 switch (atomtype)
280 {
281 case MP4_ALBUM:
282 {
283 tuple_associate_string(tuple, FIELD_ALBUM, NULL, a->data);
284 }
285 break;
286 case MP4_TITLE:
287 {
288 tuple_associate_string(tuple, FIELD_TITLE, NULL, a->data);
289 }
290 break;
291 case MP4_COPYRIGHT:
292 {
293 tuple_associate_string(tuple, FIELD_COPYRIGHT, NULL, a->data);
294 }
295 break;
296 case MP4_ARTIST:
297 case MP4_ARTIST2:
298 {
299 tuple_associate_string(tuple, FIELD_ARTIST, NULL, a->data);
300 }
301 break;
302 case MP4_TRACKNR:
303 {
304 //tuple_associate_string(tuple,FIELD_ALBUM,NULL,a->data);
305 }
306 break;
307 case MP4_YEAR:
308 {
309 tuple_associate_int(tuple, FIELD_YEAR, NULL, atoi(a->data));
310 }
311 break;
312 case MP4_GENRE:
313 {
314 guint8 *val = (guint8 *) (a->data + (a->datasize - 17));
315 const gchar *genre = ID3v1GenreList[*val - 1];
316 tuple_associate_string(tuple, FIELD_GENRE, NULL, genre);
317 }
318 break;
319 case MP4_COMMENT:
320 {
321 tuple_associate_string(tuple, FIELD_COMMENT, NULL, a->data);
322 }
323 break;
324 }
325 }
326 return tuple;
327 }
328
329 gboolean aac_write_tuple_to_file(Tuple * tuple, VFSFile * f)
330 {
331 #ifdef BROKEN
332 return FALSE;
333 #endif
334 newilstSize = 0;
335 mowgli_node_t *n, *tn;
336 mowgli_list_t *newdataAtoms;
337 newdataAtoms = mowgli_list_create();
338
339 MOWGLI_LIST_FOREACH_SAFE(n, tn, dataAtoms->head)
340 {
341 int atomtype = getAtomID(((StrDataAtom *) (n->data))->name);
342 switch (atomtype)
343 {
344 case MP4_ALBUM:
345 {
346 const gchar *strVal = tuple_get_string(tuple, FIELD_ALBUM, NULL);
347 if (strVal != NULL)
348 {
349 StrDataAtom *atom = g_new0(StrDataAtom, 1);
350 atom = makeAtomWithData(strVal, atom, MP4_ALBUM);
351 mowgli_node_add(atom, mowgli_node_create(), newdataAtoms);
352 newilstSize += atom->atomsize;
353 }
354 else
355 {
356 mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms);
357 newilstSize += ((Atom *) (n->data))->size;
358 }
359 }
360 break;
361 case MP4_TITLE:
362 {
363 const gchar *strVal = tuple_get_string(tuple, FIELD_TITLE, NULL);
364 if (strVal != NULL)
365 {
366 StrDataAtom *atom = g_new0(StrDataAtom, 1);
367 atom = makeAtomWithData(strVal, atom, MP4_TITLE);
368 mowgli_node_add(atom, mowgli_node_create(), newdataAtoms);
369 newilstSize += atom->atomsize;
370 }
371 else
372 {
373 mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms);
374 newilstSize += ((Atom *) (n->data))->size;
375 }
376 }
377 break;
378 case MP4_COPYRIGHT:
379 {
380 const gchar *strVal = tuple_get_string(tuple, FIELD_COPYRIGHT, NULL);
381 if (strVal != NULL)
382 {
383 StrDataAtom *atom = g_new0(StrDataAtom, 1);
384 atom = makeAtomWithData(strVal, atom, MP4_COPYRIGHT);
385 mowgli_node_add(atom, mowgli_node_create(), newdataAtoms);
386 newilstSize += atom->atomsize;
387 }
388 else
389 {
390 mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms);
391 newilstSize += ((Atom *) (n->data))->size;
392 }
393 }
394 break;
395 case MP4_ARTIST:
396 case MP4_ARTIST2:
397 {
398 const gchar *strVal = tuple_get_string(tuple, FIELD_ARTIST, NULL);
399 if (strVal != NULL)
400 {
401 StrDataAtom *atom = g_new0(StrDataAtom, 1);
402 atom = makeAtomWithData(strVal, atom, MP4_ARTIST2);
403 mowgli_node_add(atom, mowgli_node_create(), newdataAtoms);
404 newilstSize += atom->atomsize;
405 }
406 else
407 {
408 mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms);
409 newilstSize += ((Atom *) (n->data))->size;
410 }
411 }
412 break;
413 case MP4_TRACKNR:
414 {
415 //tuple_associate_string(tuple,FIELD_ALBUM,NULL,a->data);
416 }
417 break;
418 case MP4_YEAR:
419 {
420 int iyear = tuple_get_int(tuple, FIELD_YEAR, NULL);
421 gchar *strVal = g_strdup_printf("%d", iyear);
422 if (strVal != NULL)
423 {
424 StrDataAtom *atom = g_new0(StrDataAtom, 1);
425 atom = makeAtomWithData(strVal, atom, MP4_YEAR);
426 mowgli_node_add(atom, mowgli_node_create(), newdataAtoms);
427 newilstSize += atom->atomsize;
428 }
429 else
430 {
431 mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms);
432 newilstSize += ((Atom *) (n->data))->size;
433 }
434 }
435 break;
436 /*
437 case MP4_GENRE:
438 {
439
440 guint8 *val = (guint8*)(a->data + (a->datasize-17));
441 const gchar* genre = ID3v1GenreList[*val-1];
442 tuple_associate_string(tuple,FIELD_GENRE,NULL,genre);
443
444 }break;
445 */
446 case MP4_COMMENT:
447 {
448 const gchar *strVal = tuple_get_string(tuple, FIELD_COMMENT, NULL);
449 if (strVal != NULL)
450 {
451 StrDataAtom *atom = g_new0(StrDataAtom, 1);
452 atom = makeAtomWithData(strVal, atom, MP4_COMMENT);
453 mowgli_node_add(atom, mowgli_node_create(), newdataAtoms);
454 newilstSize += atom->atomsize;
455 }
456 else
457 {
458 mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms);
459 newilstSize += ((Atom *) (n->data))->size;
460 }
461 }
462 break;
463 default:
464 {
465 mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms);
466 newilstSize += ((Atom *) (n->data))->size;
467 }
468 break;
469 }
470 }
471
472 VFSFile *tmp;
473 const gchar *tmpdir = g_get_tmp_dir();
474 gchar *tmp_path = g_strdup_printf("file://%s/%s", tmpdir, "tmp.mp4");
475 tmp = vfs_fopen(tmp_path, "w");
476 copyAudioData(f, tmp, 0, ilstFileOffset);
477 writeAtomListToFile(f, tmp, ilstFileOffset, newdataAtoms);
478 return TRUE;
479 }