1 /* id3.c
2 *
3 * Copyright (c) 1998-2004 Mike Oliphant <grip@nostatic.org>
4 *
5 * http://www.nostatic.org/grip
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (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, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
21 */
22
23 #include <glib/gi18n.h>
24 #include <stdio.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include "grip_id3.h"
28
29 static void ID3Put(char *dest,char *src,int len,char *encoding);
30
31 /* this array contains string representations of all known ID3 tags */
32 /* taken from mp3id3 in the mp3tools 0.7 package */
33
34 ID3Genre id3_genres[] = {
35 {"Alternative",20},
36 {"Blues",0},
37 {"Classical",32},
38 {"Country",2},
39 {"Folk",80},
40 {"Jazz",8},
41 {"Metal",9},
42 {"Pop",13},
43 {"Rap",15},
44 {"Reggae",16},
45 {"Rock",17},
46 {"Other",12},
47 {"Abstract",148},
48 {"Acapella",123},
49 {"Acid Jazz",74},
50 {"Acid Punk",73},
51 {"Acid",34},
52 {"Acoustic",99},
53 {"AlternRock",40},
54 {"Ambient",26},
55 {"Anime",145},
56 {"Art Rock",149},
57 {"Audio Theatre",184},
58 {"Audiobook",183},
59 {"Avantgarde",90},
60 {"Ballad",116},
61 {"Baroque",150},
62 {"Bass",41},
63 {"Beat",135},
64 {"Bebob",85},
65 {"Bhangra",151},
66 {"Big Band",96},
67 {"Big Beat",152},
68 {"Black Metal",138},
69 {"Bluegrass",89},
70 {"Booty Bass",107},
71 {"Breakbeat",153},
72 {"BritPop",132},
73 {"Cabaret",65},
74 {"Celtic",88},
75 {"Chamber Music",104},
76 {"Chanson",102},
77 {"Chillout",154},
78 {"Chorus",97},
79 {"Christian Gangsta Rap",136},
80 {"Christian Rap",61},
81 {"Christian Rock",141},
82 {"Classic Rock",1},
83 {"Club",112},
84 {"Club-House",128},
85 {"Comedy",57},
86 {"Contemporary Christian",140},
87 {"Crossover",139},
88 {"Cult",58},
89 {"Dance Hall",125},
90 {"Dance",3},
91 {"Darkwave",50},
92 {"Death Metal",22},
93 {"Disco",4},
94 {"Downtempo",155},
95 {"Dream",55},
96 {"Drum & Bass",127},
97 {"Drum Solo",122},
98 {"Dub",156},
99 {"Dubstep",189},
100 {"Duet",120},
101 {"EBM",157},
102 {"Easy Listening",98},
103 {"Eclectic",158},
104 {"Electro",159},
105 {"Electroclash",160},
106 {"Electronic",52},
107 {"Emo",161},
108 {"Ethnic",48},
109 {"Euro-Techno",25},
110 {"Euro-house",124},
111 {"Eurodance",54},
112 {"Experimental",162},
113 {"Fast Fusion",84},
114 {"Folk/Rock",81},
115 {"Folklore",115},
116 {"Freestyle",119},
117 {"Funk",5},
118 {"Fusion",30},
119 {"G-Funk",188},
120 {"Game",36},
121 {"Gangsta",59},
122 {"Garage",163},
123 {"Garage Rock",190},
124 {"Global",164},
125 {"Goa",126},
126 {"Gospel",38},
127 {"Gothic Rock",91},
128 {"Gothic",49},
129 {"Grunge",6},
130 {"Hard Rock",79},
131 {"Hardcore",129},
132 {"Heavy Metal",137},
133 {"Hip-Hop",7},
134 {"House",35},
135 {"Humour",100},
136 {"IDM",165},
137 {"Illbient",166},
138 {"Indie Rock",187},
139 {"Indie",131},
140 {"Industrial",19},
141 {"Industro-Goth",167},
142 {"Instrumental Pop",46},
143 {"Instrumental Rock",47},
144 {"Instrumental",33},
145 {"JPop",146},
146 {"Jam Band",168},
147 {"Jazz+Funk",29},
148 {"Jungle",63},
149 {"Krautrock",169},
150 {"Latin",86},
151 {"Leftfield",170},
152 {"Lo-Fi",71},
153 {"Lounge",171},
154 {"Math Rock",172},
155 {"Meditative",45},
156 {"Merengue",142},
157 {"Musical",77},
158 {"National Folk",82},
159 {"Native American",64},
160 {"Negerpunk",133},
161 {"Neoclassical",182},
162 {"Neue Deutsche Welle",185},
163 {"New Age",10},
164 {"New Romantic",173},
165 {"New Wave",66},
166 {"Noise",39},
167 {"Nu-Breakz",174},
168 {"Oldies",11},
169 {"Opera",103},
170 {"Podcast",186},
171 {"Polka",75},
172 {"Polsk Punk",134},
173 {"Pop-Folk",53},
174 {"Pop/Funk",62},
175 {"Porn Groove",109},
176 {"Post-Punk",175},
177 {"Post-Rock",176},
178 {"Power Ballad",117},
179 {"Pranks",23},
180 {"Primus",108},
181 {"Progressive Rock",92},
182 {"Psybient",191},
183 {"Psychedelic",67},
184 {"Psychedelic Rock",93},
185 {"Psytrance",177},
186 {"Punk Rock",121},
187 {"Punk",43},
188 {"R&B",14},
189 {"Rave",68},
190 {"Reggaestep",192},
191 {"Retro",76},
192 {"Revival",87},
193 {"Rhythmic Soul",118},
194 {"Rock & Roll",78},
195 {"Salsa",143},
196 {"Samba",114},
197 {"Satire",110},
198 {"Shoegaze",178},
199 {"Showtunes",69},
200 {"Ska",21},
201 {"Slow Jam",111},
202 {"Slow Rock",95},
203 {"Sonata",105},
204 {"Soul",42},
205 {"Sound Clip",37},
206 {"Soundtrack",24},
207 {"Southern Rock",56},
208 {"Space Rock",179},
209 {"Space",44},
210 {"Speech",101},
211 {"Swing",83},
212 {"Symphonic Rock",94},
213 {"Symphony",106},
214 {"SynthPop",147},
215 {"Tango",113},
216 {"Techno",18},
217 {"Techno-Industrial",51},
218 {"Terror",130},
219 {"Thrash Metal",144},
220 {"Top 40",60},
221 {"Trailer",70},
222 {"Trance",31},
223 {"Tribal",72},
224 {"Trip-Hop",27},
225 {"Trop Rock",180},
226 {"Vocal",28},
227 {"World Music",181},
228 {NULL,193}
229 };
230
231 /* This array maps CDDB_ genre numbers to closest id3 genre */
232 int cddb_2_id3[] =
233 {
234 12, /* CDDB_UNKNOWN */
235 0, /* CDDB_BLUES */
236 32, /* CDDB_CLASSICAL */
237 2, /* CDDB_COUNTRY */
238 12, /* CDDB_DATA */
239 80, /* CDDB_FOLK */
240 8, /* CDDB_JAZZ */
241 12, /* CDDB_MISC */
242 10, /* CDDB_NEWAGE */
243 16, /* CDDB_REGGAE */
244 17, /* CDDB_ROCK */
245 24, /* CDDB_SOUNDTRACK */
246 };
247
248 /* ID3 tag structure */
249
250 typedef struct _id3_tag {
251 char tag[3];
252 char title[30];
253 char artist[30];
254 char album[30];
255 char year[4];
256 char comment[28];
257 unsigned char id3v1_1_mark;
258 unsigned char tracknum;
259 unsigned char genre;
260 } ID3v1Tag;
261
262 #ifdef HAVE_ID3V2
263
264 #include <id3.h>
265
266 /* Things you might want to mess with. Surprisingly, the code will probably
267 cope with you just messing with this section. */
268 #define NUM_FRAMES 7
269 static ID3_FrameID frameids[ NUM_FRAMES ] = {
270 ID3FID_TITLE, ID3FID_LEADARTIST, ID3FID_ALBUM, ID3FID_YEAR,
271 ID3FID_COMMENT, ID3FID_CONTENTTYPE, ID3FID_TRACKNUM
272 };
273 /* End of the section you're supposed to mess with */
274
ID3v2TagFile(char * filename,char * title,char * artist,char * album,char * year,char * comment,unsigned char genre,unsigned char tracknum,char * id3v2_encoding)275 gboolean ID3v2TagFile(char *filename, char *title, char *artist, char *album,
276 char *year, char *comment, unsigned char genre, unsigned
277 char tracknum,char *id3v2_encoding)
278 {
279 ID3Tag *tag;
280 ID3Field *field;
281 ID3Frame *frames[ NUM_FRAMES ];
282 int i;
283 gboolean retval = TRUE;
284 mode_t mask;
285 char *conv_str;
286 gsize rb,wb;
287
288 tag = ID3Tag_New();
289
290 if(tag) {
291 ID3Tag_Link(tag,filename);
292 /* GRR. No error. */
293
294 for ( i = 0; i < NUM_FRAMES; i++ ) {
295 frames[ i ] = ID3Frame_NewID( frameids[ i ] );
296
297 if ( frames[ i ] ) {
298 char *c_data = NULL;
299 char gen[ 6 ] = "( )"; /* max unsigned char: 255 */
300 char trk[ 4 ] = " "; /* max CDDA tracks: 99 */
301
302 switch( frameids[ i ] ) {
303 case ID3FID_TITLE:
304 c_data = title;
305 break;
306
307 case ID3FID_LEADARTIST:
308 c_data = artist;
309 break;
310
311 case ID3FID_ALBUM:
312 c_data = album;
313 break;
314
315 case ID3FID_YEAR:
316 c_data = year;
317 break;
318
319 case ID3FID_COMMENT:
320 c_data = comment;
321 break;
322
323 case ID3FID_CONTENTTYPE:
324 c_data = gen;
325 snprintf( gen, 6, "(%d)", genre );
326 break;
327
328 case ID3FID_TRACKNUM:
329 c_data = trk;
330 snprintf( trk, 4, "%d", tracknum );
331 break;
332
333 default:
334 /* Doh! */
335 g_printerr(_("unknown ID3 field\n"));
336 break;
337 }
338
339 if(c_data != NULL) {
340 field = ID3Frame_GetField( frames[i], ID3FN_TEXT );
341
342 if(field) {
343 /* if(!strcasecmp(id3v2_encoding,"utf-8")) {
344 ID3Field_SetUNICODE(field,(unicode_t *)c_data);
345 }
346 else {
347 */
348
349 /* Always encode pretending it is ascii */
350
351 conv_str=g_convert_with_fallback(c_data,strlen(c_data),id3v2_encoding,
352 "utf-8",NULL,&rb,&wb,NULL);
353
354 if(!conv_str) {
355 printf("***convert failed\n");
356
357 conv_str=strdup(c_data);
358 }
359
360 ID3Field_SetASCII(field,conv_str);
361
362 g_free(conv_str);
363 } else {
364 retval = FALSE;
365 }
366 }
367 } else { /* Frame->new() failed */
368 retval = FALSE;
369 break;
370 }
371 }
372 if ( retval != FALSE ) {
373 /* It would be really nice if I could have done something like
374 ID3Tag_AddFrames( tag, frames, NUM_FRAMES ), but the
375 prototypes work against me one way or another. So, this will
376 do instead. */
377 for ( i = 0; i < NUM_FRAMES; i++ ) {
378 /* Strictly speaking I should look for existing tags and
379 delete them, but hey. We're making fresh mp3 files, right?
380 */
381 ID3Tag_AddFrame( tag, frames[ i ] );
382 }
383 }
384
385 if(ID3Tag_UpdateByTagType(tag,ID3TT_ID3V2) != ID3E_NoError ) {
386 retval = FALSE;
387 }
388
389 ID3Tag_Delete( tag );
390
391 /* Reset permissions based on users umask to work around a bug in the
392 id3v2 library */
393 mask = umask(0);
394 umask(mask);
395 chmod(filename, 0666 & ~mask);
396
397 } else { /* Tag -> new() failed */
398 retval = FALSE;
399 }
400
401 return retval;
402 }
403
404 #endif /* HAVE_ID3V2 */
405
406 /* Add an ID3v1 tag to a file */
407
ID3v1TagFile(char * filename,char * title,char * artist,char * album,char * year,char * comment,unsigned char genre,unsigned char tracknum,char * id3_encoding)408 gboolean ID3v1TagFile(char *filename,char *title,char *artist,char *album,
409 char *year,char *comment,unsigned char genre,
410 unsigned char tracknum, char *id3_encoding)
411 {
412 FILE *fp;
413 ID3v1Tag tag;
414
415 fp=fopen(filename,"a");
416
417 ID3Put(tag.tag,"TAG",3,id3_encoding);
418
419 ID3Put(tag.title,title,30,id3_encoding);
420
421 ID3Put(tag.artist,artist,30,id3_encoding);
422
423 ID3Put(tag.album,album,30,id3_encoding);
424
425 ID3Put(tag.year,year,4,NULL);
426
427 ID3Put(tag.comment,comment,28,id3_encoding);
428
429 tag.id3v1_1_mark = 0U;
430
431 tag.tracknum=tracknum;
432 tag.genre=genre;
433
434 fwrite(&tag,sizeof(ID3v1Tag),1,fp);
435
436 fclose(fp);
437
438 return TRUE;
439 }
440
441 /* Copy a string padding with zeros */
442
ID3Put(char * dest,char * src,int len,char * encoding)443 static void ID3Put(char *dest,char *src,int len,char *encoding)
444 {
445 int pos;
446 int srclen;
447 char *conv_str;
448 gsize rb,wb;
449
450 if(encoding&&strcasecmp(encoding,"utf-8")) {
451 conv_str=g_convert_with_fallback(src,strlen(src),encoding,"utf-8",NULL,&rb,&wb,NULL);
452
453 if(!conv_str) conv_str=strdup(src);
454 }
455 else conv_str=strdup(src);
456
457 srclen=strlen(conv_str);
458
459 for(pos=0;pos<len;pos++) {
460 if(pos<srclen) dest[pos]=conv_str[pos];
461 else dest[pos]=0;
462 }
463
464 g_free(conv_str);
465 }
466
ID3GenreString(int genre)467 char *ID3GenreString(int genre)
468 {
469 int num;
470
471 for(num=0;id3_genres[num].name;num++) {
472 if(id3_genres[num].num==genre) return id3_genres[num].name;
473 }
474
475 return NULL;
476 }
477
ID3GenreByNum(int num)478 ID3Genre *ID3GenreByNum(int num)
479 {
480 if(!id3_genres[num].name) return NULL;
481
482 return &(id3_genres[num]);
483 }
484
485 /* Return the id3 genre id from the text name */
ID3GenreValue(char * genre)486 int ID3GenreValue(char *genre)
487 {
488 int pos;
489
490 for(pos=0;id3_genres[pos].name;pos++)
491 if(!strcasecmp(genre,id3_genres[pos].name))
492 return id3_genres[pos].num;
493
494 return 12;
495 }
496
ID3GenrePos(int genre)497 int ID3GenrePos(int genre)
498 {
499 int num;
500
501 for(num=0;id3_genres[num].name;num++) {
502 if(id3_genres[num].num==genre) return num;
503 }
504
505 return 0;
506 }
507
DiscDB2ID3(int discdb_genre)508 int DiscDB2ID3(int discdb_genre)
509 {
510 return cddb_2_id3[discdb_genre];
511 }
512
ID32DiscDB(int id3_genre)513 int ID32DiscDB(int id3_genre)
514 {
515 int discdb;
516
517 for(discdb=0;discdb<12;discdb++) {
518 if(cddb_2_id3[discdb]==id3_genre) return discdb;
519 }
520
521 return 12;
522 }
523