summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2002-04-23 08:43:04 +0000
committerDaniel Stenberg <daniel@haxx.se>2002-04-23 08:43:04 +0000
commit6755f82a7002b98fc522dab216ab32bb62c289c2 (patch)
treed012a0f33434463d52cef740d41d687c64f5818c
parente7cc45929aec0f8c2b8300cebfea3b2ac9a51c29 (diff)
downloadrockbox-6755f82a7002b98fc522dab216ab32bb62c289c2.tar.gz
rockbox-6755f82a7002b98fc522dab216ab32bb62c289c2.zip
id3 tag reading code, both v1 and v2. Still needs to be adjusted more to
remove the malloc()s and possible other stuff. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@185 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/id3.c549
1 files changed, 549 insertions, 0 deletions
diff --git a/firmware/id3.c b/firmware/id3.c
new file mode 100644
index 0000000000..825a54a718
--- /dev/null
+++ b/firmware/id3.c
@@ -0,0 +1,549 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Daniel Stenberg
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20/*
21 * Parts of this code has been stolen from the Ample project and was written
22 * by David Härdeman.
23 */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <string.h>
29#include <errno.h>
30
31struct mp3entry {
32 char *path;
33 char *title;
34 char *artist;
35 char *album;
36 int bitrate;
37 int frequency;
38 int id3v2len;
39 int id3v1len;
40 int filesize; /* in bytes */
41 int length; /* song length */
42};
43
44typedef struct mp3entry mp3entry;
45
46typedef unsigned char bool;
47#define TRUE 1
48#define FALSE 0
49
50/* Some utility macros used in getsonglength() */
51#define CHECKSYNC(x) (((x >> 21) & 0x07FF) == 0x7FF)
52#define BYTE0(x) ((x >> 24) & 0xFF)
53#define BYTE1(x) ((x >> 16) & 0xFF)
54#define BYTE2(x) ((x >> 8) & 0xFF)
55#define BYTE3(x) ((x >> 0) & 0xFF)
56
57#define UNSYNC(b1,b2,b3,b4) (((b1 & 0x7F) << (3*7)) + \
58 ((b2 & 0x7F) << (2*7)) + \
59 ((b3 & 0x7F) << (1*7)) + \
60 ((b4 & 0x7F) << (0*7)))
61
62#define HASID3V2(entry) entry->id3v2len > 0
63#define HASID3V1(entry) entry->id3v1len > 0
64
65/* Table of bitrates for MP3 files, all values in kilo.
66 * Indexed by version, layer and value of bit 15-12 in header.
67 */
68const int bitrate_table[2][3][16] =
69{
70 {
71 {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0},
72 {0,32,48,56, 64,80, 96, 112,128,160,192,224,256,320,384,0},
73 {0,32,40,48, 56,64, 80, 96, 112,128,160,192,224,256,320,0}
74 },
75 {
76 {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,0},
77 {0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0},
78 {0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0}
79 }
80};
81
82/* Table of samples per frame for MP3 files.
83 * Indexed by layer. Multiplied with 1000.
84 */
85const int bs[4] = {0, 384000, 1152000, 1152000};
86
87/* Table of sample frequency for MP3 files.
88 * Indexed by version and layer.
89 */
90const int freqtab[2][4] =
91{
92 {44100, 48000, 32000, 0},
93 {22050, 24000, 16000, 0},
94};
95
96/*
97 * Removes trailing spaces from a string.
98 *
99 * Arguments: buffer - the string to process
100 *
101 * Returns: void
102 */
103static void
104stripspaces(char *buffer)
105{
106 int i = 0;
107 while(*(buffer + i) != '\0')
108 i++;
109
110 for(;i >= 0; i--) {
111 if(*(buffer + i) == ' ')
112 *(buffer + i) = '\0';
113 else if(*(buffer + i) == '\0')
114 continue;
115 else
116 break;
117 }
118}
119
120/*
121 * Sets the title of an MP3 entry based on its ID3v1 tag.
122 *
123 * Arguments: file - the MP3 file to scen for a ID3v1 tag
124 * entry - the entry to set the title in
125 *
126 * Returns: TRUE if a title was found and created, else FALSE
127 */
128static bool
129setid3v1title(FILE *file, mp3entry *entry)
130{
131 char buffer[31];
132 int offsets[3] = {-95,-65,-125};
133 int i;
134 char *result;
135
136 for(i=0;i<3;i++) {
137 if(fseek(file, offsets[i], SEEK_END) != 0) {
138 free(result);
139 return FALSE;
140 }
141
142 buffer[0]=0;
143 fgets(buffer, 31, file);
144 stripspaces(buffer);
145
146 if(buffer[0]) {
147 switch(i) {
148 case 0:
149 entry->artist = strdup(buffer);
150 break;
151 case 1:
152 entry->album = strdup(buffer);
153 break;
154 case 2:
155 entry->title = strdup(buffer);
156 break;
157 }
158 }
159 }
160
161 return TRUE;
162}
163
164
165/*
166 * Sets the title of an MP3 entry based on its ID3v2 tag.
167 *
168 * Arguments: file - the MP3 file to scen for a ID3v2 tag
169 * entry - the entry to set the title in
170 *
171 * Returns: TRUE if a title was found and created, else FALSE
172 */
173static void
174setid3v2title(FILE *file, mp3entry *entry)
175{
176 char *buffer;
177 int minframesize;
178 int size, readsize = 0, headerlen;
179 char *title = NULL;
180 char *artist = NULL;
181 char *album = NULL;
182 char header[10];
183 unsigned short int version;
184
185 /* 10 = headerlength */
186 if(entry->id3v2len < 10)
187 return;
188
189 /* Check version */
190 fseek(file, 0, SEEK_SET);
191 fread(header, sizeof(char), 10, file);
192 version = (unsigned short int)header[3];
193
194 /* Read all frames in the tag */
195 size = entry->id3v2len - 10;
196 buffer = malloc(size + 1);
197 if(size != (int)fread(buffer, sizeof(char), size, file)) {
198 free(buffer);
199 return;
200 }
201 *(buffer + size) = '\0';
202
203 /* Set minimun frame size according to ID3v2 version */
204 if(version > 2)
205 minframesize = 12;
206 else
207 minframesize = 8;
208
209 /*
210 * We must have at least minframesize bytes left for the
211 * remaining frames to be interesting
212 */
213 while(size - readsize > minframesize) {
214
215 /* Read frame header and check length */
216 if(version > 2) {
217 memcpy(header, (buffer + readsize), 10);
218 readsize += 10;
219 headerlen = UNSYNC(header[4], header[5],
220 header[6], header[7]);
221 } else {
222 memcpy(header, (buffer + readsize), 6);
223 readsize += 6;
224 headerlen = (header[3] << 16) +
225 (header[4] << 8) +
226 (header[5]);
227 }
228 if(headerlen < 1)
229 continue;
230
231 /* Check for certain frame headers */
232 if(!strncmp(header, "TPE1", strlen("TPE1")) ||
233 !strncmp(header, "TP1", strlen("TP1"))) {
234 readsize++;
235 headerlen--;
236 if(headerlen > (size - readsize))
237 headerlen = (size - readsize);
238 artist = malloc(headerlen + 1);
239 snprintf(artist, headerlen + 1, "%s",
240 (buffer + readsize));
241 readsize += headerlen;
242 }
243 else if(!strncmp(header, "TIT2", strlen("TIT2")) ||
244 !strncmp(header, "TT2", strlen("TT2"))) {
245 readsize++;
246 headerlen--;
247 if(headerlen > (size - readsize))
248 headerlen = (size - readsize);
249 title = malloc(headerlen + 1);
250 snprintf(title, headerlen + 1, "%s",
251 (buffer + readsize));
252 readsize += headerlen;
253 }
254 else if(!strncmp(header, "TALB", strlen("TALB"))) {
255 readsize++;
256 headerlen--;
257 if(headerlen > (size - readsize))
258 headerlen = (size - readsize);
259 album = malloc(headerlen + 1);
260 snprintf(album, headerlen + 1, "%s",
261 (buffer + readsize));
262 readsize += headerlen;
263 }
264 }
265
266 if(artist)
267 entry->artist = artist;
268
269 if(title)
270 entry->title = title;
271
272 if(album)
273 entry->album = album;
274
275 free(buffer);
276}
277
278/*
279 * Calculates the size of the ID3v2 tag.
280 *
281 * Arguments: file - the file to search for a tag.
282 *
283 * Returns: the size of the tag or 0 if none was found
284 */
285static int
286getid3v2len(FILE *file)
287{
288 char buf[6];
289 int offset;
290
291 /* Make sure file has a ID3 tag */
292 if((fseek(file, 0, SEEK_SET) != 0) ||
293 (fread(buf, sizeof(char), 6, file) != 6) ||
294 (strncmp(buf, "ID3", strlen("ID3")) != 0))
295 offset = 0;
296 /* Now check what the ID3v2 size field says */
297 else if(fread(buf, sizeof(char), 4, file) != 4)
298 offset = 0;
299 else
300 offset = UNSYNC(buf[0], buf[1], buf[2], buf[3]) + 10;
301
302 return offset;
303}
304
305static int
306getfilesize(FILE *file)
307{
308 /* seek to the end of it */
309 if(fseek(file, 0, SEEK_END))
310 return 0; /* unknown */
311
312 return ftell(file);
313}
314
315/*
316 * Calculates the size of the ID3v1 tag.
317 *
318 * Arguments: file - the file to search for a tag.
319 *
320 * Returns: the size of the tag or 0 if none was found
321 */
322static int
323getid3v1len(FILE *file)
324{
325 char buf[3];
326 int offset;
327
328 /* Check if we find "TAG" 128 bytes from EOF */
329 if((fseek(file, -128, SEEK_END) != 0) ||
330 (fread(buf, sizeof(char), 3, file) != 3) ||
331 (strncmp(buf, "TAG", 3) != 0))
332 offset = 0;
333 else
334 offset = 128;
335
336 return offset;
337}
338
339/*
340 * Calculates the length (in milliseconds) of an MP3 file. Currently this code
341 * doesn't care about VBR (Variable BitRate) files since it would have to scan
342 * through the entire file but this should become a config option in the
343 * future.
344 *
345 * Modified to only use integers.
346 *
347 * Arguments: file - the file to calculate the length upon
348 * entry - the entry to update with the length
349 *
350 * Returns: the song length in milliseconds,
351 * -1 means that it couldn't be calculated
352 */
353static int
354getsonglength(FILE *file, mp3entry *entry)
355{
356 long header;
357 int version;
358 int layer;
359 int bitindex;
360 int bitrate;
361 int freqindex;
362 int frequency;
363
364 long bpf;
365 long tpf;
366 int i;
367
368 /* Start searching after ID3v2 header */
369 if(fseek(file, entry->id3v2len, SEEK_SET))
370 return -1;
371
372 /* Fill up header with first 24 bits */
373 for(version = 0; version < 3; version++) {
374 header <<= 8;
375 if(!fread(&header, 1, 1, file))
376 return -1;
377 }
378
379 /* Loop trough file until we find a frame header */
380 restart:
381 do {
382 header <<= 8;
383 if(!fread(&header, 1, 1, file))
384 return -1;
385 } while(!CHECKSYNC(header));
386
387 /*
388 * Some files are filled with garbage in the beginning,
389 * if the bitrate index of the header is binary 1111
390 * that is a good is a good indicator
391 */
392 if((header & 0xF000) == 0xF000)
393 goto restart;
394
395#ifdef DEBUG_STANDALONE
396 fprintf(stderr,
397 "We found %x-%x-%x-%x and checksync %i and test %x\n",
398 BYTE0(header), BYTE1(header), BYTE2(header), BYTE3(header),
399 CHECKSYNC(header), (header & 0xF000) == 0xF000);
400#endif
401 /* MPEG Audio Version */
402 switch((header & 0x180000) >> 19) {
403 case 2:
404 version = 2;
405 break;
406 case 3:
407 version = 1;
408 break;
409 default:
410 return -1;
411 }
412
413 /* Layer */
414 switch((header & 0x060000) >> 17) {
415 case 1:
416 layer = 3;
417 break;
418 case 2:
419 layer = 2;
420 break;
421 case 3:
422 layer = 1;
423 break;
424 default:
425 return -1;
426 }
427
428 /* Bitrate */
429 bitindex = (header & 0xF000) >> 12;
430 bitrate = bitrate_table[version-1][layer-1][bitindex];
431 if(bitrate == 0)
432 return -1;
433
434 /* Sampling frequency */
435 freqindex = (header & 0x0C00) >> 10;
436 frequency = freqtab[version-1][freqindex];
437 if(frequency == 0)
438 return -1;
439
440#ifdef DEBUG_STANDALONE
441 fprintf(stderr,
442 "Version %i, lay %i, biti %i, bitr %i, freqi %i, freq %i\n",
443 version, layer, bitindex, bitrate, freqindex, frequency);
444#endif
445 entry->bitrate = bitrate;
446 entry->frequency = frequency;
447
448 /* Calculate bytes per frame, calculation depends on layer */
449 switch(layer) {
450 case 1:
451 bpf = bitrate_table[version - 1][layer - 1][bitindex];
452 bpf *= 12000.0 * 4.0;
453 bpf /= freqtab[version-1][freqindex] << (version - 1);
454 break;
455 case 2:
456 case 3:
457 bpf = bitrate_table[version - 1][layer - 1][bitindex];
458 bpf *= 144000;
459 bpf /= freqtab[version-1][freqindex] << (version - 1);
460 break;
461 default:
462 bpf = 1.0;
463 }
464
465 /* Calculate time per frame */
466 tpf = bs[layer] / freqtab[version-1][freqindex] << (version - 1);
467
468 /*
469 * Now song length is
470 * ((filesize)/(bytes per frame))*(time per frame)
471 */
472 return entry->filesize*tpf/bpf;
473}
474
475
476/*
477 * Checks all relevant information (such as ID3v1 tag, ID3v2 tag, length etc)
478 * about an MP3 file and updates it's entry accordingly.
479 *
480 * Arguments: entry - the entry to check and update with the new information
481 *
482 * Returns: void
483 */
484bool
485mp3info(mp3entry *entry, char *filename)
486{
487 FILE *file;
488 char *copy;
489 char *title;
490
491 if((file = fopen(filename, "r")) == NULL)
492 return TRUE;
493
494 memset(entry, 0, sizeof(mp3entry));
495
496 entry->path = filename;
497
498 entry->filesize = getfilesize(file);
499 entry->id3v2len = getid3v2len(file);
500 entry->id3v1len = getid3v1len(file);
501 entry->length = getsonglength(file, entry);
502 entry->title = NULL;
503
504 if(HASID3V2(entry))
505 setid3v2title(file, entry);
506
507 if(HASID3V1(entry) && !entry->title)
508 setid3v1title(file, entry);
509
510 fclose(file);
511
512 return FALSE;
513}
514
515#ifdef DEBUG_STANDALONE
516
517int main(int argc, char **argv)
518{
519 if(argc > 1) {
520 mp3entry mp3;
521 if(mp3info(&mp3, argv[1])) {
522 printf("Failed\n");
523 return 0;
524 }
525
526 printf("Title: %s\n"
527 "Artist: %s\n"
528 "Album: %s\n"
529 "Length: %.1f secs\n"
530 "Bitrate: %d\n"
531 "Frequency: %d\n",
532 mp3.title?mp3.title:"<blank>",
533 mp3.artist?mp3.artist:"<blank>",
534 mp3.album?mp3.album:"<blank>",
535 mp3.length/1000.0,
536 mp3.bitrate,
537 mp3.frequency);
538 }
539
540 return 0;
541}
542
543#endif
544
545/* -----------------------------------------------------------------
546 * local variables:
547 * eval: (load-file "rockbox-mode.el")
548 * end:
549 */