Battery blinks if >BATTERY_LEVEL_DANGEROUS
[kugel-rb.git] / firmware / id3.c
blob021b81770408b76478c3465197f4b61e8511ee87
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Daniel Stenberg
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
21 * Parts of this code has been stolen from the Ample project and was written
22 * by David Härdeman.
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <stdbool.h>
30 #include "file.h"
31 #include "debug.h"
32 #include "atoi.h"
34 #include "id3.h"
36 /* Some utility macros used in getsonglength() */
37 #define BYTE0(x) ((x >> 24) & 0xFF)
38 #define BYTE1(x) ((x >> 16) & 0xFF)
39 #define BYTE2(x) ((x >> 8) & 0xFF)
40 #define BYTE3(x) ((x >> 0) & 0xFF)
42 #define UNSYNC(b1,b2,b3,b4) (((b1 & 0x7F) << (3*7)) | \
43 ((b2 & 0x7F) << (2*7)) | \
44 ((b3 & 0x7F) << (1*7)) | \
45 ((b4 & 0x7F) << (0*7)))
47 #define HASID3V2(entry) entry->id3v2len > 0
48 #define HASID3V1(entry) entry->id3v1len > 0
50 /* Table of bitrates for MP3 files, all values in kilo.
51 * Indexed by version, layer and value of bit 15-12 in header.
53 const int bitrate_table[2][3][16] =
56 {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0},
57 {0,32,48,56, 64,80, 96, 112,128,160,192,224,256,320,384,0},
58 {0,32,40,48, 56,64, 80, 96, 112,128,160,192,224,256,320,0}
61 {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,0},
62 {0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0},
63 {0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0}
67 /* Table of samples per frame for MP3 files.
68 * Indexed by layer. Multiplied with 1000.
70 const int bs[4] = {0, 384000, 1152000, 1152000};
72 /* Table of sample frequency for MP3 files.
73 * Indexed by version and layer.
75 const int freqtab[2][4] =
77 {44100, 48000, 32000, 0},
78 {22050, 24000, 16000, 0},
82 * Removes trailing spaces from a string.
84 * Arguments: buffer - the string to process
86 * Returns: void
88 static void
89 stripspaces(char *buffer)
91 int i = 0;
92 while(*(buffer + i) != '\0')
93 i++;
95 for(;i >= 0; i--) {
96 if(*(buffer + i) == ' ')
97 *(buffer + i) = '\0';
98 else if(*(buffer + i) == '\0')
99 continue;
100 else
101 break;
106 * Sets the title of an MP3 entry based on its ID3v1 tag.
108 * Arguments: file - the MP3 file to scen for a ID3v1 tag
109 * entry - the entry to set the title in
111 * Returns: true if a title was found and created, else false
113 static bool setid3v1title(int fd, struct mp3entry *entry)
115 char buffer[31];
116 int offsets[3] = {-95,-65,-125};
117 int i;
119 for(i=0;i<3;i++) {
120 if(-1 == lseek(fd, offsets[i], SEEK_END))
121 return false;
123 buffer[30]=0;
124 read(fd, buffer, 30);
125 stripspaces(buffer);
127 if(buffer[0]) {
128 switch(i) {
129 case 0:
130 strcpy(entry->id3v1buf[0], buffer);
131 entry->artist = entry->id3v1buf[0];
132 break;
133 case 1:
134 strcpy(entry->id3v1buf[1], buffer);
135 entry->album = entry->id3v1buf[1];
136 break;
137 case 2:
138 strcpy(entry->id3v1buf[2], buffer);
139 entry->title = entry->id3v1buf[2];
140 break;
145 return true;
150 * Sets the title of an MP3 entry based on its ID3v2 tag.
152 * Arguments: file - the MP3 file to scen for a ID3v2 tag
153 * entry - the entry to set the title in
155 * Returns: true if a title was found and created, else false
157 static void setid3v2title(int fd, struct mp3entry *entry)
159 unsigned int minframesize;
160 int size;
161 unsigned int readsize = 0, headerlen;
162 char *title = NULL;
163 char *artist = NULL;
164 char *album = NULL;
165 char *tracknum = NULL;
166 char header[10];
167 unsigned short int version;
168 int titlen=0, artistn=0, albumn=0, tracknumn=0;
169 char *buffer = entry->id3v2buf;
171 /* 10 = headerlength */
172 if(entry->id3v2len < 10)
173 return;
175 /* Check version */
176 lseek(fd, 0, SEEK_SET);
177 if(10 != read(fd, header, 10))
178 return;
180 version = (unsigned short int)header[3];
182 /* Read all frames in the tag */
183 size = entry->id3v2len - 10;
185 if(size >= (int)sizeof(entry->id3v2buf))
186 size = sizeof(entry->id3v2buf)-1;
188 if(size != read(fd, buffer, size))
189 return;
191 *(buffer + size) = '\0';
193 /* Set minimun frame size according to ID3v2 version */
194 if(version > 2)
195 minframesize = 12;
196 else
197 minframesize = 8;
200 * We must have at least minframesize bytes left for the
201 * remaining frames to be interesting
203 while(size - readsize > minframesize) {
205 /* Read frame header and check length */
206 if(version > 2) {
207 memcpy(header, (buffer + readsize), 10);
208 readsize += 10;
209 headerlen = UNSYNC(header[4], header[5],
210 header[6], header[7]);
211 } else {
212 memcpy(header, (buffer + readsize), 6);
213 readsize += 6;
214 headerlen = (header[3] << 16) +
215 (header[4] << 8) +
216 (header[5]);
218 if(headerlen < 1)
219 continue;
221 /* Check for certain frame headers */
222 if(!strncmp(header, "TPE1", strlen("TPE1")) ||
223 !strncmp(header, "TP1", strlen("TP1"))) {
224 readsize++;
225 headerlen--;
226 if(headerlen > (size - readsize))
227 headerlen = (size - readsize);
228 artist = buffer + readsize;
229 artistn = headerlen;
230 readsize += headerlen;
232 else if(!strncmp(header, "TIT2", strlen("TIT2")) ||
233 !strncmp(header, "TT2", strlen("TT2"))) {
234 readsize++;
235 headerlen--;
236 if(headerlen > (size - readsize))
237 headerlen = (size - readsize);
238 title = buffer + readsize;
239 titlen = headerlen;
240 readsize += headerlen;
242 else if(!strncmp(header, "TALB", strlen("TALB"))) {
243 readsize++;
244 headerlen--;
245 if(headerlen > (size - readsize))
246 headerlen = (size - readsize);
247 album = buffer + readsize;
248 albumn = headerlen;
249 readsize += headerlen;
251 else if(!strncmp(header, "TRCK", strlen("TRCK"))) {
252 readsize++;
253 headerlen--;
254 if(headerlen > (size - readsize))
255 headerlen = (size - readsize);
256 tracknum = buffer + readsize;
257 tracknumn = headerlen;
258 readsize += headerlen;
259 } else {
260 readsize += headerlen;
264 if(artist) {
265 entry->artist = artist;
266 artist[artistn]=0;
269 if(title) {
270 entry->title = title;
271 title[titlen]=0;
274 if(album) {
275 entry->album = album;
276 album[albumn]=0;
279 if(tracknum) {
280 tracknum[tracknumn] = 0;
281 entry->tracknum = atoi(tracknum);
286 * Calculates the size of the ID3v2 tag.
288 * Arguments: file - the file to search for a tag.
290 * Returns: the size of the tag or 0 if none was found
292 static int getid3v2len(int fd)
294 char buf[6];
295 int offset;
297 /* Make sure file has a ID3 tag */
298 if((-1 == lseek(fd, 0, SEEK_SET)) ||
299 (read(fd, buf, 6) != 6) ||
300 (strncmp(buf, "ID3", strlen("ID3")) != 0))
301 offset = 0;
303 /* Now check what the ID3v2 size field says */
304 else if(read(fd, buf, 4) != 4)
305 offset = 0;
306 else
307 offset = UNSYNC(buf[0], buf[1], buf[2], buf[3]) + 10;
309 return offset;
312 static int getfilesize(int fd)
314 int size;
316 /* seek to the end of it */
317 size = lseek(fd, 0, SEEK_END);
318 if(-1 == size)
319 return 0; /* unknown */
321 return size;
325 * Calculates the size of the ID3v1 tag.
327 * Arguments: file - the file to search for a tag.
329 * Returns: the size of the tag or 0 if none was found
331 static int getid3v1len(int fd)
333 char buf[3];
334 int offset;
336 /* Check if we find "TAG" 128 bytes from EOF */
337 if((lseek(fd, -128, SEEK_END) == -1) ||
338 (read(fd, buf, 3) != 3) ||
339 (strncmp(buf, "TAG", 3) != 0))
340 offset = 0;
341 else
342 offset = 128;
344 return offset;
347 /* check if 'head' is a valid mp3 frame header */
348 static bool mp3frameheader(unsigned long head)
350 if ((head & 0xffe00000) != 0xffe00000) /* bad sync? */
351 return false;
352 if (!((head >> 17) & 3)) /* no layer? */
353 return false;
354 if (((head >> 12) & 0xf) == 0xf) /* bad bitrate? */
355 return false;
356 if (!((head >> 12) & 0xf)) /* no bitrate? */
357 return false;
358 if (((head >> 10) & 0x3) == 0x3) /* bad sample rate? */
359 return false;
360 if (((head >> 19) & 1) == 1 &&
361 ((head >> 17) & 3) == 3 &&
362 ((head >> 16) & 1) == 1)
363 return false;
364 if ((head & 0xffff0000) == 0xfffe0000)
365 return false;
367 return true;
371 * Calculates the length (in milliseconds) of an MP3 file. Currently this code
372 * doesn't care about VBR (Variable BitRate) files since it would have to scan
373 * through the entire file but this should become a config option in the
374 * future.
376 * Modified to only use integers.
378 * Arguments: file - the file to calculate the length upon
379 * entry - the entry to update with the length
381 * Returns: the song length in milliseconds,
382 * -1 means that it couldn't be calculated
384 static int getsonglength(int fd, struct mp3entry *entry)
386 unsigned int filetime = 0;
387 unsigned long header=0;
388 unsigned char tmp;
389 unsigned char frame[64];
390 unsigned char* xing;
392 int version;
393 int layer;
394 int bitindex;
395 int bitrate;
396 int freqindex;
397 int frequency;
398 int chmode;
399 int bytecount;
401 long bpf;
402 long tpf;
404 /* Start searching after ID3v2 header */
405 if(-1 == lseek(fd, entry->id3v2len, SEEK_SET))
406 return -1;
408 /* Fill up header with first 24 bits */
409 for(version = 0; version < 3; version++) {
410 header <<= 8;
411 if(!read(fd, &tmp, 1))
412 return -1;
413 header |= tmp;
416 /* Loop trough file until we find a frame header */
417 bytecount = 0;
418 restart:
419 do {
420 header <<= 8;
421 if(!read(fd, &tmp, 1))
422 return -1;
423 header |= tmp;
425 /* Quit if we haven't found a valid header within 128K */
426 bytecount++;
427 if(bytecount > 0x20000)
428 return -1;
429 } while(!mp3frameheader(header));
432 * Some files are filled with garbage in the beginning,
433 * if the bitrate index of the header is binary 1111
434 * that is a good indicator
436 if((header & 0xF000) == 0xF000)
437 goto restart;
439 #ifdef DEBUG_VERBOSE
440 fprintf(stderr,
441 "We found %x-%x-%x-%x and checksync %i and test %x\n",
442 BYTE0(header), BYTE1(header), BYTE2(header), BYTE3(header),
443 CHECKSYNC(header), (header & 0xF000) == 0xF000);
444 #endif
445 /* MPEG Audio Version */
446 switch((header & 0x180000) >> 19) {
447 case 2:
448 version = 2;
449 break;
450 case 3:
451 version = 1;
452 break;
453 default:
454 goto restart;
457 /* Layer */
458 switch((header & 0x060000) >> 17) {
459 case 1:
460 layer = 3;
461 break;
462 case 2:
463 layer = 2;
464 break;
465 case 3:
466 layer = 1;
467 break;
468 default:
469 goto restart;
472 /* Bitrate */
473 bitindex = (header & 0xF000) >> 12;
474 bitrate = bitrate_table[version-1][layer-1][bitindex];
475 if(bitrate == 0)
476 goto restart;
478 /* Sampling frequency */
479 freqindex = (header & 0x0C00) >> 10;
480 frequency = freqtab[version-1][freqindex];
481 if(frequency == 0)
482 goto restart;
484 #ifdef DEBUG_VERBOSE
485 DEBUGF( "Version %i, lay %i, biti %i, bitr %i, freqi %i, freq %i, chmode %d\n",
486 version, layer, bitindex, bitrate, freqindex, frequency, chmode);
487 #endif
488 entry->version = version;
489 entry->layer = layer;
490 entry->frequency = frequency;
492 /* Calculate bytes per frame, calculation depends on layer */
493 switch(layer) {
494 case 1:
495 bpf = bitrate_table[version - 1][layer - 1][bitindex];
496 bpf *= 48000;
497 bpf /= freqtab[version-1][freqindex] << (version - 1);
498 break;
499 case 2:
500 case 3:
501 bpf = bitrate_table[version - 1][layer - 1][bitindex];
502 bpf *= 144000;
503 bpf /= freqtab[version-1][freqindex] << (version - 1);
504 break;
505 default:
506 bpf = 1;
509 /* Calculate time per frame */
510 tpf = bs[layer] / (freqtab[version-1][freqindex] << (version - 1));
512 /* OK, we have found a frame. Let's see if it has a Xing header */
513 if(read(fd, frame, sizeof frame) < 0)
514 return -1;
516 /* Channel mode (stereo/mono) */
517 chmode = (header & 0xc0) >> 6;
519 /* calculate position of Xing VBR header */
520 if ( version == 1 ) {
521 if ( chmode == 3 ) /* mono */
522 xing = frame + 17;
523 else
524 xing = frame + 32;
526 else {
527 if ( chmode == 3 ) /* mono */
528 xing = frame + 9;
529 else
530 xing = frame + 17;
533 if (xing[0] == 'X' &&
534 xing[1] == 'i' &&
535 xing[2] == 'n' &&
536 xing[3] == 'g')
538 /* Yes, it is a VBR file */
539 entry->vbr = true;
541 if (xing[7] & 0x01) /* Is the frame count there? */
543 int framecount = (xing[8] << 24) | (xing[9] << 16) |
544 (xing[10] << 8) | xing[11];
546 filetime = framecount * tpf;
548 if (xing[7] & 0x02) /* is byte count there? */
550 int bytecount = (xing[12] << 24) | (xing[13] << 16) |
551 (xing[14] << 8) | xing[15];
553 bitrate = bytecount * 8 / filetime;
555 /* We don't care about the TOC just yet. Maybe another time. */
558 entry->bitrate = bitrate;
560 /* If the file time hasn't been established, this may be a fixed
561 rate MP3, so just use the default formula */
562 if(filetime == 0)
565 * Now song length is
566 * ((filesize)/(bytes per frame))*(time per frame)
568 filetime = entry->filesize/bpf*tpf;
571 return filetime;
576 * Checks all relevant information (such as ID3v1 tag, ID3v2 tag, length etc)
577 * about an MP3 file and updates it's entry accordingly.
579 * Arguments: entry - the entry to check and update with the new information
581 * Returns: void
583 bool mp3info(struct mp3entry *entry, char *filename)
585 int fd;
586 fd = open(filename, O_RDONLY);
587 if(-1 == fd)
588 return true;
590 memset(entry, 0, sizeof(struct mp3entry));
592 strncpy(entry->path, filename, sizeof(entry->path));
594 entry->title = NULL;
595 entry->filesize = getfilesize(fd);
596 entry->id3v2len = getid3v2len(fd);
597 entry->tracknum = 0;
599 /* Ignore the tag if it is too big */
600 if(entry->id3v2len > sizeof(entry->id3v2buf))
601 entry->id3v2len = 0;
603 if(HASID3V2(entry))
604 setid3v2title(fd, entry);
605 entry->length = getsonglength(fd, entry);
607 entry->id3v1len = getid3v1len(fd);
608 if(HASID3V1(entry) && !entry->title)
609 setid3v1title(fd, entry);
611 close(fd);
613 return false;
616 #ifdef DEBUG_STANDALONE
618 char *secs2str(int ms)
620 static char buffer[32];
621 int secs = ms/1000;
622 ms %= 1000;
623 snprintf(buffer, sizeof(buffer), "%d:%02d.%d", secs/60, secs%60, ms/100);
624 return buffer;
627 int main(int argc, char **argv)
629 int i;
630 for(i=1; i<argc; i++) {
631 struct mp3entry mp3;
632 if(mp3info(&mp3, argv[i])) {
633 printf("Failed to get %s\n", argv[i]);
634 return 0;
637 printf("****** File: %s\n"
638 " Title: %s\n"
639 " Artist: %s\n"
640 " Album: %s\n"
641 " Length: %s / %d s\n"
642 " Bitrate: %d\n"
643 " Frequency: %d\n",
644 argv[i],
645 mp3.title?mp3.title:"<blank>",
646 mp3.artist?mp3.artist:"<blank>",
647 mp3.album?mp3.album:"<blank>",
648 secs2str(mp3.length),
649 mp3.length/1000,
650 mp3.bitrate,
651 mp3.frequency);
654 return 0;
657 #endif
659 /* -----------------------------------------------------------------
660 * local variables:
661 * eval: (load-file "rockbox-mode.el")
662 * end: